DEV Community

Don Johnson
Don Johnson Subscriber

Posted on AI-assisted

Someone Spammed My DEV Post. I Traced It to a Wombat.

A weary wombat running a spam operation from a basement server desk

Illustration generated for this article. Every prop is a finding: the sack of blank name badges is the Faker persona namespace, the rubber stamp is the inert tracking parameter, the three coins are the break-even, and the red yarn connects nothing because attribution failed.

TL;DR — A spam comment on my article led to a TinyURL, a throwaway .store domain, and finally a legitimate SaaS product with an affiliate code stapled to it. No malware, no cloaking, no exploit. The account that posted it has a name generated by faker.js and a 19th-century engraving of a wombat for a face. I costed the whole operation out: three signups a year pays for it. That's why it will never stop.

The full redirect chain, from DEV comment to affiliate link

Full evidence, raw captures and a reproduce script: github.com/copyleftdev/dev-to-comment-hustle


Act I: The comment

I published Migrating Legacy LLM Infrastructure to an AI Gateway on September 1st. Eight days later, underneath two thoughtful comments about shared-key blast radius and provider failover, this appeared:

Stop wasting time applying manually
Let AI handle your job applications every single day
Increase your chances of getting interviews fast

tinyurl.com/36nsecn5

No punctuation. No engagement with the post. A shortener.

It's funny in the way all low-effort spam is funny — it's not even trying. But a shortener is a closed door, and I have a shell. Let's open it, then let's go find who knocked.


Act II: Where the link goes

Never click. Ask for headers and refuse the redirect:

curl -sS -I 'https://tinyurl.com/36nsecn5'
Enter fullscreen mode Exit fullscreen mode
HTTP/2 301
location: https://zenviapro.store/massapply?whose=yahoo
x-robots-tag: noindex
Enter fullscreen mode Exit fullscreen mode

zenviapro.store. A route called /massapply, and a whose=yahoo parameter that looks like campaign segmentation. Follow it all the way:

curl -sSL -D - -o /dev/null 'https://zenviapro.store/massapply?whose=yahoo' | grep -iE '^HTTP/|^location:'
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 302 Found
Location: https://loopcv.pro/?via=md
HTTP/2 301
location: https://www.loopcv.pro/?via=md
HTTP/2 200
Enter fullscreen mode Exit fullscreen mode

And there it is.

LoopCV. A real, functioning job-application-automation SaaS. Not a phishing kit. Not a credential harvester. A product you can buy with a credit card — with ?via=md on the end.

?via= is Rewardful's referral parameter, and LoopCV's own affiliate page points registrations at loopcv.getrewardful.com. So md is somebody's affiliate token, and every person who clicks that comment and later subscribes puts money in a stranger's pocket.

This is not a malware campaign. It's affiliate marketing with the manners removed.


Act III: The infrastructure is held together with tape

The sloppiness is the signal.

It's a stock Express app with the wrapper still on

HTTP/1.1 302 Found
X-Powered-By: Express
Content-Type: text/plain; charset=utf-8
Content-Length: 48
Enter fullscreen mode Exit fullscreen mode
Found. Redirecting to https://loopcv.pro/?via=md
Enter fullscreen mode Exit fullscreen mode

Found. Redirecting to is the literal default body of Express's res.redirect(). X-Powered-By: Express is the header every hardening guide tells you to strip in the first five minutes. Neither was touched. Ask for anything else and you get the stock 404:

curl -sS 'https://zenviapro.store/'
# <html><head><title>Error</title></head><body><pre>Cannot GET /</pre></body></html>
Enter fullscreen mode Exit fullscreen mode

There is no website here. No landing page, no cloaked content, no fake blog. One domain, one route, about ninety seconds of JavaScript.

The tracking parameter is a prop

whose=yahoo looks like segmentation. Which list? Which provider? Let's ask:

for w in yahoo gmail outlook devto reddit '' XXtest; do
  printf '%-8s -> ' "${w:-<empty>}"
  curl -sS -o /dev/null -w '%{http_code} %{redirect_url}\n' \
    "https://zenviapro.store/massapply?whose=$w"
done
Enter fullscreen mode Exit fullscreen mode
yahoo    -> 302 https://loopcv.pro/?via=md
gmail    -> 302 https://loopcv.pro/?via=md
outlook  -> 302 https://loopcv.pro/?via=md
devto    -> 302 https://loopcv.pro/?via=md
reddit   -> 302 https://loopcv.pro/?via=md
<empty>  -> 302 https://loopcv.pro/?via=md
XXtest   -> 302 https://loopcv.pro/?via=md
Enter fullscreen mode Exit fullscreen mode

