From 0f079d62d9af263509d9826b9a856a2f33737d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E5=A4=9C?= Date: Tue, 27 Feb 2024 18:43:55 +0800 Subject: [PATCH] Add files via upload --- RDPInterceptor/API/Logger.cs | 2 + RDPInterceptor/API/NetworkInterceptor.cs | 215 +++++++++++++++++++---- RDPInterceptor/API/WebService.cs | 114 +++++++----- RDPInterceptor/App.xaml.cs | 7 +- RDPInterceptor/MainWindow.xaml | 19 +- RDPInterceptor/MainWindow.xaml.cs | 40 +++-- RDPInterceptor/Setting.xaml | 20 +++ RDPInterceptor/Setting.xaml.cs | 170 ++++++++++++++++++ RDPInterceptor/Web/Login.html | 39 ++-- 9 files changed, 500 insertions(+), 126 deletions(-) create mode 100644 RDPInterceptor/Setting.xaml create mode 100644 RDPInterceptor/Setting.xaml.cs diff --git a/RDPInterceptor/API/Logger.cs b/RDPInterceptor/API/Logger.cs index bc63b5b..087fed1 100644 --- a/RDPInterceptor/API/Logger.cs +++ b/RDPInterceptor/API/Logger.cs @@ -105,6 +105,8 @@ private static void LogAddLine(string message, SolidColorBrush color) Paragraph paragraph = new Paragraph(new Run(message)); paragraph.Foreground = color; logRichTextBox.Document.Blocks.Add(paragraph); + + logRichTextBox.ScrollToEnd(); }); } diff --git a/RDPInterceptor/API/NetworkInterceptor.cs b/RDPInterceptor/API/NetworkInterceptor.cs index d51638a..6f3a110 100644 --- a/RDPInterceptor/API/NetworkInterceptor.cs +++ b/RDPInterceptor/API/NetworkInterceptor.cs @@ -1,5 +1,10 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; using System.Net; +using System.Threading; using System.Threading.Tasks; using WindivertDotnet; @@ -7,89 +12,101 @@ namespace RDPInterceptor.API { public class NetworkInterceptor { + public static CancellationTokenSource CaptureCancellationTokenSource = new(); + public static bool IpWhitelistMode { get; set; } = true; public static ObservableCollection IpAddrList { get; set; } = new(); - public static bool IsCapture { get; set; } = true; - + public static bool IsLogConnection { get; set; } = false; + + public static bool WriteIntoLog { get; set; } = true; + + public static ushort Port { get; set; } = 3389; + private static WinDivert Divert { get; set; } private static WinDivertPacket Packet { get; set; } - + private static WinDivertAddress Addr { get; set; } - public static bool AddIpIntoList(string Ip) + public static async Task AddIpIntoList(string Ip) { if (Ip == null) { return false; } - IPAddress IpAddr = null; + IPAddress IpAddr; if (IPAddress.TryParse(Ip, out IpAddr)) { IpAddrList.Add(IpAddr); + await AddIpIntoWhitelistFile(IpAddr); } else { Logger.Error($"Invalid IP/Domain."); return false; } - + return true; } - - public static async Task StartCapture() + + public static async Task StartCapture(CancellationToken cancellationToken) { Logger.Log("Start Interceptor."); - - var filter = Filter.True.And(f => f.Tcp.DstPort == 3389); - + + var filter = Filter.True.And(f => f.Tcp.DstPort == Port); + Divert = new WinDivert(filter, WinDivertLayer.Network); Addr = new(); Packet = new(); - IsCapture = true; - - while (IsCapture) + while (!cancellationToken.IsCancellationRequested) { if (Divert != null) { try { - await Divert.RecvAsync(Packet, Addr); + await Divert.RecvAsync(Packet, Addr, cancellationToken); - if (ProcessPacket(Packet, Addr)) + if (await ProcessPacketAsync(Packet, Addr)) { - await Divert.SendAsync(Packet, Addr); + await Divert.SendAsync(Packet, Addr, cancellationToken); } } - catch (TaskCanceledException ex) + catch (OperationCanceledException ex) { Logger.Log($"Stop Interceptor."); - IsCapture = false; } } } } - public static async Task StopCapture() + public static async Task StopCapture(CancellationToken cancellationToken) { - IsCapture = false; - - if (Divert != null) + if (cancellationToken.CanBeCanceled) { - Divert.Dispose(); - Packet.Dispose(); - Addr.Dispose(); + CaptureCancellationTokenSource?.Cancel(); } - + else + { + Logger.Error("You cannot stop capture at this time."); + } + Logger.Log("Capture has now stopped."); } - private unsafe static bool ProcessPacket(WinDivertPacket Packet, WinDivertAddress Address) + private static unsafe void GetIpAddresses(IPV4Header* header, out IPAddress srcIpAddr, out IPAddress dstIpAddr) + { + IPAddress srcIp = header->SrcAddr; + IPAddress dstIp = header->DstAddr; + srcIpAddr = srcIp; + dstIpAddr = dstIp; + } + + public static async Task ProcessPacketAsync(WinDivertPacket Packet, WinDivertAddress Address) { if (Packet != null) { @@ -100,24 +117,29 @@ private unsafe static bool ProcessPacket(WinDivertPacket Packet, WinDivertAddres else { var result = Packet.GetParseResult(); - IPAddress SrcIpAddr = result.IPV4Header->SrcAddr; - IPAddress DstIpAddr = result.IPV4Header->DstAddr; - + IPAddress SrcIpAddr, DstIpAddr; + unsafe + { + GetIpAddresses(result.IPV4Header, out SrcIpAddr, out DstIpAddr); + } + if (IpAddrList.Contains(SrcIpAddr)) { - Logger.Debug($"Incomming RDP Connection from {SrcIpAddr} has been accepted."); + LogConnections(IsLogConnection,$"Incoming RDP Connection from {SrcIpAddr} has been accepted."); Packet.CalcChecksums(Address); return true; } else if (IpAddrList.Contains(DstIpAddr)) { - Logger.Debug($"Outgoing RDP Connection to {DstIpAddr} has been accepted."); + LogConnections(IsLogConnection,$"Outgoing RDP Connection to {DstIpAddr} has been accepted."); Packet.CalcChecksums(Address); return true; } else { - Logger.Debug($"Incomming RDP Connection from {SrcIpAddr} has been refused."); + LogConnections(IsLogConnection, $"Incoming RDP Connection from {SrcIpAddr} has been refused."); + await LogConnectionAsync(SrcIpAddr); + return false; } } @@ -125,6 +147,129 @@ private unsafe static bool ProcessPacket(WinDivertPacket Packet, WinDivertAddres return false; } + + private static void LogConnections(bool isLogConnection, string content) + { + if (isLogConnection) + { + Logger.Debug(content); + } + } + + private static async Task LogConnectionAsync(IPAddress srcIpAddr) + { + string logFilePath = "Connectionlist.log"; + + if (File.Exists(logFilePath)) + { + string[] lines = await File.ReadAllLinesAsync(logFilePath); + if (lines.Contains(srcIpAddr.ToString())) + { + return; + } + } + else + { + FileStream fs = File.Create(logFilePath); + fs.Close(); + } + + using (StreamWriter writer = File.AppendText(logFilePath)) + { + await writer.WriteLineAsync(srcIpAddr.ToString()); + } + } + + private static readonly SemaphoreSlim semaphore = new(1); + + public static async void ReadLinesFromFileAsync() + { + await semaphore.WaitAsync(); + + string WhitelistFilePath = "Whitelist.txt"; + + try + { + if (File.Exists(WhitelistFilePath)) + { + IPAddress IpAddr; + + foreach (string ip in await File.ReadAllLinesAsync(WhitelistFilePath)) + { + if (IPAddress.TryParse(ip, out IpAddr)) + { + IpAddrList.Add(IpAddr); + + Logger.Log($"IP {ip} has been read into whitelist."); + } + else + { + Logger.Error($"ERROR! Failed to parse {ip}."); + } + } + } + else + { + FileStream fs = File.Create(WhitelistFilePath); + fs.Close(); + } + } + finally + { + semaphore.Release(); + } + } + + public static async Task AddIpIntoWhitelistFile(IPAddress ipAddress) + { + Logger.Debug($"Method AddIpIntoWhitelistFile called."); + + try + { + string WhitelistFilePath = "Whitelist.txt"; + + if (File.Exists(WhitelistFilePath)) + { + Logger.Debug($"File {WhitelistFilePath} exists. Proceeding..."); + + await semaphore.WaitAsync(); + + string[] lines = await File.ReadAllLinesAsync(WhitelistFilePath); + if (Array.Exists(lines, line => line.Equals(ipAddress.ToString()))) + { + Logger.Debug($"IP {ipAddress} already in {WhitelistFilePath}"); + return; + } + else + { + using (StreamWriter writer = File.AppendText(WhitelistFilePath)) + { + await writer.WriteLineAsync(ipAddress.ToString()); + } + + Logger.Debug($"IP {ipAddress} has been written into {WhitelistFilePath}"); + } + } + else + { + FileStream fs = File.Create(WhitelistFilePath); + fs.Close(); + + Logger.Error($"File {WhitelistFilePath} doesn't exist. Now create file {WhitelistFilePath}."); + + await AddIpIntoWhitelistFile(ipAddress); + } + } + catch (Exception e) + { + Logger.Error(e.Message + e.Message); + throw; + } + finally + { + semaphore.Release(); + } + } } } diff --git a/RDPInterceptor/API/WebService.cs b/RDPInterceptor/API/WebService.cs index 59b178a..138196d 100644 --- a/RDPInterceptor/API/WebService.cs +++ b/RDPInterceptor/API/WebService.cs @@ -5,6 +5,7 @@ using System.Net; using System.Reflection; using System.Security.Claims; +using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Windows; @@ -15,6 +16,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using RDPInterceptor.API.Controllers; @@ -46,11 +48,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseAuthentication(); app.UseAuthorization(); - app.UseEndpoints(endpoints => + app.UseForwardedHeaders(new ForwardedHeadersOptions { - endpoints.MapControllers(); + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); + app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + app.Run(async (context) => { var assembly = Assembly.GetExecutingAssembly(); @@ -96,8 +100,8 @@ public async Task StartCapture() { try { - await NetworkInterceptor.StartCapture(); - + await NetworkInterceptor.StartCapture(NetworkInterceptor.CaptureCancellationTokenSource.Token); + return Ok("Called success."); } catch (Exception ex) @@ -113,8 +117,8 @@ public async Task StopCapture() { try { - await NetworkInterceptor.StopCapture(); - + await NetworkInterceptor.StopCapture(NetworkInterceptor.CaptureCancellationTokenSource.Token); + return Ok("Called success."); } catch (Exception ex) @@ -123,7 +127,7 @@ public async Task StopCapture() throw; } } - + [Authorize] [HttpPost("DeleteIpAddr")] public IActionResult DeleteIpAddr() @@ -131,15 +135,12 @@ public IActionResult DeleteIpAddr() try { using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) - { + { string ipAddr = reader.ReadToEndAsync().Result; IPAddress IpAddr = null; if (IPAddress.TryParse(ipAddr, out IpAddr)) { - Application.Current.Dispatcher.Invoke(() => - { - NetworkInterceptor.IpAddrList.Remove(IpAddr); - }); + Application.Current.Dispatcher.Invoke(() => { NetworkInterceptor.IpAddrList.Remove(IpAddr); }); } else { @@ -155,7 +156,7 @@ public IActionResult DeleteIpAddr() throw; } } - + [Authorize] [HttpPost("AddIpAddr")] public IActionResult PostNewIp() @@ -163,15 +164,12 @@ public IActionResult PostNewIp() try { using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) - { + { string ipAddr = reader.ReadToEndAsync().Result; IPAddress IpAddr = null; if (IPAddress.TryParse(ipAddr, out IpAddr)) { - Application.Current.Dispatcher.Invoke(() => - { - NetworkInterceptor.IpAddrList.Add(IpAddr); - }); + Application.Current.Dispatcher.Invoke(() => { NetworkInterceptor.IpAddrList.Add(IpAddr); }); } else { @@ -194,7 +192,7 @@ public IActionResult GetLog() { return Ok(Logger.GetLogs()); } - + [Authorize] [HttpGet("GetIpAddrList")] public IActionResult GetIpAddrList() @@ -202,9 +200,9 @@ public IActionResult GetIpAddrList() try { var ipAddrList = NetworkInterceptor.IpAddrList; - + var ipAddrStrList = ipAddrList.Select(ip => ip.ToString()).ToList(); - + return Ok(ipAddrStrList); } catch (Exception e) @@ -213,45 +211,59 @@ public IActionResult GetIpAddrList() throw; } } - + public class AuthInfo { public string Username { get; set; } public string Password { get; set; } } - + [AllowAnonymous] [HttpPost("LoginOut")] public async Task LoginOut() { + Logger.Debug("Client called LoginOut"); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Ok(); } - + [AllowAnonymous] [HttpPost("LoginIn")] public async Task LoginIn([FromBody] AuthInfo authInfo) { + var remoteIpAddress = HttpContext.Connection.RemoteIpAddress; + + Logger.Debug($"Client {remoteIpAddress} called LoginIn"); + + if (authInfo == null) + { + Logger.Error($"ERROR! authInfo is null!"); + return Unauthorized(); + } + string filePath = "auth.xml"; string defaultUsername = "admin"; - string defaultPassword = "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"; + string defaultPasswordHash = "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"; if (!System.IO.File.Exists(filePath)) { XDocument doc = new XDocument( new XElement("Auth", new XElement("Username", defaultUsername), - new XElement("Password", defaultPassword) + new XElement("PasswordHash", defaultPasswordHash) ) ); doc.Save(filePath); } - + XDocument authDoc = XDocument.Load(filePath); string username = authDoc.Root.Element("Username").Value; - string password = authDoc.Root.Element("Password").Value; - - if (authInfo.Username == username && authInfo.Password == password) + string passwordHash = authDoc.Root.Element("PasswordHash").Value; + + string receivedPasswordHash = ComputeSHA256Hash(authInfo.Password); + + if (authInfo.Username == username && receivedPasswordHash == passwordHash) { try { @@ -266,8 +278,8 @@ public async Task LoginIn([FromBody] AuthInfo authInfo) var authProperties = new AuthenticationProperties(); await HttpContext.SignInAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - new ClaimsPrincipal(claimsIdentity), + CookieAuthenticationDefaults.AuthenticationScheme, + new ClaimsPrincipal(claimsIdentity), authProperties); } catch (Exception e) @@ -277,7 +289,7 @@ await HttpContext.SignInAsync( } Logger.Log("Login success."); - + return Ok(); } else @@ -285,36 +297,52 @@ await HttpContext.SignInAsync( return Unauthorized(); } } - + + private string ComputeSHA256Hash(string input) + { + using (SHA256 sha256 = SHA256.Create()) + { + byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(input)); + StringBuilder builder = new StringBuilder(); + foreach (byte b in bytes) + { + builder.Append(b.ToString("x2")); + } + + return builder.ToString(); + } + } + [Authorize] [HttpPost("ChangePasswd")] public IActionResult ChangePasswd([FromBody] AuthInfo authInfo) { - Logger.Log("Called changepwd"); - + Logger.Debug("Client called ChangePasswd"); + string filePath = "auth.xml"; if (!System.IO.File.Exists(filePath)) { - return NotFound("The auth file does not exist."); + return NotFound("The auth file does not exist."); } - + XDocument authDoc = XDocument.Load(filePath); string username = authDoc.Root.Element("Username").Value; - + Logger.Log(username); Logger.Log(authInfo.Username); - + if (authInfo.Username != username) { Logger.Log("Username not fit."); return Unauthorized(); } - + authDoc.Root.Element("Password").Value = authInfo.Password; authDoc.Save(filePath); - + + LoginOut(); return Unauthorized("Please login again."); } } -} +} \ No newline at end of file diff --git a/RDPInterceptor/App.xaml.cs b/RDPInterceptor/App.xaml.cs index 96194e4..fd14f16 100644 --- a/RDPInterceptor/App.xaml.cs +++ b/RDPInterceptor/App.xaml.cs @@ -1,5 +1,5 @@ using System; -using System.IO; +using System.Net; using System.Windows; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -25,7 +25,10 @@ protected override void OnStartup(StartupEventArgs ev) if (command == "--WebOnly") { var host = new WebHostBuilder() - .UseKestrel() + .UseKestrel(options => + { + options.Listen(IPAddress.Any, RDPInterceptor.MainWindow.WebPort); + }) .ConfigureServices(services => { var webService = new Startup(); diff --git a/RDPInterceptor/MainWindow.xaml b/RDPInterceptor/MainWindow.xaml index 2aee24b..031763d 100644 --- a/RDPInterceptor/MainWindow.xaml +++ b/RDPInterceptor/MainWindow.xaml @@ -7,14 +7,15 @@ mc:Ignorable="d" Title="RDPInterceptor" Height="300" Width="500" ResizeMode="NoResize"> -