CTF Write-up: The Mosaic
Category: Misc
CTF Write-up: The Mosaic
Category: Misc
Difficulty: Medium

Chall
Deskripsi Tantangan
Diberikan 100 file gambar (potongan puzzle) dengan nama acak. Deskripsi soal menyebutkan tentang “labels on the back of the canvas” dan tugas untuk memperbaiki “masterpiece” yang pecah sebelum pameran dibuka. Salah satu file gambar memiliki comment: “Reconstruct the timeline to find the truth.”
Analisis & Percobaan Gagal (Trial & Error)
Sebelum menemukan solusi yang tepat, kami menghadapi beberapa jalan buntu dan jebakan yang disiapkan oleh pembuat soal:
- Jebakan “Timeline” (Waktu Modifikasi):
- Ide: Berdasarkan petunjuk “Reconstruct the timeline”, kami mencoba menyusun gambar dengan mengurutkan file berdasarkan waktu modifikasi (timestamp).
- Masalah: Setelah dijalankan, hasilnya adalah gambar static noise yang acak. Setelah diperiksa, ternyata ke-100 file tersebut memiliki timestamp yang identik, sehingga pengurutan berdasarkan waktu mustahil dilakukan. Petunjuk ini kemungkinan besar adalah red herring atau merujuk pada urutan data internal, bukan waktu file.
- Kesalahan Parsing Metadata:
- Ide: Kami menyadari adanya metadata posisi pada gambar.
- Masalah: Script awal kami mencoba membaca tag bernama
FragID. Script gagal dan menghasilkan peta kosong. - Solusi: Setelah menggunakan perintah
exiftool -s, kami menemukan bahwa nama tag yang benar adalahFrag_ID(menggunakan underscore).
- Jebakan “Time Waste” pada Binary:
- Masalah: Setelah berhasil mengekstrak file biner (
data.bin) dari gambar dan menjalankannya, program tersebut berjalan sangat lambat (print satu karakter per detik). Ini adalah teknik anti-debugging sederhana (menggunakansleep) untuk membuang waktu peserta. Kami memutuskan untuk membedah binary tersebut daripada menunggunya.
Langkah Penyelesaian (Solusi Valid)
1. Ekstraksi Metadata yang Benar
Setelah mengetahui nama tag yang valid, kami membuat peta koordinat menggunakan exiftool untuk mengekstrak Frag_ID dari semua file:
exiftool -p '$Frag_ID|$FileName' -q -ext png . > map_final.txt
Format output map adalah X|Y|NamaFile (contoh: 5|6|zbcb9rjm.png).
2. Rekonstruksi Gambar (Puzzle Reassembly)
Kami menggunakan script Python untuk menyusun potongan-potongan tersebut menjadi satu gambar utuh berdasarkan koordinat dari map_final.txt.
Script Penyusun (solve_puzzle.py):
import os
from PIL import Image
TILE_SIZE = 50
CANVAS_SIZE = 550 # Ukuran otomatis berdasarkan koordinat max
canvas = Image.new('RGB', (CANVAS_SIZE, CANVAS_SIZE))
with open('map_final.txt', 'r') as f:
for line in f:
parts = line.strip().split('|')
if len(parts) >= 3:
x, y, filename = int(parts[0]), int(parts[1]), parts[2]
if os.path.exists(filename):
img = Image.open(filename)
canvas.paste(img, (x * TILE_SIZE, y * TILE_SIZE))
canvas.save("flag_reconstructed.png")
Hasil: Gambar yang dihasilkan (flag_reconstructed.png) terlihat seperti static noise (semut TV) yang sangat padat dan berwarna-warni. Dalam konteks CTF, pola noise yang padat seperti ini biasanya mengindikasikan Visual Cryptography atau Raw Data (Pixel-to-Bytes), bukan gambar rusak.
3. Ekstraksi Data (Pixel to Binary)
Karena curiga gambar tersebut adalah data mentah, kami mengekstrak setiap pixel RGB kembali menjadi urutan bytes.
Script Ekstraksi:
from PIL import Image
img = Image.open("flag_reconstructed.png").convert("RGB")
data = img.tobytes()
# Menyimpan hasil ekstraksi sebagai data.bin
with open("data.bin", "wb") as f:
f.write(data)
Kami memeriksa jenis file yang dihasilkan:
$ file data.bin
data.bin: ELF 64-bit LSB executable, x86-64, ...
Ternyata benar, gambar tersebut menyembunyikan sebuah program Linux (ELF Binary).
4. Reverse Engineering Binary
Seperti disebutkan di bagian “Analisis”, program data.bin ini memiliki jebakan waktu (sleep). Kami melakukan static analysis menggunakan objdump untuk melihat logika aslinya tanpa menjalankan program.
objdump -d -M intel data.bin | grep -A 500 "<main>:"
Temuan Penting dari Assembly:
- XOR Key: Terdapat instruksi
mov DWORD PTR [rbp-0x38], 0x42. Ini menunjukkan kunci XOR adalah0x42. - Data Terenkripsi: Terdapat beberapa instruksi
movabsyang memindahkan nilai heksadesimal panjang (8 byte) ke registerraxdanrdx. Ini adalah potongan-potongan flag yang terenkripsi.
5. Final Solver (Bypass Program)
Daripada melakukan patching binary untuk menghilangkan sleep, kami membuat script Python untuk membaca data terenkripsi langsung dari file data.bin dan mendekripsinya secara manual menggunakan kunci 0x42.
Script Solver Final (solve_final.py):
import struct
filename = "data.bin"
def extract_real_flag():
print(f"[*] Membuka file {filename}...")
with open(filename, "rb") as f:
content = f.read()
# 1. Cari Pola Data Enkripsi (Signature Based)
# Hex dari instruksi movabs pertama: 0x1d313673392f3632
# Karena Little Endian, urutan byte di file dibalik:
target_pattern = b'\x32\x36\x2f\x39\x73\x36\x31\x1d'
data_offset = content.find(target_pattern)
if data_offset == -1:
print("Pola tidak ditemukan!")
return
# 2. Ambil Data Berdasarkan Offset Relatif (Dihitung dari objdump)
# Jarak antar blok data sesuai struktur instruksi assembly
block1 = content[data_offset : data_offset + 8]
block2 = content[data_offset + 10 : data_offset + 18]
block3 = content[data_offset + 28 : data_offset + 36]
block4 = content[data_offset + 38 : data_offset + 46]
block5 = content[data_offset + 57 : data_offset + 61] # Sisa 3 byte terakhir
# Ambil Key (0x42) yang berada 64 byte dari awal data
xor_key = content[data_offset + 64]
# 3. Gabungkan & Decrypt
full_encrypted_data = block1 + block2 + block3 + block4 + block5[:3]
# Lakukan XOR
flag = "".join([chr(b ^ xor_key) for b in full_encrypted_data])
print(f"FLAG: {flag}")
if __name__ == "__main__":
extract_real_flag()
Hasil Akhir
Setelah menjalankan script solver, flag berhasil didapatkan secara instan.

Flag:
ptm{1ts_ju5t_pngs_4ll_th3_w4y_d0wn} 메타데이터
- post_id
- de88cc040ffd
- slug
- ctf-write-up-the-mosaic-de88cc040ffd
- url
- https://medium.com/@rsafei731/ctf-write-up-the-mosaic-de88cc040ffd
- canonical_url
- https://medium.com/@rsafei731/ctf-write-up-the-mosaic-de88cc040ffd
- author_url
- https://medium.com/@rsafei731
- status
- ok
- fetched_at
- 2026-06-20 20:29:01