Unix Audit Script One Liners

Lately I’ve had quite a few requests come in from students and clients to review the audit script that companies are using to audit their Unix / Linux systems. It seems like every company has one person who, at some point in time, wrote a script to audit Unix systems, or they downloaded one from someone online. But in either case people keep wondering – exactly what information are they gathering, and is it the right information to help in an audit?

I know there are multiple languages a company could use to write their Unix audit script, but for me BASH (Bourne Again Shell) scripting has always been the way to go. I don’t have any problem with a company using Perl or Python for their scripts. My only concern is that there have been quite a few times that during an audit an interpreter for either language might not be on the system I’m auditing. And due to principle, I just don’t feel right asking a sysadmin to install an interpreter – not that they would even if I asked!

All that being said, I figured I would write up some Unix audit one liners to inspire you to write your own scripts. If you are writing your own, here are a few to get you started:

Display the name of the system:

Unix Host Name: $(hostname -s)

Display the DNS name of the system:

DNS Domain Name: $(hostname -d)

Display the CPU installed in the sytem:

CPU Model Name: $(cat /proc/cpuinfo | awk -F": " '/model name/{CPU=$2} END{print CPU}')

Display the CPU speed of the installed CPU:

CPU Speed (MHz): $(cat /proc/cpuinfo | awk '/cpu MHz/ { print $4 }')

Display the installed physical memory:

Total Physical Memory (MB): $(free -m | awk '/Mem:/ { print $2 }')

Display the memory in use on the system:

Used Physical Memory (MB): $(free -m | awk '/Mem:/ { print $3 }')

Display the available memory on the system:

Available Physical Memory (MB): $(free -m | awk '/Mem:/ { print $4 }')

We will post more one liners later this month. Hopefully this inspires you to take a look at the script you use or maybe even start writing your own.

PowerShell Remoting and Get-Process

One of the exciting features of Microsoft Windows PowerShell is the ability to run commands both against a local computer and against remote machines that the user has rights to access. PowerShell has a couple different ways it can do this – one is through Windows Management Instrumentation (WMI) calls against remote machines and the other is through true PowerShell Remoting capabilities (similar to having a telnet or SSH session open on a remote machine).

From an incident response point of view the idea of being able to centrally gather information from dozens or hundreds of machines simultaneously opens the door to much faster and efficient incident response activities. For example, imagine you have a process named evil.exe that you know is running on a number of machines in your organization. This is an Indicator of Compromise (IOC) that you’ve discovered and so you want to discover every machine in your organization where this command is running.

Using a cmdlet such as GET-PROCESS, an incident handler could use the –COMPUTERNAME parameter which will instantiate a WMI call against a list of machines, and as a result the incident handler could determine which machines are running the malicious process. For example, if you had a text file called computers.txt with a list of all your computer nodes in it, you could run the following script:

$Computers = get-content computers.txt

get-process -computername $computers

This script would return a list of all the running processes across all of the computers listed in the computers.txt file. From here you could perform a WHERE-OBJECT query to look for every instance of the evil.exe process as in the following code:

$Computers = get-content computers.txt

get-process -computername $computers | where {$_.ProcessName -eq "evil.exe"}

In fact if you really wanted to have fun, and you knew the effects of doing so, you could even add a few words to the above code and delete every instance of the evil.exe code that you discovered. And while it may not keep a dedicated attacker off your network perpetually, it might buy you some time as you perform your incident response work.

$Computers = get-content computers.txt

get-process -computername $computers | where {$_.ProcessName -eq "evil.exe"} | stop-process

But please be careful with the above script. Although it might be fun to kill all instances of sol.exe (Solitaire) at lunch every day, I’m not sure your staff will appreciate the humor…

Parsing Windows Firewall Rules

In our last post we discussed how to gather general information about the configuration of a Microsoft Windows Firewall, host based firewall configuration. But what most people are really interested in when doing a firewall audit is how the firewall rules themselves are configured.

One of the challenges of auditing a Microsoft Windows Firewall ruleset is how do you parse all the firewall rules that Microsoft automatically creates for you? It is great that Microsoft automatically configures most of the rules – it helps encourage us to actually leave the firewall turned on. But with all those rules, especially the disabled ones, how does an auditor easily parse through the data? And to make matters worse, the output of commands like NETSH is just text – not a PowerShell object. So that makes it even more difficult and time consuming to parse.

So that got us thinking, what if we could convert the output of a NETSH SHOW command for Microsoft Windows Firewall rules into a PowerShell object that we could more easily parse?

