Tuesday, December 6, 2016

Living off the Land: Powershell Ping Sweep "One-liners"

Introduction



The Oxford English Dictionary defines the phrase "Living off the land" as to "live on whatever food one can obtain by hunting, gathering, or subsistence farming". Similarly, in the cybersecurity world, it means that, once a machine is compromised, an attacker, in efforts to remain stealthy, could attempt to use a system's available applications and resources to their advantage. In the scenarios below, my purpose is to use Powershell on a compromised system and map the rest of the network via ICMP echo requests (with corresponding replies). I'm doing this for two reasons: 1) Powershell is on everything Windows 7 and newer and 2) Not too many organizations block ICMP to/from internal hosts. This would be a quick and easy way to start mapping out a network. There is a ton more capability built into Powershell, but that's for a later blog post. I'll keep this one simple. I'm also writing my example commands as long "one-liners" instead of scripts.

Why Not Script These?

Once I gain access to a system, I would want to remain stealthy. Pulling down Powershell scripts or other scanning applications from the outside could easily set off lots of alarms on either network monitors or host intrusion systems. Instead, I would prefer that my network mapping be done natively on the affected host using tools that are already available. In this case, powershell.exe. I've stated in previous articles that, out of the box (and in most organizations), Powershell is not monitored very well... if at all. This would most likely provide an attacker with great opportunities to do some recon or even pivot throughout the network.

The Environment

Nothing fancy at all here. I'm just running a virtual machine in Virtualbox and just have the following installed:
  • Windows 7 with latest patches
  • .Net Framework 4.6
  • Powershell 5.0 (not tested, but I believe most of the examples will work with 3.0 and newer)
Or... just use Windows 10 since it comes with all of the above and is ready to go.

Simple Class C Ping Sweep

The first example starts pretty easy, but the complexity really ramps up after this. The goal here is to just get a list of everything that'll respond to a ping on a given class C (or... better defined as a "/24" network).

