-
-
[转帖]关于定位lsass内存中的明文密码
-
发表于: 2007-5-20 14:16 6386
-
来源: 幻影旅团
作者:zzzevazzz
Codz:
所谓“不到黄河心不死”、“不撞南墙不回头”、“不见棺材不掉泪”,人是不容易死心的。在没搞清楚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
77d7d964 0f82437e0100 jb ADVAPI32!MD4Update+0x2c (77d957ad)
77d7d96a 8b5514 mov edx,[ebp+0x14] ;edx=pMD4->Count.HighPart (by bit)
77d7d96d 8bcb mov ecx,ebx
77d7d96f c1e91d shr ecx,0x1d ;ecx=edx/0x20000000
77d7d972 03d1 add edx,ecx ;edx=PassLeng*8+edx/0x20000000
77d7d974 85c0 test eax,eax ;第一次调用时pMD4->Count为0,所以此时eax为0
77d7d976 895514 mov [ebp+0x14],edx ;pMD4->Count.HighPart=edx
77d7d979 0f8761ffffff jnbe ADVAPI32!MD4Update+0x40 (77d7d8e0)
77d7d97f f644241803 test byte ptr [esp+0x18],0x3;检测pPassword是否4K对齐
77d7d984 0f85f3f8ffff jne ADVAPI32!MD4Update+0x96 (77d7d27d)
77d7d98a 83fb40 cmp ebx,0x40 ;PassLeng>64则跳转
77d7d98d 7325 jnb ADVAPI32!MD4Update+0xdb (77d7d9b4)
77d7d98f 85db test ebx,ebx ;PassLeng!=0则跳转
77d7d991 7507 jnz ADVAPI32!MD4Update+0x104 (77d7d99a)
77d7d993 5f pop edi
77d7d994 5e pop esi
77d7d995 5d pop ebp
77d7d996 5b pop ebx
77d7d997 c20c00 ret 0xc
77d7d99a 8b742418 mov esi,[esp+0x18] ;esi=pPassword <-跳到这里
77d7d99e 8bcb mov ecx,ebx ;ecx=PassLeng
77d7d9a0 8bd1 mov edx,ecx
77d7d9a2 c1e902 shr ecx,0x2
77d7d9a5 8d7c2818 lea edi,[eax+ebp+0x18] ;edi=pMD4->String
77d7d9a9 f3a5 rep movsd ;memcpy(pMD4->String, pPassword, PassLeng)
77d7d9ab 8bca mov ecx,edx
77d7d9ad 83e103 and ecx,0x3
77d7d9b0 f3a4 rep movsb
77d7d9b2 ebdf jmp ADVAPI32!MD4Update+0x11c (77d7d993) ;return
77d7d9b4 8bf3 mov esi,ebx ;以下处理PassLeng>64的情况,不关心
77d7d9b6 c1ee06 shr esi,0x6
77d7d9b9 8b442418 mov eax,[esp+0x18]
77d7d9bd 50 push eax
77d7d9be 55 push ebp
77d7d9bf e8b3faffff call ADVAPI32!MD4Transform (77d7d477)
77d7d9c4 8b542418 mov edx,[esp+0x18]
77d7d9c8 83c240 add edx,0x40
77d7d9cb 83eb40 sub ebx,0x40
77d7d9ce 4e dec esi
77d7d9cf 89542418 mov [esp+0x18],edx
77d7d9d3 75e4 jnz ADVAPI32!MD4Update+0xe0 (77d7d9b9)
77d7d9d5 8b442414 mov eax,[esp+0x14]
77d7d9d9 ebb4 jmp ADVAPI32!MD4Update+0x100 (77d7d98f)
77d7d9db 90 nop
77d7d9dc 90 nop
77d7d9dd 90 nop
77d7d9de 90 nop
77d7d9df 90 nop
ADVAPI32!MD5Init:
77d7d9e0 8b442404 mov eax,[esp+0x4]
现在知道,我们看到的明文密码是pMD4的一部分:pMD4->String。那么pMD4从哪里来的?
继续往前追。
kd> u cryptdll!md4Sum
cryptdll!md4Sum:
76624d50 ff742408 push dword ptr [esp+0x8]
76624d54 ff742410 push dword ptr [esp+0x10]
76624d58 ff74240c push dword ptr [esp+0xc]
76624d5c e89f100000 call cryptdll!MD4Update (76625e00)
76624d61 33c0 xor eax,eax
76624d63 c20c00 ret 0xc
cryptdll!md4Finalize:
76624d66 90 nop
76624d67 90 nop
md4Sum果然只是换了一下参数位置而已。函数原型:
BOOL md4Sum(MDstruct *pMD4, ULONG PassLeng, WCHAR *pPassword);
前面已经知道rc4HmacHashPassword的第一个参数是个PUNICODE_STRING,指向源密码。
分析后的函数原型:
NTSTATUS rc4HmacHashPassword(PUNICODE_STRING uPassword, USHORT *pHash);
kd> u cryptdll!rc4HmacHashPassword L30
cryptdll!rc4HmacHashPassword:
766258a0 55 push ebp
766258a1 8bec mov ebp,esp
766258a3 51 push ecx ;居然是__fastcall
766258a4 51 push ecx
766258a5 8d45fc lea eax,[ebp-0x4] ;得先分析CDLocateCheckSum才知道ebp-0x4是什么
766258a8 50 push eax
766258a9 6a02 push 0x2
766258ab e890b8ffff call cryptdll!CDLocateCheckSum (76621140)
766258b0 85c0 test eax,eax
766258b2 7d07 jge cryptdll!rc4HmacHashPassword+0x1b (766258bb)
766258b4 b842030880 mov eax,0x80080342
766258b9 eb3b jmp cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258bb 8d45f8 lea eax,[ebp-0x8] ;ebp-0x8也有待后面分析
766258be 50 push eax
766258bf 8b45fc mov eax,[ebp-0x4]
766258c2 6a00 push 0x0
766258c4 ff500c call dword ptr [eax+0xc]
766258c7 85c0 test eax,eax
766258c9 7c2b jl cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258cb 8b4508 mov eax,[ebp+0x8]
766258ce ff7004 push dword ptr [eax+0x4] ;push uPassword->Buffer
766258d1 0fb700 movzx eax,word ptr [eax]
766258d4 50 push eax ;push uPassword->Length
766258d5 ff75f8 push dword ptr [ebp-0x8] ;这个就是密码缓存的地址,位置待定
766258d8 8b45fc mov eax,[ebp-0x4]
766258db ff5010 call dword ptr [eax+0x10] ;在这里call cryptdll!md4Sum
766258de ff750c push dword ptr [ebp+0xc]
766258e1 8b45fc mov eax,[ebp-0x4]
766258e4 ff75f8 push dword ptr [ebp-0x8]
766258e7 ff5014 call dword ptr [eax+0x14]
766258ea 8d45f8 lea eax,[ebp-0x8]
766258ed 50 push eax
766258ee 8b45fc mov eax,[ebp-0x4]
766258f1 ff5018 call dword ptr [eax+0x18]
766258f4 33c0 xor eax,eax
766258f6 c9 leave
766258f7 c20800 ret 0x8
766258fa 90 nop
766258fb 90 nop
766258fc 90 nop
766258fd 90 nop
766258fe 90 nop
766258ff 90 nop
76625900 90 nop
76625901 90 nop
76625902 90 nop
76625903 90 nop
76625904 90 nop
在搞清楚CDLocateCheckSum前,一切都不明朗。先说结论:
NTSTATUS CDLocateCheckSum(ULONG Type, PCHECK_SUM *ppChechSum);
根据所给的Type,在一组CHECK_SUM中找到需要的那个,返回其指针。
kd> u cryptdll!CDLocateCheckSum L20
cryptdll!CDLocateCheckSum:
76621140 a190806276 mov eax,[cryptdll!cCheckSums (76628090)] ;eax=0x0000000f
76621145 85c0 test eax,eax
76621147 7431 jz cryptdll!CDLocateCheckSum+0x23 (7662117a)
76621149 8d0cc0 lea ecx,[eax+eax*8] ;结构大小=0xf*9=0x24
7662114c 8d0c8da0806276 lea ecx,[cryptdll!CheckSumFns (766280a0)+ecx*4] ;ecx指向_CHECK_SUM
76621153 83e924 sub ecx,0x24 ;结构指针自减
76621156 8b11 mov edx,[ecx] ;获取CheckSums的类型?
76621158 48 dec eax ;那么eax(初值0xf)就是CHECK_SUM的总数
76621159 3b542404 cmp edx,[esp+0x4] ;查找类型2?
7662115d 7406 jz cryptdll!CDLocateCheckSum+0x2b (76621165)
7662115f 85c0 test eax,eax
76621161 7417 jz cryptdll!CDLocateCheckSum+0x23 (7662117a)
76621163 ebee jmp cryptdll!CDLocateCheckSum+0x13 (76621153) ;循环
76621165 8b4c2408 mov ecx,[esp+0x8]
76621169 8d04c0 lea eax,[eax+eax*8] ;根据循环变量定位结构头
7662116c 8d0485a0806276 lea eax,[cryptdll!CheckSumFns (766280a0)+eax*4]
76621173 8901 mov [ecx],eax ;返回结构指针
76621175 33c0 xor eax,eax
76621177 c20800 ret 0x8
7662117a b842030880 mov eax,0x80080342
7662117f ebf6 jmp cryptdll!CDLocateCheckSum+0x28 (76621177)
76621181 90 nop
76621182 90 nop
76621183 90 nop
76621184 90 nop
76621185 90 nop
76621186 90 nop
76621187 90 nop
76621188 90 nop
76621189 90 nop
7662118a 90 nop
7662118b 90 nop
kd> dd 76628090 L1
76628090 0000000f
看一下CHECK_SUM究竟有什么:
kd> dd cryptdll!CheckSumFns L40
766280a0 ffffff77 00000010 00000002 76624ed0
766280b0 76621110 76621690 766213f0 76621bf0
766280c0 00000000 ffffff76 00000010 00000002
766280d0 76624ed0 76621110 76621690 766213f0
766280e0 76621840 00000000 00000002 00000010
766280f0 00000000 76624cf0 76624d50 76624d80
76628100 766211f0 76624d30 00000000 00000007
76628110 00000010 00000000 76621220 76621110
76628120 76621260 766211f0 76621bd0 00000000
76628130 00000001 00000004 00000000 76625aa0
76628140 76625980 76625b10 766211f0 76625b60
76628150 00000000 ffffff7a 00000008 00000002
76628160 76624ed0 76625040 76624f60 76624fb0
76628170 76624ef0 00000000 ffffff79 00000008
76628180 00000002 76624ed0 76621110 76624ea0
76628190 766213f0 76624db0 00000000 ffffff7b
kd> u 76624cf0
cryptdll!md4Initialize:
76624cf0 56 push esi
76624cf1 6a68 push 0x68
76624cf3 ff156c106276 call dword ptr [cryptdll!_imp__malloc (7662106c)]
76624cf9 8bf0 mov esi,eax
76624cfb 85f6 test esi,esi
76624cfd 59 pop ecx
76624cfe 7507 jnz cryptdll!md4Initialize+0x17 (76624d07)
76624d00 b8170000c0 mov eax,0xc0000017
kd> u 76624d50
cryptdll!md4Sum:
76624d50 ff742408 push dword ptr [esp+0x8]
76624d54 ff742410 push dword ptr [esp+0x10]
76624d58 ff74240c push dword ptr [esp+0xc]
76624d5c e89f100000 call cryptdll!MD4Update (76625e00)
76624d61 33c0 xor eax,eax
76624d63 c20c00 ret 0xc
cryptdll!md4Finalize:
76624d66 90 nop
76624d67 90 nop
kd> u 76624d80
cryptdll!md4Finalize:
76624d80 56 push esi
76624d81 8b742408 mov esi,[esp+0x8]
76624d85 57 push edi
76624d86 56 push esi
76624d87 e894100000 call cryptdll!MD4Final (76625e20)
76624d8c 8b7c2410 mov edi,[esp+0x10]
76624d90 83c658 add esi,0x58
76624d93 a5 movsd
kd> u 766211f0
cryptdll!md4Finish:
766211f0 56 push esi
766211f1 8b742408 mov esi,[esp+0x8]
766211f5 ff36 push dword ptr [esi]
766211f7 ff1570106276 call dword ptr [cryptdll!_imp__free (76621070)]
766211fd 832600 and dword ptr [esi],0x0
76621200 59 pop ecx
76621201 33c0 xor eax,eax
76621203 5e pop esi
kd> u 76624d30
cryptdll!md4InitializeEx:
76624d30 ff742410 push dword ptr [esp+0x10]
76624d34 6a00 push 0x0
76624d36 e8b5ffffff call cryptdll!md4Initialize (76624cf0)
76624d3b c21000 ret 0x10
cryptdll!md4Sum:
76624d3e 90 nop
76624d3f 90 nop
76624d40 90 nop
76624d41 90 nop
原来是这样:
trpedef _CHECK_SUM {
DWORD Type; //??
ULONG Unknow1;
ULONG Unknoe2;
PVOID Function[5];
ULONG Unknow3; // 总是0?
}
对于Type==2的情况,Function[5]分别指向:
cryptdll!md4Initialize
cryptdll!md4Sum
cryptdll!md4Finalize
cryptdll!md4Finish
cryptdll!md4InitializeEx
再回到rc4HmacHashPassword。
kd> u cryptdll!rc4HmacHashPassword L30
cryptdll!rc4HmacHashPassword:
766258a0 55 push ebp
766258a1 8bec mov ebp,esp
766258a3 51 push ecx
766258a4 51 push ecx
766258a5 8d45fc lea eax,[ebp-0x4] ;ebp-0x4是结构指针地址&pChechSum
766258a8 50 push eax
766258a9 6a02 push 0x2 ;查找类型2的CHECK_SUM
766258ab e890b8ffff call cryptdll!CDLocateCheckSum (76621140) ;CDLocateCheckSum(2,&pChechSum)
766258b0 85c0 test eax,eax
766258b2 7d07 jge cryptdll!rc4HmacHashPassword+0x1b (766258bb)
766258b4 b842030880 mov eax,0x80080342
766258b9 eb3b jmp cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258bb 8d45f8 lea eax,[ebp-0x8] ;ebp-0x8=&pMD4
766258be 50 push eax ;push &pMD4
766258bf 8b45fc mov eax,[ebp-0x4] ;eax=pChechSum
766258c2 6a00 push 0x0
766258c4 ff500c call dword ptr [eax+0xc] ;md4Initialize(0,&pMD4)
766258c7 85c0 test eax,eax
766258c9 7c2b jl cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258cb 8b4508 mov eax,[ebp+0x8]
766258ce ff7004 push dword ptr [eax+0x4] ;push uPassword->Buffer (pPassword)
766258d1 0fb700 movzx eax,word ptr [eax]
766258d4 50 push eax ;push uPassword->Length (PassLeng)
766258d5 ff75f8 push dword ptr [ebp-0x8] ;现在知道pMD4由md4Initialize决定
766258d8 8b45fc mov eax,[ebp-0x4]
766258db ff5010 call dword ptr [eax+0x10] ;md4Sum(pMD4,PassLeng,pPassword)
766258de ff750c push dword ptr [ebp+0xc]
766258e1 8b45fc mov eax,[ebp-0x4]
766258e4 ff75f8 push dword ptr [ebp-0x8]
766258e7 ff5014 call dword ptr [eax+0x14] ;md4Finalize(pMD4,pHash)
766258ea 8d45f8 lea eax,[ebp-0x8]
766258ed 50 push eax
766258ee 8b45fc mov eax,[ebp-0x4]
766258f1 ff5018 call dword ptr [eax+0x18] ;md4Finish(&pMD4)
766258f4 33c0 xor eax,eax
766258f6 c9 leave
766258f7 c20800 ret 0x8
766258fa 90 nop
766258fb 90 nop
766258fc 90 nop
766258fd 90 nop
766258fe 90 nop
766258ff 90 nop
76625900 90 nop
76625901 90 nop
76625902 90 nop
76625903 90 nop
76625904 90 nop
再看md4Initialize:
kd> u cryptdll!md4Initialize L10
cryptdll!md4Initialize:
76624cf0 56 push esi
76624cf1 6a68 push 0x68
76624cf3 ff156c106276 call dword ptr [cryptdll!_imp__malloc (7662106c)]
76624cf9 8bf0 mov esi,eax ;pMD4=malloc(0x68)
76624cfb 85f6 test esi,esi
76624cfd 59 pop ecx
76624cfe 7507 jnz cryptdll!md4Initialize+0x17 (76624d07)
76624d00 b8170000c0 mov eax,0xc0000017
76624d05 eb0e jmp cryptdll!md4Initialize+0x25 (76624d15)
76624d07 56 push esi
76624d08 e8d3100000 call cryptdll!MD4Init (76625de0) ;MD4Init(pMD4)
76624d0d 8b44240c mov eax,[esp+0xc]
76624d11 8930 mov [eax],esi
76624d13 33c0 xor eax,eax
76624d15 5e pop esi
76624d16 c20800 ret 0x8
搞了半天,pMD4(包括其中的密码缓存)是由malloc分配的。这下没戏了,根本无法确定它的位置。
这一点其实用脚趾头想想也知道,但非要亲眼所见才死心。 ^_^
还有最后一线“希望”——希望lsass把密码留着别释放。
md4Initialize(0,&pMD4);
md4Sum(pMD4,PassLeng,pPassword);
之后是
md4Finalize(pMD4,pHash);
kd> u cryptdll!md4Finalize L10
cryptdll!md4Finalize:
76624d80 56 push esi
76624d81 8b742408 mov esi,[esp+0x8]
76624d85 57 push edi
76624d86 56 push esi
76624d87 e894100000 call cryptdll!MD4Final (76625e20) ;MD4Final(pMD4)
76624d8c 8b7c2410 mov edi,[esp+0x10]
76624d90 83c658 add esi,0x58 ;esi=pMD4->Hash
76624d93 a5 movsd ;memcpy(pHash,pMD4->Hash,16)
76624d94 a5 movsd
76624d95 a5 movsd
76624d96 a5 movsd
76624d97 5f pop edi
76624d98 33c0 xor eax,eax
76624d9a 5e pop esi
76624d9b c20800 ret 0x8
cryptdll!md4Finish:
76624d9e 90 nop
md4Finalize调用MD4Final:
kd> u cryptdll!MD4Final
cryptdll!MD4Final:
76625e20 ff2550106276 jmp dword ptr [cryptdll!_imp__MD4Final (76621050)]
转到_imp__MD4Final:
kd> dd cryptdll!_imp__MD4Final L1
76621050 77d7d3f3
kd> u 77d7d3f3 L35
ADVAPI32!MD4Final:
77d7d3f3 83ec48 sub esp,0x48
77d7d3f6 53 push ebx
77d7d3f7 55 push ebp
77d7d3f8 56 push esi
77d7d3f9 8b742458 mov esi,[esp+0x58] ;esi=pMD4
77d7d3fd 8b4610 mov eax,[esi+0x10] ;eax=pMD4->Count.LowPart
77d7d400 8b4e14 mov ecx,[esi+0x14] ;ecx=pMD4->Count.HighPart
77d7d403 8d5e58 lea ebx,[esi+0x58] ;ebx=pMD4->Hash
77d7d406 8903 mov [ebx],eax ;下面是一些算法的操作,不关心
77d7d408 c1e803 shr eax,0x3 ;盯紧esi(=pMD4)就是了
77d7d40b 83e03f and eax,0x3f
77d7d40e 83f838 cmp eax,0x38
77d7d411 57 push edi
77d7d412 894e5c mov [esi+0x5c],ecx
77d7d415 ba38000000 mov edx,0x38
77d7d41a 0f83d3830100 jnb ADVAPI32!MD4Final+0x29 (77d957f3)
77d7d420 2bd0 sub edx,eax
77d7d422 8bca mov ecx,edx
77d7d424 8be9 mov ebp,ecx
77d7d426 c1e902 shr ecx,0x2
77d7d429 33c0 xor eax,eax
77d7d42b 8d7c2410 lea edi,[esp+0x10]
77d7d42f f3ab rep stosd
77d7d431 52 push edx
77d7d432 8d542414 lea edx,[esp+0x14]
77d7d436 8bcd mov ecx,ebp
77d7d438 83e103 and ecx,0x3
77d7d43b 52 push edx
77d7d43c f3aa rep stosb
77d7d43e 56 push esi
77d7d43f c644241c80 mov byte ptr [esp+0x1c],0x80
77d7d444 e8f1040000 call ADVAPI32!MD4Update (77d7d93a)
77d7d449 6a08 push 0x8
77d7d44b 53 push ebx
77d7d44c 56 push esi
77d7d44d e8e8040000 call ADVAPI32!MD4Update (77d7d93a)
77d7d452 8b06 mov eax,[esi]
77d7d454 8b4e04 mov ecx,[esi+0x4]
77d7d457 8b5608 mov edx,[esi+0x8]
77d7d45a 8903 mov [ebx],eax
77d7d45c 8b460c mov eax,[esi+0xc]
77d7d45f 5f pop edi
77d7d460 894e5c mov [esi+0x5c],ecx
77d7d463 895660 mov [esi+0x60],edx
77d7d466 894664 mov [esi+0x64],eax
77d7d469 5e pop esi
77d7d46a 5d pop ebp
77d7d46b 5b pop ebx
77d7d46c 83c448 add esp,0x48
77d7d46f c20400 ret 0x4
ADVAPI32!MD5Init:
77d7d472 90 nop
77d7d473 90 nop
77d7d474 90 nop
咦,怎么没释放?别高兴的太早,下面还有md4Finish呢!
md4Finish(&pMD4);
kd> u cryptdll!md4Finish L10
cryptdll!md4Finish:
766211f0 56 push esi
766211f1 8b742408 mov esi,[esp+0x8]
766211f5 ff36 push dword ptr [esi]
766211f7 ff1570106276 call dword ptr [cryptdll!_imp__free (76621070)] ;free(pMD4)
766211fd 832600 and dword ptr [esi],0x0 ;pMD4=NULL
76621200 59 pop ecx
76621201 33c0 xor eax,eax
76621203 5e pop esi
76621204 c20400 ret 0x4
cryptdll!md5Initialize:
76621207 90 nop
76621208 90 nop
76621209 90 nop
7662120a 90 nop
7662120b 90 nop
7662120c 90 nop
7662120d 90 nop
彻底没戏啦。pMD4连同里面的明文密码被free了。
我不知道关于lsass留着明文密码备用的说法究竟是不是可靠的,至少我们看到的那个确实被free了。
如果说,前面提到的多个“源密码”中的某一个被lsass留着,那就不应该搜索不到。要么密码不是明文的。
结论很明显,在lsass里找明文密码属于RP问题。如果它已经被覆盖,就算你用什么模拟登陆的方法确定它的位置,也是回天乏术。如果没有被覆盖,用WinEggDrop的办法找密码已经足够了。
我再加一个判断条件:密码前面的2个ULONG总是0x00000200和0x00000000。它们就是pMD4->Count的LowPart和HighPart,用于保存被加密数据的bit数。MD4加密以64字节(0x200 bit)为最小单位,密码不超过32个字符的话,一轮加密就完了,所以它们总是0x200和0x0。这个条件可以在密码被覆盖时减少误报。
PS:本文简直是笨笨熊前辈的windbg教程的活生生的例子! 学习!
作者:zzzevazzz
Codz:
所谓“不到黄河心不死”、“不撞南墙不回头”、“不见棺材不掉泪”,人是不容易死心的。在没搞清楚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
77d7d964 0f82437e0100 jb ADVAPI32!MD4Update+0x2c (77d957ad)
77d7d96a 8b5514 mov edx,[ebp+0x14] ;edx=pMD4->Count.HighPart (by bit)
77d7d96d 8bcb mov ecx,ebx
77d7d96f c1e91d shr ecx,0x1d ;ecx=edx/0x20000000
77d7d972 03d1 add edx,ecx ;edx=PassLeng*8+edx/0x20000000
77d7d974 85c0 test eax,eax ;第一次调用时pMD4->Count为0,所以此时eax为0
77d7d976 895514 mov [ebp+0x14],edx ;pMD4->Count.HighPart=edx
77d7d979 0f8761ffffff jnbe ADVAPI32!MD4Update+0x40 (77d7d8e0)
77d7d97f f644241803 test byte ptr [esp+0x18],0x3;检测pPassword是否4K对齐
77d7d984 0f85f3f8ffff jne ADVAPI32!MD4Update+0x96 (77d7d27d)
77d7d98a 83fb40 cmp ebx,0x40 ;PassLeng>64则跳转
77d7d98d 7325 jnb ADVAPI32!MD4Update+0xdb (77d7d9b4)
77d7d98f 85db test ebx,ebx ;PassLeng!=0则跳转
77d7d991 7507 jnz ADVAPI32!MD4Update+0x104 (77d7d99a)
77d7d993 5f pop edi
77d7d994 5e pop esi
77d7d995 5d pop ebp
77d7d996 5b pop ebx
77d7d997 c20c00 ret 0xc
77d7d99a 8b742418 mov esi,[esp+0x18] ;esi=pPassword <-跳到这里
77d7d99e 8bcb mov ecx,ebx ;ecx=PassLeng
77d7d9a0 8bd1 mov edx,ecx
77d7d9a2 c1e902 shr ecx,0x2
77d7d9a5 8d7c2818 lea edi,[eax+ebp+0x18] ;edi=pMD4->String
77d7d9a9 f3a5 rep movsd ;memcpy(pMD4->String, pPassword, PassLeng)
77d7d9ab 8bca mov ecx,edx
77d7d9ad 83e103 and ecx,0x3
77d7d9b0 f3a4 rep movsb
77d7d9b2 ebdf jmp ADVAPI32!MD4Update+0x11c (77d7d993) ;return
77d7d9b4 8bf3 mov esi,ebx ;以下处理PassLeng>64的情况,不关心
77d7d9b6 c1ee06 shr esi,0x6
77d7d9b9 8b442418 mov eax,[esp+0x18]
77d7d9bd 50 push eax
77d7d9be 55 push ebp
77d7d9bf e8b3faffff call ADVAPI32!MD4Transform (77d7d477)
77d7d9c4 8b542418 mov edx,[esp+0x18]
77d7d9c8 83c240 add edx,0x40
77d7d9cb 83eb40 sub ebx,0x40
77d7d9ce 4e dec esi
77d7d9cf 89542418 mov [esp+0x18],edx
77d7d9d3 75e4 jnz ADVAPI32!MD4Update+0xe0 (77d7d9b9)
77d7d9d5 8b442414 mov eax,[esp+0x14]
77d7d9d9 ebb4 jmp ADVAPI32!MD4Update+0x100 (77d7d98f)
77d7d9db 90 nop
77d7d9dc 90 nop
77d7d9dd 90 nop
77d7d9de 90 nop
77d7d9df 90 nop
ADVAPI32!MD5Init:
77d7d9e0 8b442404 mov eax,[esp+0x4]
现在知道,我们看到的明文密码是pMD4的一部分:pMD4->String。那么pMD4从哪里来的?
继续往前追。
kd> u cryptdll!md4Sum
cryptdll!md4Sum:
76624d50 ff742408 push dword ptr [esp+0x8]
76624d54 ff742410 push dword ptr [esp+0x10]
76624d58 ff74240c push dword ptr [esp+0xc]
76624d5c e89f100000 call cryptdll!MD4Update (76625e00)
76624d61 33c0 xor eax,eax
76624d63 c20c00 ret 0xc
cryptdll!md4Finalize:
76624d66 90 nop
76624d67 90 nop
md4Sum果然只是换了一下参数位置而已。函数原型:
BOOL md4Sum(MDstruct *pMD4, ULONG PassLeng, WCHAR *pPassword);
前面已经知道rc4HmacHashPassword的第一个参数是个PUNICODE_STRING,指向源密码。
分析后的函数原型:
NTSTATUS rc4HmacHashPassword(PUNICODE_STRING uPassword, USHORT *pHash);
kd> u cryptdll!rc4HmacHashPassword L30
cryptdll!rc4HmacHashPassword:
766258a0 55 push ebp
766258a1 8bec mov ebp,esp
766258a3 51 push ecx ;居然是__fastcall
766258a4 51 push ecx
766258a5 8d45fc lea eax,[ebp-0x4] ;得先分析CDLocateCheckSum才知道ebp-0x4是什么
766258a8 50 push eax
766258a9 6a02 push 0x2
766258ab e890b8ffff call cryptdll!CDLocateCheckSum (76621140)
766258b0 85c0 test eax,eax
766258b2 7d07 jge cryptdll!rc4HmacHashPassword+0x1b (766258bb)
766258b4 b842030880 mov eax,0x80080342
766258b9 eb3b jmp cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258bb 8d45f8 lea eax,[ebp-0x8] ;ebp-0x8也有待后面分析
766258be 50 push eax
766258bf 8b45fc mov eax,[ebp-0x4]
766258c2 6a00 push 0x0
766258c4 ff500c call dword ptr [eax+0xc]
766258c7 85c0 test eax,eax
766258c9 7c2b jl cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258cb 8b4508 mov eax,[ebp+0x8]
766258ce ff7004 push dword ptr [eax+0x4] ;push uPassword->Buffer
766258d1 0fb700 movzx eax,word ptr [eax]
766258d4 50 push eax ;push uPassword->Length
766258d5 ff75f8 push dword ptr [ebp-0x8] ;这个就是密码缓存的地址,位置待定
766258d8 8b45fc mov eax,[ebp-0x4]
766258db ff5010 call dword ptr [eax+0x10] ;在这里call cryptdll!md4Sum
766258de ff750c push dword ptr [ebp+0xc]
766258e1 8b45fc mov eax,[ebp-0x4]
766258e4 ff75f8 push dword ptr [ebp-0x8]
766258e7 ff5014 call dword ptr [eax+0x14]
766258ea 8d45f8 lea eax,[ebp-0x8]
766258ed 50 push eax
766258ee 8b45fc mov eax,[ebp-0x4]
766258f1 ff5018 call dword ptr [eax+0x18]
766258f4 33c0 xor eax,eax
766258f6 c9 leave
766258f7 c20800 ret 0x8
766258fa 90 nop
766258fb 90 nop
766258fc 90 nop
766258fd 90 nop
766258fe 90 nop
766258ff 90 nop
76625900 90 nop
76625901 90 nop
76625902 90 nop
76625903 90 nop
76625904 90 nop
在搞清楚CDLocateCheckSum前,一切都不明朗。先说结论:
NTSTATUS CDLocateCheckSum(ULONG Type, PCHECK_SUM *ppChechSum);
根据所给的Type,在一组CHECK_SUM中找到需要的那个,返回其指针。
kd> u cryptdll!CDLocateCheckSum L20
cryptdll!CDLocateCheckSum:
76621140 a190806276 mov eax,[cryptdll!cCheckSums (76628090)] ;eax=0x0000000f
76621145 85c0 test eax,eax
76621147 7431 jz cryptdll!CDLocateCheckSum+0x23 (7662117a)
76621149 8d0cc0 lea ecx,[eax+eax*8] ;结构大小=0xf*9=0x24
7662114c 8d0c8da0806276 lea ecx,[cryptdll!CheckSumFns (766280a0)+ecx*4] ;ecx指向_CHECK_SUM
76621153 83e924 sub ecx,0x24 ;结构指针自减
76621156 8b11 mov edx,[ecx] ;获取CheckSums的类型?
76621158 48 dec eax ;那么eax(初值0xf)就是CHECK_SUM的总数
76621159 3b542404 cmp edx,[esp+0x4] ;查找类型2?
7662115d 7406 jz cryptdll!CDLocateCheckSum+0x2b (76621165)
7662115f 85c0 test eax,eax
76621161 7417 jz cryptdll!CDLocateCheckSum+0x23 (7662117a)
76621163 ebee jmp cryptdll!CDLocateCheckSum+0x13 (76621153) ;循环
76621165 8b4c2408 mov ecx,[esp+0x8]
76621169 8d04c0 lea eax,[eax+eax*8] ;根据循环变量定位结构头
7662116c 8d0485a0806276 lea eax,[cryptdll!CheckSumFns (766280a0)+eax*4]
76621173 8901 mov [ecx],eax ;返回结构指针
76621175 33c0 xor eax,eax
76621177 c20800 ret 0x8
7662117a b842030880 mov eax,0x80080342
7662117f ebf6 jmp cryptdll!CDLocateCheckSum+0x28 (76621177)
76621181 90 nop
76621182 90 nop
76621183 90 nop
76621184 90 nop
76621185 90 nop
76621186 90 nop
76621187 90 nop
76621188 90 nop
76621189 90 nop
7662118a 90 nop
7662118b 90 nop
kd> dd 76628090 L1
76628090 0000000f
看一下CHECK_SUM究竟有什么:
kd> dd cryptdll!CheckSumFns L40
766280a0 ffffff77 00000010 00000002 76624ed0
766280b0 76621110 76621690 766213f0 76621bf0
766280c0 00000000 ffffff76 00000010 00000002
766280d0 76624ed0 76621110 76621690 766213f0
766280e0 76621840 00000000 00000002 00000010
766280f0 00000000 76624cf0 76624d50 76624d80
76628100 766211f0 76624d30 00000000 00000007
76628110 00000010 00000000 76621220 76621110
76628120 76621260 766211f0 76621bd0 00000000
76628130 00000001 00000004 00000000 76625aa0
76628140 76625980 76625b10 766211f0 76625b60
76628150 00000000 ffffff7a 00000008 00000002
76628160 76624ed0 76625040 76624f60 76624fb0
76628170 76624ef0 00000000 ffffff79 00000008
76628180 00000002 76624ed0 76621110 76624ea0
76628190 766213f0 76624db0 00000000 ffffff7b
kd> u 76624cf0
cryptdll!md4Initialize:
76624cf0 56 push esi
76624cf1 6a68 push 0x68
76624cf3 ff156c106276 call dword ptr [cryptdll!_imp__malloc (7662106c)]
76624cf9 8bf0 mov esi,eax
76624cfb 85f6 test esi,esi
76624cfd 59 pop ecx
76624cfe 7507 jnz cryptdll!md4Initialize+0x17 (76624d07)
76624d00 b8170000c0 mov eax,0xc0000017
kd> u 76624d50
cryptdll!md4Sum:
76624d50 ff742408 push dword ptr [esp+0x8]
76624d54 ff742410 push dword ptr [esp+0x10]
76624d58 ff74240c push dword ptr [esp+0xc]
76624d5c e89f100000 call cryptdll!MD4Update (76625e00)
76624d61 33c0 xor eax,eax
76624d63 c20c00 ret 0xc
cryptdll!md4Finalize:
76624d66 90 nop
76624d67 90 nop
kd> u 76624d80
cryptdll!md4Finalize:
76624d80 56 push esi
76624d81 8b742408 mov esi,[esp+0x8]
76624d85 57 push edi
76624d86 56 push esi
76624d87 e894100000 call cryptdll!MD4Final (76625e20)
76624d8c 8b7c2410 mov edi,[esp+0x10]
76624d90 83c658 add esi,0x58
76624d93 a5 movsd
kd> u 766211f0
cryptdll!md4Finish:
766211f0 56 push esi
766211f1 8b742408 mov esi,[esp+0x8]
766211f5 ff36 push dword ptr [esi]
766211f7 ff1570106276 call dword ptr [cryptdll!_imp__free (76621070)]
766211fd 832600 and dword ptr [esi],0x0
76621200 59 pop ecx
76621201 33c0 xor eax,eax
76621203 5e pop esi
kd> u 76624d30
cryptdll!md4InitializeEx:
76624d30 ff742410 push dword ptr [esp+0x10]
76624d34 6a00 push 0x0
76624d36 e8b5ffffff call cryptdll!md4Initialize (76624cf0)
76624d3b c21000 ret 0x10
cryptdll!md4Sum:
76624d3e 90 nop
76624d3f 90 nop
76624d40 90 nop
76624d41 90 nop
原来是这样:
trpedef _CHECK_SUM {
DWORD Type; //??
ULONG Unknow1;
ULONG Unknoe2;
PVOID Function[5];
ULONG Unknow3; // 总是0?
}
对于Type==2的情况,Function[5]分别指向:
cryptdll!md4Initialize
cryptdll!md4Sum
cryptdll!md4Finalize
cryptdll!md4Finish
cryptdll!md4InitializeEx
再回到rc4HmacHashPassword。
kd> u cryptdll!rc4HmacHashPassword L30
cryptdll!rc4HmacHashPassword:
766258a0 55 push ebp
766258a1 8bec mov ebp,esp
766258a3 51 push ecx
766258a4 51 push ecx
766258a5 8d45fc lea eax,[ebp-0x4] ;ebp-0x4是结构指针地址&pChechSum
766258a8 50 push eax
766258a9 6a02 push 0x2 ;查找类型2的CHECK_SUM
766258ab e890b8ffff call cryptdll!CDLocateCheckSum (76621140) ;CDLocateCheckSum(2,&pChechSum)
766258b0 85c0 test eax,eax
766258b2 7d07 jge cryptdll!rc4HmacHashPassword+0x1b (766258bb)
766258b4 b842030880 mov eax,0x80080342
766258b9 eb3b jmp cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258bb 8d45f8 lea eax,[ebp-0x8] ;ebp-0x8=&pMD4
766258be 50 push eax ;push &pMD4
766258bf 8b45fc mov eax,[ebp-0x4] ;eax=pChechSum
766258c2 6a00 push 0x0
766258c4 ff500c call dword ptr [eax+0xc] ;md4Initialize(0,&pMD4)
766258c7 85c0 test eax,eax
766258c9 7c2b jl cryptdll!rc4HmacHashPassword+0x56 (766258f6)
766258cb 8b4508 mov eax,[ebp+0x8]
766258ce ff7004 push dword ptr [eax+0x4] ;push uPassword->Buffer (pPassword)
766258d1 0fb700 movzx eax,word ptr [eax]
766258d4 50 push eax ;push uPassword->Length (PassLeng)
766258d5 ff75f8 push dword ptr [ebp-0x8] ;现在知道pMD4由md4Initialize决定
766258d8 8b45fc mov eax,[ebp-0x4]
766258db ff5010 call dword ptr [eax+0x10] ;md4Sum(pMD4,PassLeng,pPassword)
766258de ff750c push dword ptr [ebp+0xc]
766258e1 8b45fc mov eax,[ebp-0x4]
766258e4 ff75f8 push dword ptr [ebp-0x8]
766258e7 ff5014 call dword ptr [eax+0x14] ;md4Finalize(pMD4,pHash)
766258ea 8d45f8 lea eax,[ebp-0x8]
766258ed 50 push eax
766258ee 8b45fc mov eax,[ebp-0x4]
766258f1 ff5018 call dword ptr [eax+0x18] ;md4Finish(&pMD4)
766258f4 33c0 xor eax,eax
766258f6 c9 leave
766258f7 c20800 ret 0x8
766258fa 90 nop
766258fb 90 nop
766258fc 90 nop
766258fd 90 nop
766258fe 90 nop
766258ff 90 nop
76625900 90 nop
76625901 90 nop
76625902 90 nop
76625903 90 nop
76625904 90 nop
再看md4Initialize:
kd> u cryptdll!md4Initialize L10
cryptdll!md4Initialize:
76624cf0 56 push esi
76624cf1 6a68 push 0x68
76624cf3 ff156c106276 call dword ptr [cryptdll!_imp__malloc (7662106c)]
76624cf9 8bf0 mov esi,eax ;pMD4=malloc(0x68)
76624cfb 85f6 test esi,esi
76624cfd 59 pop ecx
76624cfe 7507 jnz cryptdll!md4Initialize+0x17 (76624d07)
76624d00 b8170000c0 mov eax,0xc0000017
76624d05 eb0e jmp cryptdll!md4Initialize+0x25 (76624d15)
76624d07 56 push esi
76624d08 e8d3100000 call cryptdll!MD4Init (76625de0) ;MD4Init(pMD4)
76624d0d 8b44240c mov eax,[esp+0xc]
76624d11 8930 mov [eax],esi
76624d13 33c0 xor eax,eax
76624d15 5e pop esi
76624d16 c20800 ret 0x8
搞了半天,pMD4(包括其中的密码缓存)是由malloc分配的。这下没戏了,根本无法确定它的位置。
这一点其实用脚趾头想想也知道,但非要亲眼所见才死心。 ^_^
还有最后一线“希望”——希望lsass把密码留着别释放。
md4Initialize(0,&pMD4);
md4Sum(pMD4,PassLeng,pPassword);
之后是
md4Finalize(pMD4,pHash);
kd> u cryptdll!md4Finalize L10
cryptdll!md4Finalize:
76624d80 56 push esi
76624d81 8b742408 mov esi,[esp+0x8]
76624d85 57 push edi
76624d86 56 push esi
76624d87 e894100000 call cryptdll!MD4Final (76625e20) ;MD4Final(pMD4)
76624d8c 8b7c2410 mov edi,[esp+0x10]
76624d90 83c658 add esi,0x58 ;esi=pMD4->Hash
76624d93 a5 movsd ;memcpy(pHash,pMD4->Hash,16)
76624d94 a5 movsd
76624d95 a5 movsd
76624d96 a5 movsd
76624d97 5f pop edi
76624d98 33c0 xor eax,eax
76624d9a 5e pop esi
76624d9b c20800 ret 0x8
cryptdll!md4Finish:
76624d9e 90 nop
md4Finalize调用MD4Final:
kd> u cryptdll!MD4Final
cryptdll!MD4Final:
76625e20 ff2550106276 jmp dword ptr [cryptdll!_imp__MD4Final (76621050)]
转到_imp__MD4Final:
kd> dd cryptdll!_imp__MD4Final L1
76621050 77d7d3f3
kd> u 77d7d3f3 L35
ADVAPI32!MD4Final:
77d7d3f3 83ec48 sub esp,0x48
77d7d3f6 53 push ebx
77d7d3f7 55 push ebp
77d7d3f8 56 push esi
77d7d3f9 8b742458 mov esi,[esp+0x58] ;esi=pMD4
77d7d3fd 8b4610 mov eax,[esi+0x10] ;eax=pMD4->Count.LowPart
77d7d400 8b4e14 mov ecx,[esi+0x14] ;ecx=pMD4->Count.HighPart
77d7d403 8d5e58 lea ebx,[esi+0x58] ;ebx=pMD4->Hash
77d7d406 8903 mov [ebx],eax ;下面是一些算法的操作,不关心
77d7d408 c1e803 shr eax,0x3 ;盯紧esi(=pMD4)就是了
77d7d40b 83e03f and eax,0x3f
77d7d40e 83f838 cmp eax,0x38
77d7d411 57 push edi
77d7d412 894e5c mov [esi+0x5c],ecx
77d7d415 ba38000000 mov edx,0x38
77d7d41a 0f83d3830100 jnb ADVAPI32!MD4Final+0x29 (77d957f3)
77d7d420 2bd0 sub edx,eax
77d7d422 8bca mov ecx,edx
77d7d424 8be9 mov ebp,ecx
77d7d426 c1e902 shr ecx,0x2
77d7d429 33c0 xor eax,eax
77d7d42b 8d7c2410 lea edi,[esp+0x10]
77d7d42f f3ab rep stosd
77d7d431 52 push edx
77d7d432 8d542414 lea edx,[esp+0x14]
77d7d436 8bcd mov ecx,ebp
77d7d438 83e103 and ecx,0x3
77d7d43b 52 push edx
77d7d43c f3aa rep stosb
77d7d43e 56 push esi
77d7d43f c644241c80 mov byte ptr [esp+0x1c],0x80
77d7d444 e8f1040000 call ADVAPI32!MD4Update (77d7d93a)
77d7d449 6a08 push 0x8
77d7d44b 53 push ebx
77d7d44c 56 push esi
77d7d44d e8e8040000 call ADVAPI32!MD4Update (77d7d93a)
77d7d452 8b06 mov eax,[esi]
77d7d454 8b4e04 mov ecx,[esi+0x4]
77d7d457 8b5608 mov edx,[esi+0x8]
77d7d45a 8903 mov [ebx],eax
77d7d45c 8b460c mov eax,[esi+0xc]
77d7d45f 5f pop edi
77d7d460 894e5c mov [esi+0x5c],ecx
77d7d463 895660 mov [esi+0x60],edx
77d7d466 894664 mov [esi+0x64],eax
77d7d469 5e pop esi
77d7d46a 5d pop ebp
77d7d46b 5b pop ebx
77d7d46c 83c448 add esp,0x48
77d7d46f c20400 ret 0x4
ADVAPI32!MD5Init:
77d7d472 90 nop
77d7d473 90 nop
77d7d474 90 nop
咦,怎么没释放?别高兴的太早,下面还有md4Finish呢!
md4Finish(&pMD4);
kd> u cryptdll!md4Finish L10
cryptdll!md4Finish:
766211f0 56 push esi
766211f1 8b742408 mov esi,[esp+0x8]
766211f5 ff36 push dword ptr [esi]
766211f7 ff1570106276 call dword ptr [cryptdll!_imp__free (76621070)] ;free(pMD4)
766211fd 832600 and dword ptr [esi],0x0 ;pMD4=NULL
76621200 59 pop ecx
76621201 33c0 xor eax,eax
76621203 5e pop esi
76621204 c20400 ret 0x4
cryptdll!md5Initialize:
76621207 90 nop
76621208 90 nop
76621209 90 nop
7662120a 90 nop
7662120b 90 nop
7662120c 90 nop
7662120d 90 nop
彻底没戏啦。pMD4连同里面的明文密码被free了。
我不知道关于lsass留着明文密码备用的说法究竟是不是可靠的,至少我们看到的那个确实被free了。
如果说,前面提到的多个“源密码”中的某一个被lsass留着,那就不应该搜索不到。要么密码不是明文的。
结论很明显,在lsass里找明文密码属于RP问题。如果它已经被覆盖,就算你用什么模拟登陆的方法确定它的位置,也是回天乏术。如果没有被覆盖,用WinEggDrop的办法找密码已经足够了。
我再加一个判断条件:密码前面的2个ULONG总是0x00000200和0x00000000。它们就是pMD4->Count的LowPart和HighPart,用于保存被加密数据的bit数。MD4加密以64字节(0x200 bit)为最小单位,密码不超过32个字符的话,一轮加密就完了,所以它们总是0x200和0x0。这个条件可以在密码被覆盖时减少误报。
PS:本文简直是笨笨熊前辈的windbg教程的活生生的例子! 学习!
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
看原图
赞赏
雪币:
留言: