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.
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).
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:
We can double check our breakpoints using the following command:
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.
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:
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:
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.
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:
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.
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.