PowerShell IP Geolocation based on State
I have been revisiting a few things in the lab environment, including monitoring and other alerting that I have had set for a while. I had an outside box that even with the other security measures in place, I wanted to have a way to see who is connected and which IP address they are coming from. I wanted to add the IP geolocation to my IP address checks that I am making so I could then use the details gathered from an IP geolocation check to hone in on more specifics of where RDP traffic is coming from. In most modern firewalls, most geolocation checks are country-based. In the United States, let’s see how we can use an API call, PowerShell and filtering on the PowerShell return to form logic around not only the Country, but also the State. Let’s look at PowerShell IP Geolocation based on State.
IPstack IP Geolocation API
There is a service that works great for performing IP geolocation checks using an API. You can use this in your PowerShell scripts to make a call to the API check the geolocation information and return the results as XML, JSON, etc. The IPstack IP geolocation API allows you to make 5000 calls to their API a month. For moderately sized businesses this wouldn’t be enough, however, for home labs, testing, and other use cases, it works great.
Sign up for the IPstack account. No payment information is requested during the sign up which is also another great aspect of the service.
Once you have your account, you will be provided an API key to work with to perform lookups that hit their API.
PowerShell cmdlets for gathering network information
You can use the get-nettcpconnection to gather network connection information much like netstat. What I like about the cmdlet as opposed to netstat is that you can do all of the great things with the returns due to PowerShell’s object-based approach. It is a lot easier to work with than trying to find strings and use information from the netstat return.
How can you display the remote IP of a remote client connected to a terminal server/workstation? You can use the following
get-nettcpconnection | where-object {$_.LocalPort -like "3389" -and $_.LocalAddress -ne "::" -and $_.LocalAddress -ne "0.0.0.0"} | select-object -expandproperty RemoteAddress
That’s it. Once you have the remote IP address, you can then pass this information into the ipstack API using PowerShell and get a return on the IP address. Below is an example of the returned output coming back from ipstack. It tells you the:
- ip
- type
- continent_code
- continent_name
- country_code
- country_name
- region_name
- region_code
- region_name
- city
- zip
- latitude
- longitude
- location
Once you get this information back, you can then select the properties you want to key off of, like the following:
$region = Invoke-RestMethod -Uri "http://api.ipstack.com/$ip$access_key" | select -expandproperty region_code
For the United States, the region_code refers to the State. So, let’s see how we can use this in a script. Below is the rough version of the script I have put together that works. Essentially, if someone connects from the state that I expect to be connected from, I will just get an email with the details of the connection. However, if it is from a state I don’t expect, I will get both an email and a page out (just email to text address provided by carrier).
- Download the script from github
##Variables
$ip = get-nettcpconnection | where-object {$_.LocalPort -like "3389" -and $_.LocalAddress -ne "::" -and $_.LocalAddress -ne "0.0.0.0"} | select-object -expandproperty RemoteAddress
$access_key = "?access_key=<yourkeygoes here>"
$region = Invoke-RestMethod -Uri "http://api.ipstack.com/$ip$access_key" | select -expandproperty region_code
##Email Alert variables
$SMTPServer = "<your mail server"
$SMTPPort = "587"
$Username = "<your username"
$Password = "<password>"
$to_email = "<recipient email>"
$to_page = "<page email""
##Checking IP and Email alert
Invoke-RestMethod -Uri "http://api.ipstack.com/$ip$access_key" | out-file alldetails.txt
if ($region -eq "CA") {
$message = New-Object System.Net.Mail.MailMessage
$message.subject = "$ip has connected"
$message.body = (Get-Content alldetails.txt) -join "`n"
$message.to.add($to_email)
$message.from = $username
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
write-host "Mail Sent"
}
if ($region -ne "CA") {
$message = New-Object System.Net.Mail.MailMessage
$message.subject = "$ip has connected"
$message.body = (Get-Content alldetails.txt) -join "`n"
$message.to.add("$to_page,$to_email")
$message.from = $username
$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
write-host "Mail Sent"
}
Where does this go?
You can add this script to fire off on a system event in your scheduled tasks. I have long used log on and reconnect events to trigger scripts to run. This is where you can place the script and it fires off when the event specified is noted. I have written about this in a previous blog post that you can read through here:
There is a lot of room to add additional functionality here. In the next version of the script, I would like to add the functionality of an RD filtering type solution where if it is not coming from the state I expect, it will be added to a Windows firewall rule upon connecting, on top of the notification received.
Wrapping up
The great thing about PowerShell is that generally real-world use cases are what cause scripts to be born. I have had a need recently to revisit my security on the edge on top of things I am already doing. You can never have too much visibility into who is connecting to your network resources, even if you have additional security measures in place such as firewall rules, third-party brute force type tools, etc.