문제풀이
40자리 해시가 있고 password를 입력해서 제출하는 구성이다.
새로고침을 할 때마다 해시값이 바뀌는 것을 확인할 수 있다.
조건에 맞는 password를 입력하면 문제가 해결되는 것 같다.
코드를 분석해보자.
<?php
sleep(1); // anti brute force
if((isset($_SESSION['chall4'])) && ($_POST['key'] == $_SESSION['chall4'])) solve(4);
$hash = rand(10000000,99999999)."salt_for_you";
$_SESSION['chall4'] = $hash;
for($i=0;$i<500;$i++) $hash = sha1($hash);
?>
1) key(password)가 chall4 세션과 동일하면 문제가 해결된다.
2) [10000000~99999999 사이 랜덤값] + "salt_for_you"을 $hash 변수에 저장하고 chall4 세션에 저장한다.
3) $hash를 500번 sha1 해싱하고 결과를 화면에 출력한다.
정리하면, 처음에 화면에 출력된 40자리 해시는 500번 해싱한 결과다. 이 값을 이용해서 500번 해싱하기 전의 값인 평문을 알아내야 하는 문제이다.
쉽게 말하면 역으로 복호화해서 값을 알아내야 한다.
문제는 해시 암호는 단방향 암호화 기법이기 때문에 복호화할 수 없다.
그렇다면 어떻게 값을 구할 수 있을까?
→ 레인보우 테이블을 이용하면 문제를 해결할 수 있다.
○ 레인보우 테이블
해시함수(MD-5, SHA-1, SHA-2 등)를 사용하여 만들어낼 수 있는 값들을 대량으로 저장한 표이다.
이번 문제는 대략 90000000만개의 랜덤값 중 하나를 골라서 500번 해싱한다. 따라서 90000000만개의 랜덤값을 500번 해싱한 결과를 모두 저장한 레인보우 테이블을 직접 생성하고 결과와 일치하는 평문을 찾으면된다.
레인보우 테이블을 직접 만들어보자.
from hashlib import *
from multiprocessing import *
def create(num):
start = 10000000*num
end = 10000000*(num+1)
f = open('table.csv', mode = 'w', encoding = 'utf-8')
for i in range(start, end):
txt = str(i) + "salt_for_you"
result = txt
for j in range(0, 500):
result = sha1(result.encode('utf-8')).hexdigest()
print(result)
f.write(f'{txt} ======> result : {result}\n')
f.close()
if __name__ == '__main__':
procs = []
for i in range(1, 10):
proc = Process(target = create, args = (i,))
procs.append(proc)
proc.start()
for proc in procs:
proc.join()
쓰레드를 10개 사용했지만 시간이 너무 오래걸렸다. (파일에 많은 데이터가 저장되다보니 txt 파일로는 오픈하면 렉 걸려서 csv 파일로 저장했다.)
오랜 시간 끝에 완성이 됐고 화면에 출렫된 해시값을 검색해보자.
검색한 해시와 일치하는 평문을 찾을 수 있다.
전송해보자.