Bypassing Password Protected VBA Modules With WinDbg

Brief Intro

Office 365 products include an IDE for developing macro’s with Visual Basic for Applications (VBA). VBA projects can be locked using a password of your choice preventing users with the document from opening and reading the VBA code. Most users will turn away from this obstacle, especially if they do not know the password… however, that’s all it is, an obstacle. For us slightly more annoying folk, we can get around it!

Rich sent me a message a few weeks back during some downtime and set me a little challenge to try and get around this password protection (this was very shortly after we had finished our solution to patch AMSI dynamically within an Office 365 macro. Check out our work on Rich’s page). He dropped a hint about one of the API calls that’s utilised when the password field opens and I began my work.

What do we need?

  • Windows 10 (I used 64 bit Professional)
  • Office 365 (I used Word, 64 bit)
  • WinDbg (I used the 64 bit version to work with Word)
  • Word document with a password protected VBA project

Figuring out where we are and what we have

The first thing we want to do is open up the document and navigate to the VBA editor, from here we should be able to see the project on the left. The VBA project I used for this write up was called “Macrotest”. Upon trying to access our project, we are presented with a password field box.

Password prompt for protected VBA module

We now want to open WinDbg and attach to the target process. In my case, I was only running one instance of Word so the process was easy to find (WINWORD.EXE).

Finding the Word process in Windbg

The API request hint I was referring to above comes into play here. I was told the name of the function that is used when popping the password request window “GetWindowTextA”, and is contained in the user32 DLL. Basically, we know that when the password box is interacted with – this function is called. So let’s drop a breakpoint on the function and see what we can see and do. This can be done with the following command:

bp user32!GetWindowTextA

We can double check our breakpoints using the following command:


Setting a breakpoint in Winbdg at the GetWindowTextA function

We now want to continue the process and make sure we can successfully hit the breakpoint and cause the program to pause. To do this, let’s input an easily recognisable string as the password value and press the “ok” button in the password prompt box. For this example I used the string “AAAAAAAA” which is hex is “4141414141414141”. The breakpoint is hit as soon as the “ok” button is pressed.

Hitting our breakpoint

Optional: WinDbg layout

For the rest of the exercise I used 4 different windows within WinDbg to monitor what was happening in the process and gain a visual representation of the data and information I needed. The four views I opened and positioned in a grid were the following:

  • Disassemly
  • Registers
  • Memory
  • Command
Optional layout for Windbg

Finding our target function

The next section took me a few attempts to get right as my x86/x64 assembly isn’t brilliant – so I couldn’t just read the disassembly and be like “yup, let’s go”. I had to step through everything instruction by instruction until the fail state was hit and then backtrack to spot which functions calls and registers were used. Anyway, long story short, the following instruction was identified to use our input password string:

lea rdx,[rsp+30h]

What this instruction does is “loads effective address” (lea) at the address “rsp+30h”, and places the value into the register “rdx”. Once we have stepped through enough instructions to reach the above, we can easily check the contents of this target address within the Memory window by typing “rsp+30h” and identifying the identified section.

Identifying our target instruction within the disassembly
Identifying our target address in memory

We now know our password string has been loaded into the “rdx” register, and will be used as a parameter within the following function call. If we step over the instructions 2 more times, we should end up on the following instruction:

call VBE7!rtcErrObj+0xb4428

If we step over this function call, we notice that the “rax” register holds the value 0. This is the returned value from the function. If I was smarter than I think I am, I would have looked up this function call and figured out what it was returning and why the value was 0, however I just guessed that it was a boolean value and had returned “false” i.e our password was wrong.

RAX holds a 'false' value, failing our password check

Continuing from here, we get the “incorrect password” pop up in Word. Either this is a very well timed coincidence, or we have identified the function that compares our password string with the project password and returns “true” or “false”.

Bypassing the password

My next step was to re-run the password input process and step through again until I hit the above function call again. Once I had, I flipped the returned value in “rax” to a 1 instead of a 0, i.e “true” instead of “false” and continued.

We have gracefully (no crashes) bypassed the password protection and can now view the VBA project and all it’s contents! I am wanting to continue this work and attempt to find the project password instead of just bypassing it, but for now – this works.

Successfully bypassing the password protected VBA module
Show Comments