sqarol

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 31, 2026 License: MIT Imports: 13 Imported by: 0

README

sqarol

A Go library and CLI tool designed to help cybersecurity analysts manually monitor lookalike domains. Given a legitimate domain name, it produces hundreds of plausible look-alike variations that attackers might register for phishing, brand impersonation, or credential harvesting. Each variation is annotated with the generation technique, Levenshtein edit distance, and a visual deceptiveness score. The CLI can also check the most effective variations against live DNS and WHOIS data to determine which are registered, who owns them, and whether they appear to be parked.

For automated, continuous domain threat monitoring, check out Symbol Security's Domain Threat Alerts.

sqarol demo

Installation

As a library:

go get github.com/symbolsecurity/sqarol

As a CLI tool:

go install github.com/symbolsecurity/sqarol/cmd/sqarol@latest

CLI

sqarol <command> [arguments]

Commands:
  generate <domain>            Generate typosquatting domain variations
  check <domain> [-n count]    Check status of top N most effective variations

Options:
  -n count   Number of top variations to check (default: 100)
  -h         Show help
generate

Generates all typosquatting variations for a domain, sorted by effectiveness (highest first).

$ sqarol generate symbolsecurity.com
KIND                 VARIANT                            DISTANCE  EFFECTIVENESS
----                 -------                            --------  -------------
typo-trick           symbo1security.com                 1         0.95
phonetic             symbolsekurity.com                 1         0.79
vowel-swap           symbolsocurity.com                 1         0.79
...

Total: 467 variations
check

Generates variations, takes the top N by effectiveness, and checks each one for registration status, WHOIS owner, A records, MX records, and parking detection. Checks run concurrently.

$ sqarol check symbolsecurity.com -n 5
Checking 5 variations...

#  VARIANT             REGISTERED  OWNER  A  MX  PARKED
-  -------             ----------  -----  -  --  ------
1  symbo1security.com  no          -      -  -   no
2  symbolsocurity.com  no          -      -  -   no
3  symbolsekurity.com  no          -      -  -   no
4  sympolsecurity.com  no          -      -  -   no
5  symbolsecuridy.com  no          -      -  -   no

Checked: 5/467 variations

Library Usage

package main

import (
    "fmt"
    "log"

    "github.com/symbolsecurity/sqarol"
)

func main() {
    vars, err := sqarol.Generate("symbolsecurity.com")
    if err != nil {
        log.Fatal(err)
    }

    for _, v := range vars {
        fmt.Printf("%-20s %-40s dist=%d eff=%.2f\n", v.Kind, v.Variant, v.Distance, v.Effectiveness)
    }
}

Full URLs are also accepted; the hostname is extracted automatically:

vars, err := sqarol.Generate("https://symbolsecurity.com/path")
// v.Original will be "symbolsecurity.com"

API

Generate(domain string) ([]Variation, error)

Normalizes the input domain and runs all fuzzing techniques against it, returning the generated variations. The domain must be an ASCII hostname or a full URL (the hostname is extracted automatically). Internationalized (non-ASCII) domain names are not supported and will return an error.

Check(ctx context.Context, domain string) (*DomainCheck, error)

Queries DNS and WHOIS to determine whether a domain is registered, who owns it, what A and MX records it has (including resolved IP addresses), and whether it appears to be parked. Registration is determined by the presence of NS records (the most reliable signal). Parking detection uses known parking nameserver suffixes and IP address prefixes. WHOIS is queried separately to extract the registrant owner, with automatic referral following for richer results. Concurrent WHOIS connections are throttled to avoid rate-limiting.

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

check, err := sqarol.Check(ctx, "example.com")
// check.IsRegistered   -> true
// check.Owner          -> "Internet Assigned Numbers Authority"
// check.HasARecords    -> true
// check.ARecords       -> []string{"93.184.215.14"}
// check.HasMXRecords   -> false
// check.MXRecords      -> []sqarol.MXRecord{}
// check.IsParked       -> false
Variation
type Variation struct {
    Original      string  `json:"original"`      // Normalized input domain
    Variant       string  `json:"variant"`        // Generated look-alike domain
    Kind          string  `json:"kind"`           // Algorithm that produced this variation
    Distance      int     `json:"distance"`       // Levenshtein distance from original
    Effectiveness float64 `json:"effectiveness"`  // Visual deceptiveness score (0.0 - 1.0)
}
DomainCheck
type DomainCheck struct {
    Domain       string     `json:"domain"`                    // Queried domain name
    IsRegistered bool       `json:"is_registered"`             // Whether the domain is registered (via NS lookup)
    Owner        string     `json:"owner,omitempty"`           // Registrant from WHOIS, if available
    HasARecords  bool       `json:"has_a_records"`             // Whether the domain has IPv4 address records
    HasMXRecords bool       `json:"has_mx_records"`            // Whether the domain has mail exchange records
    ARecords     []string   `json:"a_records,omitempty"`       // Resolved IPv4 addresses for the domain
    MXRecords    []MXRecord `json:"mx_records,omitempty"`      // Resolved mail exchange records with their IPs
    IsParked     bool       `json:"is_parked"`                 // Whether the domain appears to be parked
}
MXRecord
type MXRecord struct {
    Host string   `json:"host"`          // MX hostname
    Pref uint16   `json:"pref"`          // MX preference (lower = higher priority)
    IPs  []string `json:"ips,omitempty"` // Resolved IPv4 addresses of the MX host
}

