From 5815c59fa3c04df066284fd3742e744171a4ae57 Mon Sep 17 00:00:00 2001 From: Leandro Monaco Date: Thu, 3 Mar 2022 07:57:39 +1000 Subject: [PATCH] [Tools] Added network capture tool --- Tools/Tools.sln | 8 + .../src/NetworkCapture/NetworkCapture.csproj | 12 ++ Tools/src/NetworkCapture/Program.cs | 189 ++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 Tools/src/NetworkCapture/NetworkCapture.csproj create mode 100644 Tools/src/NetworkCapture/Program.cs diff --git a/Tools/Tools.sln b/Tools/Tools.sln index f1c721e..ec61d23 100644 --- a/Tools/Tools.sln +++ b/Tools/Tools.sln @@ -17,6 +17,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OctopusCertificateReference EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiraReporting", "src\JiraReporting\JiraReporting.csproj", "{EB5D47D5-6702-4EC0-A1E3-CD48A16C1ECE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{16F9BA96-0BC1-4D3F-91AF-0D096BDCD174}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkCapture", "src\NetworkCapture\NetworkCapture.csproj", "{9F260947-AAD2-443E-851E-B7CE5305F7C2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +51,10 @@ Global {EB5D47D5-6702-4EC0-A1E3-CD48A16C1ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB5D47D5-6702-4EC0-A1E3-CD48A16C1ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB5D47D5-6702-4EC0-A1E3-CD48A16C1ECE}.Release|Any CPU.Build.0 = Release|Any CPU + {9F260947-AAD2-443E-851E-B7CE5305F7C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F260947-AAD2-443E-851E-B7CE5305F7C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F260947-AAD2-443E-851E-B7CE5305F7C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F260947-AAD2-443E-851E-B7CE5305F7C2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tools/src/NetworkCapture/NetworkCapture.csproj b/Tools/src/NetworkCapture/NetworkCapture.csproj new file mode 100644 index 0000000..a8bef96 --- /dev/null +++ b/Tools/src/NetworkCapture/NetworkCapture.csproj @@ -0,0 +1,12 @@ + + + + Exe + net5.0 + + + + + + + diff --git a/Tools/src/NetworkCapture/Program.cs b/Tools/src/NetworkCapture/Program.cs new file mode 100644 index 0000000..5f0df0e --- /dev/null +++ b/Tools/src/NetworkCapture/Program.cs @@ -0,0 +1,189 @@ +using System; +using System.Net; +using System.Threading.Tasks; +using Titanium.Web.Proxy; +using Titanium.Web.Proxy.EventArguments; +using Titanium.Web.Proxy.Http; +using Titanium.Web.Proxy.Models; + +namespace NetworkCapture +{ + class Program + { + static void Main(string[] args) + { + var proxyServer = new ProxyServer(); + + // locally trust root certificate used by this proxy + //proxyServer.CertificateManager.TrustRootCertificate(true); + + // optionally set the Certificate Engine + // Under Mono only BouncyCastle will be supported + //proxyServer.CertificateManager.CertificateEngine = Network.CertificateEngine.BouncyCastle; + + proxyServer.BeforeRequest += OnRequest; + proxyServer.BeforeResponse += OnResponse; + proxyServer.ServerCertificateValidationCallback += OnCertificateValidation; + proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection; + + + var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) + { + // Use self-issued generic certificate on all https requests + // Optimizes performance by not creating a certificate for each https-enabled domain + // Useful when certificate trust is not required by proxy clients + //GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password") + }; + + // Fired when a CONNECT request is received + explicitEndPoint.BeforeTunnelConnectRequest += OnBeforeTunnelConnectRequest; + + // An explicit endpoint is where the client knows about the existence of a proxy + // So client sends request in a proxy friendly manner + proxyServer.AddEndPoint(explicitEndPoint); + proxyServer.Start(); + + // Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy) + // A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS + // to send data to this endPoint + var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true) + { + // Generic Certificate hostname to use + // when SNI is disabled by client + GenericCertificateName = "google.com" + }; + + proxyServer.AddEndPoint(transparentEndPoint); + + //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; + //proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 }; + + foreach (var endPoint in proxyServer.ProxyEndPoints) + Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ", + endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); + + // Only explicit proxies can be set as system proxy! + proxyServer.SetAsSystemHttpProxy(explicitEndPoint); + proxyServer.SetAsSystemHttpsProxy(explicitEndPoint); + + // wait here (You can use something else as a wait function, I am using this as a demo) + Console.Read(); + + // Unsubscribe & Quit + explicitEndPoint.BeforeTunnelConnectRequest -= OnBeforeTunnelConnectRequest; + proxyServer.BeforeRequest -= OnRequest; + proxyServer.BeforeResponse -= OnResponse; + proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation; + proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection; + + proxyServer.Stop(); + + } + + private static async Task OnBeforeTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) + { + string hostname = e.HttpClient.Request.RequestUri.Host; + + if (hostname.Contains("dropbox.com")) + { + // Exclude Https addresses you don't want to proxy + // Useful for clients that use certificate pinning + // for example dropbox.com + e.DecryptSsl = false; + } + } + + public static async Task OnRequest(object sender, SessionEventArgs e) + { + Console.WriteLine($"Request: {e.HttpClient.Request.Url}"); + + // read request headers + var requestHeaders = e.HttpClient.Request.Headers; + + var method = e.HttpClient.Request.Method.ToUpper(); + if ((method == "POST" || method == "PUT" || method == "PATCH")) + { + // Get/Set request body bytes + byte[] bodyBytes = await e.GetRequestBody(); + e.SetRequestBody(bodyBytes); + + // Get/Set request body as string + string bodyString = await e.GetRequestBodyAsString(); + e.SetRequestBodyString(bodyString); + + // store request + // so that you can find it from response handler + e.UserData = e.HttpClient.Request; + } + + //// To cancel a request with a custom HTML content + //// Filter URL + //if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("google.com")) + //{ + // e.Ok("" + + // "

" + + // "Website Blocked" + + // "

" + + // "

Blocked by titanium web proxy.

" + + // "" + + // ""); + //} + + //// Redirect example + //if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org")) + //{ + // e.Redirect("https://www.paypal.com"); + //} + } + + // Modify response + public static async Task OnResponse(object sender, SessionEventArgs e) + { + // read response headers + var responseHeaders = e.HttpClient.Response.Headers; + + //if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return; + if (e.HttpClient.Request.Method == "GET" || e.HttpClient.Request.Method == "POST") + { + if (e.HttpClient.Response.StatusCode == 200) + { + if (e.HttpClient.Response.ContentType != null && e.HttpClient.Response.ContentType.Trim().ToLower().Contains("text/html")) + { + byte[] bodyBytes = await e.GetResponseBody(); + e.SetResponseBody(bodyBytes); + + string body = await e.GetResponseBodyAsString(); + e.SetResponseBodyString(body); + Console.WriteLine($"Response: {body}"); + } + } + } + + + + if (e.UserData != null) + { + // access request from UserData property where we stored it in RequestHandler + var request = (Request)e.UserData; + } + } + + // Allows overriding default certificate validation logic + public static Task OnCertificateValidation(object sender, CertificateValidationEventArgs e) + { + // set IsValid to true/false based on Certificate Errors + if (e.SslPolicyErrors == System.Net.Security.SslPolicyErrors.None) + e.IsValid = true; + + return Task.CompletedTask; + } + + // Allows overriding default client certificate selection logic during mutual authentication + public static Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e) + { + // set e.clientCertificate to override + return Task.CompletedTask; + } + + } +}