

[原创]0day安全软件漏洞分析技术读书笔记——第二章
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.

新的一年想把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 _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年安全圈的口碑,助你快速成长!
上传的附件:
- 第二章.zip (2.78MB,5次下载)
Recommend
-
12
软件漏洞分析技巧分享 腾讯安全中心
-
12
PHP漏洞挖掘思路+实例 第二章 lxj616 ·...
-
11
0x00 前言 在近些时间基本都能在一些渗透或者是攻防演练中看到Shiro的身影,也是Shiro的该漏洞也是用的比较频繁的漏洞。本文对...
-
6
[原创]固件安全之加载地址分析-智能设备-看雪论坛-安全社区|安全招聘|bbs.pediy.com在固件分析中,我们经常需要定位固件的加载地址,尤其是Vxworks或者是Linux kernel加载到内存的地址,来方便逆向工具例如IDA PRO进行正确的反汇编以及字符串引用。在下面...
-
9
软件安全测试中日志伪造漏洞 一、什么是日志伪造漏洞? 应用程序通常使用日志文件来存储事件或事务的历史,以供以后查看、收集统计信息或调试。根据应用程序的性质,检查日志文件的任...
-
10
[原创] Windows PrintNightmare 漏洞复现分析 ...
-
16
#yyds干货盘点#Spring Cloud源码分析之Eureka第二章:注册中心启动类上的注解EnableEurekaServer 推荐 原创 程...
-
5
第二章 boltdb的核心数据结构分析第二章 boltdb的核心数...
-
5
软件安全知识之漏洞类别-结构化输出生成漏洞 作者:铸盾安全 2023-05-13 00:06:33 利用结构化输出生成漏洞的攻击技术通常取决于结构化输出语言的性质,但已知并记录了用于利用SQL注入或脚本注入的各种攻击技术。
-
8
软件安全知识之检测漏洞 作者:铸盾安全 2023-05-26 00:04:02 实现健全性需要对程序的所有执行进行推理(通常是无限多个)。这通常是通过对程序代码进行静态检查来完成的,同时对执行进行适当的抽象以使分析终止...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK