ECSC 2019 Quals Team France - pxmme

Table of contents
  1. 🔗Information
    1. 🔗CTF
  2. 🔗144 - 2tp - Crypto
  3. 🔗88 - Aarchibald - Pwn
  4. 🔗102 - qrcode - misc

🔗Information

🔗CTF

  • Name : ECSC 2019 Quals Team France
  • Website : www.ecsc-teamfrance.fr
  • Type : Online
  • Format : Jeopardy (individual)

🔗144 - 2tp - Crypto

Venez tester notre chiffreur universel ! Nous utilisons des technologies de pointe, garanties inviolables ! Pour preuve, nous vous donnons le flag chiffré et jamais vous ne pourrez le retrouver.

nc challenges.ecsc-teamfrance.fr 2000

Let's try to connect to this bad boy first, see what kind of output we get.

Alright then. Flag is indeed encrypted, and when sending a simple character, we get a 34 length string that looks nothing like the encrypted flag above.

Let's try to send a few more, see how this works internally regarding length.

Notice how the first two characters are the same from the result we got sending a single "a" ? That tells us where to go.

Since the flag format is known (ECSC{xxxx}), I'm guessing that, by sending ECSC, I should have the 8 first correct characters returned, plus some random ones. Let's see :

That's it! I now know how to "bruteforce" the cleartext flag. Let's write a python(3 !) script real quick.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python

from pwn import *
import string

flag = "7b656d3993152e8f04f8273ca1509e27a3e39249cf4784e23b81d5f2524fee75f6b28a6a07a128e4880e770bc70b32bd7d5f37bb5eba76d38edb8d1964733b"
base = 47
result = ''
for i in range(0,len(flag),2):
for lettre in string.printable:
conn = remote('challenges.ecsc-teamfrance.fr',2000)
a = conn.recvline()
a = conn.recvline()
a = conn.recvline()
a = conn.recvline()
conn.sendline(result + lettre)
a = conn.recvline()
lettre_enc = a[base+i:base+i+2]
conn.close()
if lettre_enc == flag[i:i+2]:
result = result + lettre
print result
break

This will send a character, check the first two characters of the string received vs the encrypted flag's ones. If it matches, it'll save it and start all over again, but this time, checking the four first characters and so on, until flag's reached. If nothing matches, it just sends the next character in string.printable.

Let's see it in action :

We've got a flag!

tl;dr: Script could've been way better, less floppy, but I'm just lazy.

🔗88 - Aarchibald - Pwn

Exploitez le binaire fourni pour en extraire flag.

nc challenges.ecsc-teamfrance.fr 4005

Dammit.

Well, thanks to the NSA, even loosers like me can actually flag some easy pwn challenges now.

See where I'm going with that? Fire up that Ghidra, boi!

Oh lord, this pseudo-code is so sexy :

Alright, let's see what we have here : this binary asks us for a password. It then takes our input, does a XOR operation on it with 0x36, and compares it with the string "eCfSDFwEeAYDr".

If the check passes, it stores our input inside a local variable (buffer size : 36) and then checks the value of another variable, which is previously set to 0x45435343. If the check fails this time, it pops a /bin/dash shell, because muh debug mode.

We now know what to do, where to go with that binary. First off, let's xor "eCfSDFwEeAYDr" with 0x36 which is the hexadecimal value for 54, decimal.

Wrote a very simple python script for this purpose :

1
2
3
4
5
6
#!/usr/bin/python3
password = []
for lettre in "eCfSDFwEeAYDr":
password.append((chr(ord(lettre) ^54)))

print(''.join(map(str, password)))

Which gives us :

There we have it! Let's try it on, shall we?

Beautiful. We triggered the first check with success! Now, if the logic's right, we should be able to add shit to the buffer without triggering any kind of error. Let's check :

Yup! Still triggers the check. Now, let's overflow the buffer (reminder : size 36) in order to write over the value of the local_4 var. By doing so, the second check should trigger, and give us a shell.

Plan worked. We're in. See you later, kiddo.

tl;dr : still lf girlfriend

🔗102 - qrcode - misc

QR Codes everywhere!

nc challenges.ecsc-teamfrance.fr 3001

Alright, this one turned out to be a pretty basic scripting challenge.

Server'd send us some zlib encoded, base64 over-encoded PNG image that'd contain a lot of QRcodes such as this one i.e.:

Now, each of these QR babies spits out a single number. As advertised, add 'em all up, send the answer back to the server, and hopefully get a flag!

Seeing we had only two seconds for accomplishing all those tasks, writing a script was mandatory. Wrote a sloppy one. Please don't hold it against me. I'm just, as advertised earlier, a lazy prick.

Here it comes, written in Python3 (wink wink) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python3

from pwn import *
import base64
import zlib
import image_slicer
import qreader

fichier = open("fichier", "wb")
resultat = 0

### INITIAL CONNECTION
conn = remote('challenges.ecsc-teamfrance.fr',3001)
conn.recvuntil("[Y/N]")
conn.recvline()
conn.sendline("Y\n")
data = conn.recvuntil("answer?\n>>")

### CUT OUT THE DATA
data = data[3:-23]

### DECODE BASE64 AND THEN DECOMPRESS ZLIB
fichier.write(zlib.decompress(base64.decodebytes(data)))

### SLICE THOSE MOTHERFUCKERS
image_slicer.slice('fichier', 64)

### READ 'EM AND ADD EM' UP
for i in range (8):
ligne = i + 1
for y in range (8):
colonne = y + 1
temp = qreader.read('fichier_0'+str(ligne)+'_0'+str(colonne)+'.png')
resultat = resultat + int(temp)

### SEND BACK THE RESULT
conn.sendline(str(resultat))

### GIMME MY FREAKING FLAG ALREADY
flag = conn.recvline()
print(str(flag)[6:-3])

Notice that qreader for Python3 was used. Now, if everything's right, I should get a flag :

Everything is indeed right.

L8er.

Share