Ata Kuyumcu's Blog

I did a few things at STM CTF 2018 Prelims

#ctf#python#infosec

Here are a few challenges I chose to write about from the preliminary for STM CTF 2018 which will take place in Ankara at Oct 31. I want to thank the STM team for a fun and smooth CTF experience. I started competing in CTFs about half a year ago and I heard good things about STM CTF 2017. I hope I can attend this year :)

DiEfAyAr

We are given a yara file and a binary data file. The yara rule matches the file. It looks like this:

rule APT58_Temp_Watercity {
	
	meta:
		description = "Detects malware from notorious hacker group supported by Sivas"
		in_the_wild = true

	strings:
		<snip, irrelevant>

	condition:
		all of them and
		$s7 at 0 and
		uint16(uint8(uint8(uint8(uint8(uint8(0x79)))))) == 0x6c66 and
		uint16(uint8(uint8(uint8(uint8(uint8(0x69)))))) == 0x6761 and
		uint8(0x2aa) == 0x7d and
		filesize == 792

The uint16(uint8(uint8(uint8(uint8(uint8( syntax is basically nested Load Effective Address instructions with specified word length.

uint16( 0x666c
	uint8( 0x5f _
		uint8( 0x6d m
			uint8( 0x61 a
				uint8( 0x72 r
					uint8(0x79 y)))))) 0x34 4

uint16( 0x6167
	uint8( 0x65 e
		uint8( 0x44 D
			uint8( 0x52 R
				uint8( 0x33 3
					uint8(0x69 i)))))) 0x63 c

So the flag is STMCTF{y4ram_ic3Rde}.

Rengarenk String

We are given an image and told that the color values have a meaning.

image with hidden information

It doesn’t have any transparency so we extract RGB data in order, with GIMP.

atak@silver ~>xxd rengarenk_string.dat
00000000: acab b2bc abb9 84ad cc91 98cb 8d9a 9194  ................
00000010: a0bd 968d a0bb 8a91 869e a0b6 9c96 91a0  ................
00000020: b79a 8da0 ac9a 919a a0ad 9a91 9493 96a0  ................
00000030: bd96 8da0 ac90 8d8a a0ac 908d 9e93 9692  ................
00000040: a0ac 9685 a0bb 9aa0 bc90 858a 91a0 ba98  ................
00000050: 939a 9196 91a0 bb9a 9b96 94a0 b686 96a0  ................
00000060: a69e 8f8b 9694 a092 96a0 c0a0 cecd cccb  ................
00000070: cac9 c8c7 c6a0 cecd cccb cac9 c8c7 c6a0  ................
00000080: cecd cccb cac9 c8c7 c6a0 cecd cccb cac9  ................
00000090: c8c7 c6a0 a69e 9391 9685 a0b1 9aa0 aa85  ................
000000a0: 8a91 a0b9 939e 98a0 b093 9b8a a0bd 8aa0  ................
000000b0: bd90 8693 9aa0 b19a 868c 9aa0 b796 9ca0  ................
000000c0: bb9a 9896 938c 9aa0 b2bb caa0 be93 9686  ................
000000d0: 908d 8a85 a0aa 858a 9193 8a94 a0be 8691  ................
000000e0: 96a0 b49e 9386 908d a0c4 d6a0 c5d6 82    ................

It doesn’t look very human-friendly yet it doesn’t look completely random either. Let’s check the distribution of byte values.

atak@silver ~>xxd -p -c 1 rengarenk_string.data | sort | uniq -c | sort -n
      1 82
<snip, too long>
      6 9e
      7 86
      8 8d
      9 8a
     11 93
     13 91
     14 9a
     17 96
     39 a0

Assuming this is a flag, 0xA0 probably corresponds to underscore since it’s used to break words in STM CTF flags. 0xA0 also happens to be the one’s complement of 0x5F which is the ASCII value of the underscore character. Let’s try to flip every bit in that file.

>>> data = open("rengarenk_string.dat","rb").read()
>>> "".join(map(lambda b: chr(ord(b) ^ 0xFF), data))
'STMCTF{R3ng4renk_Bir_Dunya_Icin_Her_Sene_Renkli_Bir_Soru_Soralim_Siz_De_Cozun_Eglenin_Dedik_Iyi_Yaptik_mi_?_123456789_123456789_123456789_123456789_Yalniz_Ne_Uzun_Flag_Oldu_Bu_Boyle_Neyse_Hic_Degilse_MD5_Aliyoruz_Uzunluk_Ayni_Kaliyor_;)_:)}'

And voila, there’s the flag.

Matruşka

We are given an executable, named matruşka.exe.

atak@silver Matruşka>file matruşka.exe
matruşka.exe: PE32 executable (console) Intel 80386, for MS Windows

If we try to run it with wine because who runs Windows in the year of the Linux Desktop, we run into a Python stacktrace. I’m pretty sure that was not intended but that’s okay, we learned that this is a “compiled” Python script. So we unpack it with python-exe-unpacker.

atak@silver Matruşka>./python_exe_unpack.py -i ./matruşka.exe
[*] On Python 2.7
[*] Processing ./matruşka.exe
[*] Pyinstaller version: 2.1+
[*] This exe is packed using pyinstaller
[*] Unpacking the binary now
[*] Python version: 27
[*] Length of package: 3224163 bytes
[*] Found 18 files in CArchive
[*] Beginning extraction...please standby
[*] Found 194 files in PYZ archive
[*] Successfully extracted pyinstaller exe.
atak@silver Matruşka>cd unpacked/matruşka.exe/
atak@silver matruşka.exe>ls
 bz2.pyd                       pyimod02_archive
 _hashlib.pyd                  pyimod03_importers
 Microsoft.VC90.CRT.manifest  'pyi-windows-manifest-filename rev.exe.manifest'
 msvcm90.dll                   python27.dll
 msvcp90.dll                   rev
 msvcr90.dll                   rev.exe.manifest
 out00-PYZ.pyz                 select.pyd
 out00-PYZ.pyz_extracted       struct
 pyiboot01_bootstrap           unicodedata.pyd
 pyimod01_os_path

Poking around a bit, we realize the rev file is pretty interesting.

atak@silver matruşka.exe>xxd rev
00000000: 6300 0000 0000 0000 0003 0000 0040 0000  c............@..
00000010: 0073 3000 0000 6400 0064 0100 6c00 005a  .s0...d..d..l..Z
...

So we have a base64 encoded marshall object in there. Let’s carve it out and import it.

>>> import marshal
>>> with open("sus.marshal","rb") as mf:
...     hmm = marshal.load(mf)
...
>>> hmm
<code object ctf at 0x7fbb5dd36e30, file "pythonex.py", line 4>

After a bit of research, we learn that you can disassemble code objects.

>>> import dis
>>> dis.dis(hmm)
  5           0 LOAD_CONST               1 ('')
              3 LOAD_ATTR                0 (join)
              6 BUILD_LIST               0
              9 LOAD_CONST               2 ('y')
             12 LOAD_CONST               3 ('_')
             15 LOAD_CONST               4 ('5')
             18 LOAD_CONST               5 ('w')
             21 LOAD_CONST               2 ('y')
             24 LOAD_CONST               6 ('&')
             27 LOAD_CONST               7 ('d')
             30 LOAD_CONST               8 ('8')
             33 LOAD_CONST               9 ('r')
             36 LOAD_CONST              10 ('6')
             39 LOAD_CONST              11 ('s')
             42 LOAD_CONST               7 ('d')
             45 LOAD_CONST               9 ('r')
             48 LOAD_CONST              10 ('6')
             51 LOAD_CONST              12 ('x')
             54 LOAD_CONST              10 ('6')
             57 LOAD_CONST              11 ('s')
             60 BUILD_LIST              17
             ...
        >>   95 CALL_FUNCTION            1
             98 STORE_FAST               1 (passw)

  6         101 LOAD_GLOBAL              3 (raw_input)
            104 LOAD_CONST              14 ('bul parolayi al flagi : ')
            107 CALL_FUNCTION            1
            110 STORE_FAST               2 (parola)

  7         113 LOAD_FAST                1 (passw)
            116 LOAD_FAST                2 (parola)
            119 COMPARE_OP               2 (==)
            122 POP_JUMP_IF_FALSE      287

  8         125 LOAD_CONST               1 ('')
            128 LOAD_ATTR                0 (join)
            ...
            251 BUILD_LIST              39
            ...
            267 LOAD_CONST              36 ('ROT13')
            270 CALL_FUNCTION            1
            ...
 10     >>  287 LOAD_CONST              37 ('tutmadi be ..')
            290 PRINT_ITEM
            291 PRINT_NEWLINE
        >>  292 LOAD_CONST               0 (None)
            295 RETURN_VALUE

We could put the password back together, re-assemble the program and run it to get the flag but the flag is right there, so we just reverse and ROT13 it and recover it, it’s STMCTF{4$k_3u_k1z1l0t3s1_y4r4L1_m7z3$1}.