Skip to content

Commit

Permalink
capture: add metadata packet
Browse files Browse the repository at this point in the history
  • Loading branch information
GZTimeWalker committed Aug 19, 2023
1 parent 3f5f964 commit 73cdc9a
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 41 deletions.
41 changes: 33 additions & 8 deletions src/GZCTF/Controllers/ProxyController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text.Json;
using GZCTF.Models.Internal;
using GZCTF.Repositories.Interface;
using GZCTF.Utils;
Expand All @@ -25,6 +26,10 @@ public class ProxyController : ControllerBase
private readonly bool _enableTrafficCapture = false;
private const int BUFFER_SIZE = 1024 * 4;
private const uint CONNECTION_LIMIT = 64;
private readonly JsonSerializerOptions _JsonOptions = new()
{
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};

public ProxyController(ILogger<ProxyController> logger, IDistributedCache cache,
IOptions<ContainerProvider> provider, IContainerRepository containerRepository)
Expand Down Expand Up @@ -60,7 +65,7 @@ public async Task<IActionResult> ProxyForInstance(string id, CancellationToken t
if (!await IncrementConnectionCount(id))
return BadRequest(new RequestResponse("容器连接数已达上限"));

var container = await _containerRepository.GetContainerById(id, token);
var container = await _containerRepository.GetContainerWithInstanceById(id, token);

if (container is null || container.Instance is null || !container.IsProxy)
return NotFound(new RequestResponse("不存在的容器"));
Expand All @@ -76,6 +81,24 @@ public async Task<IActionResult> ProxyForInstance(string id, CancellationToken t
if (clientIp is null)
return BadRequest(new RequestResponse("无效的访问地址"));

var enable = _enableTrafficCapture && container.Instance.Challenge.EnableTrafficCapture;
byte[]? metadata = null;

if (enable)
{
metadata = JsonSerializer.SerializeToUtf8Bytes(new
{
Challenge = container.Instance.Challenge.Title,
container.Instance.ChallengeId,

Team = container.Instance.Participation.Team.Name,
container.Instance.Participation.TeamId,

container.ContainerId,
container.Instance.FlagContext?.Flag
}, _JsonOptions);
}

CapturableNetworkStream? stream;
try
{
Expand All @@ -89,13 +112,15 @@ public async Task<IActionResult> ProxyForInstance(string id, CancellationToken t
return BadRequest(new RequestResponse("容器连接失败"));
}

stream = new CapturableNetworkStream(socket, new()
{
Source = new(clientIp, clientPort),
Dest = ipEndPoint,
EnableCapture = _enableTrafficCapture && container.Instance.Challenge.EnableTrafficCapture,
FilePath = container.TrafficPath(HttpContext.Connection.Id),
});
stream = new CapturableNetworkStream(socket, metadata,
new()
{
Source = new(clientIp, clientPort),
Dest = ipEndPoint,
EnableCapture = enable,
FilePath = container.TrafficPath(HttpContext.Connection.Id),
}
);
}
catch (Exception e)
{
Expand Down
9 changes: 7 additions & 2 deletions src/GZCTF/Repositories/ContainerRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ public ContainerRepository(IDistributedCache cache,
public override Task<int> CountAsync(CancellationToken token = default) => _context.Containers.CountAsync(token);

public Task<Container?> GetContainerById(string guid, CancellationToken token = default)
=> _context.Containers.Include(c => c.Instance)
.ThenInclude(i => i!.Challenge)
=> _context.Containers.FirstOrDefaultAsync(i => i.Id == guid, token);

public Task<Container?> GetContainerWithInstanceById(string guid, CancellationToken token = default)
=> _context.Containers.IgnoreAutoIncludes()
.Include(c => c.Instance).ThenInclude(i => i!.Challenge)
.Include(c => c.Instance).ThenInclude(i => i!.FlagContext)
.Include(c => c.Instance).ThenInclude(i => i!.Participation).ThenInclude(p => p.Team)
.FirstOrDefaultAsync(i => i.Id == guid, token);

public Task<List<Container>> GetContainers(CancellationToken token = default)
Expand Down
8 changes: 8 additions & 0 deletions src/GZCTF/Repositories/Interface/IContainerRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public interface IContainerRepository : IRepository
/// <returns></returns>
public Task<Container?> GetContainerById(string guid, CancellationToken token = default);

/// <summary>
/// 根据容器数据库 ID 获取容器及实例信息
/// </summary>
/// <param name="guid">容器数据库 ID</param>
/// <param name="token"></param>
/// <returns></returns>
public Task<Container?> GetContainerWithInstanceById(string guid, CancellationToken token = default);

/// <summary>
/// 容器数据库 ID 对应容器是否存在
/// </summary>
Expand Down
55 changes: 28 additions & 27 deletions src/GZCTF/Utils/CapturableNetworkStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ public sealed class CapturableNetworkStream : NetworkStream
private readonly CapturableNetworkStreamOptions _options;
private readonly CaptureFileWriterDevice? _device = null;
private readonly PhysicalAddress _dummyPhysicalAddress = PhysicalAddress.Parse("00-11-00-11-00-11");
private readonly IPEndPoint _host = new(IPAddress.Parse("0.0.0.0"), 65535);

public CapturableNetworkStream(Socket socket, CapturableNetworkStreamOptions options) : base(socket)
public CapturableNetworkStream(Socket socket, byte[]? metadata, CapturableNetworkStreamOptions options) : base(socket)
{
_options = options;

Expand All @@ -55,6 +56,9 @@ public CapturableNetworkStream(Socket socket, CapturableNetworkStreamOptions opt

_device = new(_options.FilePath, FileMode.Open);
_device.Open(LinkLayers.Ethernet);

if (metadata is not null)
WriteCapturedData(_host, _options.Source, metadata);
}
}

Expand All @@ -65,43 +69,40 @@ public override async ValueTask<int> ReadAsync(Memory<byte> buffer, Cancellation
if (!_options.EnableCapture)
return count;

var udp = new UdpPacket((ushort)_options.Dest.Port, (ushort)_options.Source.Port)
{
PayloadDataSegment = new ByteArraySegment(buffer[..count].ToArray())
};

var packet = new EthernetPacket(_dummyPhysicalAddress, _dummyPhysicalAddress, EthernetType.IPv6)
{
PayloadPacket = new IPv6Packet(_options.Dest.Address, _options.Source.Address) { PayloadPacket = udp }
};

udp.UpdateUdpChecksum();

_device?.Write(new RawCapture(LinkLayers.Ethernet, new(), packet.Bytes));
WriteCapturedData(_options.Dest, _options.Source, buffer);

return count;
}

public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
if (_options.EnableCapture)
{
var udp = new UdpPacket((ushort)_options.Source.Port, (ushort)_options.Dest.Port)
{
PayloadDataSegment = new ByteArraySegment(buffer.ToArray())
};
WriteCapturedData(_options.Source, _options.Dest, buffer);

var packet = new EthernetPacket(_dummyPhysicalAddress, _dummyPhysicalAddress, EthernetType.IPv6)
{
PayloadPacket = new IPv6Packet(_options.Source.Address, _options.Dest.Address) { PayloadPacket = udp }
};
await base.WriteAsync(buffer, cancellationToken);
}

udp.UpdateUdpChecksum();
/// <summary>
/// 向文件写入一条数据记录
/// </summary>
/// <param name="source">源地址</param>
/// <param name="dest">目的地址</param>
/// <param name="buffer">数据</param>
internal void WriteCapturedData(IPEndPoint source, IPEndPoint dest, ReadOnlyMemory<byte> buffer)
{
var udp = new UdpPacket((ushort)source.Port, (ushort)dest.Port)
{
PayloadDataSegment = new ByteArraySegment(buffer.ToArray())
};

_device?.Write(new RawCapture(LinkLayers.Ethernet, new(), packet.Bytes));
}
var packet = new EthernetPacket(_dummyPhysicalAddress, _dummyPhysicalAddress, EthernetType.IPv6)
{
PayloadPacket = new IPv6Packet(source.Address, dest.Address) { PayloadPacket = udp }
};

await base.WriteAsync(buffer, cancellationToken);
udp.UpdateUdpChecksum();

_device?.Write(new RawCapture(LinkLayers.Ethernet, new(), packet.Bytes));
}

public override void Close()
Expand Down
2 changes: 1 addition & 1 deletion src/GZCTF/Utils/HubHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace GZCTF.Utils;

public class HubHelper
public static class HubHelper
{
/// <summary>
/// 当前请求是否具有权限
Expand Down
5 changes: 2 additions & 3 deletions src/GZCTF/Utils/LogHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using GZCTF.Extensions;
using NpgsqlTypes;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Sinks.File.Archive;
using Serilog.Sinks.PostgreSQL;
Expand Down Expand Up @@ -138,14 +137,14 @@ public static Serilog.ILogger GetLogger(IConfiguration configuration, IServicePr
restrictedToMinimumLevel: LogEventLevel.Debug
))
.WriteTo.Async(t => t.File(
path: $"files/logs/log_.log",
path: $"{FilePath.Logs}/log_.log",
formatter: new ExpressionTemplate(LogTemplate),
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 10 * 1024 * 1024,
restrictedToMinimumLevel: LogEventLevel.Debug,
rollOnFileSizeLimit: true,
retainedFileCountLimit: 5,
hooks: new ArchiveHooks(CompressionLevel.Optimal, "files/logs/archive/{UtcDate:yyyy-MM}")
hooks: new ArchiveHooks(CompressionLevel.Optimal, $"{FilePath.Logs}/archive/{{UtcDate:yyyy-MM}}")
))
.WriteTo.Async(t => t.PostgreSQL(
connectionString: configuration.GetConnectionString("Database"),
Expand Down

0 comments on commit 73cdc9a

Please sign in to comment.