Adversarial C2 Protocol Implemented in Zig
0
fork

Configure Feed

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

Start breaking out net logic to NetWriter

+264 -163
+46 -156
src/Client.zig
··· 1 1 const base64Enc = std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '='); 2 2 const base64Dec = std.base64.Base64Decoder.init(std.base64.standard_alphabet_chars, '='); 3 3 4 - rand: Random, 5 4 writer: *std.Io.Writer, 6 5 7 6 const Self = @This(); ··· 9 8 const max_message_size = 2048; 10 9 11 10 pub fn init(writer: *std.Io.Writer) !Self { 12 - var prng = Random.DefaultPrng.init(blk: { 13 - var seed: u64 = undefined; 14 - try posix.getrandom(mem.asBytes(&seed)); 15 - break :blk seed; 16 - }); 17 - const rand = prng.random(); 18 - 19 11 return .{ 20 - .rand = rand, 21 12 .writer = writer, 22 13 }; 23 14 } ··· 31 22 fn broadcastInitialInterestMessage(self: *Self, msg_bytes: []u8) !void { 32 23 const writer = self.writer; 33 24 34 - const total_len = @sizeOf(EthernetHeaders) + @sizeOf(IpHeaders) + @sizeOf(UdpHeaders) + msg_bytes.len; 35 - 36 25 // Ensure the writer is in a valid state 37 - std.debug.assert(writer.buffer.len >= total_len); 38 - _ = writer.consumeAll(); 39 - 40 - const ether_headers: EthernetHeaders = .{ 41 - .dest_mac = .{ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff }, 42 - .src_mac = blk: { 43 - var output_bytes: [6]u8 = undefined; 44 - output_bytes[0] = 0xee; 45 - self.rand.bytes(output_bytes[1..]); 46 - break :blk output_bytes; 47 - }, 48 - .ether_type = 0x0800, 49 - }; 50 - 51 - const ip_headers: IpHeaders = .{ 52 - .total_length = @intCast(total_len - 92), 53 - .ttl = 0x64, 54 - .protocol = 0x11, 55 - .src_ip = .{ 0xff, 0x02, 0x03, 0x04 }, 56 - .dest_ip = .{ 0xff, 0xff, 0xff, 0xff }, 57 - }; 58 - 59 - const udp_headers: UdpHeaders = .{ 60 - .src_port = 0xbbbb, 61 - .dest_port = 8888, 62 - .length = @intCast(msg_bytes.len), 63 - }; 64 - 65 - try ether_headers.write(writer); 66 - try ip_headers.write(writer); 67 - try udp_headers.write(writer); 26 + std.debug.assert(writer.buffer.len - writer.end >= msg_bytes.len); 68 27 69 28 // Saprus 70 29 const msg_target_bytes = try writer.writableSlice(msg_bytes.len); ··· 118 77 try self.broadcastInitialInterestMessage(msg_bytes); 119 78 } 120 79 121 - fn randomPort(self: Self) u16 { 122 - return self.rand.intRangeAtMost(u16, 1024, 65000); 123 - } 124 - 125 - pub fn sendInitialConnection( 126 - self: Self, 127 - payload: []const u8, 128 - output_bytes: []u8, 129 - initial_port: u16, 130 - ) !*align(1) SaprusMessage { 131 - const dest_port = self.randomPort(); 132 - const msg_bytes = output_bytes[0..try SaprusMessage.calcSize( 133 - .connection, 134 - base64Enc.calcSize(payload.len), 135 - )]; 136 - const msg: *align(1) SaprusMessage = .init(.connection, msg_bytes); 137 - 138 - const connection = (try msg.getSaprusTypePayload()).connection; 139 - connection.src_port = initial_port; 140 - connection.dest_port = dest_port; 141 - _ = base64Enc.encode(connection.getPayload(), payload); 142 - 143 - try broadcastSaprusMessage(msg_bytes, 8888); 144 - 145 - return msg; 146 - } 147 - 148 - pub fn connect(self: Self, payload: []const u8) !?SaprusConnection { 149 - const initial_port = self.randomPort(); 150 - 151 - var initial_conn_res: ?*align(1) SaprusMessage = null; 152 - 153 - var sock = try network.Socket.create(.ipv4, .udp); 154 - defer sock.close(); 155 - 156 - // Bind to 255.255.255.255:8888 157 - const bind_addr = network.EndPoint{ 158 - .address = network.Address{ .ipv4 = network.Address.IPv4.broadcast }, 159 - .port = 8888, 160 - }; 161 - 162 - // timeout 1s 163 - try sock.setReadTimeout(1 * std.time.us_per_s); 164 - try sock.bind(bind_addr); 165 - 166 - var sent_msg_bytes: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined; 167 - const msg = try self.sendInitialConnection(payload, &sent_msg_bytes, initial_port); 168 - 169 - var response_buf: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined; 170 - _ = try sock.receive(&response_buf); // Ignore message that I sent. 171 - const len = try sock.receive(&response_buf); 172 - 173 - initial_conn_res = try .networkBytesAsValue(response_buf[0..len]); 174 - 175 - // Complete handshake after awaiting response 176 - try broadcastSaprusMessage(msg.asBytes(), self.randomPort()); 80 + // pub fn sendInitialConnection( 81 + // self: Self, 82 + // payload: []const u8, 83 + // output_bytes: []u8, 84 + // initial_port: u16, 85 + // ) !*align(1) SaprusMessage { 86 + // const dest_port = self.randomPort(); 87 + // const msg_bytes = output_bytes[0..try SaprusMessage.calcSize( 88 + // .connection, 89 + // base64Enc.calcSize(payload.len), 90 + // )]; 91 + // const msg: *align(1) SaprusMessage = .init(.connection, msg_bytes); 177 92 178 - if (false) { 179 - return initial_conn_res.?; 180 - } 181 - return null; 182 - } 93 + // const connection = (try msg.getSaprusTypePayload()).connection; 94 + // connection.src_port = initial_port; 95 + // connection.dest_port = dest_port; 96 + // _ = base64Enc.encode(connection.getPayload(), payload); 183 97 184 - const EthernetHeaders = struct { 185 - dest_mac: @Vector(6, u8), 98 + // try broadcastSaprusMessage(msg_bytes, 8888); 186 99 187 - src_mac: @Vector(6, u8), 100 + // return msg; 101 + // } 188 102 189 - ether_type: u16, 103 + // pub fn connect(self: Self, payload: []const u8) !?SaprusConnection { 104 + // const initial_port = self.randomPort(); 190 105 191 - fn write(hdr: @This(), writer: *std.Io.Writer) !void { 192 - try writer.writeInt(u48, @bitCast(hdr.dest_mac), .big); 193 - try writer.writeInt(u48, @bitCast(hdr.src_mac), .big); 194 - try writer.writeInt(u16, hdr.ether_type, .big); 195 - } 196 - }; 106 + // var initial_conn_res: ?*align(1) SaprusMessage = null; 197 107 198 - const IpHeaders = struct { 199 - _: u8 = 0x45, 200 - // ip_version: u4, 201 - // header_length: u4 = 0, 202 - type_of_service: u8 = 0, 203 - total_length: u16 = 0x04, 108 + // var sock = try network.Socket.create(.ipv4, .udp); 109 + // defer sock.close(); 204 110 205 - identification: u16 = 0, 206 - __: u16 = 0x0, 207 - // ethernet_flags: u3 = 0, 208 - // fragment_offset: u13 = 0, 209 - ttl: u8 = 0, 210 - protocol: u8 = 0, 111 + // // Bind to 255.255.255.255:8888 112 + // const bind_addr = network.EndPoint{ 113 + // .address = network.Address{ .ipv4 = network.Address.IPv4.broadcast }, 114 + // .port = 8888, 115 + // }; 211 116 212 - header_checksum: @Vector(2, u8) = .{ 0, 0 }, 213 - src_ip: @Vector(4, u8), 117 + // // timeout 1s 118 + // try sock.setReadTimeout(1 * std.time.us_per_s); 119 + // try sock.bind(bind_addr); 214 120 215 - dest_ip: @Vector(4, u8), 121 + // var sent_msg_bytes: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined; 122 + // const msg = try self.sendInitialConnection(payload, &sent_msg_bytes, initial_port); 216 123 217 - fn write(hdr: @This(), writer: *std.Io.Writer) !void { 218 - try writer.writeInt(u8, 0x45, .big); // ip version and header length 219 - try writer.writeByte(hdr.type_of_service); 220 - try writer.writeInt(u16, hdr.total_length, .big); 221 - try writer.writeInt(u16, hdr.identification, .big); 222 - try writer.writeInt(u16, 0x00, .big); // ethernet flags and fragment offset 223 - try writer.writeByte(hdr.ttl); 224 - try writer.writeByte(hdr.protocol); 225 - try writer.writeInt(u16, @bitCast(hdr.header_checksum), .big); 226 - try writer.writeInt(u32, @bitCast(hdr.src_ip), .big); 227 - try writer.writeInt(u32, @bitCast(hdr.dest_ip), .big); 228 - } 229 - }; 124 + // var response_buf: [max_message_size]u8 align(@alignOf(SaprusMessage)) = undefined; 125 + // _ = try sock.receive(&response_buf); // Ignore message that I sent. 126 + // const len = try sock.receive(&response_buf); 230 127 231 - const UdpHeaders = packed struct { 232 - src_port: u16, 128 + // initial_conn_res = try .networkBytesAsValue(response_buf[0..len]); 233 129 234 - dest_port: u16, 235 - length: u16, 236 - checksum: @Vector(2, u8) = .{ 0, 0 }, 130 + // // Complete handshake after awaiting response 131 + // try broadcastSaprusMessage(msg.asBytes(), self.randomPort()); 237 132 238 - fn write(hdr: @This(), writer: *std.Io.Writer) !void { 239 - try writer.writeInt(u16, hdr.src_port, .big); 240 - try writer.writeInt(u16, hdr.dest_port, .big); 241 - try writer.writeInt(u16, hdr.length, .big); 242 - try writer.writeInt(u16, @bitCast(hdr.checksum), .big); 243 - } 244 - }; 133 + // if (false) { 134 + // return initial_conn_res.?; 135 + // } 136 + // return null; 137 + // } 245 138 246 139 const SaprusMessage = @import("message.zig").Message; 247 140 const SaprusConnection = @import("Connection.zig"); 248 141 249 142 const std = @import("std"); 250 - const Random = std.Random; 251 - const posix = std.posix; 252 - const mem = std.mem; 253 143 254 144 const network = @import("network");
+205
src/NetWriter.zig
··· 1 + //! Wraps a writer with UDP headers. 2 + //! This is useful for wrapping RawSocket Writer with appropriate headers. 3 + 4 + rand: Random, 5 + wrapped: *Writer, 6 + interface: Writer, 7 + 8 + pub fn init(w: *Writer) !NetWriter { 9 + std.debug.assert(w.buffer.len > @sizeOf(EthernetHeaders) + @sizeOf(IpHeaders) + @sizeOf(UdpHeaders)); 10 + 11 + var prng = Random.DefaultPrng.init(blk: { 12 + var seed: u64 = undefined; 13 + try posix.getrandom(mem.asBytes(&seed)); 14 + break :blk seed; 15 + }); 16 + 17 + return .{ 18 + .rand = prng.random(), 19 + .wrapped = w, 20 + .interface = .{ 21 + .vtable = &.{ 22 + .drain = drain, 23 + .flush = flush, 24 + }, 25 + .buffer = &.{}, 26 + }, 27 + }; 28 + } 29 + 30 + fn drain(io_w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { 31 + const w: *NetWriter = @alignCast(@fieldParentPtr("interface", io_w)); 32 + 33 + var res: usize = 0; 34 + 35 + if (io_w.end == 0) { 36 + const ether_headers: EthernetHeaders = .{ 37 + .dest_mac = .{ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff }, 38 + .src_mac = blk: { 39 + var output_bytes: [6]u8 = undefined; 40 + output_bytes[0] = 0xee; 41 + w.rand.bytes(output_bytes[1..]); 42 + break :blk output_bytes; 43 + }, 44 + .ether_type = 0x0800, 45 + }; 46 + 47 + const ip_headers: IpHeaders = .{ 48 + .total_length = @intCast(res - 92), 49 + .ttl = 0x64, 50 + .protocol = 0x11, 51 + .src_ip = .{ 0xff, 0x02, 0x03, 0x04 }, 52 + .dest_ip = .{ 0xff, 0xff, 0xff, 0xff }, 53 + }; 54 + 55 + const udp_headers: UdpHeaders = .{ 56 + .src_port = 0xbbbb, 57 + .dest_port = 8888, 58 + .length = @intCast(res), 59 + }; 60 + 61 + res += try ether_headers.write(w.wrapped); 62 + res += try ip_headers.write(w.wrapped); 63 + res += try udp_headers.write(w.wrapped); 64 + } 65 + 66 + res += try w.wrapped.writeSplat(data, splat); 67 + return res; 68 + } 69 + 70 + fn flush(io_w: *Writer) Writer.Error!void { 71 + const w: *NetWriter = @alignCast(@fieldParentPtr("interface", io_w)); 72 + try w.wrapped.flush(); 73 + } 74 + 75 + const EthernetHeaders = struct { 76 + dest_mac: @Vector(6, u8), 77 + 78 + src_mac: @Vector(6, u8), 79 + 80 + ether_type: u16, 81 + 82 + fn write(hdr: EthernetHeaders, writer: *std.Io.Writer) Writer.Error!usize { 83 + comptime var res: usize = 0; 84 + 85 + res += @sizeOf(u48); 86 + try writer.writeInt(u48, @bitCast(hdr.dest_mac), .big); 87 + 88 + res += @sizeOf(u48); 89 + try writer.writeInt(u48, @bitCast(hdr.src_mac), .big); 90 + 91 + res += @sizeOf(u16); 92 + try writer.writeInt(u16, hdr.ether_type, .big); 93 + 94 + return res; 95 + } 96 + 97 + const byte_len = @bitSizeOf(EthernetHeaders) / 8; 98 + 99 + fn bytes(hdr: EthernetHeaders) [byte_len]u8 { 100 + var res: [byte_len]u8 = undefined; 101 + hdr.write(Writer.fixed(&res)) catch unreachable; 102 + } 103 + }; 104 + 105 + const IpHeaders = struct { 106 + // ip_version: u4, 107 + // header_length: u4 = 0, 108 + type_of_service: u8 = 0, 109 + total_length: u16 = 0x04, 110 + 111 + identification: u16 = 0, 112 + // ethernet_flags: u3 = 0, 113 + // fragment_offset: u13 = 0, 114 + ttl: u8 = 0, 115 + protocol: u8 = 0, 116 + 117 + header_checksum: @Vector(2, u8) = .{ 0, 0 }, 118 + src_ip: @Vector(4, u8), 119 + 120 + dest_ip: @Vector(4, u8), 121 + 122 + fn write(hdr: @This(), writer: *std.Io.Writer) Writer.Error!usize { 123 + comptime var res: usize = 0; 124 + 125 + res += @sizeOf(u8); 126 + try writer.writeInt(u8, 0x45, .big); // ip version and header length 127 + 128 + res += @sizeOf(u8); 129 + try writer.writeByte(hdr.type_of_service); 130 + 131 + res += @sizeOf(u16); 132 + try writer.writeInt(u16, hdr.total_length, .big); 133 + 134 + res += @sizeOf(u16); 135 + try writer.writeInt(u16, hdr.identification, .big); 136 + 137 + res += @sizeOf(u16); 138 + try writer.writeInt(u16, 0x00, .big); // ethernet flags and fragment offset 139 + 140 + res += @sizeOf(u8); 141 + try writer.writeByte(hdr.ttl); 142 + 143 + res += @sizeOf(u8); 144 + try writer.writeByte(hdr.protocol); 145 + 146 + res += @sizeOf(u16); 147 + try writer.writeInt(u16, @bitCast(hdr.header_checksum), .big); 148 + 149 + res += @sizeOf(u32); 150 + try writer.writeInt(u32, @bitCast(hdr.src_ip), .big); 151 + 152 + res += @sizeOf(u32); 153 + try writer.writeInt(u32, @bitCast(hdr.dest_ip), .big); 154 + 155 + return res; 156 + } 157 + 158 + const byte_len = @bitSizeOf(IpHeaders) / 8; 159 + 160 + fn bytes(hdr: IpHeaders) [byte_len]u8 { 161 + var res: [byte_len]u8 = undefined; 162 + hdr.write(Writer.fixed(&res)) catch unreachable; 163 + } 164 + }; 165 + 166 + const UdpHeaders = packed struct { 167 + src_port: u16, 168 + 169 + dest_port: u16, 170 + length: u16, 171 + checksum: @Vector(2, u8) = .{ 0, 0 }, 172 + 173 + fn write(hdr: @This(), writer: *std.Io.Writer) Writer.Error!usize { 174 + comptime var res: usize = 0; 175 + 176 + res += @sizeOf(u16); 177 + try writer.writeInt(u16, hdr.src_port, .big); 178 + 179 + res += @sizeOf(u16); 180 + try writer.writeInt(u16, hdr.dest_port, .big); 181 + 182 + res += @sizeOf(u16); 183 + try writer.writeInt(u16, hdr.length, .big); 184 + 185 + res += @sizeOf(u16); 186 + try writer.writeInt(u16, @bitCast(hdr.checksum), .big); 187 + 188 + return res; 189 + } 190 + 191 + const byte_len = @bitSizeOf(UdpHeaders) / 8; 192 + 193 + fn bytes(hdr: UdpHeaders) [byte_len]u8 { 194 + var res: [byte_len]u8 = undefined; 195 + hdr.write(Writer.fixed(&res)) catch unreachable; 196 + } 197 + }; 198 + 199 + const std = @import("std"); 200 + const Random = std.Random; 201 + const posix = std.posix; 202 + const Writer = std.Io.Writer; 203 + const mem = std.mem; 204 + 205 + const NetWriter = @This();
+12 -7
src/main.zig
··· 47 47 } 48 48 49 49 var sock_buffer: [2048]u8 = undefined; 50 - var rawSocketWriter: RawSocketWriter = try .init("enp7s0", &sock_buffer); // /proc/net/dev 51 - var client = try SaprusClient.init(&rawSocketWriter.interface); 50 + var raw_socket_writer: RawSocketWriter = try .init("enp7s0", &sock_buffer); // /proc/net/dev 51 + var net_writer: NetWriter = try .init(&raw_socket_writer.interface); 52 + var client = try SaprusClient.init(&net_writer.interface); 52 53 defer client.deinit(); 53 54 54 55 if (res.args.relay) |r| { ··· 60 61 // std.debug.print("Sent: {s}\n", .{r}); 61 62 return; 62 63 } else if (res.args.connect) |c| { 63 - _ = client.connect(if (c.len > 0) c else "Hello darkness my old friend") catch |err| switch (err) { 64 - error.WouldBlock => null, 65 - else => return err, 66 - }; 67 - return; 64 + if (false) { 65 + _ = client.connect(if (c.len > 0) c else "Hello darkness my old friend") catch |err| switch (err) { 66 + error.WouldBlock => null, 67 + else => return err, 68 + }; 69 + return; 70 + } 71 + @panic("Not implemented"); 68 72 } 69 73 70 74 return clap.helpToFile(.stderr(), clap.Help, &params, .{}); ··· 93 97 const SaprusClient = zaprus.Client; 94 98 const SaprusMessage = zaprus.Message; 95 99 const RawSocketWriter = zaprus.RawSocketWriter; 100 + const NetWriter = zaprus.NetWriter; 96 101 97 102 const clap = @import("clap");
+1
src/root.zig
··· 1 1 pub const Client = @import("Client.zig"); 2 2 pub const Connection = @import("Connection.zig"); 3 3 pub const RawSocketWriter = @import("RawSocketWriter.zig"); 4 + pub const NetWriter = @import("NetWriter.zig"); 4 5 5 6 const msg = @import("message.zig"); 6 7