Techniques

Technique Description
omission Removes one character at a time
transposition Swaps adjacent character pairs
vowel-swap Replaces each vowel with every other vowel
hyphenation Inserts a hyphen between adjacent characters
repetition Doubles each character one at a time
extra-random-letter Inserts a random letter at each position
prefix Prepends common phishing prefixes (login, auth, account, etc.)
suffix Appends common phishing suffixes (secure, verify, portal, etc.)
subdomain Inserts a dot at each position to simulate subdomains
tld-swap Replaces the TLD with alternatives from a catalog of ~113 TLDs
typo-trick Substitutes visually similar characters (o/0, l/1, m/rn, etc.)
phonetic Substitutes phonetically similar groups (f/ph, c/k, etc.)
inflect Generates plural/singular forms of the domain name
complete-with-tld Splits name parts that end with a known TLD string

License

This project is licensed under the MIT License.

Documentation

Overview

Package sqarol generates domain typosquatting variations for a given domain name. It produces look-alike domains annotated with the generation technique, Levenshtein edit distance, and a visual deceptiveness score.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CheckOption added in v0.1.3

type CheckOption func(*checkConfig)

CheckOption configures which checks to perform during a domain check.

func WithDNS added in v0.1.3

func WithDNS() CheckOption

WithDNS enables A record lookup.

func WithMX added in v0.1.3

func WithMX() CheckOption

WithMX enables MX record lookup.

func WithOwner added in v0.1.3

func WithOwner() CheckOption

WithOwner enables WHOIS owner lookup.

func WithParking added in v0.1.3

func WithParking() CheckOption

WithParking enables parking detection.

func WithRegistration added in v0.1.3

func WithRegistration() CheckOption

WithRegistration enables NS-based registration check.

type DomainCheck

type DomainCheck struct {
	// Domain is the queried domain name.
	Domain string `json:"domain"`
	// IsRegistered indicates whether the domain is registered.
	// Determined by the presence of NS records for the domain.
	IsRegistered bool `json:"is_registered"`
	// Owner is the registrant organization or name extracted from WHOIS,
	// if available.
	Owner string `json:"owner,omitempty"`
	// HasARecords indicates whether the domain has IPv4 address records.
	HasARecords bool `json:"has_a_records"`
	// HasMXRecords indicates whether the domain has mail exchange records.
	HasMXRecords bool `json:"has_mx_records"`
	// ARecords contains the resolved IPv4 addresses for the domain.
	ARecords []string `json:"a_records,omitempty"`
	// MXRecords contains the resolved mail exchange records with their IPs.
	MXRecords []MXRecord `json:"mx_records,omitempty"`
	// IsParked indicates whether the domain appears to be parked,
	// based on known parking nameservers and IP addresses.
	IsParked bool `json:"is_parked"`
}

DomainCheck holds the results of checking a domain's registration and DNS status.

func Check

func Check(ctx context.Context, domain string, opts ...CheckOption) (*DomainCheck, error)

Check queries DNS and WHOIS to determine whether a domain is registered, who owns it, and what A and MX records it has.

Registration is determined by the presence of NS records, which is the most reliable signal — a domain has NS records if and only if it has been delegated by the registry. WHOIS is queried separately to extract the registrant owner, with automatic referral following for richer results.

By default, all checks are performed. Use the variadic options to selectively enable specific checks (e.g., Check(ctx, domain, sqarol.WithDNS())) to only check DNS records.

It uses the provided context for cancellation and timeouts.

type MXRecord added in v0.1.1

type MXRecord struct {
	// Host is the MX hostname (without trailing dot).
	Host string `json:"host"`
	// Pref is the MX preference value (lower = higher priority).
	Pref uint16 `json:"pref"`
	// IPs contains the resolved IPv4 addresses of the MX host.
	IPs []string `json:"ips,omitempty"`
}

MXRecord holds a resolved mail exchange record.

type Variation

type Variation struct {
	// Original is the normalized input domain.
	Original string `json:"original"`
	// Variant is the generated typo-squatted domain.
	Variant string `json:"variant"`
	// Kind identifies the algorithm that produced this variation.
	Kind string `json:"kind"`
	// Distance is the Levenshtein distance between Original and Variant.
	Distance int `json:"distance"`
	// Effectiveness is a score from 0.0 to 1.0 representing how hard
	// it is for a human to visually detect the difference. Higher values
	// mean the variation is more deceptive.
	Effectiveness float64 `json:"effectiveness"`
}

Variation represents a single domain variation produced by one of the fuzzing algorithms.

func Generate

func Generate(domain string) ([]Variation, error)

Generate normalizes the given domain and runs every registered fuzzing algorithm on it, returning all produced variations or an error if the domain is not valid.

Directories

Path Synopsis
cmd
sqarol command
sqarol is a command-line tool for domain typosquatting analysis.
sqarol is a command-line tool for domain typosquatting analysis.
Package generators provides domain typosquatting variation algorithms.
Package generators provides domain typosquatting variation algorithms.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL