Initial commit
This commit is contained in:
112
Makefile
Executable file
112
Makefile
Executable file
@ -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)"
|
||||||
95
knock_client.c
Executable file
95
knock_client.c
Executable file
@ -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 <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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 <target_ip> [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;
|
||||||
|
}
|
||||||
83
knock_client_linux.c
Normal file
83
knock_client_linux.c
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// Simple TCP knock client - Linux version
|
||||||
|
// Compile: gcc -o knock_client_linux knock_client_linux.c
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
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 <target_ip> [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;
|
||||||
|
}
|
||||||
121
port_knock.c
Executable file
121
port_knock.c
Executable file
@ -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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#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 <linux/types.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include <bpf/bpf_helpers.h>
|
||||||
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user