Inert. Every value routes identically. It splits no traffic and sub-tags nothing. The one piece of the URL that looks like operational sophistication is decoration.

There is no cloaking whatsoever

Real malicious redirectors fingerprint you — benign page for the researcher, payload for the victim. This one returns the same 302 to Googlebot, to curl, to an iPhone, and to a request with no User-Agent at all.

No cloaking is itself a finding. This operator has no threat model, because nothing in the chain is illegal. It's a terms-of-service violation wearing a trench coat.

The mail server is the real pivot

dig +short zenviapro.store MX
# 10 mail.beeservices.shop.
Enter fullscreen mode Exit fullscreen mode

The mail exchanger lives on a different domain — which resolves right back to the same box:

zenviapro.store        A   50.114.206.36
mail.beeservices.shop  A   50.114.206.36
beeservices.shop       A   (nothing)
Enter fullscreen mode Exit fullscreen mode

beeservices.shop has no A record and no certificate in Certificate Transparency, ever. It's a mail-only domain. One box, two domains, two roles — that's a portfolio, not a one-off.

And the box is listening. Port 80 closed; 443 and 25 open:

220 mail.zenviapro.store ESMTP
250-PIPELINING
250-8BITMIME
250 SMTPUTF8
Enter fullscreen mode Exit fullscreen mode

No STARTTLS. No AUTH. A minimal MTA that does nothing but move mail in cleartext.

One box, two domains: the mail server is the pivot

The stale HELO is a fingerprint

My favourite detail. The banner announces mail.zenviapro.store.

dig +short mail.zenviapro.store A
# (nothing)
Enter fullscreen mode Exit fullscreen mode

That hostname does not resolve. It's a leftover from an earlier config, before the MX was swapped to beeservices.shop. They rebuild the domains; they don't rebuild the server. A banner that disagrees with DNS is a durable pivot you can hunt across an entire fleet.


Act IV: Who posted it

This is the part I actually enjoyed.

DEV has a public API, so the commenter isn't a mystery:

curl -s 'https://dev.to/api/comments?a_id=4547359' | jq -r '.[].user.username'
Enter fullscreen mode Exit fullscreen mode
max_quimby
mudassirworks
jaylonstiedemannterry78-993
Enter fullscreen mode Exit fullscreen mode

Meet jaylonstiedemannterry78-993, display name Jaylon_Stiedemann-Terry78, user ID 3748129, joined February 2, 2026.

The profile is a vacuum:

{
  "username": "jaylonstiedemannterry78-993",
  "name": "Jaylon_Stiedemann-Terry78",
  "joined_at": "Feb  2, 2026",
  "summary": null, "location": null, "website_url": null,
  "twitter_username": null, "github_username": null
}
Enter fullscreen mode Exit fullscreen mode

Zero published articles. No bio, no location, no links, no socials. Seven months of membership and a single comment to show for it.

A note on durability. If DEV removes this account — which it may well do — the profile
lookup above starts returning 404 and the account page goes dead. That doesn't retract
anything: the raw captures are committed in the
evidence repo,
checksummed, and dated. Read a 404 as the platform doing its job, not as a claim being
withdrawn. The infrastructure findings are independent of the account either way, and the
pattern — a Faker-templated name, an empty profile, an aged dormant account — outlives any
single username.

The name is machine-generated, and I can prove it

Faker token decomposition and the zero-hyphen proof

Say Jaylon_Stiedemann-Terry78 out loud. Something's off — Stiedemann-Terry is a double-barrelled surname that doesn't sound like a family, it sounds like a draw.

It is. Those tokens come straight out of Faker, the library every developer on earth uses to generate fake test data. Let's check the actual source:

curl -sO https://raw.githubusercontent.com/faker-js/faker/next/src/locales/en/person/last_name.ts
curl -sO https://raw.githubusercontent.com/faker-js/faker/next/src/locales/en/person/first_name.ts

grep -n "'Stiedemann'" last_name.ts   # 408:    'Stiedemann',
grep -n "'Terry'"       last_name.ts  # 417:    'Terry',
grep -n "'Jaylon'"      first_name.ts # 2514:    'Jaylon',
Enter fullscreen mode Exit fullscreen mode

All three. Jaylon from the first-name list, Stiedemann and Terry both from the surname list.

And here's the kicker:

grep -c -- '-' last_name.ts   # 0
Enter fullscreen mode Exit fullscreen mode

