[webhacking.kr] old-4 문제

2026. 2. 2. 15:10·워게임/webhacking.kr

1. 문제 정보

  • 문제 번호 : old -4
  • 문제 점수 : 30점

 


2. 문제 분석

문제를 보면dd156f7cf80164fd8afbc11c95d0e33e237d0275

 

가 있고 password를 제출하는 칸이 보인다.

 

코드를 확인해보자. 

<?php
  include "../../config.php";
  if($_GET['view-source'] == 1) view_source();
?><html>
<head>
<title>Challenge 4</title>
<style type="text/css">
body { background:black; color:white; font-size:9pt; }
table { color:white; font-size:10pt; }
</style>
</head>
<body><br><br>
<center>
<?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);
?><br>
<form method=post>
<table border=0 align=center cellpadding=10>
<tr><td colspan=3 style=background:silver;color:green;><b><?=$hash?></b></td></tr>
<tr align=center><td>Password</td><td><input name=key type=text size=30></td><td><input type=submit></td></tr>
</table>
</form>
<a href=?view-source=1>[view-source]</a>
</center>
</body>
</html>

 

가장 중요한 부분은 천만~1억 사이의 숫자에 salt_for_you 더 한값을 session값에 저장하고  sha1을 으로 500번 더한 값을 $hash로 나타낸다. 그 값이 처음 문제에 나온 값이다.

dd156f7cf80164fd8afbc11c95d0e33e237d0275

 

게다가 한번 잘못 요청하면 값이 바뀐다.

 


3. 풀이

이 문제는 90000000번 동안 루프를 돌아 맞는 값을 찾을 수 도 있지만 메모리가 너무 많이 든다.

따라서 10000000개의 테이블을 만들고 시행 횟수를 늘려서 찾는 방법을 사용했다.

 

그리고 단일 cpu로 하면 오래 걸리기 때문에 멀티 cpu를 사용했다.

 

 

코드로 짜보면 다음과 같다.

import hashlib
import multiprocessing
import requests
import re
import sys
import time
import os

# ==========================================
# 1. Configuration
# ==========================================
URL = "https://webhacking.kr/challenge/web-04/"
TABLE_FILE = "rainbow_table.txt" # File name to save/load

# [IMPORTANT] Update PHPSESSID
COOKIES = {
    "PHPSESSID": "" 
}

HEADERS = {
    "User-Agent": ",
    "Content-Type": ""
}

# Range settings
START_NUM = 10000000
TOTAL_COUNT = 10000000 

# ==========================================
# 2. Worker Function
# ==========================================
def generate_rainbow_table(r_start, r_end):
    local_table = {}
    for number in range(r_start, r_end):
        data = str(number) + "salt_for_you"
        temp_hash = data
        for _ in range(500):
            temp_hash = hashlib.sha1(temp_hash.encode()).hexdigest()
        local_table[temp_hash] = number
    return local_table

