GCC 2024- Quelques writeups
Introduction
Voici quelques notes sur certains challenges résolus ou étudiés par mon équipe. Nous n’étions pas particulièrement disponibles, et séchions sur certaines catégories (osint,web3,pwn), cependant les défis étaient originaux , de qualité et comprenaient souvent plusieurs solutions non prévues par les auteurs.
Finalement, nous avons pu obtenir un classement respectable (~15%) en nous focalisant sur nos domaines de préférences.
Crypto
GCC News
Signature
Le but de ce challenge est d’accéder à la page /news
du site une fois connecté.
Une fois notre utilisateur a
crée, nous disposons d’un token et d’un message passés en GET
pour cette route:
http://worker04.gcc-ctf.com:14307/news?token=9[...]03&message=eydhJzogW0ZhbHNlXX0%3D
, avec
|
|
Le message étant formatté en JSON {'utilisateur': [False]}
, on se doute qu’un parseur vérifiera si la valeur associée au nom utilisateur vaut True mais il faut générer un token valide pour ce message.
Regardons le code source d’un peu plus près:
|
|
Notre token est en fait une signature RSA, dont les clés publiques et privées sont générées à partir du message, que l’on contrôle et qui semble unique (pas de SGBD pour stocker les tokens) … Pour rappel, en notant (n,e) la clé publique, (n,d) la clé privée et s la signature:
$$s = m^{d}[n]$$
C’est exactement le travail de verify_signature
. Mais comment fonctionne generate_key(username)
?
Utilisation d’un PRNG
Le module n est en fait crée à partir d’une décomposition en facteurs premiers, puis l’indicatrice d’Euler ainsi que la clé privée en sont déduites.
Comme on s’en doutait, chaque paire de clés ne dépendant que du paramètre username
, la faille réside dans la génération de ces facteurs.
En effet le module random
est utilisé comme PRNG pour initialiser la seed (graine) générant ces facteurs:
|
|
Pour plus d’information sur PRNG-RSA, voir https://www.0x0ff.info/2014/prng-et-generateur-de-cle/ . Grossièrement, il suffira de réutiliser la génération de clés qui nous sont fournies:
|
|
Il ne reste plus qu’à accéder à /news
avec ces paramètres : http://worker04.gcc-ctf.com:14307/news?token=[signature]03&message=[message]
Reverse
Ransom
D’après l’énonce, nous disposons d’un binaire ransom
étrange, chiffrant un certain fichier en secret.txt.enc
sur une machine Debian:
|
|
En éxécutant ce binaire, le contenu de secret.txt
, s’il existe est chiffré dans secrets.txt.enc
.
Pour s’en apercevoir, il est préférable de débugger le programme avec GDB, ou de tracer les appels systèmes:
|
|
Nous avons donc affaire à un keygen en assembleur x86 comme l’indique le challenge.
En 32 bits, plusieurs conventions d’appel existent, ici le ret est fait dans eax
puis le syscall correspondant est effectué avec int 80
, dans la convention FASTCALL.
Afin de faire un peu d’analyse statique, on peut penser à Ghidra, plutôt efficace pour traiter du C ainsi que diverses architectures (ARM,MIPS) mais seules 2 des 4 fonctions présentes seront décompilées, en plus de noter les syscall avec swi
(ARM) sans plus de détail.
Binary Ninja facilite alors le travail:
En s’armant de documentation (https://x86.syscall.sh/), on comprend que la longue suite de inc eax
dans la fin du premier bloc correspond au uname
(syscall 0x7a
=122
), puis que le mot de passe secret_content
lu et stocké dans esi
en 0x0804903c
puis stocké dans eax
en 0x080490c9
subit une comparaison et un xor:
En affichant la valeur contenue à l’addresse de data_804a05d
, on se rend compte qu’il s’agit du résultat du uname
appliqué sur notre machine.
Nous allons donc reverser cet algorithme grâce à cette clé, connaissant le format du flag:
|
|
ou, en devinant la clé, d’après l’énoncé et notre analyse sur le hostname:
|
|
GCC Chat 1
Analyse statique
On dispose d’un fichier GCC_Chat.apk
. Étudions son conde source avec JADX.
Dans, AndroidManifest.xml
on constate que MainActivity
est directement lancée avec l’intent LAUNCHER
.
|
|
On repère également d’autres classes associées à React
et Expo
.
On clique alors sur le MainActivity
qui hérite de la classe abstraite ReactActivity
:
En effectuant Find Usage(x)
sur onCreate
, on remarque en effet une définition de Bundle()
dans ComponentActivity
:
D’autre part on pourrait remarquer l’import de android.os.Bundle
, également présent dans le bytecode Kotlin.
Beaucoup de bibliothèques dynamiques sont présentes , à ce stade on peut lancer l’application dans Android Studio
On peut aussi chercher quelques strings relatives à l’erreur de connection dans:
- ressources/ressources.arsc/res/values/strings.xml
- ressources/res/layout
Ce qui n’est pas concluant. On peut aussi perdre beaucoup de temps à inspecter le code les bibliothèques dynamiques dans Ghidra, mais cela ne mène à rien.
Reverse du bytecode Hermes
Si on n’avait pas prêté attention au Bundle
, on peut alors effectuer un grep dans les ressources statiques:
|
|
Intéressant:
|
|
Avec google, on tombe rapidement sur l’outil hbctool, qui doit être patché pour supporter la version du bytecode:
|
|
Après coup, on aurait pu utiliser hermes-dec:
|
|
Qui permet de produire un assembleur de la fonction de Login:
|
|
Les identifiants sont donc GCC_Staff
et P4ssw0rd
, ce qui permet de valider la première partie.
Misc
DGA
Le nom du challenge faisait référence aux algorithmes de génération de noms de domaines, ayant souvent pour but de cacher la communication entre une machine infectée par un logiciel Command & Control et le serveur de l’attaquant.
Ici nous disposons d’une liste d’url valides et d’une liste malveillante afin de pouvoir entraîner un modèle devant classifier en temps réel avec un taux de réussite de plus de 85%, via un service TCP.
Un collègue ayant résolu ce challenge, je vous propose un writeup qualitatif d’un joueur du CTF pour plus de détails https://nathan-out.github.io).
Voici donc une forme de notre modèle:
|
|
Pour optimiser la détection, on peut supprimer de notre liste les prédictions extrêmes:
|
|
Il ne reste plus qu’à utiliser notre modèle sur le service fourni:
GCC{M4ch1n3_L34rn1ng_15_v3ry_g00d_4t_d3t3ct1ng_m4l1c10us_4ct1v135}
Crédits :
- https://www.elastic.co/fr/blog/supervised-and-unsupervised-machine-learning-for-dga-detection
- https://nathan-out.github.io/posts/ml-x-cybersecurite-analyse-url/
- https://www.kaggle.com/code/omurcantatar/domain-generation-algorithm-dga-detection/notebook
GCC Online
Le challenge se présente sous la forme d’une page web permettant de compiler un code C en donnant le choix de définir des flags de compilation ainsi que des variables d’environnement. Le binaire généré étant tout à fait classique, il faudra donc opérer à la compilation.
Grâce à un coéquipier, on s’arme rapidement de GTFOBins afin d’éxécuter des commandes sur le serveur.
En guise de test, on peut penser à lire /etc/passwd
, ce qui échoue:
Toutefois, en pensant à adapter le premier payload ouvrant un shell, on constate que -wrapper
n’est pas correctement filtré:
On peut donc effectuer un pwd
et constater que l’application Flask lancée par __init__.py
filtre les options à bannir et vérifie que leur syntaxe est correcte:
|
|
Il ne reste plus qu’à afficher le flag, en fournissant en argument:
|
|
Notes:
Le fichier banlist.txt
devait empêcher de fournir les arguments suivants avant sa suppression:
-wrapper
-E
-B
-o
–output
La solution attendue était de lire le code source de __init__.py
|
|
puis de supprimer cette liste avant de réutiliser -wrapper
:
|
|
Web
Remarque: Ces challenges étants résolus par un coéquipier, les exploitations ont été refaites avec les Dockerfile
fournis (voir Introduction)
Free Cider
IDOR
Une feature de réinitialisation du mdp est présente, mais il ne semble pas y avoir d’injections triviales ds ces champs.
On remarque un cookie de session flask
qui ne semble pas vulnérable:
|
|
Le fuzzing était possible mais semblait compromis à cause d’un un rate limiting. En revanche, le code source fait référence à un Swagger:
|
|
On se doute qu’il existe une API Swagger, souvent reliée à /api/v1/swagger.json
, ce qui est le cas ici:
L’API Restful comporte 4 endpoints:
/api/v1/register/
qui est désactivé/api/v1/login/
, nécessite un compte/api/v1/reset-password/
que l’on a déjà entrevu, mais nécessite un compte/api/v1/user/{user_id}
que l’on peut exploiter
Énumérons ainsi les utilisateurs disponibles grâce à l’IDOR fournie par ce dernier endpoint:
|
|
Password reset poisoning
Que faire maintenant? En lisant PayloadAllTheThings sur les faiblesse des password reset on note:
- différence entre la gestion de l’identifiant ou le mail
- code “maison de cette feature et
- jeton prédictible
- empoisonnement du nom de domaine du lien
Il s’agissait de cette dernière technique. Pour la mettre en place plusieurs outils existent, l’important est de disposer d’un serveur:
burp collaborator (Pro..)
requestbin
ngrok
Essayons avec la première utilisatrice:
Ni une, ni deux on reçoit ce token:
On s’empresse de suivre le lien:
Et c’est gagné!
Free Chat
Pour faire simple et concis, voici le writeup de l’auteur
LFI
En se créeant un compte pour l’application, nous avons accès à une application de chat
Le paramètre name
de visualisation de notre photo de profil http://worker02.gcc-ctf.com:12033/pfp?name=default.png
est vulnérable à une File Inclusion (la suite de caractères ../
étant filtrée):
Wildcard DNS & SSRF
Il y a un manque de validation quand on “upload une image”.
L’application vérifie que l’url d’upload commence par
https://pbs.twimg.com
maishttp://pbs.twimg.com@7.tcp.eu.ngrok.io:19922
est accepté et redirige vers notre domaine. Voir plus de détails sur https://nip.io .Si l’on décode en base64 l’url-avatar
http://pbs.twimg.com@localhost:1337
, on obtient le code source de la page:
|
|
En regardant le hint
avec http://pbs.twimg.com@localhost:1337/static/js/hint.js
, on confirme qu’il faut là aussi un token:
|
|
Locate: /var/cache/locate/locatedb
Ici vient une partie plus vicieuse, ne pouvant pas énumérer les dossiers, on doit utiliser en supposant qu’elle existe locatedb
, stockant le PATH de l’application.
On se rend donc ici http://worker02.gcc-ctf.com:12033/pfp?name=....//....//....//....//....//....//var/cache/locate/locatedb
, ce qui télécharge le fichier puis on recherche:
|
|
Le fichier http://worker05.gcc-ctf.com:12033/pfp?name=....//....//....//....//....//....//home/freechat/.for_d3vs_0nly/access_token.txt
contient le sésame.
On peut donc réutiliser la SSRF avec le token:
http://pbs.twimg.com@devpanel:8000/?token=af33aa8342e212020e0ba08bca94e3559f82d3efb0eb7ec6e7eafbc11710f05b
|
|
Merci pour votre lecture!