From 7d09b14b0fc82ac255fb0e84d40001b0c198114d Mon Sep 17 00:00:00 2001 From: Nischay Date: Wed, 1 Apr 2026 18:16:49 +0530 Subject: [PATCH] Initial commit --- Makefile | 112 +++++++++++++++++++++++++++++++++++++++ knock_client.c | 95 +++++++++++++++++++++++++++++++++ knock_client_linux.c | 83 +++++++++++++++++++++++++++++ port_knock.c | 121 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 411 insertions(+) create mode 100755 Makefile create mode 100755 knock_client.c create mode 100644 knock_client_linux.c create mode 100755 port_knock.c diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..e286487 --- /dev/null +++ b/Makefile @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: MIT +# +# Makefile for eBPF Port Knocking Programs +# Supports both Linux (using libbpf) and Windows eBPF (using eBPF for Windows) +# + +# Detect platform +ifeq ($(OS),Windows_NT) + PLATFORM := windows + EBPF_SDK_DIR := C:/Users/vagrant/Downloads/ebpf-for-windows +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + PLATFORM := linux + EBPF_SDK_DIR := /home/tlh/VMs/ebpf-for-windows + else + $(error Unsupported platform: $(UNAME_S)) + endif +endif + +# Directories +SRC_DIR := . +OUT_DIR := out +INCLUDE_DIR := include + +# Toolchain +CLANG := clang + +# Output files +BPF_OBJ_LINUX := $(OUT_DIR)/port_knock_linux.o +BPF_OBJ_WINDOWS := $(OUT_DIR)/port_knock.o + +# Compiler flags +COMMON_FLAGS := -Wall -Wextra -Wno-unused-value -O2 -g + +# Linux-specific flags +LINUX_CFLAGS := -D__TARGET_ARCH_x86 -D__LINUX_BPF__ $(COMMON_FLAGS) +LINUX_INCLUDES := -I/usr/include/bpf -I/usr/include -I$(INCLUDE_DIR) + +# Windows-specific flags +# Point to eBPF for Windows SDK headers +WINDOWS_CFLAGS := -D_WIN32 $(COMMON_FLAGS) +WINDOWS_INCLUDES := -I$(INCLUDE_DIR) -I$(EBPF_SDK_DIR)/include -I$(EBPF_SDK_DIR)/tests/include + +.PHONY: all clean build-linux build-windows make-windows help + +# Default target - show help +all: help + +# Linux build +build-linux: $(BPF_OBJ_LINUX) + +make-windows: $(BPF_OBJ_WINDOWS) + +$(OUT_DIR): + mkdir -p $(OUT_DIR) + +$(BPF_OBJ_LINUX): $(SRC_DIR)/port_knock.c $(INCLUDE_DIR)/common.h | $(OUT_DIR) + @echo "=== Building eBPF for Linux ===" + $(CLANG) $(LINUX_CFLAGS) $(LINUX_INCLUDES) \ + -target bpf \ + -D__BPF_TRACING__ \ + -c $< -o $@ + @echo "Built: $@" + @echo " Sections: $$(bpftool prog show $$@ 2>/dev/null || echo ' (use bpftool to inspect)')" + +# Windows build (using eBPF for Windows) +build-windows: $(BPF_OBJ_WINDOWS) + +$(BPF_OBJ_WINDOWS): $(SRC_DIR)/port_knock.c $(INCLUDE_DIR)/common.h | $(OUT_DIR) + @echo "=== Building eBPF for Windows ===" + $(CLANG) $(WINDOWS_CFLAGS) $(WINDOWS_INCLUDES) \ + -target bpf \ + -c $< -o $@ + @echo "Built: $@" + +# Verify program +verify: $(BPF_OBJ_LINUX) + @echo "=== Verifying eBPF program ===" + @bpftool prog show $(BPF_OBJ_LINUX) 2>/dev/null || echo "Use bpftool to verify" + +# Show sections +show-sections: $(BPF_OBJ_LINUX) + @bpftool prog show $(BPF_OBJ_LINUX) + +# Show disassembly +show-disasm: $(BPF_OBJ_LINUX) + @bpftool prog disasm $(BPF_OBJ_LINUX) + +# Clean +clean: + rm -rf $(OUT_DIR) + +# Help +help: + @echo "eBPF Port Knocking Program - Build System" + @echo "" + @echo "Targets:" + @echo " build-linux Build for Linux eBPF (using libbpf/aya)" + @echo " build-windows Build for Windows eBPF (using eBPF for Windows)" + @echo " verify Verify the compiled program" + @echo " show-sections Show eBPF program sections" + @echo " show-disasm Show disassembly of the program" + @echo " clean Remove build artifacts" + @echo "" + @echo "Usage:" + @echo " Linux: make build-linux" + @echo " Windows: make build-windows" + @echo "" + @echo "Prerequisites:" + @echo " Linux: clang, libbpf, bpftool" + @echo " Windows: clang, eBPF for Windows SDK (at Z:/ebpf-for-windows)" diff --git a/knock_client.c b/knock_client.c new file mode 100755 index 0000000..c610851 --- /dev/null +++ b/knock_client.c @@ -0,0 +1,95 @@ +// Simple TCP knock client - sends single SYN packets without retries +// Compile: cl /W4 /O2 knock_client.c /ws2_32.lib + +#include +#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") + +void knock_port(const char* ip, int port) { + SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == INVALID_SOCKET) { + printf("socket failed: %d\n", WSAGetLastError()); + return; + } + + // Set socket to close immediately after connect attempt + BOOL dontlinger = FALSE; + setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (char*)&dontlinger, sizeof(dontlinger)); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((unsigned short)port); + inet_pton(AF_INET, ip, &addr.sin_addr); + + printf("Knocking on port %d...\n", port); + + // Connect will send SYN, and we close immediately after + int result = connect(s, (struct sockaddr*)&addr, sizeof(addr)); + + // Give it a moment to send the packet + Sleep(100); + + // Close immediately without retrying + closesocket(s); + + if (result == SOCKET_ERROR) { + printf(" Connection refused (expected - no listener)\n"); + } else { + printf(" Connected!\n"); + } +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("Usage: %s [port1 port2 port3 ...]\n", argv[0]); + printf("Default ports: 7000 8000 9000\n"); + return 1; + } + + const char* ip = argv[1]; + + int ports[10]; + int num_ports; + + if (argc > 2) { + num_ports = argc - 2; + for (int i = 0; i < num_ports && i < 10; i++) { + ports[i] = atoi(argv[i + 2]); + } + } else { + num_ports = 3; + ports[0] = 7000; + ports[1] = 8000; + ports[2] = 9000; + } + + WSADATA wsa; + if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) { + printf("WSAStartup failed\n"); + return 1; + } + + printf("Knocking on %s: ", ip); + for (int i = 0; i < num_ports; i++) { + printf("%d ", ports[i]); + } + printf("\n\n"); + + for (int i = 0; i < num_ports; i++) { + knock_port(ip, ports[i]); + if (i < num_ports - 1) { + // Wait between knocks + Sleep(500); + } + } + + printf("\nKnock sequence complete!\n"); + printf("Now try connecting to port 31337\n"); + + WSACleanup(); + return 0; +} diff --git a/knock_client_linux.c b/knock_client_linux.c new file mode 100644 index 0000000..d012221 --- /dev/null +++ b/knock_client_linux.c @@ -0,0 +1,83 @@ +// Simple TCP knock client - Linux version +// Compile: gcc -o knock_client_linux knock_client_linux.c + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void knock_port(const char *ip, int port) { + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("socket failed"); + return; + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons((unsigned short)port); + inet_pton(AF_INET, ip, &addr.sin_addr); + + printf("Knocking on port %d...\n", port); + + int result = connect(s, (struct sockaddr *)&addr, sizeof(addr)); + + usleep(100000); + + close(s); + + if (result < 0) { + printf(" Connection refused (expected - no listener)\n"); + } else { + printf(" Connected!\n"); + } +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + printf("Usage: %s [port1 port2 port3 ...]\n", argv[0]); + printf("Default ports: 7000 8000 9000\n"); + return 1; + } + + const char *ip = argv[1]; + + int ports[10]; + int num_ports; + + if (argc > 2) { + num_ports = argc - 2; + for (int i = 0; i < num_ports && i < 10; i++) { + ports[i] = atoi(argv[i + 2]); + } + } else { + num_ports = 3; + ports[0] = 7000; + ports[1] = 8000; + ports[2] = 9000; + } + + printf("Knocking on %s: ", ip); + for (int i = 0; i < num_ports; i++) { + printf("%d ", ports[i]); + } + printf("\n\n"); + + for (int i = 0; i < num_ports; i++) { + knock_port(ip, ports[i]); + if (i < num_ports - 1) { + usleep(500000); + } + } + + printf("\nKnock sequence complete!\n"); + printf("Now try connecting to port 31337\n"); + + return 0; +} diff --git a/port_knock.c b/port_knock.c new file mode 100755 index 0000000..7ea5bc3 --- /dev/null +++ b/port_knock.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +/** + * eBPF Port Knocking Program + * Cross-platform: Linux and Windows + * + * This program monitors incoming TCP connection attempts and tracks sequences + * of ports. When the correct sequence (7000, 8000, 9000 by default) is + * detected, it allows access to port 31337. + * Some includes and other files are missing because this is not the full PoC. + */ + +#include +#include + +#define SEQUENCE_LEN 3 +#define TARGET_PORT 31337 + +#ifdef _WIN32 +#include "bpf_helpers.h" +#include "xdp_hooks.h" +typedef xdp_md_t xdp_md_ctx_t; +typedef xdp_action_t xdp_return_t; +#define XDP_RETURN_PASS XDP_PASS +#define XDP_RETURN_DROP XDP_DROP +#else +#include +#include +#include +#include +#include +#include +typedef struct xdp_md xdp_md_ctx_t; +typedef int xdp_return_t; +#define XDP_RETURN_PASS XDP_PASS +#define XDP_RETURN_DROP XDP_DROP +#endif + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, uint32_t); + __type(value, uint32_t); + __uint(max_entries, 1024); +} knock_tracker SEC(".maps"); + +#ifndef _WIN32 +char _license[] SEC("license") = "GPL"; +#endif + +SEC("xdp") +xdp_return_t port_knock_xdp(xdp_md_ctx_t* ctx) { +#ifdef _WIN32 + void* data = ctx->data; + void* data_end = ctx->data_end; +#else + void* data = (void*)(long)ctx->data; + void* data_end = (void*)(long)ctx->data_end; +#endif + + if ((unsigned char*)data + 54 > (unsigned char*)data_end) + return XDP_RETURN_PASS; + + unsigned char* pkt = (unsigned char*)data; + + if (pkt[12] != 0x08 || pkt[13] != 0x00) + return XDP_RETURN_PASS; + + if (pkt[23] != 6) + return XDP_RETURN_PASS; + + uint32_t src_ip = pkt[26] | (pkt[27] << 8) | (pkt[28] << 16) | (pkt[29] << 24); + + if ((pkt[47] & 0x02) != 0x02) + return XDP_RETURN_PASS; + + uint16_t dport = (pkt[36] << 8) | pkt[37]; + + bpf_printk("Packet: src_ip=%d, dport=%d", src_ip, dport); + + if (dport == TARGET_PORT) { + uint32_t* val = bpf_map_lookup_elem(&knock_tracker, &src_ip); + uint32_t state = val ? *val : 0; + + bpf_printk("Target port check: state=%d", state); + + if (state == 3) { + bpf_printk("ALLOWED: IP authorized"); + return XDP_RETURN_PASS; + } else { + bpf_printk("DENIED: IP not authorized"); + return XDP_RETURN_DROP; + } + } + + if (dport == 7000 || dport == 8000 || dport == 9000) { + uint32_t* val = bpf_map_lookup_elem(&knock_tracker, &src_ip); + uint32_t state = val ? *val : 0; + + bpf_printk("Knock port: dport=%d, current_state=%d", dport, state); + + /* Only advance if we're moving forward in the sequence */ + if ((state == 0 && dport == 7000) || + (state == 1 && dport == 8000) || + (state == 2 && dport == 9000)) { + + if (state == 2) { + /* Complete sequence */ + uint32_t new_state = 3; + bpf_map_update_elem(&knock_tracker, &src_ip, &new_state, 0); + bpf_printk("Sequence COMPLETE! IP authorized"); + } else { + /* Advance to next stage */ + uint32_t new_state = state + 1; + bpf_map_update_elem(&knock_tracker, &src_ip, &new_state, 0); + bpf_printk("Advanced to stage %d", new_state); + } + } + /* Don't update state on retries - just pass */ + } + + return XDP_RETURN_PASS; +}