Skip to main content

EasyCTF IV - Special Endings

·2 mins·
Forensic Stegano Write-Up Ctf
Table of Contents
EasyCTF - This article is part of a series.
Part 5: This Article

Description
#

She taught us so much… tribute

Resolution
#

When opening the file, you can see a lot of base64 encoded strings, one per line. After decoding them all, it becomes clear that it’s a collection of writings of Ursula K. Le Guin. This challenge can be difficult to grasp because there is absolutely nothing else hidden anywhere.

It’s now that the title comes into place “Special Endings”. That could mean that the end of each line has something “special” but it seems to be just normal base64 encoding. After reading the RFC 4648, one paragraph caught my attention :

When padding is used, there are some non-significant bits that
   warrant security concerns, as they may be abused to leak information
   or used to bypass string equality comparisons or to trigger
   implementation problems. - Chapter 12, Security Considerations

Data can be hidden in the padding bits as they are ignored during the decoding process. Let’s decode the first line and re-encode it to see if there is any difference :

"WW91IHdpbGwgZGllLiBZb3Ugd2lsbCBub3QgbGl2ZSBmb3JldmVyLm==".decode("base64")
# 'You will die. You will not live forever.'
'You will die. You will not live forever.'.encode("base64")
# 'WW91IHdpbGwgZGllLiBZb3Ugd2lsbCBub3QgbGl2ZSBmb3JldmVyLg==\n'

Yes there is ! The last letter should have been a “g” but it was a “m”. To understand what’s going on, you must look at the bits and the way base64 works. For this, there is nothing better than the RFC. 😉 From the examples of the RFC :

BASE64("f") = “Zg==” : ‘f’ is transformed in it’s binary representation and split in chunks of 6 bits.

bin("f") = 01100110 => 011001 100000 => 25 32 => ‘Z’ ‘g’

Then you add one ‘=’ per pair of padding bits to tell the decoding process how many bits should be ignored.

Let’s apply this to the first line and look at the last block :

Expected: ‘g’ => 100000 Got: ’m’ => 100110

This means that the beginning of the embedded data is 0110.

Now you only need to script the extraction of all the data :

def extract(m):
    if not m:
        return ""
    if m[-1] == "=":
        alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
        if m[-2] == "=":
            return bin(alpha.index(m[-3]))[2:][-4:]
        else:
            return bin(alpha.index(m[-2]))[2:][-2:]
    return ""

s = ""
f = open("file.txt").readlines()
for e in f:
    s += extract(e.strip())

s += "0" * (8 - len(s)%8)
s = hex(int(s,2))[2:].rstrip('L')
if len(s) & 1:
    s = "0" + s
print s.decode('hex')

The flag is : ill_miss_you

EasyCTF - This article is part of a series.
Part 5: This Article