所谓“不到黄河心不死”、“不撞南墙不回头”、“不见棺材不掉泪”,人是不容易死心的。在没搞清楚lsass内存中明文密码从哪里来到哪里去之前,对它总抱有一丝幻想。最早是在netxeyes看到关于lsass里有明文密码的帖子,当时就想跟踪一下,确定它的位置。后来在安焦又见有人提起,于是建议高手研究一下,可是没有回应。可能是因为高手都一眼看出这没有研究价值。为了消灭“最终の幻想”,我只好自己动手。对于Debug我是很菜的,乘机练习一下。
在VMWare上登陆几次win2003,密码位置一样。好,有可重复性。再挂上WinDbg。因为要跟踪登陆过程,用内核调试器比较好。用户态调试器也可以,在注册表里把lsass.exe的debugger设置为ntsd就行。不过WinDbg的窗口模式用着比较爽。
先找到lsass.exe
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 80ead020 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00039000 ObjectTable: e1000d58 HandleCount: 184.
Image: System
PROCESS ffbdd868 SessionId: none Cid: 0174 Peb: 7ffdf000 ParentCid: 0004
DirBase: 0324f000 ObjectTable: e1274f58 HandleCount: 17.
Image: smss.exe
PROCESS 80d7c970 SessionId: 0 Cid: 01ac Peb: 7ffdf000 ParentCid: 0174
DirBase: 0414c000 ObjectTable: e12bb720 HandleCount: 243.
Image: csrss.exe
PROCESS ffb73440 SessionId: 0 Cid: 01c4 Peb: 7ffdf000 ParentCid: 0174
DirBase: 00459000 ObjectTable: e14aab18 HandleCount: 417.
Image: winlogon.exe
PROCESS 80d9f7d8 SessionId: 0 Cid: 01f0 Peb: 7ffdf000 ParentCid: 01c4
DirBase: 001b0000 ObjectTable: e15e6ea0 HandleCount: 254.
Image: services.exe
PROCESS 80d91550 SessionId: 0 Cid: 01fc Peb: 7ffdf000 ParentCid: 01c4
DirBase: 03343000 ObjectTable: e15e9e08 HandleCount: 375.
Image: lsass.exe
PROCESS ffb88b08 SessionId: 0 Cid: 02a4 Peb: 7ffdf000 ParentCid: 01f0
DirBase: 00914000 ObjectTable: e163c108 HandleCount: 146.
Image: svchost.exe
PROCESS ffb8c690 SessionId: 0 Cid: 02e0 Peb: 7ffdf000 ParentCid: 01f0
DirBase: 03164000 ObjectTable: e16628c8 HandleCount: 112.
Image: svchost.exe
PROCESS ffb557d8 SessionId: 0 Cid: 0350 Peb: 7ffdf000 ParentCid: 01f0
DirBase: 004d6000 ObjectTable: e17290a0 HandleCount: 55.
Image: svchost.exe
PROCESS ffb52938 SessionId: 0 Cid: 035c Peb: 7ffdf000 ParentCid: 01f0
DirBase: 00494000 ObjectTable: e17134e0 HandleCount: 76.
Image: svchost.exe
PROCESS ffb4d7f0 SessionId: 0 Cid: 038c Peb: 7ffdf000 ParentCid: 01f0
DirBase: 00e19000 ObjectTable: e174b078 HandleCount: 270.
Image: svchost.exe
PROCESS ffb34620 SessionId: 0 Cid: 040c Peb: 7ffdf000 ParentCid: 01f0
DirBase: 0adb3000 ObjectTable: e176df38 HandleCount: 156.
Image: inetinfo.exe
PROCESS ffb2c020 SessionId: 0 Cid: 0434 Peb: 7ffdf000 ParentCid: 01f0
DirBase: 0ab79000 ObjectTable: e168a218 HandleCount: 45.
Image: VMwareService.exe
PROCESS ff9e2d88 SessionId: 0 Cid: 0500 Peb: 7ffdf000 ParentCid: 01f0
DirBase: 09d6c000 ObjectTable: e1879210 HandleCount: 136.
Image: svchost.exe
切换到lsass的进程空间,然后在明文密码的起始位置设一个内存读写断点。
kd> .context 03343000
WARNING: .cache forcedecodeuser is not enabled
我的虚拟机上,密码总是从0x002b5cd0开始。
kd> ba r2 /p 80d91550 002b5cd0
kd> g
然后登陆,过一会就到断到了。
Breakpoint 0 hit
001b:77d7d9a9 f3a5 rep movsd
重新加载symbol
kd> .reload
Connected to Windows Server 2003 3790 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
..............................................................................................
Loading unloaded module list
..
Loading User Symbols
...........................................................
看一下寄存器
kd> r
eax=00000000 ebx=00000010 ecx=00000003 edx=00000010 esi=00082014 edi=002b5cd4
eip=77d7d9a9 esp=00b3faac ebp=002b5cb8 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
ADVAPI32!MD4Update+0x113:
001b:77d7d9a9 f3a5 rep movsd ds:00082014=006b0068 es:002b5cd4=00000000
中断在一个串传送指令上。因为此时该指令已经执行过一次了,所以源地址是esi-4
kd> du esi-4
00082010 "ph4nt0m!.." <-- 看到密码了
密码从哪里来的?当然是作为参数传递进来的,查一下堆栈。
kd> kb
ChildEBP RetAddr Args to Child
00b3fab8 76624d61 00000000 00082010 00000010 ADVAPI32!MD4Update+0x113
00b3fac8 766258de 002b5cb8 00000010 00082010 cryptdll!md4Sum+0x11
00b3fae4 71c388be 00b3fb00 00081ff8 00093b60 cryptdll!rc4HmacHashPassword+0x3e
00b3fb0c 71c189c7 00b3fd74 00b3fb94 00000017 kerberos!_imp__lstrlenA <PERF> (kerberos+0x388be)
00b3fbb0 71c09d1c 00b3fd74 0000009c 00093aa0 kerberos!KerbCreateEmptyContext+0xd
00b3fbec 71c2bbd0 00000000 00b3fd6c 00b3fd74 kerberos!KerbGetTgsTicket+0x477
00b3fc1c 71c1f34b 00093a40 00b3fd64 00b3fd6c kerberos!KRB5_Module_Startup+0xa886
00b3fc70 7423d44a 00000002 000cf1b8 00b3fd5c kerberos!KerbQueryTicketCache+0x231
00b3fcc4 74246b56 00000002 000cf1b8 00b3fd5c LSASRV!LsaIAuditAccountLogon+0x15b
00b3fd00 7421f583 00000002 000cf1b8 006110f8 LSASRV!LsarSetInformationTrustedDomain+0x2
00b3fe74 74236843 00b3fe8c 005f8e98 00000000 LSASRV!NegHandleClientRequest+0x523
00b3fe84 74208cbf 00602270 00625ce8 005f8e98 LSASRV!LsapCaptureSamInfo+0x84
00000000 00000000 00000000 00000000 00000000 LSASRV!NegpAcquireCredHandle+0x20e
可以看到,ADVAPI32!MD4Update第二个参数0x00082010正是密码的源地址,后面的0x10则是密码长度。
注意到cryptdll!md4Sum也有同样的参数,只是顺序不同,它的第一个参数0x002b5cb8什么意思呢?
kd> dd 002b5cb8
002b5cb8 67452301 efcdab89 98badcfe 10325476 <-- 一串很有规律的值
002b5cc8 00000080 00000000 00340035 00000000
002b5cd8 00000000 00000000 00000000 00000000
002b5ce8 00000000 00000000 00000000 00000000
002b5cf8 00000000 00000000 00000000 00000000
002b5d08 00000000 00000000 e0cfd631 31e96ad1
002b5d18 d7593cb7 c089c0e0 000e0013 000c0182
002b5d28 00000344 ffffffff 00000000 00000000
结合函数名md4Sum和MD4Update,我们很容易知道,那串有规律的值其实是MD4算法要求的hash初始值。
继续查看各个函数的入口参数,我们还能发现一些有趣的东西。
kd> dS 00b3fb00
00082010 "ph4nt0m!"
kd> dS 00b3fd74
00082010 "ph4nt0m!"
kd> dS 000cf1b8
01284660 "Administrator"
用户名和密码的源头一直可以追溯到LSASRV!NegHandleClientRequest。而真正的源头则是winlogon,它通过LPC将用户在登陆界面输入的内容传递给lsass。但是,这些都不是我们关心的。既然lsass内存里只能找到一处明文密码,那就说明这些源密码、源源密码最后都被清零了。我们只关心那个串操作的目的地址是怎么来的。这需要反汇编ADVAPI32!MD4Update。先把经过分析后的函数原型写出来:
void MD4Update(MDstruct *pMD4, WCHAR *pPassword, ULONG PassLeng);
其中第一个参数pMD4是个结构指针,指向下面这个结构。
typedef _MD4 {
ULONG Buffer[0]; // = 0x67452301; // Initial values for MD4
ULONG Buffer[1]; // = 0xefcdab89;
ULONG Buffer[2]; // = 0x98badcfe;
ULONG Buffer[3]; // = 0x10325476;
LARGE_INTEGER Count; // by bit
WCAHR String[32];
USHORT Hash[16];
} MD4, *pMD4
这个结构是参考了RFC1186中MD4的数据结构MDstruct,再加上后面的分析得出的。
下面来看MD4Update。
kd> u ADVAPI32!MD4Update L40
ADVAPI32!MD4Update:
77d7d93a 53 push ebx
77d7d93b 8b5c2410 mov ebx,[esp+0x10] ;ebx=PassLeng (by byte)
77d7d93f 55 push ebp
77d7d940 8b6c240c mov ebp,[esp+0xc] ;ebp=pMD4
77d7d944 8b4d10 mov ecx,[ebp+0x10] ;ecx=pMD4->Count.LowPart (by bit)
77d7d947 8bc1 mov eax,ecx
77d7d949 c1e803 shr eax,0x3 ;eax=pMD4->Count.LowPart/8 (by byte)
77d7d94c 8d0cd9 lea ecx,[ecx+ebx*8] ;ecx+=PassLeng*8
77d7d94f 83e03f and eax,0x3f ;eax%=64
77d7d952 8d14dd00000000 lea edx,[00000000+ebx*8] ;edx=PassLeng*8
77d7d959 3bca cmp ecx,edx
77d7d95b 56 push esi
77d7d95c 57 push edi
77d7d95d 89442414 mov [esp+0x14],eax ;pMD4=eax
77d7d961 894d10 mov [ebp+0x10],ecx ;pMD4->Count.LowPart+=PassLeng*8
7