Description
We found this file. Recover the flag.
Setup
Download the file tunn3l_v1s10n.
wget <url>/tunn3l_v1s10nSolution
Walk me through it- Step 1Identify the file formatxxd shows the first bytes start with 42 4d (ASCII 'BM'). That's the BMP signature. Rename to .bmp so image viewers will open it.bash
xxd tunn3l_v1s10n | headbashcp tunn3l_v1s10n tunn3l_v1s10n.bmpLearn more
Magic bytes (file signatures) are the first bytes of a file that identify its format, independent of the extension.
42 4Dis ASCII "BM" (BMP). Other common signatures:FF D8 FF(JPEG),89 50 4E 47(PNG),25 50 44 46(%PDF). Thefilecommand uses a database of these signatures to identify files. See hex dumps for CTF for the broader cheat sheet. - Step 2Open the BMP and notice the truncated imageOpen tunn3l_v1s10n.bmp in an image viewer. It shows only a small portion (a decoy message) at the top. The real flag is in the lower portion of the pixel data, hidden because the BMP header lies about the height.
- Step 3Fix the BMP height field in a hex editorBMP stores width at file offset 0x12 and height at 0x16, both 4-byte little-endian signed integers. The current value at 0x16 (32 01 00 00 = 306) is too small. Compute the right value from the file size and width, then patch with xxd or a hex editor.bash
xxd tunn3l_v1s10n.bmp | head -2bash# Sample line at offset 0x10:bash# 00000010: 28 00 00 00 6e 04 00 00 32 01 00 00 01 00 18 00bash# width=0x46e height=0x132 <-- patch thisbashvim -b tunn3l_v1s10n.bmp # then :%!xxd, edit, :%!xxd -r, :wqbash# Or use bless / hexedit for a graphical editorLearn more
BMP header byte offsets (all little-endian):
- 0x00-0x01:
BMsignature - 0x02-0x05: total file size in bytes
- 0x0A-0x0D: pixel data offset
- 0x12-0x15: image width
- 0x16-0x19: image height
- 0x1C-0x1D: bits per pixel
Computing the corrected height. The pixel data occupies
file_size - pixel_data_offsetbytes (typicallyfile_size - 54for a basic BMP). Each row iswidth * (bits_per_pixel / 8)bytes, padded up to a 4-byte boundary. So:pixel_data_size = file_size - pixel_data_offset row_bytes = ((width * bpp + 31) // 32) * 4 # 4-byte aligned height = pixel_data_size // row_bytesFor this challenge: width 0x46e (1134), bpp 24, so
row_bytes = 1134 * 3 = 3402rounded to 3404. Divide the pixel-data size by that and write the result at offset 0x16 in little-endian.Verification. Save and reopen the image. The top should still show the decoy line, but now the bottom rows render and the flag appears as drawn text in the previously hidden region.
- 0x00-0x01:
Flag
picoCTF{...}
BMP headers define the image dimensions. Shrinking the height field hides the bottom portion of the image that contains the real flag. Patch the height at file offset 0x16 to reveal it.