From 0591277645cda392b5323c4289ffd498b1a8d113 Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 20:32:17 +0530 Subject: [PATCH 1/8] Update ReadMe.md --- ReadMe.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index f629dc6..5edfae9 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,21 +1,21 @@ ![GitHub Actions CI](https://github.com/Stratus-Security/Subdominator/workflows/CI/badge.svg) ![GitHub all releases](https://img.shields.io/github/downloads/Stratus-Security/Subdominator/total) -# Subdominator πŸš€ +# Subdominator ## Welcome to the Subdominator Club! -Meet **Subdominator**, your new favourite CLI tool for detecting subdomain takeovers. It's designed to be fast, accurate, and dependable, offering [a significant improvement over other available tools](https://www.stratussecurity.com/post/the-ultimate-subdomain-takeover-tool). +- Meet **Subdominator**, your new favourite CLI tool for detecting subdomain takeovers. It's designed to be fast, accurate, and dependable, offering [a significant improvement over other available tools](https://www.stratussecurity.com/post/the-ultimate-subdomain-takeover-tool). -πŸ” Precision and speed are our goal. Subdominator delivers better results without the wait, see the benchmark and feature comparison below for details. +- Precision and speed are our goal. Subdominator delivers better results without the wait, see the benchmark and feature comparison below for details. -## Installing πŸ› οΈ -To quickly, get up and running, you can download the latest release for [windows](https://github.com/Stratus-Security/Subdominator/releases/latest/download/Subdominator.exe) or [linux](https://github.com/Stratus-Security/Subdominator/releases/latest/download/Subdominator). +## Installing +- To quickly, get up and running, you can download the latest release for [windows](https://github.com/Stratus-Security/Subdominator/releases/latest/download/Subdominator.exe) or [linux](https://github.com/Stratus-Security/Subdominator/releases/latest/download/Subdominator). Alternatively, download it via CLI (remove .exe for linux version): ```bash wget https://github.com/Stratus-Security/Subdominator/releases/latest/download/Subdominator.exe ``` -## Quick Start 🚦 +## Quick Start To quickly check a list of domains, simply run: ```bash Subdominator -l subdomains.txt -o takeovers.txt @@ -25,7 +25,7 @@ Or to quickly check a single domain, run: Subdominator -d sub.example.com ``` -## Options πŸŽ›οΈ +## Options ``` -d, --domain A single domain to check -l, --list A list of domains to check (line delimited) @@ -61,17 +61,17 @@ For example, this shows the same vulnerable domain and another non-vulnerable do [-] www.stratussecurity.com ``` -Finally, if a domain is vulnerable and passes validation with the --validation flag, it will be prepended with a βœ…. +Finally, if a domain is vulnerable and passes validation with the --validation flag, it will be prepended with a . These domains have been validated to be vulnerable with the services directly, not just the fingerprint. For example: ``` -βœ… [Microsoft Azure] example.stratussecurity.com - CNAME: stratus-cdn-stg.azureedge.net + [Microsoft Azure] example.stratussecurity.com - CNAME: stratus-cdn-stg.azureedge.net ``` ## Demo The tool running across 1000 passively gathered subdomains: ![Demo](https://raw.githubusercontent.com/Stratus-Security/Subdominator/master/Demo.gif) -## Benchmark πŸ“Š +## Benchmark A benchmark was run across ~100,000 subdomains to compare performance with other popular tools | Tool | Threads | Time Taken | |--------------|---------|--------------------| @@ -79,7 +79,7 @@ A benchmark was run across ~100,000 subdomains to compare performance with other | Subjack | 50 | 2 hours, 30 minutes, 2 seconds | | Subdover | 50 | 2 hours, 33 minutes, 27 seconds | -## Key Features πŸ”₯ +## Key Features - **Advanced DNS Matching**: Supports DNS matching for CNAME, A, and AAAA records. - **Recursive DNS Queries**: Performs in-depth queries to enhance accuracy and reduce false positives. - **Intelligent Domain Matching**: Uses a custom `public_suffix_list.dat` for more effective domain matching. @@ -89,7 +89,7 @@ A benchmark was run across ~100,000 subdomains to compare performance with other - **Comprehensive Detection**: Capable of identifying takeovers missed by other tools. - **Validation**: Dynamic takeover validation modules to check beyond fingerprints. -## Feature Comparison πŸ₯Š +## Feature Comparison | Feature | Subdominator | Subjack | Subdover | |----------------------------------|--------------|---------|----------| | Advanced DNS Matching | βœ… | ❌ | ❌ | @@ -104,7 +104,7 @@ A benchmark was run across ~100,000 subdomains to compare performance with other | Fingerprints | 97 | 35 | 80 | ## Contributions -Got a suggestion, fingerprint, or want to chip in? We're all ears! Open a PR or issue – this will keep subdominator on top! πŸ˜„ +Got a suggestion, fingerprint, or want to chip in? We're all ears! Open a PR or issue – this will keep subdominator on top! ## Fingerprints The fingerprints and services are dynamically pulled from the [CanITakeOverXYZ repo](https://github.com/EdOverflow/can-i-take-over-xyz) as a source of truth. To fill in the gaps and correct incorrect fingerprints, this tool also has its own [custom fingerprints list](https://github.com/Stratus-Security/Subdominator/blob/master/Subdominator/custom_fingerprints.json) which is used in conjunction. @@ -209,4 +209,4 @@ Below is the current list of services supported, to ignore edge cases use the `- | Wufoo | Vulnerable | | Zendesk | Edge case | | Zoho Forms | Vulnerable | -| Zoho Forms India | Vulnerable | \ No newline at end of file +| Zoho Forms India | Vulnerable | From d77bfcbb9017ddaf6a66e9c407df4eeb0ad47aa7 Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 20:40:56 +0530 Subject: [PATCH 2/8] Update ReadMe.md --- ReadMe.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 5edfae9..6e579fc 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,7 +1,20 @@ +
+ +
+ 
+β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
+β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
+β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
+β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
+β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
+β•šβ•β•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β• β•šβ•β•     β•šβ•β•β•šβ•β•β•šβ•β•  β•šβ•β•β•β•β•šβ•β•  β•šβ•β•   β•šβ•β•    β•šβ•β•β•β•β•β• β•šβ•β•  β•šβ•β•
+
+ 
+ ![GitHub Actions CI](https://github.com/Stratus-Security/Subdominator/workflows/CI/badge.svg) ![GitHub all releases](https://img.shields.io/github/downloads/Stratus-Security/Subdominator/total) -# Subdominator +
## Welcome to the Subdominator Club! - Meet **Subdominator**, your new favourite CLI tool for detecting subdomain takeovers. It's designed to be fast, accurate, and dependable, offering [a significant improvement over other available tools](https://www.stratussecurity.com/post/the-ultimate-subdomain-takeover-tool). From baf855b0b7bf64516cb535a9580be3b7241485fb Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 20:59:20 +0530 Subject: [PATCH 3/8] Update FingerprintTests.cs --- Subdominator.Tests/FingerprintTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Subdominator.Tests/FingerprintTests.cs b/Subdominator.Tests/FingerprintTests.cs index 136a3aa..256de3e 100644 --- a/Subdominator.Tests/FingerprintTests.cs +++ b/Subdominator.Tests/FingerprintTests.cs @@ -17,6 +17,7 @@ public async Task Setup() } [TestMethod] + public void ShouldNotHaveDuplicateServices() { Assert.IsTrue( @@ -79,4 +80,4 @@ public async Task ShouldExcludeEdgeCaseFingerprints() var filteredFingerprints = await hijack.GetFingerprintsAsync(true); Assert.IsFalse(filteredFingerprints.Any(f => f.Status.ToLower() == "edge case")); } -} \ No newline at end of file +} From 8f005c3ff8373777af61e34e3696397c08e17ad3 Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 21:01:19 +0530 Subject: [PATCH 4/8] Create MatcherTests.cs --- Subdominator.Tests/MatcherTests.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Subdominator.Tests/MatcherTests.cs diff --git a/Subdominator.Tests/MatcherTests.cs b/Subdominator.Tests/MatcherTests.cs new file mode 100644 index 0000000..d99bfe0 --- /dev/null +++ b/Subdominator.Tests/MatcherTests.cs @@ -0,0 +1,20 @@ +namespace Subdominator.Tests; + +[TestClass] +public class MatcherTests +{ + [TestMethod] + public void FingerprintMatchingShouldBeCaseInsensitiveAndNormalizeDnsValues() + { + Assert.IsTrue(Subdominator.SubdomainHijack.MatchesFingerprintValue("Foo.Example.COM.", "example.com")); + Assert.IsTrue(Subdominator.SubdomainHijack.MatchesFingerprintValue("151.101.12.34", "151.101.")); + Assert.IsFalse(Subdominator.SubdomainHijack.MatchesFingerprintValue("foo.example.com", "bar.example.com")); + } + + [TestMethod] + public void FingerprintBodyMatchingShouldIgnoreCase() + { + Assert.IsTrue(Subdominator.SubdomainHijack.ContainsFingerprintText("You Have Found A Missing Link", "missing link")); + Assert.IsFalse(Subdominator.SubdomainHijack.ContainsFingerprintText("No matching string here", "missing link")); + } +} From fd26a24d07df3a71887734825e5722ef84374291 Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 21:02:22 +0530 Subject: [PATCH 5/8] Initialize FingerprintTexts with an empty list --- Subdominator/Models/Fingerprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Subdominator/Models/Fingerprint.cs b/Subdominator/Models/Fingerprint.cs index 98e2871..4d88f4d 100644 --- a/Subdominator/Models/Fingerprint.cs +++ b/Subdominator/Models/Fingerprint.cs @@ -15,7 +15,7 @@ public class Fingerprint [JsonPropertyName("fingerprint")] [JsonConverter(typeof(SingleOrArrayConverter))] - public List FingerprintTexts { get; set; } + public List FingerprintTexts { get; set; } = new(); [JsonPropertyName("http_status")] public int? HttpStatus { get; set; } From 027858a336096af2b9d869587a00e2663a3986b7 Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 21:02:51 +0530 Subject: [PATCH 6/8] ensure consistent braces in Fingerprint.cs --- Subdominator/Models/Fingerprint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Subdominator/Models/Fingerprint.cs b/Subdominator/Models/Fingerprint.cs index 4d88f4d..ec1a5ee 100644 --- a/Subdominator/Models/Fingerprint.cs +++ b/Subdominator/Models/Fingerprint.cs @@ -1,4 +1,4 @@ -ο»Ώusing System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace Subdominator; @@ -15,7 +15,7 @@ public class Fingerprint [JsonPropertyName("fingerprint")] [JsonConverter(typeof(SingleOrArrayConverter))] - public List FingerprintTexts { get; set; } = new(); + public List FingerprintTexts { get; set; } = new(); [JsonPropertyName("http_status")] public int? HttpStatus { get; set; } From 888a6c0dd33cf6dc52d56d06dd253760ceae8446 Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 21:03:54 +0530 Subject: [PATCH 7/8] Create Assemblyinfo.cs --- Subdominator/Properties/Assemblyinfo.cs | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Subdominator/Properties/Assemblyinfo.cs diff --git a/Subdominator/Properties/Assemblyinfo.cs b/Subdominator/Properties/Assemblyinfo.cs new file mode 100644 index 0000000..74a21a3 --- /dev/null +++ b/Subdominator/Properties/Assemblyinfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Subdominator.Tests")] From 07af925205b29f9c13035ef07049f2b3b28144fe Mon Sep 17 00:00:00 2001 From: Chintan Patel <216989679+Chintanpatel24@users.noreply.github.com> Date: Fri, 1 May 2026 21:04:33 +0530 Subject: [PATCH 8/8] Refactor fingerprint matching logic in SubdomainHijack --- Subdominator/SubdomainHijack.cs | 52 +++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/Subdominator/SubdomainHijack.cs b/Subdominator/SubdomainHijack.cs index 5e618cd..1653801 100644 --- a/Subdominator/SubdomainHijack.cs +++ b/Subdominator/SubdomainHijack.cs @@ -1,4 +1,4 @@ -ο»Ώusing DnsClient; +using DnsClient; using Microsoft.Extensions.Configuration; using Nager.PublicSuffix; using Nager.PublicSuffix.RuleProviders.CacheProviders; @@ -8,6 +8,7 @@ using System.Collections.Concurrent; using System.Text; using System.Text.Json; +using System.Net; namespace Subdominator; @@ -400,9 +401,9 @@ public async Task IsDomainVulnerable(string domain, bool validat // Check an individual fingerprint against and domain name and dns records public async Task<(bool, MatchedRecord, MatchedLocation)> IsFingerprintVulnerable(Fingerprint fingerprint, CnameResolutionResult dns, string domain) { - var isCnameMatch = dns.Cnames.Any(r => fingerprint.Cnames.Any(r.Contains)); - var isAMatch = dns.A.Any(r => fingerprint.ARecords.Any(r.Contains)); - var isAaaaMatch = dns.AAAA.Any(r => fingerprint.AAAARecords.Any(r.Contains)); + var isCnameMatch = dns.Cnames.Any(r => fingerprint.Cnames.Any(fingerprintValue => MatchesFingerprintValue(r, fingerprintValue))); + var isAMatch = dns.A.Any(r => fingerprint.ARecords.Any(fingerprintValue => MatchesFingerprintValue(r, fingerprintValue))); + var isAaaaMatch = dns.AAAA.Any(r => fingerprint.AAAARecords.Any(fingerprintValue => MatchesFingerprintValue(r, fingerprintValue))); var matchedRecord = MatchedRecord.None; if (isCnameMatch) @@ -462,7 +463,7 @@ public async Task IsDomainVulnerable(string domain, bool validat { foreach (var fingerprintMatch in fingerprint.FingerprintTexts) { - if (!string.IsNullOrWhiteSpace(fingerprintMatch) && responseBody.Contains(fingerprintMatch)) + if (ContainsFingerprintText(responseBody, fingerprintMatch)) { return (true, matchedRecord, MatchedLocation.HttpBody); } @@ -707,4 +708,45 @@ private bool IsDomainRegistered(string whoisResponse) } } } + + internal static bool MatchesFingerprintValue(string actualValue, string fingerprintValue) + { + if (string.IsNullOrWhiteSpace(actualValue) || string.IsNullOrWhiteSpace(fingerprintValue)) + { + return false; + } + + var normalizedActual = NormalizeDnsValue(actualValue); + var normalizedFingerprint = NormalizeDnsValue(fingerprintValue); + + if (LooksLikeIpPrefix(normalizedFingerprint)) + { + return normalizedActual.StartsWith(normalizedFingerprint, StringComparison.OrdinalIgnoreCase); + } + + if (normalizedFingerprint.Contains('.')) + { + return normalizedActual.Equals(normalizedFingerprint, StringComparison.OrdinalIgnoreCase) + || normalizedActual.EndsWith($".{normalizedFingerprint}", StringComparison.OrdinalIgnoreCase); + } + + return normalizedActual.Contains(normalizedFingerprint, StringComparison.OrdinalIgnoreCase); + } + + internal static bool ContainsFingerprintText(string responseBody, string fingerprintMatch) + { + return !string.IsNullOrWhiteSpace(responseBody) + && !string.IsNullOrWhiteSpace(fingerprintMatch) + && responseBody.Contains(fingerprintMatch, StringComparison.OrdinalIgnoreCase); + } + + private static string NormalizeDnsValue(string value) + { + return value.Trim().TrimEnd('.'); + } + + private static bool LooksLikeIpPrefix(string value) + { + return value.All(character => char.IsDigit(character) || character is '.' or ':' or 'x' or 'X'); + } }