Faker's 466-entry English surname list contains zero hyphens. So Stiedemann-Terry isn't one surname from the list — it's two independent draws that the operator joined with a hyphen. This isn't stock faker.internet.username(). It's a custom template:

{firstName}_{lastName}-{lastName}{2 digits}
Enter fullscreen mode Exit fullscreen mode

Which means we can size their supply:

3,185 first names × 466 surnames × 466 surnames × 100
= 69,164,186,000
Enter fullscreen mode Exit fullscreen mode

Sixty-nine billion distinct personas. Roughly eight per human being alive. They will never run out of names, and no blocklist of usernames will ever catch up.

(One thing I checked so I wouldn't over-claim: the un-suffixed jaylonstiedemannterry78 returns a 404 — nobody has it. So the -993 is DEV's own username normalization, not evidence of a prior collision.)

The avatar is a wombat

The profile image is a 400×400 PNG, 8-bit grayscale-plus-alpha, stripped of all metadata. I downloaded it expecting a GAN face or a default monogram.

It is a Victorian-era scientific engraving of a wombat.

Not a stock photo of a person. Not an AI-generated headshot. A piece of public-domain 19th-century natural-history line art of a stout Australian marsupial, serving as the face of a fake job-spam persona. Whoever built this pipeline wired the avatar slot to some public-domain clipart source and never looked at the output.

I want to be precise about something: there is no real person here to name. The name is provably synthetic, the face is a public-domain animal illustration, and the profile is empty. That's not me protecting anyone's privacy — it's the finding.

The timeline says something the Express config doesn't

Provisioning timeline: six days apart, then 184 days dormant

Line the dates up:

Date Event Δ
2026-01-27 zenviapro.store registered (Namecheap)
2026-02-02 DEV account created +6 days
2026-07-30 First TLS certificate issued +184 days
2026-09-09 Spam comment posted +41 days

The domain and the account were provisioned six days apart — same procurement burst. Then both sat completely dormant for six months before the certificate was issued and the thing went live.

That's aged-asset tradecraft. New domains and new accounts trip reputation heuristics; seven-month-old ones don't. And it sits in genuine tension with everything in Act III: sloppy at the application layer, disciplined at the account-aging layer.

Which makes sense once you think about who this is. Aging assets doesn't take skill. It takes patience, and a calendar. Stripping X-Powered-By takes knowing what it is.

It wasn't a blast

I swept the comments on all 30 of my published articles for the pattern:

for id in $(jq -r '.[]|select(.comments_count>0)|.id' mine.json); do
  curl -s "https://dev.to/api/comments?a_id=$id" \
  | jq -r '.. | objects | select(has("id_code"))
      | ((.body_html // "") | gsub("<[^>]*>";"")) as $t
      | select($t | test("tinyurl|applying manually|job application";"i"))
      | "HIT \(.id_code) @\(.user.username)"'
done
Enter fullscreen mode Exit fullscreen mode

Exactly one hit. Not a shotgun across my whole back catalogue — one comment, on one post, eight days after it went up. Whether that's targeting or just a slow drip, I can't tell from one sample. But it isn't volume.


Act V: I went looking for them on GitHub. That search is the finding.

Affiliate spammers leave GitHub artifacts more often than you'd think, because GitHub repos rank well and a repo full of referral links is free SEO. So I went hunting with gh.

Start with the direct IOCs

for q in '36nsecn5' '50.114.206.36' 'zenviapro.store' 'beeservices.shop'; do
  gh search code "$q" --limit 5
done
Enter fullscreen mode Exit fullscreen mode

Zero hits. All four. The TinyURL slug, the origin IP, the redirector domain, the mail domain — none of it appears anywhere in GitHub's index. The operator has published nothing.

Then check whether anyone else has flagged them

PhishDestroy maintains destroylist, a curated blocklist of phishing and scam domains. I pulled the whole thing:

curl -sL .../destroylist/HEAD/list.txt -o dl.txt
wc -l dl.txt        # 202659
grep -ixc 'zenviapro.store'  dl.txt   # 0
grep -ixc 'beeservices.shop' dl.txt   # 0
Enter fullscreen mode Exit fullscreen mode

202,659 curated malicious domains, and ours isn't one of them. Combined with the NOT_OBSERVED reputation verdict, that's now two independent sources agreeing: nobody is tracking this, because by every technical definition there's nothing to track.

The lead that looked incredible and wasn't

The ruled-out zenvia*.info cluster

Here's where I nearly fooled myself, so I'm showing my work.

Searching zenviapro turned up a hit in phishdestroy/namesilo-evidence — a registrar-abuse investigation filed with ICANN. In a list of flagged NameSilo domains:

zenviaetc.info
zenviahub.info
zenviapro.info     ← same second-level label as ours
zenvias.info
zenviatime.info
zenviazone.info
Enter fullscreen mode Exit fullscreen mode

A six-domain family, same generation pattern, one of them sharing our exact label. My pulse went up. And they really are one operator — all six resolve through the identical Cloudflare nameserver pair:

zenviapro.info    asa.ns.cloudflare.com  harley.ns.cloudflare.com
zenviahub.info    asa.ns.cloudflare.com  harley.ns.cloudflare.com
zenviaetc.info    asa.ns.cloudflare.com  harley.ns.cloudflare.com
... all six identical
Enter fullscreen mode Exit fullscreen mode

But it isn't our operator, and the contrast kills it:

zenvia*.info cluster zenviapro.store (ours)
Registrar NameSilo Namecheap
DNS Cloudflare (asa/harley) registrar-servers.com
Hosting Cloudflare proxy Linveo direct, no proxy
TLD .info × 6 .store

Nothing shared but six letters. Both are almost certainly riding the name of Zenvia, a real Brazilian CPaaS company — which is exactly why the label collides. Two unrelated operators reaching for the same brandable string.

A matching name is not a matching operator. If I'd stopped at the grep I'd have published a confident, wrong attribution to a phishing cluster that has nothing to do with this.

What GitHub did give me

The affiliate ecosystem, in the open. LoopCV referral links are scattered across GitHub in exactly the SEO-backlink genre I expected:

Repo Token
Ramas68/LoopCV-Promo-Codes — *"LoopCV Promo Codes \ 50% Off Discount"*
diaodiaozhuye/awesome-ai-startups ?via=toolify
heukshow/aicity-os ?via=sang-kwon

Look at those tokens. abdul. toolify. sang-kwon. A first name, a company, a full handle. The affiliates who promote LoopCV in public sign their work.

Ours is md. Two characters, no name, nothing to search. That's the only genuinely deliberate piece of operational security in this entire campaign — and it's not on the server, the domain, or the account. It's on the one string that would have led back to a person.

So: no attribution. And the shape of the failure is the story. Someone who leaves X-Powered-By: Express on, ships a fake tracking parameter, and picks a wombat for an avatar still knew to make the payout token anonymous. They didn't secure the operation. They secured the part that gets paid.


Act VI: The economics, which are the actual vulnerability

Exact interval arithmetic: break-even at 0.06-2.4 conversions per year

Stop thinking like a defender and think like the operator.

LoopCV's affiliate program pays 25% commission. Publicly reported figures put subscriptions at $50–$200 with customers staying 6–12 months. Exact interval arithmetic, gross revenue per referred customer:

[$50, $200] × [6, 12] = [$300, $2,400]
Enter fullscreen mode Exit fullscreen mode

At 25%:

[$300, $2,400] ÷ 4 = [$75, $600]     ← commission per conversion
Enter fullscreen mode Exit fullscreen mode

Cost side: a .store domain plus a year of budget hosting — call it $38–$180 all-in. Break-even:

[$38, $180] ÷ [$75, $600] = [19/300, 12/5] = [0.06, 2.4]
Enter fullscreen mode Exit fullscreen mode

Between one-sixteenth of a signup and three signups per year covers the entire operation.

That's the whole thesis. Nothing here needs to work well. The copy can be terrible. The tracking parameter can be fake. The 404s can leak the framework. The HELO can be stale. The mascot can be a wombat. Three conversions and the year is paid for, and everything after that is margin on infrastructure that costs less than lunch.

You cannot out-moderate that math.


Reputation feeds have nothing, and they're right

I ran the origin IP through an offline reputation lens:

{
  "target": { "type": "ip", "value": "50.114.206.36" },
  "verdict": { "disposition": "unknown", "confidence": 0.0,
               "reason_codes": ["NOT_OBSERVED"] },
  "observations": []
}
Enter fullscreen mode Exit fullscreen mode

Not observed. No feed has it — and that's correct. It hosts no malware, no C2, no phishing. Threat intel is tuned for technical harm, and this campaign's harm is economic and reputational. It will sit below every threshold you own, forever.


Who the victim actually is

Not me. I scrolled past it.

LoopCV is the victim. A real company with a real product is having its brand welded to comment spam by an affiliate they've likely never spoken to. They eat the reputational damage, they pay commission on the conversions, and the operator's total exposure is a $12 domain and a wombat.

To be explicit, because it matters: I found no evidence that LoopCV is running or is aware of this campaign. Open affiliate programs get abused; that's the risk of the model. The fix belongs to the vendor:

  • Require affiliates to declare traffic sources, and enforce it
  • Ban unsolicited comment/forum posting in the program terms, in writing
  • Flag referral tokens whose traffic arrives overwhelmingly via shorteners with no referrer
  • Kill tokens on abuse reports — fast, without requiring a lawyer

?via=md is a token. Tokens can be revoked. That's a one-line fix that permanently ends this specific campaign, and exactly one party can perform it.


IOCs

Campaign infrastructure — safe to blocklist:

Indicator Type Notes
tinyurl.com/36nsecn5 URL Shortener entry point
zenviapro.store Domain Redirector; Namecheap; created 2026-01-27
https://zenviapro.store/massapply URL Only live route
beeservices.shop Domain Mail-only sibling; no A record, no CT history
mail.beeservices.shop Hostname MX for zenviapro.store
50.114.206.36 IPv4 Origin; 443 + 25 open, 80 closed
AS62564 / oh2.linveo.com ASN / rDNS Linveo, Ohio
jaylonstiedemannterry78-993 DEV account user_id 3748129; joined 2026-02-02; 0 articles

Behavioural signatures — this is what actually generalises:

Signature Value
Affiliate token via=md (Rewardful format)
Inert campaign param whose=<anything> — routing-neutral
Server fingerprint X-Powered-By: Express + body Found. Redirecting to
Root response Cannot GET / (Express default 404)
SMTP banner 220 mail.zenviapro.store ESMTPdoes not resolve
SMTP capabilities No STARTTLS, no AUTH
SPF v=spf1 mx ip4:50.114.206.36 ~all
DMARC Absent on both domains
Persona template {firstName}_{lastName}-{lastName}{2 digits}, all tokens ∈ Faker en
Avatar class Public-domain engraving, grayscale+alpha PNG, metadata stripped
Provisioning pattern Domain + account within 7 days, then ~6 months dormant

⚠️ loopcv.pro is NOT an indicator of compromise. It's a legitimate destination being abused by a third-party affiliate. Do not blocklist it. Blocklisting the victim is how threat intel gets a bad name.


What to actually do

If you run a comment platform: the highest-signal feature isn't the text, it's the shape. Zero-engagement first comments containing a shortener, from accounts with no posts and an empty profile, are trivially clusterable. Resolve shorteners server-side at submission time and score the destination. And the persona template is a gift — a name whose tokens all appear in Faker's en locale, with a structure Faker itself doesn't emit, is close to a free classifier feature.

If you write on DEV: don't click, and don't just delete. curl -I takes four seconds. Then report the destination to the vendor, not the comment to the platform. The platform can remove one comment; the vendor can revoke the token behind all of them.

If you run an affiliate program: you are one unsupervised token away from your brand appearing under a headline like this one. Read your traffic sources.


The takeaway

I went in expecting a dropper. I found four lines of Express, a fake tracking parameter, a name drawn from a test-data library, and a 19th-century wombat.

That's the uncomfortable part. The most durable spam on the internet isn't sophisticated — it's cheap and legal. There's no CVE here, no payload to reverse, no C2 to sinkhole. Every traditional defensive tool I own returns NOT_OBSERVED, and every one of them is right to.

The economics are the vulnerability. Three conversions a year, and the whole thing pays for itself forever.

The wombat is just a bonus.


All findings are from passive reconnaissance — DNS, WHOIS, Certificate Transparency, HTTP headers, a TCP banner grab, and DEV's own public API — against infrastructure and accounts the operator published for public consumption. No systems were accessed, no credentials used, nothing exploited.

Top comments (1)

Collapse
 
gnomeman4201 profile image
GnomeMan4201

Really solid investigation, especially the fact that you documented the false attribution lead instead of quietly dropping it.

One small evidentiary distinction I’d make: showing that Jaylon, Stiedemann, and Terry all exist in Faker datasets demonstrates that the username is consistent with automated generation, but it doesn’t quite prove Faker provenance from a single account.

That argument would get much stronger if you found multiple accounts using the same {firstName}_{lastName}-{lastName}{NN} structure with tokens mapping back to the same locale or generator family.

That aside, the preservation, redirect tracing, IOC work, and disconfirmation section are unusually well done for a spam investigation.