Почувствуй радугу (Feel the Rainbow)
Почувствуй радугу (Feel the Rainbow)
Platform: bug-makers | Category: Reversing | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-05-27 | Status: Solved Techniques: disassembly_hash_order_recovery, per_character_md5_reversal, rainbow_table_attack
Summary
Task: C++ ELF binary that checks each input character by computing its MD5 hash and comparing against 24 hardcoded hashes (17 unique). Solution: build rainbow table for single-byte MD5, recover full 24-hash sequence from main() disassembly to handle repeated characters.
Почувствуй радугу (Feel the Rainbow) — Bug Makers CTF
Description
Больше хешей – надежнее. Или нет? (More hashes – more secure. Or not?)
English summary: given a C++ ELF binary md5_flag_checker that reads user input and validates it character-by-character using MD5 hashes. The goal is to reverse the hashes and recover the flag.
Analysis
Binary reconnaissance
$ file md5_flag_checker
ELF 64-bit LSB pie executable, x86-64, dynamically linked, not stripped
Key symbols from the symbol table:
check_char_md5— takes a single character and an expected hash, computes MD5 of the character and comparescalculate_md5— standard MD5 implementationMD5_Transform— MD5 block transform function
The MD5 implementation uses standard initialization constants (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476), confirming it is unmodified MD5.
Algorithm
- Binary reads user input and expects exactly 24 characters
- For each character at position
i, it callscheck_char_md5(input[i], expected_hash[i]) - The function computes
MD5(single_byte)and compares the hex digest against the hardcoded expected hash - If all 24 characters match, the input is accepted as the correct flag
The trap: unique vs. total hashes
Running strings on the binary reveals only 17 unique 32-character hex strings in the .rodata section. However, the flag is 24 characters long, meaning some hashes are reused for repeated characters.
Extracting only unique hashes and building a set loses the ordering and duplicates, producing a wrong result like BugCTF{r3v_h4sck} (17 chars). The correct approach requires disassembling main() to recover the exact sequence of 24 hash pointer references.
Hash address references from main() disassembly
The 24 lea instructions in main() reference hash strings at these addresses:
0x6168, 0x6190, 0x61b8, 0x61e0, 0x6208, 0x6230, 0x6258, 0x6280,
0x62a8, 0x62d0, 0x62f8, 0x6320, 0x6348, 0x6370,
0x6320, 0x62f8, ← repeats: h, _
0x6398, 0x6280, 0x6348, 0x6398, 0x63c0, 0x62a8, 0x6280, 0x63e8
Note addresses 0x6320 (h), 0x62f8 (_), 0x6280 (r), 0x6348 (4), 0x6398 (c), 0x62a8 (3) each appear multiple times.
Solution
Step 1: Extract all 24 hashes in order
From the disassembly, the ordered hash sequence is:
| # | Hash | Char |
|---|---|---|
| 1 | 9d5ed678fe57bcca610140957afab571 | B |
| 2 | 7b774effe4a349c6dd82ad4f4f21d34c | u |
| 3 | b2f5ff47436671b6e533d8dc3614845d | g |
| 4 | 0d61f8370cad1d412f80b84d143e1257 | C |
| 5 | b9ece18c950afbfa6b0fdbfa4ff731d3 | T |
| 6 | 800618943025315f869e4e1f09471012 | F |
| 7 | f95b70fdc3088560732a5ac135644506 | { |
| 8 | 4b43b0aee35624cd95b910189b3dc231 | r |
| 9 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 3 |
| 10 | 9e3669d19b675bd57058fd4664205d2a | v |
| 11 | b14a7b8059d9c055954c92674ce60032 | _ |
| 12 | 2510c39011c5be704182423e3a695e91 | h |
| 13 | a87ff679a2f3e71d9181a67b7542122c | 4 |
| 14 | 03c7c0ace395d80182db07ae2c30f034 | s |
| 15 | 2510c39011c5be704182423e3a695e91 | h |
| 16 | b14a7b8059d9c055954c92674ce60032 | _ |
| 17 | 4a8a08f09d37b73795649038408b5f33 | c |
| 18 | 4b43b0aee35624cd95b910189b3dc231 | r |
| 19 | a87ff679a2f3e71d9181a67b7542122c | 4 |
| 20 | 4a8a08f09d37b73795649038408b5f33 | c |
| 21 | 8ce4b16b22b58894aa86c421e8759df3 | k |
| 22 | eccbc87e4b5ce2fe28308fd9f2a7baf3 | 3 |
| 23 | 4b43b0aee35624cd95b910189b3dc231 | r |
| 24 | cbb184dd8e05c9709e5dcaedaa0495cf | } |
| |
Step 2: Build rainbow table and reverse
python read-only • copy allowed#!/usr/bin/env python3 import hashlib # All 24 hashes in order from main() disassembly hashes = [ '9d5ed678fe57bcca610140957afab571', # B '7b774effe4a349c6dd82ad4f4f21d34c', # u 'b2f5ff47436671b6e533d8dc3614845d', # g '0d61f8370cad1d412f80b84d143e1257', # C 'b9ece18c950afbfa6b0fdbfa4ff731d3', # T '800618943025315f869e4e1f09471012', # F 'f95b70fdc3088560732a5ac135644506', # { '4b43b0aee35624cd95b910189b3dc231', # r 'eccbc87e4b5ce2fe28308fd9f2a7baf3', # 3 '9e3669d19b675bd57058fd4664205d2a', # v 'b14a7b8059d9c055954c92674ce60032', # _ '2510c39011c5be704182423e3a695e91', # h 'a87ff679a2f3e71d9181a67b7542122c', # 4 '03c7c0ace395d80182db07ae2c30f034', # s '2510c39011c5be704182423e3a695e91', # h (repeat) 'b14a7b8059d9c055954c92674ce60032', # _ (repeat) '4a8a08f09d37b73795649038408b5f33', # c '4b43b0aee35624cd95b910189b3dc231', # r (repeat) 'a87ff679a2f3e71d9181a67b7542122c', # 4 (repeat) '4a8a08f09d37b73795649038408b5f33', # c (repeat) '8ce4b16b22b58894aa86c421e8759df3', # k 'eccbc87e4b5ce2fe28308fd9f2a7baf3', # 3 (repeat) '4b43b0aee35624cd95b910189b3dc231', # r (repeat) 'cbb184dd8e05c9709e5dcaedaa0495cf', # } ] # Build rainbow table: MD5(single_byte) -> character lookup = {} for i in range(32, 127): c = chr(i) h = hashlib.md5(c.encode()).hexdigest() lookup[h] = c # Reverse all 24 hashes in order flag = ''.join(lookup.get(h, '?') for h in hashes) print(flag) # BugCTF{REDACTED}