Adversarial C2 Protocol Implemented in Zig
0
fork

Configure Feed

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

Attach BPF filter to get all the saprus messages

+66 -7
+66 -7
src/main.zig
··· 86 86 return error.InvalidArguments; 87 87 } 88 88 89 - std.debug.print("relay: {s}\n", .{flags.relay orelse "<null>"}); 90 - std.debug.print("dest: {s}\n", .{flags.dest orelse "<null>"}); 91 - std.debug.print("connect: {s}\n", .{flags.connect orelse "<null>"}); 89 + // std.debug.print("relay: {s}\n", .{flags.relay orelse "<null>"}); 90 + // std.debug.print("dest: {s}\n", .{flags.dest orelse "<null>"}); 91 + // std.debug.print("connect: {s}\n", .{flags.connect orelse "<null>"}); 92 92 93 93 const rand = blk: { 94 94 const io_source: std.Random.IoSource = .{ .io = init.io }; ··· 169 169 .len = undefined, 170 170 }, 171 171 }; 172 - std.debug.print("headers: {any}\n", .{&headers.toBytes()}); 173 172 174 173 if (flags.relay != null) { 175 174 const relay: SaprusMessage = .{ ··· 181 180 182 181 var relay_buf: [2048]u8 = undefined; 183 182 const relay_bytes = relay.toBytes(&relay_buf); 184 - std.debug.print("payload: {any}\n", .{relay_bytes}); 185 183 headers.setPayloadLen(relay_bytes.len); 186 184 187 185 const full_msg = blk: { ··· 200 198 const dest = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 201 199 const src = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 202 200 // udp dest port should not be 8888 after first 203 - const udp_dest_port = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 201 + const udp_dest_port = rand.intRangeAtMost(u16, 9000, std.math.maxInt(u16)); 204 202 const connection: SaprusMessage = .{ 205 203 .connection = .{ 206 204 .src = src, ··· 210 208 .payload = flags.connect.?, 211 209 }, 212 210 }; 211 + 212 + try socket.attachSaprusPortFilter(src); 213 213 214 214 var connection_buf: [2048]u8 = undefined; 215 215 const connection_bytes = connection.toBytes(&connection_buf); ··· 224 224 }; 225 225 226 226 try socket.send(full_msg); 227 + var res_buf: [4096]u8 = undefined; 228 + 229 + const res = try socket.receive(&res_buf); 230 + std.debug.print("response: {any}\n", .{res}); 227 231 228 232 headers.udp.dst_port = udp_dest_port; 229 - try init.io.sleep(.fromSeconds(3), .real); 230 233 231 234 full_msg = blk: { 232 235 var msg_buf: [2048]u8 = undefined; ··· 387 390 null, 388 391 ); 389 392 return buf[0..len]; 393 + } 394 + 395 + fn attachSaprusPortFilter(self: RawSocket, port: u16) !void { 396 + const linux = std.os.linux; 397 + // BPF instruction structure for classic BPF 398 + const sock_filter = extern struct { 399 + code: u16, 400 + jt: u8, 401 + jf: u8, 402 + k: u32, 403 + }; 404 + 405 + const sock_fprog = extern struct { 406 + len: u16, 407 + filter: [*]const sock_filter, 408 + }; 409 + 410 + // BPF instruction opcodes 411 + const BPF_LD = 0x00; 412 + const BPF_H = 0x08; // Half-word (2 bytes) 413 + const BPF_ABS = 0x20; 414 + const BPF_JMP = 0x05; 415 + const BPF_JEQ = 0x10; 416 + const BPF_K = 0x00; 417 + const BPF_RET = 0x06; 418 + 419 + // Build the filter program 420 + const filter = [_]sock_filter{ 421 + // Load 2 bytes at offset 46 (absolute) 422 + .{ .code = BPF_LD | BPF_H | BPF_ABS, .jt = 0, .jf = 0, .k = 46 }, 423 + // Jump if equal to port (skip 0 if true, skip 1 if false) 424 + .{ .code = BPF_JMP | BPF_JEQ | BPF_K, .jt = 0, .jf = 1, .k = @as(u32, port) }, 425 + // Return 0xffff (pass) 426 + .{ .code = BPF_RET | BPF_K, .jt = 0, .jf = 0, .k = 0xffff }, 427 + // Return 0x0 (fail) 428 + .{ .code = BPF_RET | BPF_K, .jt = 0, .jf = 0, .k = 0x0 }, 429 + }; 430 + 431 + const fprog = sock_fprog{ 432 + .len = filter.len, 433 + .filter = &filter, 434 + }; 435 + 436 + // Attach filter to socket using setsockopt 437 + const SO_ATTACH_FILTER = 26; 438 + const rc = linux.setsockopt( 439 + self.fd, 440 + linux.SOL.SOCKET, 441 + SO_ATTACH_FILTER, 442 + @ptrCast(&fprog), 443 + @sizeOf(sock_fprog), 444 + ); 445 + 446 + if (rc != 0) { 447 + return error.BpfAttachFailed; 448 + } 390 449 } 391 450 }; 392 451