Inshall'hack
Security if God wills it
Inshall'hack

[FR] Debug en production Writeup (SigSegv1 CTF)

Note : n'étant plus en mesure d'accéder à l'épreuve, je vais expliquer de mémoire certains outputs – train

Debug en production a été développé avec le framework Flask. Étant un adepte de Flask et connaissant parfaitement cette techno je me suis mis sur l'épreuve après avoir jeté un coup d’œil sur les autres challenges.

Description du challenge

LOL mon pote dit qu'il peut se connecter en tant qu'admin sur mon app. Je le crois pas,
mais avant d'accepter son challenge, est-ce que tu peux vérifier que c'est bon stp ?

Découverte

L'application n'avait que deux pages, l'une pour s'inscrire sur le site et l'autre pour s'y connecter. On commence par essayer de s'inscrire mais ça ne marche pas.

On regarde alors la réponse lorsque l'on essaye de s'inscrire en mettant des caractères non alphanumériques et on a un message nous disant que seuls les caractères suivants sont utilisables : [A-Za-z0-9].

Ni une ni deux, on ne cherche pas plus loin et on se dit directement que ça doit être un bug lié à Jinja2.

À ce moment-là, il était environ 2h du matin et j'étais en mode "easy ça doit être une SSTI avec des filtres".

Après plus de 2h30 à essayer toutes les possibilités avec un bug quelconque en lien à Flask ou Python, je me suis rendu à l'évidence : cela n'a aucun rapport.

Analyse des sources

Nous avions des bouts de sources en commentaire dans chacune des deux pages. Sur la page http://finale-docker.rtfm.re:8001/showSignUp nous avions ceci :

<!--
    30/11/18 - added a stored procedure to add users

    BEGIN
    IF (select exists (select 1 from tbl_user where login = p_login)) THEN
        select 'Username exists!';
    ELSE
        START TRANSACTION;
        insert into tbl_user
        (
            login,
            password
        )
        values
        (
            p_login,
            p_password
        );
    END IF;
    END$$
    DELIMITER ;

    01/12/18 - my friend told me not to store passwords as plaintext
    Removed everything from the database and disabled persistence of changes in db until I fix this.
-->

Sur la page http://finale-docker.rtfm.re:8001/showSignIn nous avions ceci :

<!-- 30/11/18 - added secret form parameters debugVar and debugVal to help debug the app. -->

Si ces paramètres secrets ne servent pas à notre SSTI imaginaire, alors comment les utiliser ?

Seconde idée d'exploitation

Après quelques minutes on se rend compte qu'en concaténant les paramètres de connexion et les paramètres secrets, nous n'avons plus d'erreur.

Intéressant. Essayons de voir ce que nous retourne l'insertion d'un caractère non-alphanumérique.

~$ curl -d "inputName=test&inputPassword=test&debugVar=siben!is!sadistic&debugVal=yesSadistic" -X POST http://finale-docker.rtfm.re:8001/signIn"
Set variable and value fields can only contain chars in [_\-0-9A-Za-z]

Hum, intéressant. Nous pouvons utiliser deux caractères supplémentaires, _ et -.

À partir d'ici, je me suis dit que l'on pouvait certainement modifier directement le login et le password de la table tbl_user , la question était comment modifier ces champs avec si peu de caractères autorisés. Après beaucoup de recherches sur les oneliners/tricks possibles en SQL, j'ai fini par abandonner cette hypothèse.

Il était déjà 8h du matin. La frustration se faisait sentir, et une bonne pause s'est imposée.

De retour vers 9h, j'ai encore une fois décidé de reprendre les choses à zéro et d'imaginer quel bug il pouvait y avoir en espérant que ce qui avait été testé précédemment n'ait pas servi à rien.

Frustration puissance 1000 mais bon, abandonner c'est comme se couper une couille pour moi, et le dieu du Tryhard sait que j'adore mes couilles, donc on est reparti sur une nouvelle idée.

Troisième idée d'exploitation

Et si les deux paramètres debugVar et debugVal étaient utilisés directement dans le SQL ? Serait-ce possible que des variables systèmes SQL nous permettent de résoudre ce challenge ?

