A nice little Stego challenge from SharifCTF
Challenge:
We have hidden a message in png file using jar file. Flag is hidden message. Flag is in this format: SharifCTF{flag}
Files:
Extracting the supplied tar.gz archive provided a PNG image:
Also included was Hide.jar, the Java code used to embed the flag in the image.
Pushing the .jar through CFR via JavaDecompilers.com resulted in a clean dump of the source code (PasteBin dump of code).
Identifying important chunks of the code
We can see that the point structure starts at position 0, 0:
Point point = new Point(0, 0);
Next, the movePointer() function shows that the point moves positively along the x axis of the image, wrapping to the next row if the position exceeds the image width.
protected void movePointer(Point point, BufferedImage bufferedImage) {
if (point.x == bufferedImage.getWidth() - 1) {
point.x = -1;
++point.y;
}
++point.x;
if (point.y == bufferedImage.getHeight()) {
throw new RuntimeException("Pointer moved beyond the end of the image");
}
}
So we know that that data starts at 0,0 and is written across the pixel rows.
The setLeastSignificantBit() function tells us that we’ll only find encoded information in the LSB of changed data.
protected int setLeastSignificantBit(int n, boolean bl) {
n >>= 1;
n <<= 1;
if (bl) {
++n;
}
return n;
}
We see the use of setLeastSignificantBit() in this section of the code where each letter is encoded bit by bit:
if ((n & 128) == 128) {
bufferedImage.setRGB(point.x, point.y,
this.setLeastSignificantBit(bufferedImage.getRGB(point.x, point.y),
true));
} else {
bufferedImage.setRGB(point.x, point.y,
this.setLeastSignificantBit(bufferedImage.getRGB(point.x, point.y),
false));
}
n <<= 1;
this.movePointer(point, bufferedImage);
getRGB() and setRGB() use a single 32bit value to represent the Alpha, Red, Green and Blue components of a given pixel (8 bits per value). This means that the setLeastSignificantBit() function only changes the LSB of the Blue value. We can also see that the mask with 128 and left shifts on n encode the character bits from MSB to LSB.
Retrieving the Message
We now have enough information to extract the encoded message:
The PIL load() function gives us access to the image’s pixel data as an RGB tuple. We can address element [2] to read the green value.
Then it’s just a case of rebuilding the message bytes from the LSBs:
import PIL.Image, sys
x = 0
pixelData = PIL.Image.open("AsianCheetah1.png").load()
for letters in range(0,100):
ch = 0
for bits in range(0,8):
bit = (pixelData[x,0][2] & 0x1)
ch = (ch << 1)
ch += bit
x += 1
sys.stdout.write(chr(ch))
Running our Python results in the following decode:
43:SharifCTF{e8e12db2fc654f3b50f3da4901ab986e}S...
There’s our flag!