0x478C Dynamic Loose Learning (DLL)
I was looking for some excuse to improve my
nil little experience with IDA and building a CLI for Vasco Digipass came up in a conversation.
While keeping the OTP generator in the same machine where you use the OTP itself is unlikely to be endorsed by the security team, Vasco already provides that with
Digipass For Windows (even if EOL is April 2022).
Challenge accepted, took a set of valid credentials1, the installer2 and fired up a brand new IDA VM.
While I expected to start a long session of spamming F5 hoping IDA would give me pseudo-code easy enough to decipher, looking at the files in Digipass folder there was a
Let’s load that DLL in IDA and check the exports
Interesting export names, let’s see how they’re used:
- Add a breakpoint on each of them: right click, add breakpoint
Process Optionsand set
First prompt we get from the app is Profile URL with a default of http://sc.vasco.com/update/dp4windows/50/digipass.xml. Back to this one later.
Next one prompts for serial and activation and entering these brings us to the first breakpoint:
*Activate, makes sense. Thank you IDA for such a clear breakdown of the args as well!
8 args. Let’s dump data for the last 8 items pushed to the stack (or 9 but skip the last as that’s the ret address for the call)
Python> arg_addrs = [ idaapi.get_dword(idautils.cpu.esp+X*4) for X in range(1, 9) ] Python> init_vals = [ ida_bytes.get_bytes(X, 50) for X in arg_addrs ] Python> for l in init_vals : print(l) b'3806564553AEF4E69858FB1FA8165D51F7EA40293792D20F06' b'*SERIAL\x00...' b'**********ACTIVATION\x00...' b'\xff\xff\xff...' b'111111\x00\x00\x00\x00...' b'\xff\xff\xff...' b'\x00\x00\x00\x00...' b'\x00\x00\x00\x00...'
Using the variable names assigned by IDA:
Src: back to that xml from the start, this matches the
StaticVectorvalue (confirmed by looking into a few more bytes)
a2: serial number used (redacted)
a3: activation code (redacted)
a4: value in stack is 0
a5: seems like a fixed value of
111111(even with different credentials1)
a6: value in stack is 0
a7: output buffer?
a8: output buffer?
Double click on current ESP value takes us to the return of the function, so we can add a breakpoint there (press
C to convert to code if not decompiled).
Then continue execution until it stops there. It does through other functions in the DLL but those were called by
*Activate, not by the GUI.
If we look at the values of the args now (re-using the addresses we saved)
[ ida_bytes.get_bytes(X, 50) for X in arg_addrs ]
We can confirm
a8 have been changed and I’ll call them
EAX value is
0, that should be the expected success return code.
Click continue and next stop:
4 args, do the same:
Python> arg_addrs = [ idaapi.get_dword(idautils.cpu.esp+X*4) for X in range(1, 5) ] Python> init_vals = [ ida_bytes.get_bytes(X, 50) for X in arg_addrs ] Python> for l in init_vals : print(l) b'***ACTIVATION_KEY1' b"***ACTIVATION_KEY2" b'111111\x00\...' b'\x00\x00\x00\x00...'
Last argument (at least) looks like output buffer. Again, break on return address, continue, dump.
Content did change, let’s call it
EAX value is
1, so that should be the expected success return code.
Python> arg_addrs = [ idaapi.get_dword(idautils.cpu.esp+X*4) for X in range(1, 8) ] Python> init_vals = [ ida_bytes.get_bytes(X, 50) for X in arg_addrs ] Python> for l in init_vals : print(l) b'***ACTIVATION_KEY1' b"***ACTIVATION_KEY2" b'\xff\xff\xff\xff...' b'***VALID_TOKEN' b'\xff\xff\xff\xff...' b'\x00\x00\x00\x00\x00...' b'\x00\x00\x00\x00\x00...'
args 3 and 5 have value 0 and last two arguments look like output buffers. Break on ret, continue, dump.
EAX value is
0, last argument was not changed (used only for errors?) but the one before has a six-digit OTP 🏆
I’ve put this all to use in dp4cli
➜ dp4cli 120954
Next step will be to spam F5 on all these DLL functions, but it was an interesting first step (for me)!