chore: turn cos to cdn
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
#### 栈介绍
|
||||
栈是一种典型的后进先出 (Last in First Out) 的数据结构,其操作主要有压栈 (push) 与出栈 (pop) 两种操作,如下图所示(维基百科)。两种操作都操作栈顶,当然,它也有栈底。
|
||||
|
||||

|
||||

|
||||
|
||||
高级语言在运行时都会被转换为汇编程序,在汇编程序运行过程中,充分利用了栈这一数据结构。每个程序在运行时都有虚拟地址空间,其中某一部分就是该程序对应的栈,用于保存函数调用信息和局部变量。此外,常见的操作也是压栈与出栈。需要注意的是,**程序的栈是从进程地址空间的高地址向低地址增长的**。
|
||||
#### 栈溢出基本原理
|
||||
@@ -21,7 +21,7 @@ int main()
|
||||
|
||||
对于如上程序,运行后可以发现`ch`和`a`的地址相差不大(`a`和`ch`的顺序不一定固定为`a`在前`ch`在后):
|
||||
|
||||

|
||||

|
||||
|
||||
可以发现`ch`和`ch2`刚好差`8`个字节,也就是`ch`的长度。
|
||||
`ch`只有`8`个字节,那么如果我们向`ch`中写入超过`8`个字节的数据呢?很显然,会从`ch`处发生溢出,写入到`ch2`的空间中,覆盖`ch2`的内容。
|
||||
@@ -37,7 +37,7 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
这就是栈溢出的基本原理。
|
||||
|
||||
@@ -131,7 +131,7 @@ retn
|
||||
|
||||
可以看到其中使用`call`指令来调用`add`函数。那么该指令是如何工作的呢?其实`call`指令相当于`push next_loc;jmp loc`,通过将`call`指令下一行汇编的地址压栈的方式,等到函数调用完再取回,从而从`call`指令的下一行继续执行。由于栈地址从高向低生长,新调用的函数的局部变量生成在返回地址的上方(低地址处),因此如果我们在新函数中使用栈溢出来修改这一返回地址,如果将返回地址修改为某个函数的地址,就可以执行任意函数:
|
||||
|
||||

|
||||

|
||||
|
||||
> 注意该图中,使用32位的寄存器(EBP、ESP、EIP),实际原理一样的,并且上方为高地址,下方为低地址
|
||||
|
||||
@@ -139,22 +139,22 @@ retn
|
||||
|
||||
32位的程序,我们使用IDA来打开该题目,查看反编译代码,可以发现有非常明显的栈溢出:
|
||||
|
||||

|
||||

|
||||
|
||||
由于第`8`行`gets`函数并没有检查输入的长度和`s`的长度,我们可以轻易地通过栈溢出来控制`main`函数的返回地址。而在程序中,存在另外一个函数`secure`,在该函数中有一个后门`system("/bin/sh")`,如果我们想办法执行该后门,就可以拿到目标机器的`shell`,从而控制目标计算机。
|
||||
|
||||
由于我们需要将返回地址在标准输入中输入待测程序,而返回地址拆分成小端序的字节后经常无法手动输入到待测程序中,所以此处我们使用`pwntools`这一`python`包来方便地进行攻击。
|
||||
首先查看后门的地址:
|
||||
|
||||

|
||||

|
||||
|
||||
接着计算溢出长度,这里我们使用gdb来调试程序,图中的gdb安装了pwndbg插件,该插件在pwn调试时比较好用:
|
||||
|
||||

|
||||

|
||||
|
||||
将断点打在`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 *
|
||||
@@ -165,7 +165,7 @@ sh.sendline(exp)
|
||||
sh.interactive() # 切换为手动交互模式
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### 0x1
|
||||
通过上面的学习,我们已经可以知道执行任意函数的办法,但很多情况下,对于攻击者来说,程序中并没有可用的后门函数来达到攻击的目的,因此我们需要一种手段,来让程序执行任意代码(任意汇编代码),这样就可以最高效地进行攻击。ROP(Return Oriented Programming)面向返回编程就是这样的一种技术,在栈溢出的基础上,通过在程序中寻找以retn结尾的小片段(gadgets),来改变某些寄存器、栈变量等的值,再结合Linux下的系统调用,我们就可以执行需要的任意代码。
|
||||
|
||||
Reference in New Issue
Block a user