Cybersocks Regional 2016 - color writeup

color

the challenge was given as a 300x300 png file. the usual drill for an image challenge is to check the exif data. but everything was perfectly fine. opening the file with stegsolve, it was visible quite soon that there are some data embedded in the LSB (first bitplane) for every channel

stegsolve

unfortunately, using built-in data extract function in stegsolve will only produce some garbage values. bummer

extract

since i was certain the flag was embeded in the LSB. let’s extract the data manually (or well, ‘scriptally’). yeah, sure there are existing tools that can extract the data (like zsteg ;p). but for the sake of knowledge, i’ll write this through.

LSB steganography

before we jump to the extracting part, let’s talk how the data is embeded in each pixel. well a pixel is the simplest unit in data imagery and each of it can be expressed further in terms of channels. there are many color models (RGB, CYMK, etc.) available out there and each of these are made of combinations of primary color channels – in our case, each pixel is represented by red channel, green channel, blue channel and alpha channel (stores transparency information) which also known as RGBA. this individual channel are represented in a whole number (up to 255) which can be expressed further in 8 bits (example: 255 = 0b11111111)

pixel

every bits in the number represent a “bit plane” with the least significant bit representing the first bit plane, and the most significant bit represent the eighth bit plane

bitplanes

in LSB stenography, the least significant bit plane in every channel is manipulated to store the hidden data. consider we want to hide the letter “A”. the letter has an ASCII code of 65 which can be represented as 1000001 in binary. since every pixel in our image contains 4 channels, we will need 2 pixel to accommodate an 8 bit data (4 bit in each pixel) in LSB

embed

solving the problem

now let’s move on how to extract the hidden data from our image. all we need to do is get all the channel values for each pixel (in our only the first row pixels are embeded with data) and mask off all the bits except LSB using AND bitwise trick (& 1). the result will then saved to a stream which will be decoded to ASCII char in order to produce the hidden data. simple extraction script are as below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from PIL import Image
img = Image.open("990d11bb95600342bd63afe6a9e962a5_color.png")
stream = ""
output = ""
# loop each pixel in the first row (y = 0)
for x in range(img.size[0]):
# loop for every channel
for channel in img.getpixel( (x, 0) ):
# mask all bit except LSB & concat to stream
stream += str(channel & 1)
# loop through every 8 bit
for i in range(0, len(stream), 8):
# convert binary to ascii char & concat to output stream
output += chr( int(stream[i:i+8], 2) )
print output

flag

a working alternative

since stegsolve kinda fucked up with the channel order (the channels were read as argb instead of rgba). HERE is a substitute script for data extraction. unlike zsteg which can only decode from one bitplane at a time, the script can include multiple bitplane (just like stegsolve, with an actual working channel order). might be useful in the future!