Tags: misc

Rating: 5.0

## 3DSCTF - A HUNDRED BASES OF GREY - MISC

When we enter the page, we are greeted with a beautiful soundtrack (which unfortunately doesn't hold any useful information for retrieving the flag) and a couple of gifs. From the website we can extract flag1-6, 1st, 2nd, 3rd, 4th gifs.

From the names we can expect that flag1-6 are the gifs that somehow decoded can give us the flag. All frames of gifs are one-colored, so let's look at their RGB values:

python
from PIL import ImageSequence, Image

colors = []
for i in range(6):
im = Image.open('flag' + str(i + 1) + '.gif')
colors.append([])

try:
while True:
new_frame = im.convert('RGB')
colors[i].append(new_frame.getdata()[0])
im.seek(im.tell()+1)
except EOFError:
pass

from pprint import pprint
pprint(colors)


After running, we get this output:


[[(3, 3, 3),
(5, 5, 5),
(5, 5, 5),
(3, 3, 3),
(3, 3, 3),
(5, 5, 5),
...
(3, 3, 3),
(5, 5, 5)],
[(3, 3, 3),
(18, 18, 18),
(18, 18, 18),
(3, 3, 3),
(18, 18, 18),
(10, 10, 10),
...
(13, 13, 13),
(5, 5, 5)],

...
[(189, 189, 189),
(217, 217, 217),
(199, 199, 199),
(189, 189, 189),
(15, 15, 15),
...
(217, 217, 217),
(194, 194, 194)]]


It seems like all colors have the same R, G and B values - maybe it will be a good idea to convert every frame to a number and inspect these sequences? Worth a try:

python
for i in range(6):
colors[i] = [x for (x, j, k) in colors[i]]
print('flag' + str(i + 1) + '.gif:', colors[i])


After conversion we get these sequences:

flag1.gif: [3, 5, 5, 3, 3, 5, 3, 5, 3, 3, 5, 5, 3, 3, 5, 3, 3, 3, 5, 5, 3, 5, 5, 5, 3, 5, 5, 3, 3, 5, 3, 5, 3, 5, 5, 3, 3, 3, 3, 5, 3, 3, 5, 5, 3, 5, 3, 5, 3, 3, 5, 5, 3, 5, 3, 3, 3, 3, 5, 5, 3, 5, 5, 5, 3, 3, 5, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 5, 3, 3, 3, 3, 5, 5, 3, 3, 3, 5]
flag2.gif: [3, 18, 18, 3, 18, 10, 3, 18, 15, 5, 13, 13, 3, 20, 3, 5, 13, 18, 3, 18, 18, 5, 13, 15, 3, 18, 3, 5, 13, 18, 5, 13, 5]
flag3.gif: [10, 20, 18, 10, 18, 15, 18, 8, 18, 15, 18, 8, 10, 18, 10, 13, 18, 15, 18, 15, 10, 13]
flag4.gif: [33, 43, 74, 59, 33, 66, 46, 59, 18, 23, 64, 10, 8, 38, 26, 66, 20, 3, 84, 84, 84, 84, 84, 84]
flag5.gif: [64, 133, 26, 89, 64, 59, 23, 143, 36, 51, 36, 94, 36, 140, 13, 166]
flag6.gif: [189, 217, 199, 189, 15, 140, 120, 31, 13, 145, 166, 135, 33, 115, 181, 168, 217, 194]


Hmmm... first sequence has only two different values, maybe it holds some information in binary? Let's try to decode flag1 sequence to binary by mapping 3 to '0' and 5 to '1':

python
guessed_mapping = {3: '0', 5: '1'}
print("".join([guessed_mapping[i] for i in colors[0]]))


We get 0110010100110010001101110110010101100001001101010011010000110111001100000110010000110001, which after translation to ASCII is e27ea5470d1.

That's a good sign! We got something printable, it even looks like something in hex. But what now? We guessed values that can be mapped to numbers in flag1 sequence - with other gifs it's a little bit more complicated. That's where 1st, 2nd, 3rd and 4th gifs come in handy. Let's look at RGB values on them (spoiler - they also have R = G = B):

python
strips = []
gifs = ['1st.gif', '2nd.gif', '3rd.gif', '4th.gif']

for i, name in enumerate(gifs):
im = Image.open(name)
strips.append([])

for j in range(0, 500, 20):
frame = im.convert('RGB')
strips[i].append(frame.getdata()[j])

strips[i] = [x for (x, j, k) in strips[i]]
print(name + ':', strips[i])


Our script will print:


1st.gif: [3, 5, 8, 10, 13, 15, 18, 20, 23, 26, 28, 31, 33, 36, 38, 41, 43, 46, 48, 51, 54, 56, 59, 61, 64]
2nd.gif: [66, 69, 71, 74, 77, 79, 82, 84, 87, 89, 92, 94, 97, 99, 102, 105, 107, 110, 112, 115, 117, 120, 122, 125, 127]
3rd.gif: [130, 133, 135, 138, 140, 143, 145, 148, 150, 153, 156, 158, 161, 163, 166, 168, 171, 173, 176, 179, 181, 184, 186, 189, 191]
4th.gif: [194, 196, 199, 201, 204, 207, 209, 212, 214, 217, 219, 222, 224, 227, 229, 232, 235, 237, 240, 242, 245, 247, 250, 252, 255]


It looks like pallette - all numbers from flag1-6 gifs can be found here. Looks like 2nd sequence is continuation of 1st, 3rd of 2nd and 4th of 3rd, so it might be a good idea to merge these into one pallette list. Also - we got something interesting from flag1.gif by mapping 3 to 0 and 5 to 1. Natural thought that could come is to map all numbers to their corresponding indices in our pallete:

python
pallette = sum(strips, [])
colors = [map(pallette.index, i) for i in colors]

for i in range(6):
print('flag' + str(i + 1) + '.gif:', colors[i])


We get:


flag1.gif: [0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1]
flag2.gif: [0, 6, 6, 0, 6, 3, 0, 6, 5, 1, 4, 4, 0, 7, 0, 1, 4, 6, 0, 6, 6, 1, 4, 5, 0, 6, 0, 1, 4, 6, 1, 4, 1]
flag3.gif: [3, 7, 6, 3, 6, 5, 6, 2, 6, 5, 6, 2, 3, 6, 3, 4, 6, 5, 6, 5, 3, 4]
flag4.gif: [12, 16, 28, 22, 12, 25, 17, 22, 6, 8, 24, 3, 2, 14, 9, 25, 7, 0, 32, 32, 32, 32, 32, 32]
flag5.gif: [24, 51, 9, 34, 24, 22, 8, 55, 13, 19, 13, 36, 13, 54, 4, 64]
flag6.gif: [73, 84, 77, 73, 5, 54, 46, 11, 4, 56, 64, 52, 12, 44, 70, 65, 84, 75]


Unfortunately, mapping every number to binary (0 -> '0', 1 -> '1', 2 -> '10' and so on), merging into one string and translating to ASCII doesn't give any interesting results for flag2-6 gifs. So let's try a different approach...

So far we've used GRAY and HUNDRED from task name, what about BASES? Let's do some converting!

Highest number on the second list is 7, so that may suggest octal. Indeed, after joining numbers on a list we get 066063065144070146066145060146141 which converted to ASCII gives us 635d8f6e0fa.

However, if we use the same approach to the next list, we do not get anything that makes sense. So let's try next "sensible" base - decimal. 3763656265623634656534 maps to 7cebeb64ee4.

Next list has many 32 at the end, which suggests Base32 with 32 as a padding. MQ4WMZRWGIYDCOJZHA====== maps to d9ff6201998.

Penultimate list is encoded in Base64 (the same idea - last value is 64). YzJiYWI3NTNkN2E= maps to c2bab753d7a.

Last, but not least - the last list is encoded in Ascii85, Adobe version (highest number is 84). <~@<5skB4u\$qCi+%~> maps to asdfghjklmn.

So, after concatenating all of those together and adding necessary 3DS{ and } we obtain 3DS{e27ea5470d1635d8f6e0fa7cebeb64ee4d9ff6201998c2bab753d7aasdfghjklmn} which is the correct flag.

Whole script (with decoding included) can be found [here](https://gist.github.com/michsiw96/14b1e60ae2e93137ec4cde61e99c7309).

Original writeup (https://hackmd.io/s/rJuijPSzG).