diff --git a/6.计算机安全/6.2.3漏洞挖掘、漏洞利用.md b/6.计算机安全/6.2.3漏洞挖掘、漏洞利用.md new file mode 100644 index 0000000..d1c4a28 --- /dev/null +++ b/6.计算机安全/6.2.3漏洞挖掘、漏洞利用.md @@ -0,0 +1,172 @@ +# 漏洞挖掘、漏洞利用 +## 常见二进制安全漏洞 +### 栈溢出 +#### 栈介绍 +栈是一种典型的后进先出 (Last in First Out) 的数据结构,其操作主要有压栈 (push) 与出栈 (pop) 两种操作,如下图所示(维基百科)。两种操作都操作栈顶,当然,它也有栈底。 +![](./static/stack.png) + +高级语言在运行时都会被转换为汇编程序,在汇编程序运行过程中,充分利用了栈这一数据结构。每个程序在运行时都有虚拟地址空间,其中某一部分就是该程序对应的栈,用于保存函数调用信息和局部变量。此外,常见的操作也是压栈与出栈。需要注意的是,**程序的栈是从进程地址空间的高地址向低地址增长的**。 +#### 栈溢出基本原理 +以最基本的C语言为例,C语言的函数局部变量就保存在栈中。 +```C +#include +int main() +{ + char ch[8]={0}; + char ch2[8]={0}; + printf("ch: %p, ch2: %p",ch,ch2); +} +``` +对于如上程序,运行后可以发现`ch`和`a`的地址相差不大(`a`和`ch`的顺序不一定固定为`a`在前`ch`在后): +![](./static/out1.PNG) + +可以发现`ch`和`ch2`刚好差`8`个字节,也就是`ch`的长度。 +`ch`只有`8`个字节,那么如果我们向`ch`中写入超过`8`个字节的数据呢?很显然,会从`ch`处发生溢出,写入到`ch2`的空间中,覆盖`ch2`的内容。 +```C +#include +int main() +{ + char ch[8]={0}; + char ch2[8]={0}; + scanf("%s",ch); + printf("ch: %s, ch2: %s",ch,ch2); +} +``` +![](./static/out2.PNG) + +这就是栈溢出的基本原理。 + +#### 栈溢出的基本利用 +##### 0x0 +对于以上程序,“栈溢出”带来的后果仅仅是修改了局部变量的值,会造成一些程序的逻辑错误: +```C +#include +int main() +{ + char input[20]; + char password[]="vidar-team"; + scanf("%s",input); + if(!strcmp(password,input)) + { + printf("login success!"); + } + else + { + printf("password is wrong!"); + } + return 0; +} +``` +如上代码所示,如果我们想办法通过向input中输入过长的字符串覆盖掉password的内容,我们就可以实现任意password“登录”。 +那么能不能有一些更劲爆的手段呢? +> 以下内容涉及x86汇编语言知识 +在C语言编译之后,通常会产生汇编语言,汇编语言的字节码可以直接在物理CPU上运行。而C语言函数调用会被编译为如下形式: +```C +#include +int add(int a,int b) +{ + return a+b; +} +int main() +{ + int a,b; + scanf("%d %d",&a,&b); + printf("%d",add(a,b)); + return 0; +} +``` +```assembly +add: +endbr64 +push rbp +mov rbp, rsp +mov [rbp+var_4], edi +mov [rbp+var_8], esi +mov edx, [rbp+var_4] +mov eax, [rbp+var_8] +add eax, edx +pop rbp +retn + +main: +endbr64 +push rbp +mov rbp, rsp +sub rsp, 10h +mov rax, fs:28h +mov [rbp+var_8], rax +xor eax, eax +lea rdx, [rbp+var_C] +lea rax, [rbp+var_10] +mov rsi, rax +lea rax, format ; "%d %d" +mov rdi, rax ; format +mov eax, 0 +call _scanf +mov edx, [rbp+var_C] +mov eax, [rbp+var_10] +mov esi, edx +mov edi, eax +call add +mov esi, eax +lea rax, aD ; "%d" +mov rdi, rax ; format +mov eax, 0 +call _printf +mov eax, 0 +leave +retn +``` +可以看到其中使用`call`指令来调用`add`函数。那么该指令是如何工作的呢?其实`call`指令相当于`push next_loc;jmp loc`,通过将`call`指令下一行汇编的地址压栈的方式,等到函数调用完再取回,从而从`call`指令的下一行继续执行。由于栈地址从高向低生长,新调用的函数的局部变量生成在返回地址的上方(低地址处),因此如果我们在新函数中使用栈溢出来修改这一返回地址,如果将返回地址修改为某个函数的地址,就可以执行任意函数: +![](./static/stack2.png) +> 注意该图中,使用32位的寄存器(EBP、ESP、EIP),实际原理一样的,并且上方为高地址,下方为低地址 + +在此给出一道题作为例子:https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2text/bamboofox-ret2text/ret2text + +32位的程序,我们使用IDA来打开该题目,查看反编译代码,可以发现有非常明显的栈溢出: +![](./static/main.png) + +由于第`8`行`gets`函数并没有检查输入的长度和`s`的长度,我们可以轻易地通过栈溢出来控制`main`函数的返回地址。而在程序中,存在另外一个函数`secure`,在该函数中有一个后门`system("/bin/sh")`,如果我们想办法执行该后门,就可以拿到目标机器的`shell`,从而控制目标计算机。 +由于我们需要将返回地址在标准输入中输入待测程序,而返回地址拆分成小端序的字节后经常无法手动输入到待测程序中,所以此处我们使用`pwntools`这一`python`包来方便地进行攻击。 +首先查看后门的地址: +![](./static/backdoor.png) + +接着计算溢出长度,这里我们使用gdb来调试程序,图中的gdb安装了pwndbg插件,该插件在pwn调试时比较好用: + +![](./static/gdb.png) + +将断点打在`gets`函数前后,可以看到此时`esp`值为`0xffffcd80`,`ebp`值为`0xffffce08`,在 IDA 中我们又可以看到`s`相对于`esp`的偏移为`+1C`,此时我们即可计算`hex(0xffffcd80+0x1c-0xffffce08)=-0x6C`,即`s`相对于`ebp`的偏移为`0x6C`,由于在`main`函数的开头有`push ebp`的操作,所以将`0x6C`再加`4`,即可到达返回地址处: +```python +from pwn import * +sh=process("./pwn") +exp=b'a'*(0x6c+4) +exp+=p32(0x0804863A) # 4字节的返回地址 +sh.sendline(exp) +sh.interactive() # 切换为手动交互模式 +``` +![](./static/shell.png) +##### 0x1 +通过上面的学习,我们已经可以知道执行任意函数的办法,但很多情况下,对于攻击者来说,程序中并没有可用的后门函数来达到攻击的目的,因此我们需要一种手段,来让程序执行任意代码(任意汇编代码),这样就可以最高效地进行攻击。ROP(Return Oriented Programming)面向返回编程就是这样的一种技术,在栈溢出的基础上,通过在程序中寻找以retn结尾的小片段(gadgets),来改变某些寄存器、栈变量等的值,再结合Linux下的系统调用,我们就可以执行需要的任意代码。 +ROP网上已有非常系统的资料,在这里不做过多的叙述,可参考ctf-wiki: https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/#ret2shellcode +### 格式化字符串 +格式化字符串的利用思路来源于`printf`函数中的`%n`format标签,该标签的作用和`%s`、`%d`等不同,是将已打印的字符串的长度返回到该标签对应的变量中。在正常情况下的使用不会出现什么问题: +```C +printf("abcd%n",&num); +//输出abcd,并且num的值为4 +``` +但如果在编写代码时忘记format字符串: +``` +printf(something_want_print); +``` +此时若攻击者可以自定义该字符串,就可以使用`%d`、`%p`、`%s`等打印栈上数据,或者`%n`来覆写栈上的数据,如果覆写了返回地址,就可以实现任意代码执行。 +```C +char ch[20]; +scanf("%s",ch);// 输入 %d%n%n%n%n%n +printf(ch); +``` +## 漏洞挖掘技术 +### 代码审计 +代码审计分人工代码审计和自动化代码审计,人工审计由安全研究人员查看代码来发现漏洞,需要安全研究人员很高的研究经验,投入大量的人力。自动化代码审计目前的发展进度迅速,如由 Vidar-Team 毕业学长 LoRexxar 主导的开源项目Kunlun-M:https://github.com/LoRexxar/Kunlun-M +以及字节跳动公司开源的appshark:https://github.com/bytedance/appshark +### fuzz +fuzz是一种自动化测试手段,通过一定的算法生成一定规律的随机的数据输入到程序中,如果程序发生崩溃等异常,即可知道此处可能有漏洞。比较著名的有[AFL](https://github.com/google/AFL)、[AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)、[libfuzzer](https://llvm.org/docs/LibFuzzer.html)、[honggfuzz](https://github.com/google/honggfuzz)等。 diff --git a/6.计算机安全/static/backdoor.png b/6.计算机安全/static/backdoor.png new file mode 100644 index 0000000..5ef4558 Binary files /dev/null and b/6.计算机安全/static/backdoor.png differ diff --git a/6.计算机安全/static/gdb.png b/6.计算机安全/static/gdb.png new file mode 100644 index 0000000..c3b6081 Binary files /dev/null and b/6.计算机安全/static/gdb.png differ diff --git a/6.计算机安全/static/main.png b/6.计算机安全/static/main.png new file mode 100644 index 0000000..d28b631 Binary files /dev/null and b/6.计算机安全/static/main.png differ diff --git a/6.计算机安全/static/out1.PNG b/6.计算机安全/static/out1.PNG new file mode 100644 index 0000000..64143d7 Binary files /dev/null and b/6.计算机安全/static/out1.PNG differ diff --git a/6.计算机安全/static/out2.PNG b/6.计算机安全/static/out2.PNG new file mode 100644 index 0000000..7dfbd2d Binary files /dev/null and b/6.计算机安全/static/out2.PNG differ diff --git a/6.计算机安全/static/s.png b/6.计算机安全/static/s.png new file mode 100644 index 0000000..0f6d2ca Binary files /dev/null and b/6.计算机安全/static/s.png differ diff --git a/6.计算机安全/static/shell.png b/6.计算机安全/static/shell.png new file mode 100644 index 0000000..abf643c Binary files /dev/null and b/6.计算机安全/static/shell.png differ diff --git a/6.计算机安全/static/stack.png b/6.计算机安全/static/stack.png new file mode 100644 index 0000000..0c9242e Binary files /dev/null and b/6.计算机安全/static/stack.png differ diff --git a/6.计算机安全/static/stack2.png b/6.计算机安全/static/stack2.png new file mode 100644 index 0000000..43990e4 Binary files /dev/null and b/6.计算机安全/static/stack2.png differ