During the course of my analysis on these types of cases, I have found several indications that PowerShell has been utilized by an attacker. These include installed services, registry entries and PowerShell scripts on disk. If logging is enabled, that can provide some nice artifacts as well. The perspective of my post is going to be from that of an analyst that may not be too familiar with PowerShell. I am going to discuss how I locate malicious PowerShell artifacts during my analysis, as well as some methods I use to decode obfuscated PowerShell scripts. This will be Part 1 in a 3 part series written over the next few weeks.
Part 1: PowerShell Scripts Installed as Services
First up to bat is my favorite - PowerShell scripts that I find as installed services in the System event log. To find these, one of the first things I do is look for Event ID 7045. This event occurs when a service is installed on a system. An example of a PowerShell script installed as a service is shown below:
Of note are the following red flags:
1) Random Service Name
2) The Service File Name has "%COMSPEC%", which is the environment variable for cmd.exe
3) A reference to the powershell executable
4) Base 64 encoded data
So how might an entry like this make its way into an event log? While there are various ways to do this, one method would be to use the built in Windows Service Control Manger to create a service:
sc.exe create MyService binPath=%COMSPEC% powershell.exe -nop -w hidden -encodedcommand <insertbase64>
sc start MyService
sc start MyService
The above commands create a service named "MyService" and uses the binPath= option to launch cmd.exe which in turns executes the PowerShell code.
An interesting thing to note - there may be some failed errors logged after the service is created in this manner. The errors do not mean that it was unsuccessful. Windows was just expecting a "real" service binary to be installed and "times out" waiting for the "service" to report back. How do I know this? In my testing I was able to set up a successful reverse shell using the above methodology, which generated a failed service error on the Windows machine. On the left is a Metaspolit session I started on an attack virtual machine. On the right is a Windows 7 host virtual machine. Although the Windows 7 machine states "The service did not respond to the start or control request in a timely fashion," a reverse shell was still opened in the Metatsploit session:
The 7045 System event log PoweShell command is encoded in base64 and python can be used to decode it. Interesting note - this base64 code is in Unicode, so there will be extra parameter specified when decoding it. (For display reasons I have truncated the base64 text - you would need to include the full base64 text to decode it):
import base64
code="JABjACAAPQAgAEAAIgAKAFsARABsAGwASQBtAHAA...."
base64.b64decode(code).decode('UTF16')
code="JABjACAAPQAgAEAAIgAKAFsARABsAGwASQBtAHAA...."
base64.b64decode(code).decode('UTF16')
Here is what the decoded PowerShell command looks like. A quick sweep of the code reveals some telling signs - references to creating a Net Socket with the TCP protocol and an IP address:
This is similar to the type of code that Meterpreter uses to set up a reverse shell. The above PowerShell code was pretty easy to decode, however, it's usually more involved.
Next up is another example - this time its just "regular" base64. Note again the %COMSPEC% variable and reference to powershell.exe:
Again, Python can be used to decode the base64 encoded PowerShell:
This time, the decoded output is less than helpful. If we go back and take a look at the System event log entry more closely, we can see that there are references to "Gzip" and "Decompress":
import base64
code="H4sICCSPh1kCADEAT..."
decoded=base64.b64decode(code)
f=open("decoded.gzip",'wb')
f.write(decoded)
f.close
code="H4sICCSPh1kCADEAT..."
decoded=base64.b64decode(code)
f=open("decoded.gzip",'wb')
f.write(decoded)
f.close
Using 7zip I am successfully able to unpack the gzip file! Since I did not get any errors, I may be on the right track:
Now if I open the unzipped file with a text editor, hopefully I will see some PowerShell code:
Ahh..what??? Ok - time to take a peek in a hex editor:
Not much help either. I am thinking this may be shellcode. As a next step, I am going to run it through PDF Stream Dumper's shellcode analysis tool, scdbg.exe:
Ta-Da! scdbg.exe was able to pull out some IOCs for me from the shellcode.
- Decoded the base64 PowerShell string
- Wrote out the decoded base64 to a zip file
- Decompressed the Gzip file using 7zip
- Ran the binary output through scdbg.exe
One final example:
This looks familiar. First step, decoding the Unicode base64 gives the following result - which contains more base64 code inside the base64 code! :
decoded2="nVPvT9swEP2ev+IUR...."
f=open("decoded2.zip,"wb")
f.write(base64.b64decode(decoded2)
f.close()
When trying to open up the zipped file with 7Zip I get an error:f=open("decoded2.zip,"wb")
f.write(base64.b64decode(decoded2)
f.close()
And the same with the built in Window's utility:
I also tried various python libraries to unzip the compressed file. After some research, I discovered that the compression used is related to some .Net libraries. Now, since I am a python gal, I wanted to figure out how to decompress this using Python so I could easily implement it into my scripting. Since Python is cross compatible with Linux, Windows and Mac, .Net is not native to its core. As such, I used Iron Python to do my bidding. (Now yes, you could absolutely use PowerShell to decode this, but what can I say - I wanted to do it Python)
According to the Iron Python website "IronPython is an open-source implementation of the Python programming language which is tightly integrated with the .NET Framework. IronPython can use the .NET Framework and Python libraries, and other .NET languages can use Python code just as easily." Neat. Installing it on Windows is a breeze - just an MSI. Once installed, you simple run the scripts calling ipy.exe (I'll show an example later).
Armed with this, I was able to write some python code (io_decompress.py) that decompressed the zip file using the python IO compression Library:
#import required .Net libraries
from System.IO import BinaryReader, StreamReader, MemoryStream
from System.IO.Compression import CompressionMode, DeflateStream
from System import Array, Byte
from System.IO import FileStream, FileMode
from System.Text import Encoding
from System.IO import File
#functions to decompress the data
def decompress(data):
io_zip = DeflateStream(MemoryStream(data), CompressionMode.Decompress)
str = StreamReader(io_zip).ReadToEnd()
io_zip.Close()
return str
print "Decompressing stream..."
compressedBytes = File.ReadAllBytes("decoded2.zip")
decompressedString = decompress(compressedBytes)
f = open("decompressed.txt", "wb")
f.write(decompressedString)
f.close()
from System.IO import BinaryReader, StreamReader, MemoryStream
from System.IO.Compression import CompressionMode, DeflateStream
from System import Array, Byte
from System.IO import FileStream, FileMode
from System.Text import Encoding
from System.IO import File
#functions to decompress the data
def decompress(data):
io_zip = DeflateStream(MemoryStream(data), CompressionMode.Decompress)
str = StreamReader(io_zip).ReadToEnd()
io_zip.Close()
return str
print "Decompressing stream..."
compressedBytes = File.ReadAllBytes("decoded2.zip")
decompressedString = decompress(compressedBytes)
f = open("decompressed.txt", "wb")
f.write(decompressedString)
f.close()
To run the script using IronPython was easy: ipy.exe io_decompress.py:
I was able to open the decompressed.txt file created by the script and was rewarded with the following plain text PowerShell script. Once again, note the IP address:
To summarize the steps taken for this event log entry:
- Decoded Unicode base64
- Decoded embedded base64 code
- Decompressed resulting decoded base64 code
So how to go about finding these and decoding them in your exams?
- Look for event log ID 7045 with "%COMSPEC%, powershell.exe, -encodedcommand, -w hidden , "From Base64String" etc.
- Look for "Gzipstream" or "[IO.Compression.CompressionMode]::Decompress" for hints on what type of compression was used
- Try running the resulting binary files through sdbg.exe, shellcode2exe or other malware analysis tools
Part 2 will be about PowerShell in the registry, followed by Part 3 on PowerShell logging and pulling information from memory.
Really great post, Mari! As always! Thanks!
ReplyDeleteThis is an excellent write up and research! Thanks for sharing!
ReplyDeleteAwesome post Mari ! Many thanks !
ReplyDeleteExcellent write-up
ReplyDelete