5

[原创]0day安全软件漏洞分析技术读书笔记——第二章

 3 years ago
source link: https://bbs.pediy.com/thread-271418.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
[原创]0day安全软件漏洞分析技术读书笔记——第二章
2022-2-8 14:07 8833

新的一年想把0day漏洞安全这本书读完,会一步一步踏踏实实的学完,和大家共享笔记。
环境我想用vs2019运行,老的编译器很多人不用了,渐渐的会被淘汰。用新的编译器可以锻炼下编译选项的功底。
我的笔记是配合书写的,理论部分会少用笔墨,着重在实验上。

1.基础知识

这一部分书上讲解的足够了,如果暂时看不懂,多看看就好。功夫不负有心人,迟早学会的事情。

2.栈溢出原理与实践

2.1.系统栈的工作原理

这一部分书上讲解的足够了,如果暂时看不懂,多看看就好。功夫不负有心人,迟早学会的事情。

2.2.修改邻接变量

运行环境:

VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】
然后运行代码,输入qqqqqqqq即,可完成简单的溢出覆盖。
图片.png

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PASSWORD "1234567"
int verify_password(char* password)
{
int authenticated;
char buffer[8];// add local buffto be overflowed
authenticated = strcmp(password, PASSWORD);
strcpy(buffer, password);//over flowed here!
return authenticated;
}
void main()
{
int valid_flag = 0;
char password[1024];
while (1)
{
printf("please input password: ");
scanf("%s", password);
valid_flag = verify_password(password);
if (valid_flag)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
}

实验情况:

SDL关闭和头文件的增加为了让程序代码跑起来。
堆栈帧 (/RTCs)的关闭,则是为了让程序不在运行时检查程序。
如果不关闭堆栈帧 (/RTCs),原本buffer[8]会因为检查多出8字节。然后程序报错,我原本以为时字符对齐的问题,检查后发现不是。是运行时检查的问题。
图片.png
图片.png

关闭堆栈帧 (/RTCs)的汇编代码:

int verify_password(char* password)
{
004015F0  push        ebp 
004015F1  mov         ebp,esp 
004015F3  sub         esp,50h 
004015F6  mov         eax,dword ptr [__security_cookie (0407004h)] 
004015FB  xor         eax,ebp 
004015FD  mov         dword ptr [ebp-4],eax 
00401600  push        ebx 
00401601  push        esi 
00401602  push        edi 
00401603  mov         ecx,offset _06E17EB3_Test@cpp (0409008h
00401608  call        @__CheckForDebuggerJustMyCode@4 (0401285h
int authenticated;
char buffer[8];// add local buffto be overflowed
authenticated = strcmp(password, PASSWORD);
0040160D  push        offset string "1234567" (0405B30h
00401612  mov         eax,dword ptr [password] 
00401615  push        eax 
00401616  call        _strcmp (040103Ch
0040161B  add         esp,8 
0040161E  mov         dword ptr [authenticated],eax 
strcpy(buffer, password);//over flowed here!
00401621  mov         eax,dword ptr [password] 
00401624  push        eax 
00401625  lea         ecx,[buffer
00401628  push        ecx 
00401629  call        _strcpy (040119Ah
0040162E  add         esp,8 
return authenticated;
00401631  mov         eax,dword ptr [authenticated] 
}
00401634  pop         edi 
00401635  pop         esi 
00401636  pop         ebx 
00401637  mov         ecx,dword ptr [ebp-4
0040163A  xor         ecx,ebp 
0040163C  call        @__security_check_cookie@4 (040111Dh
00401641  mov         esp,ebp 
00401643  pop         ebp 
00401644  ret

开启堆栈帧 (/RTCs)的汇编代码:

int verify_password(char* password)
{
00E317A0  push        ebp 
00E317A1  mov         ebp,esp 
00E317A3  sub         esp,0E0h 
00E317A9  push        ebx 
00E317AA  push        esi 
00E317AB  push        edi 
00E317AC  lea         edi,[ebp-20h
00E317AF  mov         ecx,8 
00E317B4  mov         eax,0CCCCCCCCh 
00E317B9  rep stos    dword ptr es:[edi] 
00E317BB  mov         eax,dword ptr [__security_cookie (0E3A004h)] 
00E317C0  xor         eax,ebp 
00E317C2  mov         dword ptr [ebp-4],eax 
00E317C5  mov         ecx,offset _06E17EB3_Test@cpp (0E3C008h
00E317CA  call        @__CheckForDebuggerJustMyCode@4 (0E3132Fh
int authenticated;
char buffer[8];// add local buffto be overflowed
authenticated = strcmp(password, PASSWORD);
00E317CF  push        offset string "1234567" (0E37B30h
00E317D4  mov         eax,dword ptr [password] 
00E317D7  push        eax 
00E317D8  call        _strcmp (0E31046h
00E317DD  add         esp,8 
00E317E0  mov         dword ptr [authenticated],eax 
strcpy(buffer, password);//over flowed here!
00E317E3  mov         eax,dword ptr [password] 
00E317E6  push        eax 
00E317E7  lea         ecx,[buffer
00E317EA  push        ecx 
00E317EB  call        _strcpy (0E31212h
00E317F0  add         esp,8 
return authenticated;
00E317F3  mov         eax,dword ptr [authenticated] 
}
00E317F6  push        edx 
00E317F7  mov         ecx,ebp 
00E317F9  push        eax 
00E317FA  lea         edx,ds:[0E31828h
00E31800  call        @_RTC_CheckStackVars@8 (0E311EFh
00E31805  pop         eax 
00E31806  pop         edx 
00E31807  pop         edi 
00E31808  pop         esi 
00E31809  pop         ebx 
00E3180A  mov         ecx,dword ptr [ebp-4
00E3180D  xor         ecx,ebp 
00E3180F  call        @__security_check_cookie@4 (0E31154h
00E31814  add         esp,0E0h 
00E3181A  cmp         ebp,esp 
00E3181C  call        __RTC_CheckEsp (0E31253h
00E31821  mov         esp,ebp 
00E31823  pop         ebp 
00E31824  ret 
00E31825  nop         dword ptr [eax] 
00E31828  add         dword ptr [eax],eax 
00E3182A  add         byte ptr [eax],al 
00E3182C  xor         byte ptr [eax],bl 
00E3182E  jecxz       __$EncStackInitStart+84h (0E31830h
00E31830  in          al,0FFh 
00E31832  ?? ??????
}
00E31833  dec         dword ptr [eax] 
00E31835  add         byte ptr [eax],al 
00E31837  add         byte ptr [eax+ebx],bh 
00E3183A  jecxz       __$EncStackInitStart+90h (0E3183Ch
00E3183C  bound       esi,qword ptr [ebp+66h
00E3183F  jb          00001843

通过代码的比较,我们可以发现,函数使用@_RTC_CheckStackVars@8进行检测,跟进去发现该函数容纳了两个_RTC_CheckStackVars,当检测不是0CCCCCCCCh的时候,会报错,并进入_RTC_StackFailure函数。

006E1DD4  mov         ecx,dword ptr [ebx+4
006E1DD7  mov         eax,dword ptr [frame] 
006E1DDA  mov         edx,dword ptr [ecx+edi] 
006E1DDD  cmp         dword ptr [edx+eax-4],0CCCCCCCCh 
006E1DE5  jne         _RTC_CheckStackVars+39h (06E1DF9h
006E1DE7  mov         eax,dword ptr [ecx+edi+4
006E1DEB  add         eax,edx 
006E1DED  mov         edx,dword ptr [frame] 
006E1DF0  cmp         dword ptr [eax+edx],0CCCCCCCCh 
006E1DF7  je          _RTC_CheckStackVars+49h (06E1E09h
006E1DF9  push        dword ptr [ecx+edi+8
006E1DFD  mov         eax,dword ptr [ebp+4
006E1E00  push        eax 
006E1E01  call        _RTC_StackFailure (06E1352h)

2.3.控制程序的执行流程

运行环境:VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】

实验情况:

修改上述配置后,发现无法正常运行,单步调试发现fopen断点出现问题。搜索查询后发现有些编译器不支持rw+的格式。这里我们将rw+修改为r+即可正确运行。r和r+的区别是r+拥有写权限。我大胆猜测,rw+不支持的原因是功能设计上的重复。

在这里我们运行还会遇到一个问题

我们需要把栈保护天使GS关闭。
属性->c/c++->代码运行->安全检查->关闭【禁用安全检查 (/GS-)】
这个时候代码就会报书上期望我们出现的错误,返回值出现错误。理论方面书上说的已经很全面,这里就简单画个图。

最终结果如下图,符合书上预期:

2.4.向进程中植入代码

运行环境:VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】
属性->c/c++->代码运行->安全检查->关闭【禁用安全检查 (/GS-)】

实验情况:

创建一个文件,password.txt,构造shellcode。这里按照书上就好,然后看下面的步骤。

配置需要进行修改,因为要静态获取buffer地址,aslr随机基址要关闭,因为要在数据区执行代码,数据执行保护(DEP)也要关闭。
属性->链接器->高级->随机基址->否
属性->链接器->高级->数据执行保护(DEP)->否

password.txt,有两个地方需要修改,根据书中描述。
一个是Messagebox的地址,在下图中会讲解如何寻找。
一个是buffer的地址,下文中也会详细介绍细节。

我们在程序中添加messagebox,这样我们的代码就会调用User32.dll,我们使用dependency可以获取该模块函数地址。

根据书中方法,计算正确值

>>> hex(0x69E00000+0x83670)
'0x69e83670'

但是可以看到,程序并不能正确运行,这问题常出在现在的windows操作系统里,优先基址常常不是实际加载地址,在PE格式中DLL会给出一个优先加载地址,当程序并未占用该地址时,优先按照该基址进行计算。占用的话,会重新申请空间。
问题发生了,如何解决,这里使用Ollydbg查看模块地址。

下面是Ollydbg的正确解决方案
步骤1:点击E字母

步骤2:找到User32,此时使用该模块地址计算,可以获得正确地址,成功弹出messagebox
如果不想算,就按一下User32,快捷键ctrl+n,找到messageboxA一样可以。

>>> hex(0x75cc0000+0x83670)
'0x75d43670'

【公告】看雪团队招聘安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

最后于 2022-2-11 23:35 被瑞皇编辑 ,原因:

上传的附件:

最新回复 (2)

lukarl 活跃值 2022-2-8 17:51
2 楼
有配套的视频课程
https://ke.qq.com/course/3485787?tuin=178b36ba
都是参考0Day这本书的
瑞皇 活跃值 2022-2-8 19:08
3 楼

有配套的视频课程 https://ke.qq.com/course/3485787?tuin=178b36ba 都是参考0Day这本书的

咦,还有这个东西呀,谢谢提醒了~
不过他只有前四章还要88,觉得有点贵了,我的需求是整本书读完。
目前刷起来不是很吃力,我还是愿意更扎实的慢慢看书。
谢谢小伙伴啦13.gif13.gif13.gif
游客
登录 | 注册 方可回帖
返回

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK