Browse Source

STC: multi name, server cert validation

HOME 9 months ago
parent
commit
3fcac5d1f4
2 changed files with 113 additions and 24 deletions
  1. 59 17
      SniTamperPoc/Program.cs
  2. 54 7
      SniTamperProxy/Program.cs

+ 59 - 17
SniTamperPoc/Program.cs

@@ -6,12 +6,12 @@ using System.Net;
 using System.Net.Security;
 using System.Net.Sockets;
 using System.Runtime.ConstrainedExecution;
+using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
 using System.Threading;
 using DnsClient;
 
-
 //////////////////////////////////
 
 #region __________ INIT __________
@@ -71,8 +71,8 @@ finally
 //////////////////////////////////
 async Task RealMain()
 {
-    const string url = "http://www.example.org/index.html";
-    const string host = "www.example.org";
+    const string url = "http://example.com/index.html";
+    const string host = "example.com";
     const string path = "/index.html";
 
     const string dnsServerName = "reliable-dns-server-in-hosts";
@@ -98,19 +98,31 @@ async Task RealMain()
 
             if (certificate is not X509Certificate2 cert2) return false;
 
-            // 比较证书名称和主机名称
-            var certName = cert2.GetNameInfo(X509NameType.DnsName, false);
-            if (certName.StartsWith("*."))
+            // 先验证证书的有效期
+            if (DateTime.Now < cert2.NotBefore || DateTime.Now > cert2.NotAfter) return false;
+
+            // 然后比较证书名称和主机名称 (任意一个匹配)
+            var names = GetAllSubjectAlternativeNames(cert2);
+            var flagNameMatched = false;
+            foreach (var certName in names)
             {
-                if (!host.EndsWith(certName[2..], StringComparison.OrdinalIgnoreCase)) return false;
-            }
-            else if (!certName.Equals(host, StringComparison.OrdinalIgnoreCase)) return false;
+                if (certName.StartsWith("*."))
+                {
+                    if (!host.EndsWith(certName[2..], StringComparison.OrdinalIgnoreCase)) continue;
+                    flagNameMatched = true;
+                    break;
+                }
 
+                if (certName.Equals(host, StringComparison.OrdinalIgnoreCase))
+                {
+                    flagNameMatched = true;
+                    break;
+                }
+            }
 
-            // 验证证书的有效期
-            if (DateTime.Now < cert2.NotBefore || DateTime.Now > cert2.NotAfter) return false;
+            if (flagNameMatched == false) return false;
 
-            // 构建证书链
+            // 最后检查信任
             if (chain == null) return false;
 
             chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; //检测吊销耗时太长,忽略
@@ -136,7 +148,6 @@ async Task RealMain()
             }
 
             return true;
-
         }
     };
 
@@ -148,15 +159,46 @@ async Task RealMain()
 
     var reader = new StreamReader(ssl);
 
-    var lines = new List<string>(); // <- 200 OK , PoC SUCC!
+    var lines = new List<string>(); // <- HTTP/1.1 200 OK -- PoC SUCC!
     do
     {
         var line = reader.ReadLine();
         if (line == "") break;
         lines.Add(line);
-
     } while (true);
 
-
     int bp = 0;
-}
+}
+
+IReadOnlyList<string> GetAllSubjectAlternativeNames(X509Certificate2 cert)
+{
+    var names = new HashSet<string>();
+
+    foreach (var extension in cert.Extensions)
+    {
+        if (extension is X509SubjectAlternativeNameExtension sanExtension)
+        {
+            foreach (var name in sanExtension.EnumerateDnsNames()) names.Add(name);
+        }
+        else if (extension.Oid?.Value == "2.5.29.17") // Subject Alternative Name OID
+        {
+            var asnData = new AsnEncodedData(extension.Oid, extension.RawData);
+            var sanString = asnData.Format(true);
+
+            var sanParts = sanString.Split(new[] { ", ", "DNS Name=", " " }, StringSplitOptions.RemoveEmptyEntries);
+            foreach (var part in sanParts)
+            {
+                if (!string.IsNullOrEmpty(part) && !part.StartsWith("IPAddress") && !part.StartsWith("Uri"))
+                {
+                    names.Add(part);
+                }
+            }
+        }
+    }
+
+    // 添加证书的CN(通用名称)
+    var certName = cert.GetNameInfo(X509NameType.DnsName, false);
+    if (!string.IsNullOrEmpty(certName)) names.Add(certName);
+
+    return [.. names];
+}

