/ reverse

abctf - Frozen Recursion - 250

Énoncé

I finally learned recursion! Am I doing it right?

write up

Un ELF nous est fourni, son execution ne nous indique rien de particulier

guy►./recursive_python 
You wish it was that easy!

le fichier est tres volumineux, on voit venir le coté "récursif du challenge", le nom du challenge FROZEN et les différentes fonctions qu'on trouve en désassemblant le code poussent mes recherches vers la library python Freeze ainsi que sur ce post de stack exchange

on a donc à faire à un ELF généré via Freeze, je devrais donc pouvoir retrouver le code python originel et l'analyser !

tout les modules chargés sont stockés dans une structure de cette forme

struct _frozen {
    char *name;
    unsigned char *code;
    int size;
};

je sais aussi que ces modules seront chargés à la sortie de la fonction PyImport_ImportFrozenModule cette fonction est appellée par la fonctionPy_FrozenMain

on lance gdb

gdb recursive_python
(gdb) b *Py_FrozenMain + 220 
Breakpoint 1 at 0x41fce4
(gdb) r
Starting program: /home/ctf/abctf/recusivePython/recursive_python 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
You wish it was that easy!

Breakpoint 1, 0x000000000041fce4 in Py_FrozenMain ()

à ce stade du programme tout nos modules python sont chargés dans la variable PyImport_FrozenModules
depuis gdb on peut afficher le contenu de cette variable

(gdb) print PyImport_FrozenModules 
$1 = (struct _frozen *) 0x825cc0 <_PyImport_FrozenModules>
(gdb) print PyImport_FrozenModules[0]
$2 = {name = 0x5a1841 "BaseHTTPServer", code = 0x8268c0 <M_BaseHTTPServer> "c", 
  size = 21704}
(gdb) print PyImport_FrozenModules[8]
$3 = {name = 0x5a181f "__main__", code = 0x869380 <M___main__> "c", 
  size = 48627303}

le module qui nous intéresse est le main, il nous faut extraire son le code !
(gdb) dump memory flag.pyc 0x869380 0x36c91e7
0x36c91e7 = 48627303 + 0x869380
on recupere un fichier .pyc avec un petit probleme de magic numbers ! on l'édite avec bless et on lui rajoute le bon header 03 F3 0D 0A 63 E3 18 57 on peut maintenant l'executer et même le décompiler

guy►pycdc flag_corrected.pyc > flag.py

maintenant le flag.py contient une enorme chaine encodé en base64, ce script va décoder cette chaine, enregistrer son resultat dans un fichier puis supprimer ce fichier

on modifie le code pour que le script ne modifie pas le fichier et on l'execute

guy►tail -c 250 flag.py
AAAAMAAAAAAAAAAAAAAAAAAAAAAAAAkMgpAgAAAAC0qwIAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=='))
tmp = open('unstep_84fc2d39', 'w+')
tmp.write(data)
tmp.close()
os.chmod('unstep_84fc2d39', 511)
os.system('./unstep_84fc2d39')
os.system('rm unstep_84fc2d39')
guy►sed -i "s/os.system('rm unstep_84fc2d39')//" flag.py
guy►python flag.py
You wish it was that easy!
guy►file unstep_84fc2d39 
unstep_84fc2d39: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=c7a327d48b5d9c70eeb0757f0d5fd47df3a13161, not stripped

on recupere un ELF....généré depuis Freeze.... voila la récurisvité mentionnée dans le challenge. il Faudra réitérer ces opérations plusieurs fois pour voir apparaître le code source original

unlocked_flag = False
if unlocked_flag == True:
    print 'flag{python_taken_2_far}'
else:
    print 'You wish it was that easy!'