title: ret2libc实战
date: 2021-05-13 22:00:00
tags:
- binary security
- study report
- ret2libc
comments: true
categories: - ctf
- pwn
ret2libc
是一个pwner
必备的基础知识。
ret2libc
为return to libc
的缩写,我们需要执行libc
函数里面的system("/bin/sh")
下面为32位程序并且带.so文件的题目:buuctf[OGeek2019]babyrop
[OGeek2019]babyrop
下载两个文件先丢进IDA里面
首先是pwn.elf
shift+F12
查看字符串,看到比较有用的就是那个Correct\n
但是这个不是逆向题,不用从结果分析,所以这个也是没什么用的,只能等会分析没有看到这个的时候再去整这个。然后也没有看到/bin/sh
字符串,那么我们就先放弃字符串入手了。
查看main的伪C代码,得到
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);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}
首先给你虚晃一枪,自己获得一个我们不知道的数,如果大于0才执行read(fd,&buf,4u);
而我们都知道,read()
函数第一个参数必须为0才能让我们输入内容,那么这一段代码直接抛弃,它注定啥也干不了。然后执行了一个函数,跟进去看
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);
v5 = read(0, buf, 0x20u);
buf[v5 - 1] = 0;
v1 = strlen(buf);
if ( strncmp(buf, s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return (unsigned __int8)buf[7];
}
因为前面传入的buf
指针我们并不可以输入任何值,又是局部变量,所以它的值也是不确定的,然后下面比较要求buf==s
字符串,而这个s
是main()
的buf
,这个函数的buf
是我们可以决定的。但是可惜它用的是strncmp
指定长度比较字符串,而长度是从这个函数的buf里面算到的,那么我们就可以把字符串第一位置为\x00
以躲过检测,然后返回了buf[7]
,那么这里我们就知道应该输入\x00
开始的字符串,至于后面还得看它这个返回值干了啥,返回main()
函数发现返回值为下一个函数的参数,而下一个函数
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, 200u);
else
result = read(0, buf, a1);
return result;
}
很明显我们要在这里溢出了,但是缓冲区大小有足足231,而第一个选项不足以让我们溢出,所以我们如果把参数设为\xff
那么就能输入255长度的字符串足以让我们溢出。所以前面的一个payload
就可以这么构造
payload1=b'\x00'*7+