Idée plausible mais il manque beaucoup de choses pour que cette théorie marche. Comment utiliser notre page d'inscription sachant que nos paramètres de debug ne sont utilisable que sur la page de connexion ?

Cette question n'a en réalité que très peu de réponses et la plus envisageable reste la race condition.

En effet si on y réfléchit, nous savons que lors de la phase de création d'un utilisateur, la transaction est finalement rollback.

La supposition est donc maintenant qu'il existe un couple variable/valeur permettant d'exploiter cette race condition.

On a maintenant une nouvelle théorie qui semble jouable, testons-la directement en envoyant une variable système.

~$ curl -d "inputName=test&inputPassword=test&debugVar=admin_address&debugVal=0" -X POST http://finale-docker.rtfm.re:8001/signIn
Forbidden variable name!

Ce message nous permet de savoir que nous sommes sur la bonne voie. Nous allons commencer par faire l'énumeration de toutes les variables qui sont accessibles et donc qui ne génèrent pas ce message d'erreur.

J'ai d'abord commencé par essayer l'enumération de toutes les variables systèmes de cette page.

Après pas mal de tests et de temps à lire la documentation de chacune des variables que l'on pouvait utiliser, je me suis rendu compte que MySQL avait des variables systèmes dynamiques.

En voyant cela j'ai directement pensé à dlopen qui permet de charger dynamiquement des dll, qui est parfois utilisé dans certaines épreuves.

Allons jeter un coup d'oeil ici.

Comme vous pouvez le voir, il y a beaucoup de variable à tester donc il va falloir encore une fois réfléchir pour trouver la bonne.

La première hypothèse était d'utiliser une variable en lien avec des logs pour récupérer de l'information. Hypothèse peu fructueuse, mais la seconde, la bonne, a été de chercher des variable qui comportent le mot transaction ou rollback dans leur nom pour essayer d’altérer dynamiquement le comportement de la transaction ou du rollback.

Ce test nous permet de trouver 14 variables, 13 contenant transaction dans leur nom et une contenant rollback.

Après avoir relancé le petit script qui nous permet de voir les variables accessibles, nous en avons environ la moitié d'accessibles (je ne me rappelle plus du nombre exact).

On repart donc sur la documentation de celles qui sont accessibles et nous trouvons la variable transaction_isolation.

Nous avons ici quatre valeurs possible pour cette variable :

    READ-UNCOMMITTED
    READ-COMMITTED
    REPEATABLE-READ
    SERIALIZABLE

J'ai commencé par essayer avec READ-COMMITTED. On lance donc deux terminaux pour effectuer notre race condition :

shell 1 :

while true; do curl -d "inputName=test&inputPassword=test" -X POST http://finale-docker.rtfm.re:8001/signUp; done

shell 2 :

while true; do curl -d "inputName=test&inputPassword=test&debugVar=transaction_isolation&debugVal=READ-COMMITTED" -X POST http://finale-docker.rtfm.re:8001/signIn; done

On attend quelques secondes et là... et bien rien du tout. Je me disais que si le READ-UNCOMMITTED ne marchait pas, j'allais... ("je vous laisse deviner").

Bref, si on lit la documentation de READ-UNCOMMITTED on trouve ceci :

"An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed. It is only possible with the isolation level known as read uncommitted."

On fini donc par modifier dans notre shell 2 READ-COMMITTED par READ-UNCOMMITTED et là... et bien toujours rien !!!!

Mais alors pourquoi ? Du coup on relit la description du challenge et on voit que l'utilisateur qui essaie de se connecter a comme username admin.

On finit par lancer ceci:

shell 1 :

while true; do curl -d "inputName=admin&inputPassword=lala" -X POST http://finale-docker.rtfm.re:8001/signUp; done

shell 2 :

while true; do curl -d "inputName=admin&inputPassword=lala&debugVar=transaction_isolation&debugVal=READ-UNCOMMITTED" -X POST http://finale-docker.rtfm.re:8001/signIn; done

Et flagged : sigsegv{1s0l4t10n_mY_455}, ce qui me permettra d'être first/only flag.

Conclusion

Je conclurai par ceci : la clé, c'est la combinaison du RTFM et du tryhard.

Merci à SIben pour ce chall.


comments powered by Disqus

Receive Updates

ATOM

Contacts