upd
This commit is contained in:
parent
80fe58d1e5
commit
2c16541654
@ -2,36 +2,59 @@
|
||||
layout: blog.njk
|
||||
title: Making eBPF "Cursed Ping" Work on Linux, macOS, and BusyBox
|
||||
date: 2025-07-25
|
||||
description: How I made my eBPF ping hack universal
|
||||
description: How I made eBPF ping hack universal
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Messing with ping using eBPF is fun-just mutate the timestamp in the ICMP Echo Reply and watch the round-trip time go wild!
|
||||
Messing with ping using eBPF is fun—just mutate the timestamp in the ICMP Echo Reply and watch the round-trip time go wild!
|
||||
But in practice, different ping implementations use different payload formats. If you corrupt the wrong bytes, BSD/macOS ping will instantly complain about "wrong data byte".
|
||||
|
||||
In this post, I’ll show how I made eBPF hack universal: it now works on Linux and macOS/BSD!
|
||||
In this post, I'll show how I made my eBPF hack universal: it now works on Linux, macOS/BSD, and even BusyBox.
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
## The Problem: Different Ping Implementations
|
||||
|
||||
- **Linux (iputils):** timestamp is 16 bytes (2 x uint64)
|
||||
- **macOS/BSD:** timestamp is only 8 bytes, followed by a signature sequence (0x08, 0x09, 0x0a, ...), which must not be touched!
|
||||
- **BusyBox:** timestamp is just 4 bytes (uint32_t)
|
||||
### ICMP Echo Request/Reply Structure
|
||||
|
||||
If you mutate the "extra" bytes, BSD ping immediately reports a corrupted payload.
|
||||
All ping implementations follow the same basic ICMP structure:
|
||||
```
|
||||
[Ethernet Header] -> [IP Header] -> [ICMP Header] -> [Payload]
|
||||
```
|
||||
|
||||
The payload contains the timestamp and additional data, but the format varies:
|
||||
|
||||
- **Linux (iputils):** 16-byte timestamp (2 x uint64_t)
|
||||
```
|
||||
[8 bytes: timestamp_sec] [8 bytes: timestamp_nsec] [rest of payload...]
|
||||
```
|
||||
|
||||
- **macOS/BSD:** 8-byte timestamp + integrity signature
|
||||
```
|
||||
[8 bytes: timestamp] [0x08] [0x09] [0x0a] [0x0b] [0x0c] [0x0d] [0x0e] [0x0f] [rest...]
|
||||
```
|
||||
|
||||
- **BusyBox:** 4-byte timestamp (uint32_t)
|
||||
```
|
||||
[4 bytes: timestamp] [rest of payload...]
|
||||
```
|
||||
|
||||
### Why BSD Ping Complains
|
||||
|
||||
BSD ping validates the integrity signature after the timestamp. If you mutate bytes 8-15 (which contain the signature), ping detects corruption and reports "wrong data byte".
|
||||
|
||||
---
|
||||
|
||||
## The Solution
|
||||
## The Solution: Automatic Detection
|
||||
|
||||
In the eBPF program, we:
|
||||
- Automatically detect the ping type by inspecting the payload signature
|
||||
- Mutate only the correct number of bytes:
|
||||
- Linux: 16 bytes
|
||||
- BSD/macOS: 8 bytes
|
||||
- BusyBox: 4 bytes
|
||||
### Detection Algorithm
|
||||
|
||||
We inspect the payload to determine the ping type:
|
||||
|
||||
1. **Check for BSD signature** at offset 8: `0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f`
|
||||
2. **Check payload size** to identify BusyBox (smaller payloads)
|
||||
3. **Default to Linux** if neither condition is met
|
||||
|
||||
### Key eBPF Code Snippet
|
||||
|
||||
@ -39,19 +62,25 @@ In the eBPF program, we:
|
||||
__u8* payload = (void*)(icmphdr + 1);
|
||||
int ping_type = 0; // 0=linux, 1=bsd, 2=busybox
|
||||
|
||||
// Check for BSD signature: 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
|
||||
if ((void*)(payload + 16) <= data_end) {
|
||||
if (payload[8] == 0x08 && payload[9] == 0x09 && payload[10] == 0x0a && payload[11] == 0x0b) {
|
||||
if (payload[8] == 0x08 && payload[9] == 0x09 && payload[10] == 0x0a && payload[11] == 0x0b &&
|
||||
payload[12] == 0x0c && payload[13] == 0x0d && payload[14] == 0x0e && payload[15] == 0x0f) {
|
||||
ping_type = 1; // BSD/macOS
|
||||
}
|
||||
}
|
||||
|
||||
// Check for BusyBox (smaller payload, no BSD signature)
|
||||
if (ping_type == 0 && (void*)(payload + 8) <= data_end) {
|
||||
if ((void*)(payload + 16) > data_end ||
|
||||
(payload[8] != 0x08 && payload[9] != 0x09)) {
|
||||
(payload[8] != 0x08 && payload[9] != 0x09 && payload[10] != 0x0a && payload[11] != 0x0b)) {
|
||||
ping_type = 2; // BusyBox
|
||||
}
|
||||
}
|
||||
|
||||
// Mutate based on detected type
|
||||
if (ping_type == 2) {
|
||||
// BusyBox: mutate only first 4 bytes (uint32_t timestamp)
|
||||
__u32* ts_busybox = (void*)(icmphdr + 1);
|
||||
if ((void*)(ts_busybox + 1) <= data_end) {
|
||||
__u32 old_ts = *ts_busybox;
|
||||
@ -59,6 +88,7 @@ if (ping_type == 2) {
|
||||
recalc_icmp_csum(icmphdr, old_ts, *ts_busybox);
|
||||
}
|
||||
} else if (ping_type == 1) {
|
||||
// BSD: mutate only first 8 bytes (timestamp)
|
||||
__u64* ts_secs = (void*)(icmphdr + 1);
|
||||
if ((void*)(ts_secs + 1) <= data_end) {
|
||||
__u64 old_secs = *ts_secs;
|
||||
@ -66,6 +96,7 @@ if (ping_type == 2) {
|
||||
recalc_icmp_csum(icmphdr, old_secs, *ts_secs);
|
||||
}
|
||||
} else {
|
||||
// Linux: mutate both 8-byte timestamps
|
||||
__u64* ts_secs = (void*)(icmphdr + 1);
|
||||
if ((void*)(ts_secs + 1) <= data_end) {
|
||||
__u64 old_secs = *ts_secs;
|
||||
@ -81,11 +112,24 @@ if (ping_type == 2) {
|
||||
}
|
||||
```
|
||||
|
||||
### Checksum Recalculation
|
||||
|
||||
After mutating the timestamp, we must recalculate the ICMP checksum:
|
||||
|
||||
```c
|
||||
__attribute__((__always_inline__))
|
||||
static inline void recalc_icmp_csum(struct icmphdr* hdr, __be32 old_value, __be32 new_value) {
|
||||
__u64 csum = hdr->checksum;
|
||||
update_csum(&csum, old_value, new_value);
|
||||
hdr->checksum = csum;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Real-World Examples
|
||||
|
||||
### Linux (iputils):
|
||||
### Linux (iputils) - 16-byte mutation:
|
||||
|
||||
```bash
|
||||
$ ping 77.110.117.147
|
||||
@ -94,7 +138,9 @@ PING 77.110.117.147 (77.110.117.147): 56 data bytes
|
||||
64 bytes from 77.110.117.147: icmp_seq=2 ttl=63 time=9997898 ms
|
||||
```
|
||||
|
||||
### macOS/BSD:
|
||||
**Analysis:** Both timestamp_sec and timestamp_nsec are mutated, resulting in massive RTT values.
|
||||
|
||||
### macOS/BSD - 8-byte mutation:
|
||||
|
||||
```bash
|
||||
platon@Moofbook~> ping 77.110.117.147
|
||||
@ -108,7 +154,9 @@ PING 77.110.117.147 (77.110.117.147): 56 data bytes
|
||||
round-trip min/avg/max/stddev = -872349695955.565/-178891434622.269/704643072044.353/657684464294.250 ms
|
||||
```
|
||||
|
||||
### BusyBox:
|
||||
**Analysis:** Only the 8-byte timestamp is mutated, but the integrity signature remains intact. Negative values occur due to timestamp underflow.
|
||||
|
||||
### BusyBox - 4-byte mutation:
|
||||
|
||||
```bash
|
||||
/ # ping 77.110.117.147
|
||||
@ -116,14 +164,36 @@ PING 77.110.117.147 (77.110.117.147): 56 data bytes
|
||||
64 bytes from 77.110.117.147: seq=893 ttl=63 time=82.448 ms
|
||||
64 bytes from 77.110.117.147: seq=664 ttl=63 time=61.749 ms
|
||||
```
|
||||
> On busybox mutate ONLY seq (idk why)
|
||||
|
||||
**Analysis:** Only the 4-byte timestamp is mutated. BusyBox shows more reasonable RTT values because it uses a different calculation method.
|
||||
|
||||
---
|
||||
|
||||
## Technical Challenges
|
||||
|
||||
### 1. Memory Safety
|
||||
All pointer arithmetic must be bounds-checked to prevent eBPF verification failures:
|
||||
```c
|
||||
if ((void*)(payload + 16) <= data_end) {
|
||||
// Safe to access payload[8] through payload[15]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Checksum Updates
|
||||
ICMP checksum must be recalculated after each mutation to maintain packet integrity.
|
||||
|
||||
### 3. Random Number Generation
|
||||
Using `bpf_get_prandom_u32()` ensures different mutations for each packet, making the effect more noticeable.
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Now our eBPF hack works on all major ping implementations, without breaking the payload or triggering "wrong data byte" errors.
|
||||
The automatic detection algorithm ensures compatibility across different platforms while maintaining the fun factor of corrupted timestamps.
|
||||
|
||||
You can safely troll sysadmins and test network tools on any platform!
|
||||
|
||||
---
|
||||
|
||||
If you’re interested, the original source code is on [GitHub](https://github.com/x3lfyn/cursed-ping) and mine on [Github](https://github.com/crypetxctl/cursed-ping) and [own Gitea](https://git.iwakurahome.ru/lain/cursed-ping)
|
||||
If you're interested, the original source code is on [GitHub](https://github.com/x3lfyn/cursed-ping) and mine on [GitHub](https://github.com/cryptexctl/cursed-ping) and [own Gitea](https://git.iwakurahome.ru/lain/cursed-ping)
|
||||
|
Loading…
x
Reference in New Issue
Block a user