본문 바로가기

WarGame/webhacking.kr

[webhacking.kr] Challenge 55 :: Blind SQL Injection 2


▼ Challenge 55는 다음과 같습니다.



마우스 포인트 움직임에 따라 Score가 올라가고, Image와 X좌표, Y좌표가 일치하면 "Game Over"가 뜨면서 Score가 저장됩니다.


 rank.php는 다음과 같습니다.



score 부분의 Link를 클릭하면 "?score=2147483647" Query를 전송하면서 상단에 id와 score가 출력됩니다.


처음 문제를 풀 때, JavaScript를 조작하여 높은 점수를 받는 것인지 알았지만, 

Proxy Tool을 이용하여 높은 점수를 보내봐도 Rank 테이블에 변화가 없으므로, 이러한 방식은 아닌 것 같습니다.


 rank.php 소스코드의 hint라는 주석문이 있고, 그 내용은 다음과 같습니다.



rank라는 테이블이 있고, 그 안에 ip(=id), score, password라는 Column명이 있다는 hint인 것 같고, 

"password가 소문자로만 이뤄져 있다?"라는 말인 것 같습니다.

그렇다면 password Column을 출력하면 문제가 해결될 것 같습니다.


 문제해결 방법은 다음과 같습니다.

            1. Injection Vector 찾아내기
            2. Column명 알아내기
            3. Blind SQL Injection을 사용하여 password 알아내기


1. Injection Vector 찾아내기

"?score=2147483647" 이 부분으로 SELECT를 하고 있는 것으로 보아,

score 파라미터가 Injection Vector라는 것을 추측할 수 있습니다.


 SELECT 구문을 추측하면 다음과 같습니다.


1
SELECT id, score, password FROM Table명 WHERE score=$score
cs


score에 주석문을 삽입하여 뒷 부분의 Single Quote를 없애봐도 그대로 출력되는 것을 보니,

 INT Type으로 Quote로 묶여 있지 않다는 것을 예측해 볼 수 있습니다.


필터링이 되어 있는 것을 확인하기 위해, "union, select, from, char, (), 0x, and, or ..." 등을 입력해 봅니다.

일단 "union, select, from ,char"는 필터링되어 있고 "(), 0x, and, or"은 사용가능합니다.


2. Column명 알아내기

"union, select, from" 문자가 필터링되어 Table명은 필요없지만, 정확한 Column명을 알아낼 필요가 있습니다.

마침 Challenge 53(http://limjunyoung.tistory.com/97)에서 "procedure analyse()" 라는 함수를 알아보았는데,

 이것을 사용해 보도록 하겠습니다.


Input]     ?score=21474836470 procedure analyse()

Output]     id : oldzombie.challenge55_game.ip //


가장 상단의 값만 출력이 되는 것 같습니다. 일단 DB명, Table명, ip라는 Column명을 알아냈습니다.


limit를 사용하여 다음 Column명도 알아내도록 하겠습니다. 

( limit에 대한 설명은 Challenge18(http://limjunyoung.tistory.com/48) 참고 )


Input]     ?score=21474836470 limit 1,1 procedure analyse()

Output]     id : oldzombie.challenge55_game.score //


Input]     ?score=21474836470 limit 2,1 procedure analyse()

Output]     id : oldzombie.challenge55_game.pAsSw0RdzzzZ //


이것으로 "pAsSw0RdzzzZ"라는 Column명을 알아냈습니다.


3. Blind SQL Injection을 사용하여 password 알아내기

password가 직접 출력되지는 않으므로, True일 때 id가 출력되고, False 일때 id가 출력되지 않는 것을 이용하여

Blind SQL Injection으로 "pAsSw0RdzzzZ" Column의 값을 추측해야 할 것 같습니다.


?score=21474836470 and substr(pAsSw0RdzzzZ,1,1)>0x61 라고 입력하면서 찾으면 쉽겠지만, 

substr() 함수도 필터링되어 있습니다. ( char() 함수가 필터링 되어 있으므로, 0x 즉 Hex 값을 이용하여 해결 )


substr() 필터링 우회에 대한 힌트는 rubiya님의 글(http://hackerschool.org/Sub_Html/HS_Posting/?uid=43)에서 얻었습니다.


left()와 right()함수를 통해서 문자열을 짜를수 있습니다.

        • left($string, length) : 문자열의 왼쪽에서 부터 명시된 길이 만큼 반환

        • right($string, length)문자열의 오른쪽에서 부터 명시된 길이 만큼 반환


?score=2147483647 and right(left(pAsSw0RdzzzZ,1),1)>0x61 라고 입력하지만 결과가 False 입니다.

계속 Blind 해보니, 0x20이라는 값에서 True가 나옵니다. Null인 것 같습니다. 

모든 id에 password가 저장되어 있는 것이 아니라, 특정 id에만 저장되어 있는 것 같습니다.


?score=없는 번호 or 1 을 입력하면 id : localhost // 가 출력됩니다. 

이것을 이용하여 id가 localhost인 Record의 password를 알아내야겠다는 추측을 할 수 있습니다.


magic_quote_gpc로 Signle Quote를 사용할 수 없으므로 Hex값을 이용하여 문자열을 입력해야 합니다.


다음과 같이 입력하면 True가 나옵니다.

?score=없는 번호 or ip=0x6C6F63616C686F7374 and right(left(pAsSw0RdzzzZ,1),1)>0x61


본격적으로 Blind를 시작하기 전에, password의 길이를 알아볼 필요가 있습니다. 

right(left(pAsSw0RdzzzZ,21),1)라고 입력하면, 

만약 실제로는 20자리 밖에 없지만 계속해서 끝자리의 값이 출력이 되는 것을 확인할 수 있습니다.


그럼으로 MySQL의 length() 함수를 사용하여 자리수를 파악해야 합니다.

?score=없는 번호 or ip=0x6C6F63616C686F7374 and length(pAsSw0RdzzzZ)=20 라고 입력하면 True가 나오므로, 

password가 20자리인 것을 확인 할 수 있습니다.


이제 본격적으로 Blind를 진행할 수 있습니다.

?score=없는 번호 or ip=0x6C6F63616C686F7374 and right(left(pAsSw0RdzzzZ,2),1)>0x61

?score=없는 번호 or ip=0x6C6F63616C686F7374 and right(left(pAsSw0RdzzzZ,3),1)>0x61

.

.

.


이런식으로 password를 한 글자씩 알아내면 Success!!