So we did a little digging and found out that Jaap Brasser had already created a basic script to do that at PowerShell.com (http://powershell.com/cs/forums/t/13260.aspx). The following code allows us to take the output from a NETSH command, and convert it to a PowerShell object, so we can more easily parse the ruleset:

$Output = @(netsh advfirewall firewall show rule name=all)

$Object = New-Object -Type PSObject

$Output | Where {$_ -match '^([^:]+):\s*(\S.*)$' } | Foreach -Begin {
$FirstRun = $true
$HashProps = @{}
} -Process {
if (($Matches[1] -eq 'Rule Name') -and (!($FirstRun))) {
New-Object -TypeName PSCustomObject -Property $HashProps

$HashProps = @{}
} $HashProps.$($Matches[1]) = $Matches[2]
$FirstRun = $false
} -End {
New-Object -TypeName PSCustomObject -Property $HashProps}

Now that the firewall rules are a PowerShell object we can use cmdlets like WHERE-OBJECT and SELECT-OBJECT to filter the information. We can perform ad hoc queries and work with the information however we see fit. Enjoy!

Parsing Active Directory Groups

In a previous post we shared a PowerShell script that would allow an auditor to parse a list of groups and group members on a Microsoft Windows system as a part of a security assessment or baselining process. The question has come up though – what if someone wants to follow the same process but parse a list of Active Directory groups and their members instead?

It turns out that it is much easier to perform the task against Active Directory than it is to do it against a local Microsoft Windows local Security Accounts Manager (SAM) database. It seems that the native capabilities of the Active Directory PowerShell cmdlets is going to make it easier for us to accomplish this task.

The output requirement for this script will be the same as for the previous. The output should be in a CSV format that could be easily parsed by a spreadsheet program like Microsoft Excel. Also, like the previous script, empty groups should still be listed, but just without any group members listed along with them.

The code we used to parse a list of AD groups and their members is:

Get-ADGroup -Filter * | ForEach-Object { 
    $GroupName = $_.Name 
    $Members = Get-ADGroupMember $_.samaccountname -Recursive | foreach{$_.Name} 
        If ($Members.count -ge 1){
            $Out = $GroupName + "," + [String]::Join(",", $Members)
            $out | Out-File -append ad_group_members.txt
        }
        Else{
            $Out = $GroupName
            $out | Out-File -append ad_group_members.txt  
        }
    } 

Ideally an auditor would combine this script with our previous example and using PowerShell remoting run one script that would parse local and Active Directory groups at the same time and output the data as an easily readable file. But between the two examples, hopefully it will give you enough inspiration to make this usable for you in your audit efforts.

Parsing Local Windows Groups

One step that has become a staple part of any audit of Microsoft Windows systems is a listing of all the local groups on the system. Listing all the groups on a system with all the members of that group can help establish a baseline for the security configuration of a system. Certainly groups like the local Administrators group is a concern during a security audit. Ideally though auditors would have the ability to look at the members of each group on a system and validate that the list of members is appropriate for that system.

As with many of the scripts we have recommended, we decided to write a script using PowerShell to pull the information from a local system. We also wanted to make sure that when we pulled the information from a system that we could parse it easily once we were done. Therefore we wrote the script to allow us to pull all the local computer groups from a system, with the members of the group, and then save it as a CSV file for easier parsing with Excel.

Here is the code we used for the script:

$GroupList = Get-WmiObject Win32_Group | ForEach {$_.name}

$ComputerName = "."

ForEach ($GL in $GroupList)

{

    $Computer = [ADSI]("WinNT://" + $ComputerName + ",computer")

    $Group = $Computer.psbase.children.find($GL)

    $Members= $Group.psbase.invoke("Members") | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}

        If ($Members.count -ge 1){
            $Out = $GL + "," + [String]::Join(",", $Members)
            $out | Out-File -append local_group_members.csv
        }
        Else{
            $Out = $GL
            $out | Out-File -append local_group_members.csv
        }

}

We ran into a couple issues when writing the script. One was the formatting of the output to a CSV file. Normally we would use an EXPORT-CSV cmdlet to do that, but in this case since we had multiple data sources we had to do some custom formatting. Also since some of the groups were empty, we had to add the IF clause to make sure that even blank groups were reported in our output.

Once the concept of the script starts to make sense to you, it should be an easy next step to consider adding PowerShell remoting capabilities or a computer name list to the script to parse multiple systems at once. You might also consider saving the output from the script in a format (such as HTML) that is easier to read and include in your audit reports.

We hope the script is useful as you audit Microsoft Windows servers or establish security baselines for those systems.