capture the :flag:
LSB steganography

Description

It's always in the place you least expect
Hints:
    LSB
    RGB
    If you have found something previously, try looking again
    Remember to get the full image
author: spamakin

Solution

The EXIF data on the image contains an interesting description.
1
$ exiftool flag.png
2
​
3
ExifTool Version Number : 12.26
4
File Name : flag.png
5
Directory : .
6
File Size : 2.4 KiB
7
File Modification Date/Time : 2021:07:31 22:05:15+08:00
8
File Access Date/Time : 2021:07:31 22:06:10+08:00
9
File Inode Change Date/Time : 2021:07:31 22:06:09+08:00
10
File Permissions : -rw-r--r--
11
File Type : PNG
12
File Type Extension : png
13
MIME Type : image/png
14
Image Width : 120
15
Image Height : 120
16
Bit Depth : 8
17
Color Type : RGB with Alpha
18
Compression : Deflate/Inflate
19
Filter : Adaptive
20
Interlace : Noninterlaced
21
Description : LSBs(Pixels[1337:])
22
Image Size : 120x120
23
Megapixels : 0.014
Copied!
It says LSBs(Pixels[1337:]). Hmm... maybe it's telling us to get the LSBs of everything after the 1337th pixel.
My initial method was to go row by row, left to right (like reading English).
1
list(im.getdata())[1337:]
Copied!
This did not yield any meaningful results.
Plugging it into a steganography tool, like StegOnline, helps us to figure out what's going on. In all of the 0th-bit (LSB) planes, there appeared to be some data hidden on the flag pole.
That explains! The pixels were meant to be read column-by-column instead.
Let's rearrange the pixels array to go column-by-column:
1
from PIL import Image
2
​
3
pix_val = []
4
​
5
with Image.open('flag.png') as secret:
6
width, height = secret.size
7
for x in range(width):
8
for y in range(height):
9
pixel = list(secret.getpixel((x, y)))
10
pix_val.append(pixel)
11
​
12
pixels = pix_val[1337:]
Copied!
Now, we can read the LSBs of each pixel to get the hidden data.
1
result = ''
2
for pixel in pixels:
3
​
4
for byte in pixel[:3]:
5
6
if byte & 1:
7
result += '1'
8
else:
9
result += '0'
10
​
11
if len(result) == 8:
12
13
if result == '0' * 8:
14
result = ''
15
continue
16
​
17
char = chr(int(result, 2))
18
print(char, end='')
19
​
20
result = ''
Copied!
We can see the flag right at the beginning.
​
Last modified 2mo ago
Copy link