Adversarial C2 Protocol Implemented in Zig
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at dev 203 lines 6.8 kB view raw
1// Copyright 2026 Robby Zambito 2// 3// This file is part of zaprus. 4// 5// Zaprus is free software: you can redistribute it and/or modify it under the 6// terms of the GNU General Public License as published by the Free Software 7// Foundation, either version 3 of the License, or (at your option) any later 8// version. 9// 10// Zaprus is distributed in the hope that it will be useful, but WITHOUT ANY 11// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12// A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13// 14// You should have received a copy of the GNU General Public License along with 15// Zaprus. If not, see <https://www.gnu.org/licenses/>. 16 17const RawSocket = @This(); 18 19const is_debug = builtin.mode == .Debug; 20 21fd: i32, 22sockaddr_ll: std.posix.sockaddr.ll, 23mac: [6]u8, 24 25pub const max_payload_len = 1000; 26 27const Ifconf = extern struct { 28 ifc_len: i32, 29 ifc_ifcu: extern union { 30 ifcu_buf: ?[*]u8, 31 ifcu_req: ?[*]std.os.linux.ifreq, 32 }, 33}; 34 35pub fn init() error{ 36 SocketError, 37 NicError, 38 NoInterfaceFound, 39 BindError, 40}!RawSocket { 41 const socket: i32 = std.math.cast(i32, std.os.linux.socket(std.os.linux.AF.PACKET, std.os.linux.SOCK.RAW, 0)) orelse return error.SocketError; 42 if (socket < 0) return error.SocketError; 43 44 var ifreq_storage: [16]std.os.linux.ifreq = undefined; 45 var ifc = Ifconf{ 46 .ifc_len = @sizeOf(@TypeOf(ifreq_storage)), 47 .ifc_ifcu = .{ .ifcu_req = &ifreq_storage }, 48 }; 49 50 if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFCONF, @intFromPtr(&ifc)) != 0) { 51 return error.NicError; 52 } 53 54 const count = @divExact(ifc.ifc_len, @sizeOf(std.os.linux.ifreq)); 55 56 // Get the first non loopback interface 57 var ifr = for (ifreq_storage[0..@intCast(count)]) |*ifr| { 58 if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFFLAGS, @intFromPtr(ifr)) == 0) { 59 if (ifr.ifru.flags.LOOPBACK) continue; 60 break ifr; 61 } 62 } else return error.NoInterfaceFound; 63 64 // 2. Get Interface Index 65 if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFINDEX, @intFromPtr(ifr)) != 0) { 66 return error.NicError; 67 } 68 const ifindex: i32 = ifr.ifru.ivalue; 69 70 // 3. Get Real MAC Address 71 if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFHWADDR, @intFromPtr(ifr)) != 0) { 72 return error.NicError; 73 } 74 var mac: [6]u8 = ifr.ifru.hwaddr.data[0..6].*; 75 if (builtin.cpu.arch.endian() == .little) std.mem.reverse(u8, &mac); 76 77 // 4. Set Flags (Promiscuous/Broadcast) 78 if (std.os.linux.ioctl(socket, std.os.linux.SIOCGIFFLAGS, @intFromPtr(ifr)) != 0) { 79 return error.NicError; 80 } 81 ifr.ifru.flags.BROADCAST = true; 82 ifr.ifru.flags.PROMISC = true; 83 if (std.os.linux.ioctl(socket, std.os.linux.SIOCSIFFLAGS, @intFromPtr(ifr)) != 0) { 84 return error.NicError; 85 } 86 87 const sockaddr_ll = std.mem.zeroInit(std.posix.sockaddr.ll, .{ 88 .family = std.posix.AF.PACKET, 89 .ifindex = ifindex, 90 .protocol = std.mem.nativeToBig(u16, @as(u16, std.os.linux.ETH.P.IP)), 91 }); 92 93 const bind_ret = std.os.linux.bind(socket, @ptrCast(&sockaddr_ll), @sizeOf(@TypeOf(sockaddr_ll))); 94 if (bind_ret != 0) return error.BindError; 95 96 return .{ 97 .fd = socket, 98 .sockaddr_ll = sockaddr_ll, 99 .mac = mac, 100 }; 101} 102 103pub fn setTimeout(self: *RawSocket, sec: isize, usec: i64) error{SetTimeoutError}!void { 104 const timeout: std.os.linux.timeval = .{ .sec = sec, .usec = usec }; 105 const timeout_ret = std.os.linux.setsockopt(self.fd, std.os.linux.SOL.SOCKET, std.os.linux.SO.RCVTIMEO, @ptrCast(&timeout), @sizeOf(@TypeOf(timeout))); 106 if (timeout_ret != 0) return error.SetTimeoutError; 107} 108 109pub fn deinit(self: *RawSocket) void { 110 _ = std.os.linux.close(self.fd); 111 self.* = undefined; 112} 113 114pub fn send(self: RawSocket, payload: []const u8) !void { 115 const sent_bytes = std.os.linux.sendto( 116 self.fd, 117 payload.ptr, 118 payload.len, 119 0, 120 @ptrCast(&self.sockaddr_ll), 121 @sizeOf(@TypeOf(self.sockaddr_ll)), 122 ); 123 _ = sent_bytes; 124} 125 126pub fn receive(self: RawSocket, buf: []u8) ![]u8 { 127 const len = std.os.linux.recvfrom( 128 self.fd, 129 buf.ptr, 130 buf.len, 131 0, 132 null, 133 null, 134 ); 135 if (std.os.linux.errno(len) != .SUCCESS) { 136 return error.Timeout; // TODO: get the real error, assume timeout for now. 137 } 138 return buf[0..len]; 139} 140 141pub fn attachSaprusPortFilter(self: RawSocket, incoming_src_port: ?u16, incoming_dest_port: u16) !void { 142 const BPF = std.os.linux.BPF; 143 // BPF instruction structure for classic BPF 144 const SockFilter = extern struct { 145 code: u16, 146 jt: u8, 147 jf: u8, 148 k: u32, 149 }; 150 151 const SockFprog = extern struct { 152 len: u16, 153 filter: [*]const SockFilter, 154 }; 155 156 // Build the filter program 157 const filter = if (incoming_src_port) |inc_src| &[_]SockFilter{ 158 // Load 2 bytes at offset 46 (absolute) 159 .{ .code = BPF.LD | BPF.H | BPF.ABS, .jt = 0, .jf = 0, .k = 46 }, 160 // Jump if equal to port (skip 1 if true, skip 0 if false) 161 .{ .code = BPF.JMP | BPF.JEQ | BPF.K, .jt = 1, .jf = 0, .k = @as(u32, inc_src) }, 162 // Return 0x0 (fail) 163 .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0x0 }, 164 // Load 2 bytes at offset 48 (absolute) 165 .{ .code = BPF.LD | BPF.H | BPF.ABS, .jt = 0, .jf = 0, .k = 48 }, 166 // Jump if equal to port (skip 0 if true, skip 1 if false) 167 .{ .code = BPF.JMP | BPF.JEQ | BPF.K, .jt = 0, .jf = 1, .k = @as(u32, incoming_dest_port) }, 168 // Return 0xffff (pass) 169 .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0xffff }, 170 // Return 0x0 (fail) 171 .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0x0 }, 172 } else &[_]SockFilter{ 173 // Load 2 bytes at offset 48 (absolute) 174 .{ .code = BPF.LD | BPF.H | BPF.ABS, .jt = 0, .jf = 0, .k = 48 }, 175 // Jump if equal to port (skip 0 if true, skip 1 if false) 176 .{ .code = BPF.JMP | BPF.JEQ | BPF.K, .jt = 0, .jf = 1, .k = @as(u32, incoming_dest_port) }, 177 // Return 0xffff (pass) 178 .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0xffff }, 179 // Return 0x0 (fail) 180 .{ .code = BPF.RET | BPF.K, .jt = 0, .jf = 0, .k = 0x0 }, 181 }; 182 183 const fprog = SockFprog{ 184 .len = @intCast(filter.len), 185 .filter = filter.ptr, 186 }; 187 188 // Attach filter to socket using setsockopt 189 const rc = std.os.linux.setsockopt( 190 self.fd, 191 std.os.linux.SOL.SOCKET, 192 std.os.linux.SO.ATTACH_FILTER, 193 @ptrCast(&fprog), 194 @sizeOf(SockFprog), 195 ); 196 197 if (rc != 0) { 198 return error.BpfAttachFailed; 199 } 200} 201 202const std = @import("std"); 203const builtin = @import("builtin");