# ==========================================
# 3. Main Logic
# ==========================================
if __name__ == '__main__':
    multiprocessing.freeze_support()
    rainbow_table = {}

    # [Step 1] Check if table file exists
    if os.path.exists(TABLE_FILE):
        print(f"[-] '{TABLE_FILE}' found. Loading data from file...")
        start_time = time.time()
        
        # Load from file
        try:
            with open(TABLE_FILE, "r") as f:
                for line in f:
                    # Format: hash,number
                    parts = line.strip().split(",")
                    if len(parts) == 2:
                        rainbow_table[parts[0]] = parts[1]
            
            end_time = time.time()
            print(f"[+] Load complete! (Total {len(rainbow_table)} entries)")
            print(f"[-] Loading time: {end_time - start_time:.2f} seconds")
            
        except Exception as e:
            print(f"[!] Error loading file: {e}")
            sys.exit()

    else:
        # [Step 2] If file doesn't exist, generate it
        print(f"[-] '{TABLE_FILE}' not found.")
        print("[-] Starting Rainbow Table generation using all CPU cores...")
        
        cpu_count = multiprocessing.cpu_count()
        print(f"[-] Available CPU cores: {cpu_count}")

        chunk_size = TOTAL_COUNT // cpu_count
        ranges = []
        for i in range(cpu_count):
            s = START_NUM + (i * chunk_size)
            e = s + chunk_size
            if i == cpu_count - 1:
                e = START_NUM + TOTAL_COUNT
            ranges.append((s, e))

        start_time = time.time()
        with multiprocessing.Pool(processes=cpu_count) as pool:
            results = pool.starmap(generate_rainbow_table, ranges)
        
        print("[-] Merging data in memory...")
        for partial_table in results:
            rainbow_table.update(partial_table)

        # Save to file
        print(f"[-] Saving data to '{TABLE_FILE}' for future use...")
        with open(TABLE_FILE, "w") as f:
            for h, n in rainbow_table.items():
                f.write(f"{h},{n}\n")
        
        end_time = time.time()
        print(f"[+] Generation & Save complete! (Total {len(rainbow_table)} entries)")
        print(f"[-] Time elapsed: {end_time - start_time:.2f} seconds")


    # [Step 3] Attack Loop
    print("---------------------------------------------------")
    print("[-] Starting server attack... (Press Ctrl+C to stop)")

    attempt = 0
    with requests.Session() as session:
        while True:
            attempt += 1
            try:
                response = session.post(URL, headers=HEADERS, cookies=COOKIES, data={'key': ''})
                match = re.search(r"<b>([a-f0-9]{40})</b>", response.text)
                
                if match:
                    server_hash = match.group(1)
                    sys.stdout.write(f"\r[*] Attempt {attempt} | Received Hash: {server_hash[:10]}...")
                    sys.stdout.flush()

                    if server_hash in rainbow_table:
                        answer = rainbow_table[server_hash]
                        print(f"\n\n[★] HIT! Found answer in table: {answer}")
                        
                        print("[-] Submitting answer...")
                        payload = {'key': answer}
                        final_res = session.post(URL, headers=HEADERS, cookies=COOKIES, data=payload)
                        
                        if "pwned" in final_res.text or "alert" in final_res.text or "solve" in final_res.text:
                            print("\n[SUCCESS] Challenge Solved!")
                            print("▼▼▼ Server Response ▼▼▼")
                            print(final_res.text[:500])
                        else:
                            print("\n[?] Answer submitted but success message not confirmed.")
                            print("Please check the website manually.")
                        
                        break
                    
                else:
                    print("\n[!] Hash not found. Check PHPSESSID.")
                    break

            except requests.exceptions.RequestException as e:
                print(f"\n[!] Connection Error: {e}")
                time.sleep(1)
            except KeyboardInterrupt:
                print("\n[!] Stopped by user.")
                break

 

테이블을 만든 로그약 10분 걸렸다.

 

그리고 시도를 해보면

 

약 10%정도 확률로 맞출 수 있다.


4. 요약

 

  • 1000만에서 9999만 사이의 랜덤 숫자와 솔트를 조합해 SHA1으로 500번 해시한 값을 역추적해야 하며 매 요청마다 값이 변하는 문제다.
  • 전체 9천만 개의 데이터를 모두 계산하는 비효율을 줄이기 위해 멀티프로세싱을 활용하여 약 1천만 개의 레인보우 테이블을 생성하고 파일로 저장했다.
  • 생성된 테이블 내의 해시값이 서버에서 출제될 때까지 반복 요청하는 확률적 공격을 수행하여 약 10퍼센트의 확률로 정답을 찾아 해결했다.

 

 

 

 

 

'워게임 > webhacking.kr' 카테고리의 다른 글

[webhacking.kr] old-7문제  (0) 2026.02.03
[webhacking.kr] old-11 문제  (0) 2026.02.02
[webhacking.kr] old-56 문제  (0) 2026.02.02
[webhacking.kr] old-59 문제  (0) 2026.02.02
[webhacking.kr] old-61 문제  (0) 2026.02.02
'워게임/webhacking.kr' 카테고리의 다른 글
  • [webhacking.kr] old-7문제
  • [webhacking.kr] old-11 문제
  • [webhacking.kr] old-56 문제
  • [webhacking.kr] old-59 문제
yt_5246
yt_5246
yt5246 님의 블로그 입니다.
  • yt_5246
    yt의 공부 블로그
    yt_5246
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (61)
      • IT (1)
      • Security (11)
        • 시스템해킹 (3)
        • 리버싱 (6)
        • 암호학 (0)
        • tools (2)
      • Book (0)
      • 자격증 (3)
      • 워게임 (46)
        • DVWA (7)
        • WebGoat (4)
        • webhacking.kr (35)
      • 버그바운티 (0)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
yt_5246
[webhacking.kr] old-4 문제
상단으로

티스토리툴바