栈溢出原理与实践之读书笔记
2022-3-1 17:59:0 Author: mp.weixin.qq.com(查看原文) 阅读量:22 收藏


本文为看雪论坛优秀文章
看雪论坛作者ID:瑞皇

新的一年想把0day漏洞安全这本书读完,会一步一步踏踏实实的学完,和大家共享笔记。


环境我想用vs2019运行,老的编译器很多人不用了,渐渐的会被淘汰。用新的编译器可以锻炼下编译选项的功底。

我的笔记是配合书写的,理论部分会少用笔墨,着重在实验上。

1

基础知识

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

2

栈溢出原理与实践

2.1 系统栈的工作原理

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

2.2 修改邻接变量

运行环境:

VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】
然后运行代码,输入qqqqqqqq即,可完成简单的溢出覆盖。
#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字节。然后程序报错,我原本以为时字符对齐的问题,检查后发现不是。是运行时检查的问题。


 
关闭堆栈帧 (/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 [email protected] (0409008h) 00401608  call        @[email protected] (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        @[email protected] (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 [email protected] (0E3C008h) 00E317CA  call        @[email protected] (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        @[email protected] (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        @[email protected] (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

通过代码的比较,我们可以发现,函数使用@[email protected]进行检测,跟进去发现该函数容纳了两个_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'


 

看雪ID:瑞皇

https://bbs.pediy.com/user-home-848111.htm

*本文由看雪论坛 瑞皇 原创,转载请注明来自看雪社区

# 往期推荐

1.符号执行挖掘开源库命令注入

2.CVE-2021-4034 pkexec本地提权漏洞复现与原理分析

3.Microsoft Windows提权漏洞CVE-2013-3660 x86、x64双平台分析

4.Flutter应用逆向分析相关讨论

5.XX弹幕投票助手分析

6.什么是runC?

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458430325&idx=1&sn=a067de381e49e8338cca1f84fb72fd1d&chksm=b18f9bff86f812e952d9d1c6b9838ee38143713dcc4bb5cf625e1234e7da777e0f69be350db8#rd
如有侵权请联系:admin#unsafe.sh