+ 54 - 7
SniTamperProxy/Program.cs

@@ -11,6 +11,7 @@ using System.Text;
 using Microsoft.Extensions.Logging.Console;
 using System.Net.Security;
 using System.Net.Sockets;
+using System.Security.Cryptography;
 
 var builder = WebApplication.CreateBuilder(args);
 
@@ -130,16 +131,29 @@ public class NoSniProxyHandler : ConnectionHandler
 
         if (certificate is not X509Certificate2 cert2) return false;
 
-        // 比较证书名称和主机名称
-        var certName = cert2.GetNameInfo(X509NameType.DnsName, false);
-        if (certName.StartsWith("*."))
+        // 验证证书的有效期
+        if (DateTime.Now < cert2.NotBefore || DateTime.Now > cert2.NotAfter) return false;
+
+        // 然后比较证书名称和主机名称 (任意一个匹配)
+        var names = GetAllSubjectAlternativeNames(cert2);
+        var flagNameMatched = false;
+        foreach (var certName in names)
         {
-            if (!targetHost.EndsWith(certName[2..], StringComparison.OrdinalIgnoreCase)) return false;
+            if (certName.StartsWith("*."))
+            {
+                if (!targetHost.EndsWith(certName[2..], StringComparison.OrdinalIgnoreCase)) continue;
+                flagNameMatched = true;
+                break;
+            }
+
+            if (certName.Equals(targetHost, StringComparison.OrdinalIgnoreCase))
+            {
+                flagNameMatched = true;
+                break;
+            }
         }
-        else if (!certName.Equals(targetHost, StringComparison.OrdinalIgnoreCase)) return false;
 
-        // 验证证书的有效期
-        if (DateTime.Now < cert2.NotBefore || DateTime.Now > cert2.NotAfter) return false;
+        if (flagNameMatched == false) return false;
 
         // 构建证书链
         if (chain == null) return false;
@@ -168,4 +182,37 @@ public class NoSniProxyHandler : ConnectionHandler
 
         return true;
     }
+
+    IReadOnlyList<string> GetAllSubjectAlternativeNames(X509Certificate2 cert)
+    {
+        var names = new HashSet<string>();
+
+        foreach (var extension in cert.Extensions)
+        {
+            if (extension is X509SubjectAlternativeNameExtension sanExtension)
+            {
+                foreach (var name in sanExtension.EnumerateDnsNames()) names.Add(name);
+            }
+            else if (extension.Oid?.Value == "2.5.29.17") // Subject Alternative Name OID
+            {
+                var asnData = new AsnEncodedData(extension.Oid, extension.RawData);
+                var sanString = asnData.Format(true);
+
+                var sanParts = sanString.Split(new[] { ", ", "DNS Name=", " " }, StringSplitOptions.RemoveEmptyEntries);
+                foreach (var part in sanParts)
+                {
+                    if (!string.IsNullOrEmpty(part) && !part.StartsWith("IPAddress") && !part.StartsWith("Uri"))
+                    {
+                        names.Add(part);
+                    }
+                }
+            }
+        }
+
+        // 添加证书的CN(通用名称)
+        var certName = cert.GetNameInfo(X509NameType.DnsName, false);
+        if (!string.IsNullOrEmpty(certName)) names.Add(certName);
+
+        return [.. names];
+    }
 }