Hack.lu 2010 CTF – Fun 300 writeup
Une fois de plus Nibbles a concouru lors du CTF Hack.lu 2010 organisé par les FluxFingers (@Fluxfingers), on a pas réussi à poncer la première team “bobsleigh” (Française aussi) mais on a poncé la team Russe de Leetmore, résultats des courses on a fini second.
La première épreuve “Fun” du CTF consistait à se connecter sur une socket pour jouer au jeu “pierre, papier, ciseaux, lézard, Spock” qui je rapelle est une extension du fameux pierre, papier, ciseaux introduit dans un épisode 8 de la deuxième saison de The Big Bang Theory (sorte de vraie série) !
On tâte un peu voir ce à quoi on est confronté:
% host pirates.fluxfingers.net
pirates.fluxfingers.net has address 213.251.165.94
% nc 213.251.165.94 6565
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
W00t de la base64, décodons ça...
% nc 213.251.165.94 6565 | while read a; do echo $a | base64 -d - >&2| tee | file - ; done
����JFIF``��pa$$: d0esntexists��C
(1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egc��C//cB8Bcccccccccccccccccccccccccccccccccccccccccccccccccc�XC"����; 6Us�r����!"#25QR��134Vbcq��������?�kRw�Y�e*隦�aK
��$��y�)ҧ�dNg��1�/��m�n�=���6�N��t��m�n�=�y8�ܘ���.<[+����j����i�ڔ�J�)X��6M�J}5*�Yg�IW�M��RH�7'C;_WM�3R��N���L�T�/�Y�uQ��q<沕%��*H�4ok4l���ib����<�=�x����p��g��y�۵!�Q\�z�Ć��<�{�O��sQ��f��O�O�ᗈ0�z���{��~�C�^Cw���K�f��k��!��)��\2���-.I��X|�@���/dev/stdin: JPEG image data, JFIF standard 1.01, comment: "pa$$: d0esntexists\377"
% nc 213.251.165.94 6565 | while read a; do echo $a | base64 -d - > jpeg ; done
% file jpeg
jpeg: JPEG image data, JFIF standard 1.01, comment: "pass: NULL\377"Ok, l’image JPEG renvoyée est jamais la même, d’ailleurs c’est comme ça qu’on a pu voir qu’il s’agissait du “pierre, papier, ciseaux, lézard, Spock”. De plus, dans leurs commentaires elles contiennent un mot de passe différent.
On fait différents essais afin d’établir la correspondance entre les mots de passe et les images pour arriver aux relations suivantes:
d0esntexit => papier maybe => ciseaux md5wins => spok nothere => lezard NULL => pierre
Bon bein, reste plus qu’a script pour pwn l’algo et choper le flag. On se rend compte vite qu’il est nécessaire de renvoyer le md5sum de l’image pour répondre correctement et valider. On doit donc procéder par étapes:
1/ Faire une passe d’apprentissage des correspondances “mots de passe” <=> “données binaires de l’image”
2/ Lire le commentaire de l’image pour récupérer le mot de passe à challenger
3/ Implémenter l’algorithme permettant de gagner au jeux en sortant la bonne réponse
4/ Renvoyer la réponse et boucler jusqu’ à avoir le flag pour valider l’épreuve
Ce qui nous donne le script suivant:
% cat fun.py #!/usr/bin/python import hashlib import socket import sys import binascii import base64 """ d0esntexit => papier maybe => ciseaux md5wins => spok nothere => lezard NULL => pierre """ d=lambda x: base64.decodestring(x) def recv_chunk(s): string = b'' while True: buf = s.recv(750) if not buf: break string = string + buf if len(buf) < 600: break return string def send_chunk(s, string): pos = 0 pair = 0 sended = 0 while True: if len(string) - pos < 690: sended += s.send(string[pos:]) break elif pair: sended += s.send(string[pos:pos+690]) pair = 0 pos += 690 else: sended += s.send(string[pos:pos+750]) pair = 1 pos += 750 return sended def read_comment(bytes): comment = "" for k in range(len(bytes)-1): if bytes[k] == 'FF' and bytes[k+1] == 'FE': length = int(bytes[k+2],16)*256 + int(bytes[k+3],16) for m in range(length-3): comment = comment + chr(int(bytes[k + m + 4],16)) if ':' in comment: comment = comment.split(':')[1].strip() return comment def md5sum(data): md5 = hashlib.md5() md5.update(data) return md5.hexdigest() def compute_rock_paper_cissors(input): dispatch={ 'd0esntexist' : 'papier', 'd0esntexists': 'papier', 'mayb': 'ciseaux', 'maybe': 'ciseaux', 'md5win': 'spock', 'md5wins': 'spock', 'nother': 'lezard', 'nothere': 'lezard', 'NUL': 'pierre', 'NULL': 'pierre' } dispatch_invert={ 'papier': 'd0esntexists', 'ciseaux': 'maybe', 'spock': 'md5wins', 'lezard': 'nothere', 'pierre': 'NULL' } dispatch_solve={ 'pierre': 'papier', 'papier': 'ciseaux', 'ciseaux': 'spock', 'spock' : 'lezard', 'lezard': 'pierre', } if input not in dispatch.keys(): print "input (%s)" % input return dispatch_invert[dispatch_solve[dispatch[input]]] if __name__ == '__main__': HOST="213.251.165.94" PORT=6565 pictures = {} try: # learning while len(pictures.keys()) < 5: s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) data = d(recv_chunk(s)) print len(data) bytes = [] # hexlify [ bytes.append("%02X" % ord(c)) for c in data] comment = read_comment(bytes) pictures[comment] = binascii.unhexlify( "".join(bytes).replace(' ','') ) s.close() # handle alias pictures['maybe'] = pictures['mayb'] pictures['d0esntexists'] = pictures['d0esntexist'] pictures['md5wins'] = pictures['md5win'] pictures['nothere'] = pictures['nother'] pictures['NULL'] = pictures['NUL'] #print pictures s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) while True: try: b64 = recv_chunk(s) data = d(b64) except binascii.Error: print b64, data bytes = [] # hexlify [ bytes.append("%02X" % ord(c)) for c in data] # parse comment in jpeg hex bytes comment = read_comment(bytes) img = binascii.unhexlify( "".join(bytes).replace(' ', '') ) # write picture open('%s.jpeg' % comment, 'w').write(img) print "+ recieve [ %s ]" % comment if comment == '': break # pwn algo res = compute_rock_paper_cissors(comment) # compute md5 sum = md5sum(pictures[res]) print "+ sent [ %s ] (%s)" % (res, sum) send_chunk(s, sum+'\n') s.close() except KeyboardInterrupt: s.close() sys.exit(1)
Let’s running this baby:
[2/4.3.10]{20}sbz@ogoshi:~/downloads/hacklu/fun_files> python fun.py
1532
839
3072
839
2957
1532
2957
3072
2957
1532
3072
1532
2957
1532
1532
2957
1764
+ recieve [ d0esntexist ]
+ sent [ maybe ] (e174dc1b5f820171298f08e503d755e4)
+ recieve [ d0esntexist ]
+ sent [ maybe ] (e174dc1b5f820171298f08e503d755e4)
+ recieve [ md5win ]
+ sent [ nothere ] (19d63c2dc8126d6afab5b6e49cd59387)
+ recieve [ nother ]
+ sent [ NULL ] (3ddba5785322064ef976a524f0e515d1)
+ recieve [ d0esntexist ]
+ sent [ maybe ] (e174dc1b5f820171298f08e503d755e4)
+ recieve [ NUL ]
+ sent [ d0esntexists ] (5939711843183aea0fd81b8d2afca7cd)
+ recieve [ nother ]
+ sent [ NULL ] (3ddba5785322064ef976a524f0e515d1)
+ recieve [ nother ]
+ sent [ NULL ] (3ddba5785322064ef976a524f0e515d1)
the secret is: ev!lsYcerf0xxr0xx
0:*,#0E=IGD=CALVm]LQhRAC_�`hqu{|{J\���w�mx{v��C88vOCOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv�c["����0!1AQaq�"����#�2B�����!1AQaq��?�;UM�]��g����~W������n﹊�r���^�ij4��ou��CSd�9�z=ʋ��I2c�q��ߜqQ�7]6���!s��bGO��ٻ�cr۩�$�m���^�d��(�s���rr�Б�ˌB����]f��m���v/���0{�-�c�Ku<3f��ܾ<�1�~G��D��~�Ri�A�nQ�NR�m��i/yWE���)�%\���V���yQ-�S���VMc��[�н4����Y�88횱�o�y�i��\���'����+e����xs���=��*�-<�>�,8�voB�<Pre3���gS��nk#K< �9}*�o��i#乄Lg ����ڭ�R�4�JR��JR�V��-�:)ҶҀ������d�s��4�ggV���f�����k��|Xǽq���n���s��9�^Ⲵ��m���fWe-3܋&V�S9;w��Z4{>��j-� K���8q���h�:��)i�$�H^=N�{U�p�:��dfڂ����g����p���~�e�;��d#��U�}=���n6�B ���V�(" �V�*W�m�#=�D�Y�JR�V3Y�W�k'���1Uz��L�>�q\�oN� �4$G�<�p��kv��J�,��Bv���z��䫬W#����>\�~x��8��O���5ֈ�9����)Z)JP��I�Vv�C�'��Ԯ֡��,�+F��i�6
R���Y����J$��r��z�����gf�.�2> /���qW�m���q�t��5�uӗl���ϣڴ�3��aq�G�c��d�W����(ҵ R���()JJS����ӿj�Ҧ�x�d��1�u=��ʿ���H^�N
\�������*)|�����Vj�K�[���*��q����V?�4K�UdS �|5�K�9k�#^a�qݴR��:�N\�W��*W����3m)J��JR�S�)@1攠!_��u�O��D�����.�w�+���>ݣә�����1Ҕ�����
+ recieve [ nother ]
+ sent [ NULL ] (3ddba5785322064ef976a524f0e515d1)
+ recieve [ ]Dad` et sbz
Les images récupérées lors du challenges et le script sont disponibles ici
FLAG: ev!lsYcerf0xxr0xx
























jj@bobsleigh: nous on l’a fait comme ca
(bon ok c’est triché, c’est du ruby donc c’est plus rapide et plus simple)
Il faut le lancer plusieurs fois au début pour chopper les images et les renommer correctement
[...] http://blog.nibbles.fr/2278 [...]
[...] second. La première épreuve « Fun » du CTF consistait à se connecter [...] Nibbles microblog This entry was posted in Sécurité Informatique and tagged 2010, Hack.lu, Writeup. Bookmark the [...]