PS> $results = ""; for ($d = 0; $d -le 255; $d++) { $test = ping -n 1 -w 5 172.16.0.$d; if ($test[2] -match "TTL") {$results += $test[2].split(": ")[2] + "`n"}}; $results
  • $results = "";
    • Start with a blank string that, later on, gets results appended to it
  • for ($d = 0; $d -le 255; $d++)
    • "For" loop for the last octet's range
    • Just replace the "0" and the "255" if you'd like a different range
  • $test = ping -n 1 -w 5 172.16.0.$d
    • Save the output of each ping to the "test" variable
    • If you want to scan a different /24 network, simply replace "172.16.0" with the first three octets of the target network
  • if ($test[2] -match "TTL") {$results += $test[2].split(": ")[2] + "`n"
    • Complex, but, in a nutshell, if test contains TTL (successful ping), then add the IP to the results string and make a new line for the next one
  • $results
    • Write the results to the screen

Simple Class C Ping Sweep (verbose)

For this example, it's pretty much the same, except there may be times where you would want to see some sort of progress instead of just waiting until the end. For this, I've highlighted the additions to the string of commands in red.

PS> $results = ""; for ($d = 0; $d -le 255; $d++) { "pinging 172.16.0.$d"; $test = ping -n 1 -w 5 172.16.0.$d; if ($test[2] -match "TTL") { "ALIVE"; $results += $test[2].split(": ")[2] + "`n"}}; $results
  • "pinging 172.16.0.$d"
    • Simply put, prior to every ping, "pinging 172.16.0.X" is printed
  • "ALIVE";
    • If the host is up ("TTL" received), print "ALIVE" to the screen

Simple Class C Ping Sweep (write to file)

This example just extends the previous one by adding a write to a file in the current directory.

PS> $results = ""; for ($d = 0; $d -le 255; $d++) { "pinging 172.16.0.$d"; $test = ping -n 1 -w 5 172.16.0.$d; if ($test[2] -match "TTL") { "ALIVE"; $results += $test[2].split(": ")[2] + "`n"}}; $results | Out-file "ping_results.txt"
  • | Out-file "ping_results.txt"
    • Write the contents of the results variable to "ping_results.txt"

Low and Slow Class C Ping Sweep

In some instances, network and host defenses may have preprocessors to determine that scanning is happening and, if time is not an issue, a "low and slow" scan may be the way forward to remain undetected. I've simply taken the first example and added some delay to it, but, if you crave verbosity or file writing, simply take this strings of commands and include the additions above.

PS> $results = ""; for ($d = 0; $d -le 255; $d++) { $test = ping -n 1 -w 5 172.16.0.$d; if ($test[2] -match "TTL") {$results += $test[2].split(": ")[2] + "`n"} sleep 60}; $results
  • sleep 60
    • After each iteration of the loop, do nothing for 60 seconds

Class C Ping Sweep with Randomized Hosts

To shake things up a little more and to potentially evade any IDSes that may notice sequential pings, I'm going to randomize the hosts. This one took some time and is VERY complex, but it gets the job done and all without the use of a script or other resources.

$allHosts = @(); for ($d = 0; $d -le 255; $d++) { $allHosts += "172.16.0.$d" }; [System.Collections.ArrayList]$allHosts = $allHosts; $results = ""; for ($i = ($allHosts.Count - 1); $i -ge 0; $i--) { $rand = Get-Random($i + 1); "pinging " + $allHosts[$rand]; $test = ping -n 1 -w 5 $allHosts[$rand]; if ($test[2] -match "TTL") { "ALIVE"; $results += $test[2].split(": ")[2] + "`n"; } $allHosts.RemoveAt($rand); } $results
  • $allHosts = @()
    • Create a blank string array that will eventually contain all targets
  • $allHosts += "172.16.0.$d"
    • Each time through the loop, add the target host to the array (should have 256 entries in this example... 172.16.0.0 through 255)
  • [System.Collections.ArrayList]$allHosts = $allHosts
    • Convert the array to an ArrayList since, apparently, you can't remove items from a standard string array
  • for ($i = ($allHosts.Count - 1); $i -ge 0; $i--)
    • Another for loop that starts at an integer ($i) equal to how many items are in the array (minus one) and subtracts one from $i each time through until it's less than 0
    • The reason for this is that array indexes start at 0 (instead of one). The number of entries in this array is 256, but the indexes go from 0 to 255
  • $rand = Get-Random($i + 1)
    • Get a random number between 0 and whatever $i is set to. The "+ 1" is there for a strange reason. If you use Get-Random(2), it returns either 0 or 1... which is two possibilities
    • The first time through, it's a random number between 0 and 255... just what we want
    • The second time through, it's a random number between 0 and 254... etc (this will make shortly)
  • "pinging " + $allHosts[$rand]
    • Just a status update printed to the screen of which host is being pinged at random
  • $test = ping -n 1 -w 5 $allHosts[$rand]
    • Set the results of the randomly-pinged host to the "test" variable
  • $allHosts.RemoveAt($rand)
    • Remove the randomly-pinged host from the array so it's not scanned again
    • This is how the array decrements every time (which matches the i variable throughout) until there are no members left in the array. This prevents duplication and ensures pings of all hosts are attempted

Simple Class B Ping Sweep

That last one was quite a lot, so now back to something a little basic. With this one, I've taken the first example and made more additions (in red) in an effort to scan a /16 network. 

$results = ""; for ($c = 0; $c -le 255; $c++) { for ($d = 0; $d -le 255; $d++) { $test = ping -n 1 -w 5 172.16.$c.$d; if ($test[2] -match "TTL") {$results += $test[2].split(": ")[2] + "`n"}}}; $results
  • for ($c = 0; $c -le 255; $c++)
    • Create another "for" loop for the third octet portion of the IP address
    • In this example, it'll scan 172.16.0.0 through 255, then 172.16.1.0 through 255, ... and finally 172.16.255.0 through 255
  • $test = ping -n 1 -w 5 172.16.$c.$d
    • Just sets the third octet the value of "c"

Conclusion

There would easily be hundreds more examples, but hopefully you learned enough to take this and be creative in your own (legitimate) endeavors. Soon, I plan on doing a Linux Bash version of this, so stay tuned! Comment below if you have any suggestions or insights to share!


Sunday, December 4, 2016

Red vs. Blue: Monitoring User-Agent Strings

The Purpose

Security teams can only go so far when it comes to trying to keep control over what browsers or applications tenants on their networks are using. Often, there are attempts to limit users' rights to install software. This typically works pretty well with standard users, but what about the administrators? Sure, lots of larger organizations have host-based solutions to prevent even administrators from modifying their systems in efforts to bypass corporate policy, but what about those organizations that do not yet have this implemented due to cost or expertise? Below I will go back and forth between different "Red" tactics and the corresponding "Blue" response to try to capture those abusers of our policy (or, better yet, detect compromised systems).

The Environment

All of the following machines will be running in Virtualbox on a Windows 10 host machine. I will be using the same Security Onion virtual machine as my first post and a Windows 7 virtual machine acting as our malicious insider. Additionally, I am setting up a web server the easiest way I can think of: a Xubuntu Linux client that will be serving a "web page" via the following command:

sudo python -m SimpleHTTPServer 80
  • Run as an administrator
  • Execute Python
  • Import module, SimpleHTTPServer
  • Listen on port 80 (thus, requiring administrative rights)

Note: Most (if not all) popular Linux distributions come with Python and this corresponding SimpleHTTPServer module. By default, the above command can be run as a standard user if attempting to listen on an ephemeral port (TCP port greater than 1023).

What will be looked at?

Most of this article will be focused on user agent strings. User agent strings are simply what a browser or application uses to identify itself and the host to the web server. Based upon the user agent string, a web server may decide the content or format that is displayed to the browser. For example, this is how web servers know when to use the mobile version of their site. Below is an example of the user agent string that I am using on my host machine that is typing this blog post:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36

Looks to be pretty much as I expected... a 64-bit Windows 10 host running Google Chrome 54.0. The rest of fields are simply browser information and capabilities. This link has a funny read if you're interested in understanding the different user agent string verbiage.

Why look at user agent strings? 

It's a simple indication that, unless the malicious actor is a little sophisticated, the user agent will differ from what should be seen on the network if they would be using an unapproved browser or application to access a web server. I'll cover a couple of these "more sophisticated" means that could be used a little later.

The "Corporate Baseline"

Let's assume that this sample corporation in the following scenarios has a policy that allows only for those applications in the baseline to be used on the corporate machines. The corporation utilizes Linux servers and Windows 7 workstations. As a best practice and part of the baseline, the only machines that can access the internet are the Windows clients and they must use Internet Explorer 11. I'll admit... a lame policy, but it's what I'm going with to keep it simple.

First, the Security Onion virtual machine will use Wireshark to listen to the network and capture some "internet" traffic from the sample Windows client to the Xubuntu server. I'll accomplish this by:
  • Security Onion VM: open wireshark from the command line and listen to my sniffing port (in my case, eth1)
  • Windows 7 VM: Open IE 11 and navigate to the Xubuntu "web server" (in my case, http://172.16.0.201)
  • Security Onion VM: Stop the Wireshark capture
Now I have what a "normal" user agent string would look like, but I do have a lot of other data. A good way to find user agent strings in Wireshark would be to look at GET requests by using the filter, frame contains "GET".


That's the GET request from the client (172.16.0.50) to the server (172.16.0.201). To get what the actual user agent string is, I have to drill into at the HTTP header in the Packet Details pane:


This is the user agent string that the Windows 7 machine is sending (you'll notice Windows NT 6.1... Microsoft had made numbering a bit confusing until recently as seen here) running Internet Explorer 11.0.

Blue Team Response: Snort

Now that I know what "normal" looks like, a simple Snort rule can take it from here and show what doesn't match the approved user agent string. This actually took a bit of Googling as 99% of the time when developing a Snort rule, you're looking for a positive match, not a negative one. After a bit of trial and error, I added this rule to /etc/nsm/rules/local.rules on the Security Onion VM:

alert tcp $HOME_NET any -> any $HTTP_PORTS (msg: "Unapproved User-Agent"; content: "User-Agent"; content: !"Mozilla/5.0 (Windows NT 6.1\; WOW64\; Trident/7.0\;  rv\:11.0) like Gecko"; sid: 4000005; rev:1;)
  • Send an alert (which will be viewed in Sguil)
  • Only look at the home network headed to a web server on HTTP ports
  • Message to be sent to the analyst
  • Look for packets that contain the string, "User-Agent"
  • Highlight any of the filtered packets that do not have the approved user agent string (Note the "\" escapes that are required if the string contains a ";" or ":")
  • Snort ID and revision number
After this update, I'm going to run sudo rule-update to implement the new rule.

Monitor with Sguil

Now to test. I'm going to launch Sguil on the Security Onion VM and rerun the baseline test above with the Windows 7 and Xubuntu client. If an alert comes up, the alert was likely typed wrong as it should not fire if the user agent string matches.

The next test for this rule is to install an alternate browser and rerun the test again. Hopefully, I'll see an alert (which should contain the user agent string of the misconfigured system). Here's what was found in Sguil after installing Google Chrome on the Windows VM and connecting to the Xubuntu VM again:



Success! It seems that the alert caught a Windows 7 machine running Google Chrome 55.0. So, this should be fine, right?

Red Team Tactic: Downloading with Powershell

The above rule would certainly catch administrators that install unapproved web browsers and simply start using them, but there's another scenario that may prove a bit more malicious and would completely evade this rule.

In this scenario, there is a malicious insider that wants to download unapproved files, but does not want to have a trail of Internet Explorer browsing logs. Before actually running the command below, though, I will bring up Wireshark again on the Security Onion VM to watch this traffic. On the Xubuntu "web server", I'm hosting a file named FreeMusicPlayer.exe. Let's see if our rule catches it if the insider uses the following Powershell command to download the file:

(New-Object System.Net.WebClient).DownloadFile("http://172.16.0.201/
FreeMusicPlayer.exe","FreeMusicPlayer.exe")
  • Create a new Web Client
  • Download a file from the Xubuntu host and name it "FreeMusicPlayer.exe" inside the current directory
After the Powershell command is executed, I would expect to have my alert count increase to 3 (it was 2... and it still is 2). So what's going on? Let's take a look at the new "GET" request in Wireshark:


That's strange... no User-Agent field. It seems that Powershell, by default, does not send a user agent string when downloading a file! This tells me that a user agent string is not required and, after digging into RFC 2616, there's a lot of SHOULDs but no REQUIREDs in regards to user agent strings. So that means, our pre-requisite that we look for "User-Agent" before doing the check against our whitelisted string is not met. Also by default on the system, there are no Powershell command logs to be found when digging through Event Viewer. FireEye has a great article on Powershell logging that should be implemented in any Windows shop to really see what Powershell users are really doing.

Blue Team Response: Another Snort Rule

This is a rather simple fix. Since this was a "GET" request, a second rule can be made to catch users that are trying to download via Powershell. Sure, the first rule could be modified to key on "GET /" instead of "User-Agent", but I would have to generalize my message to an analyst... something like "Unapproved download method". I like to stay a little more descriptive, so that's why I'm creating a second rule named "GET Request without User-Agent String Detected". That's nice and descriptive. I've also realized this time around, that everything being looked for is in the HTTP header -- not the data, so both the previous and new alerts can be made more efficient by adding "http_header". The new alert looks like this:

alert tcp $HOME_NET any -> any $HTTP_PORTS (msg: "GET Request without User-Agent String Detected"content: "GET /"content: !"User-Agent"http_headersid: 4000006; rev:1;)
  • New message to be sent to the analyst
  • Look for packets that contain the string, "GET /"
  • Highlight any of the filtered packets that do not have a user agent string
  • Restrict the search to the HTTP header fields
  • Snort ID and revision number
Now, I'm going to re-run sudo rule-update and try the Powershell download again...


Voila! We caught our guy/gal!

Red Team Tactic: Modified User Agent String

We're not out of the woods yet. Powershell, wget, curl, and even web browsers offer the ability to change the default user agent strings. I will be running Wireshark again to see the frame and Sguil to check to see if any alerts pop up. Below is an example of Powershell downloading a file after setting a custom user agent string:

$wc = New-Object System.Net.WebClient
$wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1\; WOW64\; Trident/7.0\;  rv\:11.0) like Gecko")
$wc.DownloadFile("http://172.16.0.201/FreeMusicPlayer.exe", "FreeMusicPlayer.exe")
  • Create a new WebClient variable named $wc
  • Add the User-Agent header information
  • Download the file using the WebClient


Looking back at Sguil I see... nothing new! What was seen in Wireshark?



Looks like IE 11.0 to me... and to Snort.

Red Team Tactic: Spoofing the Web Browser

This is actually pretty easy to do in Chrome as well using Developer tools (no third-party plugins needed):

  • Open Chrome to a blank page so no GETs are sent
  • Open Developer Tools (Ctrl+Shift+I in Windows)
  • Click on the three vertical dots >> More tools >> Network conditions
  • Here, you can set custom and default  user agent strings
  • Navigate to desired site and appear as IE 11.0

Running the test again, no new alerts! Wireshark again verifies that the user agent is successfully spoofed:


Blue Team Response: ?

This is quite the cat and mouse game! There's even several factors that would keep any rule-writing analyst a bit busy which could take this article down many paths:
  • What if the insider is using TCP ports not defined in Snort (right now, I have only 80 and 8080)?
  • If a user is spoofing user agent strings, what are they trying to hide? What should be looked for?
  • What other mechanisms could be implemented on the network to catch some of this?
  • What about potential false positives? Example: Java applications can create their own user agent strings as well
The intent was to get security team members thinking, so please comment below to add your thoughts as I just scratched the surface on this topic.