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!


No comments:

Post a Comment