Kita diberikan sebuah file PNG dengan petunjuk:
We received this PNG file, but we're a bit concerned the transmission may have not quite been perfect.Dengan menggunakan pngcheck, kita bisa tahu apa salahnya file ini:
yohanes@ubuntu:~$ pngcheck corrupt_735acee15fa4f3be8ecd0c6bcf294fd4.png
corrupt_735acee15fa4f3be8ecd0c6bcf294fd4.png: CORRUPTED by text conversion
ERROR: corrupt_735acee15fa4f3be8ecd0c6bcf294fd4.png
Header PNG dirancang supaya kita bisa mendeteksi jika ada konversi ASCII ketika mentransfer file. Tapi sayangnya tidak membantu dalam hal recoverynya.
http://www.libpng.org/pub/png/pngintro.html
PNG supports three main types of integrity-checking to help avoid problems with file transfers and the like. The first and simplest is the eight-byte magic signature at the beginning of every PNG image. It will detect the most common type of file corruption: that due to the transfer of a binary file in text (or "ASCII") mode.Dalam kasus ini ketika kita lihat header PNG-nya, tidak cukup untuk memperbaiki headernya saja, karena di dalam data PNG (yang terkompresi), masih ada byte yang tadinya CRLF (0x0d 0x0a) berubah menjadi LF (0x0a). Masalahnya adalah: tidak semua 0x0a tadinya adalah (0x0d 0x0a). Contohnya seperti ini. Jika data asli adalah:
PNG's magic signature cleverly includes both a CR/LF pair and a single LF.
0x0a 0x01 0x02 0x0d 0x0a 0x02 0x0a
Maka setelah konversi CRLF:
0x0a 0x01 0x02 0x0a 0x02 0x0a
Kita tidak tahu 0x0a mana yang harus dikembalikan menjadi 0x0d 0x0a (bisa jadi ada beberapa 0x0a yang harus dikembalikan menjadi 0x0d 0x0a).
Bagaimana kita bisa mengembalikan byte-byte yang benar? file PNG disusun dalam bentuk chunk, seperti ini:
- Length (4 byte)
- Chunk Type (4 byte)
- Chunk Data (N byte, sesuai dengan length)
- CRC (4 byte, CRC sesuai algoritma di sini)
Data PNG ada dalam chunk IDAT, dalam file soal ada 10 IDAT yang sebagian besar corrupt. Perhatikan bahwa karena konversi CRLF, maka kita tidak bisa memparsing menggunakan LENGTH, karena datanya akan bergeser ketika CRLF berubah menjadi LF.
Kode yang saya pakai untuk menyelesaikan agak berantakan (Karena ini adalah CTF dan ini satu-satunya soal yang sempat saya selesaikan sebelum perjalanan panjang). Saya punya dua kode, pertama dalam python untuk mensplit PNG menjadi chunk IDAT, dan kode dalam C (supaya cepat) untuk memperbaiki CRC-nya.
Kedua kode ini sebenarnya bisa digabung supaya semuanya otomatis. Skrip python juga mengoutputkan HEADER dan FOOTER supaya bisa digabung lagi hasil akhirnya. Kelebihan menggunakan cara terpisah adalah: saya bisa copy chunk-nya ke komputer lain dan menjalankan perbaikan untuk chunk itu saja.
Bagian parsing IDAT dalam python hanya mencari string IDAT dan ditulis ke file:
Di situ saya juga memprint padding, berapa byte yang berubah dari yang seharusnya. Setelah tahu berapa byte yang berubah tiap blok, saya membuat program dalam C untuk memperbaiki chunk IDAT tersebut.
Cara perbaikannya adalah sebagai berikut: kita tahu berapa jumlah 0x0a (misalnya N) di dalam sebuah chunk IDAT (bisa dihitung dengan melihat bytenya), kita tahu berapa 0x0d0x0a yang berubah menjadi 0x0a (dari jumlah padding di skrip python, misalnya K), kita tahu CRC dari tiap blok IDAT.
Kita bisa mencoba-coba memilih kombinasi K dari N. Untuk IDAT tertentu, hanya 1 byte dari sekitar 400 byte yang perlu diubah (bisa dicoba-coba manual kalau rajin), untuk IDAT-4, ada 459 byte 0x0a, dan paddingnya 3, artinya ada lebih dari 16 juta kombinasi, tidak mungkin diperbaiki manual.
Berikut ini source saya untuk memperbaiki IDAT dengan mencoba semua kombinasi dan mengecek crcnya:
Setelah selesai, saya gabungkan lagi HEADER, IDAT, dan FOOTER-nya.
Hasilnya adalah gambar berikut:
flag{have_a_wonderful_starcrafts}