I'm working on releasing my second, way more reliable and advanced Visual Basic obfuscator that I have been using for a few years by now. This obfuscator therefore becomes outdated and will be no longer maintained.
Visual Basic Code universal Obfuscator intended to be used during penetration testing assignments. To be used mainly to avoid AV and mail filters detections as well as Blue Teams inspection tasks.
Here is a sample VirusTotal.com scan against malicious Word document with embedded RobustPentestMacro geared up with Empire-generated powershell stager:
No obfuscation | Obfuscated |
---|---|
15 / 58 | 9 / 58 |
- In case of Non-obfuscated sample, the AV software has flagged the file with certain malicious-macro statement.
- As of obfuscated sample we've been left with obfuscated flag.
There is still a huge area of improvement in testing which obfuscation techniques trigger what patterns, and work towards reducing such detection rate.
- Able to extract HTML/HTA/<script> contents
- Able to obfuscate arrays of numbers and characters
- Obfuscating strings via Bit Shuffling and base64 encoding (as described in D.Knuth's vol.4a chapter 7.1.3). This method produces smaller in size results (approx. 66% smaller resulting scripts)
- Merging long concatenated lines into variables appendings to avoid maximum number of continuing lines (24)
- Junk insertion, smart enough to avoid breaking syntax outside of routines
- Sensitive to quote escapes within strings, detecting consecutive lines concatenation
- Variables, Globals, Constants, function names, function parameters names randomization
- Comments, indents and blank lines removal,
usage: obfuscate.py [-h] [-o OUTPUT] [-N | -g GARBAGE | -G | -C]
[-m MIN_VAR_LEN] [-r RESERVED] [-v | -q]
input_file
Attempts to obfuscate an input visual basic script in order to prevent curious
eyes from reading over it.
positional arguments:
input_file Visual Basic script to be obfuscated.
optional arguments:
-h, --help show this help message and exit
-o OUTPUT, --output OUTPUT
Output file. Default: stdout
-N, --normalize Don't perform obfuscation, do only code normalization
(like long strings transformation).
-g GARBAGE, --garbage GARBAGE
Percent of garbage to append to the obfuscated code.
Default: 12%.
-G, --no-garbage Don't append any garbage.
-C, --no-colors Dont use colors.
-m MIN_VAR_LEN, --min-var-len MIN_VAR_LEN
Minimum length of variable to include in name
obfuscation. Too short value may break the original
script. Default: 5.
-r RESERVED, --reserved RESERVED
Reserved word/name that should not be obfuscated (in
case some name has to be in original script cause it
may break it otherwise). Repeat the option for more
words.
-v, --verbose Verbose output.
-q, --quiet No unnecessary output.
Original form
Const MY_CONSTANTS = "Some super const"
Dim AnotherVariable As String
Sub MyTestSub(ByVal arg1 As String)
' The below real-world used query contains nested quotes/apostrophes
' That could confuse `removeComments` routine
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 " _
& "WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' " _
& "AND TargetInstance.SystemUpTime >= 200 AND " _
& "TargetInstance.SystemUpTime < 320"
Dim arr As Variant
arr = Array(1, 2, 3, 4, 5, 6) ' This is an inline comment
Dim var1, var2
var1 = "Testing Short string"
var2 = "short"
'
' This is a comment
'
Dim longString
longString = "1. this is some very long string concatenated."
longString = longString + "2. this is some very long string concatenated."
longString = longString + "3. this is some very long string concatenated."
longString = longString + "4. this is some very long string concatenated."
longString = longString + "5. this is some very long string concatenated." _
& "6. this is some very long string concatenated." _
& "7. this is some very long string concatenated." _
& "8. this is some very long string concatenated." _
& "9. this is some very long string concatenated."
Dim somecondition As Boolean
somecondition = False
If somecondition <> False Then
Exit Sub
End If
MsgBox ("Test1(Constant): " & MY_CONSTANTS)
MsgBox ("Test2(Query): " & Query)
MsgBox ("Test3(var1 + var2): " & var1 & var2)
MsgBox ("Test4(longString): " & longString)
MsgBox ("Test5(Array's contents): " & Join(arr, ","))
End Sub
Sub test()
MyTestSub (0)
End Sub
Obfuscated form:
Invocation - we have to specify that the macro is named test
and therefore this Sub name shall not be mangled:
bash $ ./obfuscate.py -r test -v demo.vbs
Output (mind that Constants contents cannot be anyhow obfuscated, as they have to be known at compile time):
Const MY_CONSTANTS="Some super const"
Dim H5AyJRiT As String
Sub LdzqEiHkpovt(ByVal arg1 As String)
K8fqa=onOrpZJSZL("Aq0oV8IwZBFohAVdSO6+TJjg8124pPFV0K9zUbqn0FD4q9xWwLr1VWiukEpQooxWoBBRCQ==") _
& onOrpZJSZL("EuqkUgCucEii6dFXmODzXbik8VU4hYBYaI3RSep3F0yIoVRf2qdRXaC8cF2g4sBVMLVhXSrsnVOC9+9aoK/cFw==") _
& onOrpZJSZL("EKhhEYinVF5gsvxV4rD0V6ptPFGgvXNeoPMiXbCp8hGyU1YDoBQQQg==") _
& onOrpZJSZL("iKdUXmCy/FXisPRXqm08UaC9c16g8yJdsKnyEahC1go=")
Dim arr As Variant
arr=Array(3256/3256,293-291,-1964+1967,-2979+2983,1678-1673,342-336)
Dim var1,var2
var1=onOrpZJSZL(Chr(Int("&H69"))&Chr(&H4f)&Chr(Int("&H50"))&Chr(Int("&H51"))&"X"&Chr(55)&Chr(Int("&H69"))&Chr(Int("111"))&Chr(Int("56"))&Chr(Int("120"))&Chr(&H47)&Chr(&H61)&Chr(&H36)&Chr(-209+289)&Chr(Int("120"))&Chr(Int("&H53"))&Chr(113)&Chr(Int("&H4f"))&Chr(76)&"Q"&Chr(Int("84"))&Chr(114)&"q"&Chr(&H74)&Chr(88)&Chr(70)&Chr(-2196+2285)&Chr(Int("&H3d")))
var2=Chr(Int("115"))&Chr(Int("104"))&Chr(111)&"r"&Chr(1900-1784)
Dim oAQ96qYI As String
oAQ96qYI=Chr(&H4c)&Chr(56)&Chr(Int("&H79"))&Chr(112)&Chr(111)&Chr(76)&Chr(Int("48"))&Chr(Int("&H50"))&Chr(78645/749)&Chr(&H45)&Chr(&H49)&Chr(110)&Chr(Int("78"))&Chr(Int("97"))&Chr(2431-2328)&Chr(&H52)&Chr(-1842+1955)&Chr(3538-3458)&"7"&Chr(Int("104"))&Chr(121)
Dim ChtxGWGe As String
ChtxGWGe=Chr(1513-1429)&Chr(80)&Chr(Int("69"))&Chr(121)&Chr(Int("122"))&Chr(&H41)&Chr(Int("&H73"))&Chr(Int("&H31"))&Chr(Int("&H76"))&"l"&"k"&Chr(Int("&H33"))&Chr(Int("67"))&Chr(&H42)&Chr(51)&Chr(&H78)&Chr(&H37)&Chr(&H71)&Chr(878-766)&Chr(2660-2571)&Chr(Int("71"))&Chr(Int("&H58"))&Chr(77)&Chr(113)&Chr(78276/1186)
Dim E1X9VVBEe
E1X9VVBEe=onOrpZJSZL("sEBxR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=")
E1X9VVBEe=E1X9VVBEe+onOrpZJSZL("skBRR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=")
E1X9VVBEe=E1X9VVBEe+onOrpZJSZL("skBxR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=")
E1X9VVBEe=E1X9VVBEe+onOrpZJSZL("sEJRR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=")
E1X9VVBEe=E1X9VVBEe+onOrpZJSZL("sEJxR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=") _
& onOrpZJSZL("skJRR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=") _
& onOrpZJSZL("skJxR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=") _
& onOrpZJSZL("sEBTR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=") _
& onOrpZJSZL("sEBzR7ih0higdXdQsqvyEernUFv4iNxVquLwTLqtXFboid1Uoq1wXKKvUlw=")
Dim EKyef8cVxSrr As Boolean
Dim v9yzH As String
v9yzH="t"&Chr(Int("&H62"))&Chr(Int("57"))&"r"&Chr(&H57)&Chr(Int("&H61"))&Chr(Int("&H4c"))&"W"&Chr(Int("&H6b"))&Chr(109)&"x"&Chr(108)&Chr(Int("50"))&Chr(115)&Chr(&H6e)&"P"&Chr(&H36)&Chr(80)&Chr(86)&Chr(Int("108"))&Chr(Int("&H74"))
EKyef8cVxSrr=False
If EKyef8cVxSrr<>False Then
Exit Sub
End If
Dim P0HBOB As String
P0HBOB=Chr(-1358+1446)&Chr(-3235+3316)&Chr(27010/365)&"7"&Chr(Int("&H6c"))&Chr(75)&"R"&Chr(6462-6396)&Chr(Int("&H57"))&Chr(86)&Chr(Int("106"))&Chr(99)&Chr(49)&Chr(&H56)&Chr(52496/772)&Chr(&H64)&"s"&Chr(Int("&H66"))&Chr(8944-8832)&Chr(Int("&H63"))&Chr(Int("104"))&Chr(72)&Chr(&H43)&Chr(Int("107"))&Chr(Int("&H4b"))&Chr(-1162+1278)&Chr(3073-2984)&"e"
MsgBox ("Test1(Constant): " & MY_CONSTANTS)
MsgBox (onOrpZJSZL(Chr(105)&Chr(79)&Chr(75840/948)&Chr(Int("&H51"))&Chr(Int("&H58"))&"z"&Chr(-344+420)&"E"&Chr(Int("48"))&"E"&Chr(55)&"g"&Chr(2905-2789)&Chr(-294+412)&Chr(&H6b)&Chr(Int("89"))) & K8fqa)
MsgBox (onOrpZJSZL(Chr(&H69)&Chr(79)&Chr(80)&Chr(&H51)&Chr(895-807)&Chr(6451-6396)&Chr(&H71)&"M"&Chr(Int("&H63"))&Chr(&H45)&Chr(114)&Chr(-1738+1843)&Chr(&H4e)&Chr(Int("&H56"))&"Q"&Chr(&H43)&Chr(283938/2558)&Chr(Int("78"))&"D"&Chr(86)&Chr(85)&"b"&Chr(Int("&H6f"))&Chr(Int("66"))&"W"&"A"&Chr(111)&Chr(61)) & var1 & var2)
MsgBox (onOrpZJSZL(Chr(105)&"O"&Chr(Int("80"))&Chr(2870-2789)&Chr(&H58)&Chr(2709-2662)&Chr(Int("&H43"))&Chr(79)&Chr(Int("88"))&Chr(Int("69"))&Chr(196601/2209)&Chr(Int("&H71"))&Chr(&H34)&Chr(Int("&H39"))&"N"&Chr(3900/39)&Chr(Int("&H75"))&Chr(113)&Chr(-1542+1591)&"c"&Chr(Int("86"))&Chr(189417/1839)&Chr(&H3d)&"=") & E1X9VVBEe)
' Cut for brevity. One can see the rest of this output in 'demo-obfuscated.vbs'
' [...]
- Improve obfuscation routines
Add strings encoding/decoding routine based on some simple transformations- Add strings array based obfuscation
- Add polymorphic code obfuscation with given key to put into document's metadata
- Add
Eval
based code obfuscation routines - Offer the user functionality of storing encryption/decryption key in document's metadata.
- Reinforce variable names randomization to have var names randomized each time within function-block
- Implement several techniques for avoiding detections implemented in Revoke-Obfuscation, mainly for fun and for avoidance of generic PUA (Potentially Unwatend Application) detection algorithms in modern Anti-Virus softwares
- HTA script encoding when Javascript was used, should not be possible. Also - more tests are needed whether VBS script encoding within HTA actually works all the time.
- There is a bug within
removeComments
being called afterobfuscateString
that has added comments to surroundDeclare PtrSafe Function
instructions. Such dynamically added junk-comments should be marked not to be removed, or get added after callingremoveComments
instead. - Inserting junk that is not a comment doesn't work properly (something about function boundaries detection) (also junk insertion breaks the syntax, so it had been disabled temporarily until the bug gets fixed).
There is a bug with function parameters mangling that introduces various syntax errors and breaks- If there is a string within quotes placed in the comment - it will wrongly get obfuscated breaking the syntax
This and other projects are outcome of sleepless nights and plenty of hard work. If you like what I do and appreciate that I always give back to the community, Consider buying me a coffee (or better a beer) just to say thank you! πͺ
Mariusz Banach / mgeeky, 21
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)