At the first glance it looked like an easy challenge, we had the following image and the description stated that only one bit was flipped.
So all we have to do to get the flag was to flip that bit again. Easy, huh? Well, no.
Downloading the image we find out that it's size is
146 KB which is
1,168,000 bits which is quite a lot to bruteforce through.
We spent sometime searching for similar old challenges but ended up only finding bruteforcing scripts and nothing more so we decided to write a script looping on each bit, flipping it, saving the image, running
tesseract -an OCR tool- on it then delete the image.
import codecs import os start_byte = 0 stop_byte = 1168000 with codecs.open('../bs.jpg', 'rb') as input_file: data = input_file.read() for byte in range(start_byte, stop_byte): for bit in range(8): modified = chr(ord(data[byte]) ^ (1 << bit)) output_data = data[:byte] + modified + data[byte + 1:] with codecs.open(str(byte) + '_' + str(bit) + '.jpg', 'wb') as output_file: output_file.write(output_data) os.system('tesseract ' + str(byte) + '_' + str(bit) + '.jpg ' + str(byte) + '_' + str(bit) + '.txt '+'-l eng') os.system('rm ' + str(byte) + '_' + str(bit) + '.jpg')
We ran the script and we'd check the text files generated by
tesseract every once in a while to check if we got anything. But after a while we stopped the script as it was an overhead for our laptops and surely it wasn't the right way to go.
After solving a couple of challenges, we decided to give it another look. There was a new hint for the challenge which stated that
if you know the structure of the JPEG, you'll narrow the bruteforcing range
The script wasn't a waste after all but we have to find the right offsets for the
stop_byte so we started googling the structure of JPEG right until we landed on this presentation and on slide 18 we got what we were looking for
Rereading the hint, we suspected that we're aiming either for Huffman tables or Quantization Tables as they're both essencial for the image data compression.
Firing up a hex editor and started looking for our candidate offsets until we found this one
start_byte = 20736 and
end_byte = 20800 and running our script then checking out the text files
We got some dummy text so we decided to have a look for ourself, maybe
tesseract isn't seeing what we can. We ran the script again with
start_byte = 20760 and
end_byte = 20773 without
tesseract and keeping the images this time.
Browsing throw these images we find out that we successfully repaired the image and got the flag by flipping the 7th bit in the 20771 byte
Here's a closer look on the flag
We submitted the flag literally with 1 minute left on the CTF to steal the first place back and if you're wondering how one bit can change your life, here's how