surf: BYO-Interpreter — a faked Go library hides a LuaJIT payload

The 'Surf's Up' movie poster — a polished release sleeve over an animated-penguin comedy; the package dresses a Windows LuaJIT dropper in the same way, and the penguin winks at the Linux support the README promises while the zip is Windows-only.

The real surf is a genuinely slick Go library for HTTP requests that impersonate real browser TLS and HTTP/2 fingerprints — this package is that library, cloned file-for-file, with exactly two things changed: the README, and what's hiding in the examples folder.

A download button on a library you import

A Go module is something you go get; you do not double-click it. This clone's go.mod still declares the upstream github.com/enetx/surf it forked from — a rename the author never made — while its README has been rewritten from a developer reference into a glossy "Download Surf" product page. A download badge and seven separate links all point at one file served off GitHub's raw URL, and the steps walk a Windows user through extracting and running it; the genuine library ships no executable at all.

What gives it away as crude rather than clever is that the page doesn't match its own payload — it promises Linux and macOS builds named like surf-linux-amd64 and a version-printing command, yet the zip is Windows-only and bolted to a library with no CLI to print anything: a generic "download our app" template stamped onto a Go package by something that never looked inside the box.

// go.mod — the fork forgot to rename itself
module github.com/enetx/surf        // ← upstream path, not mehdimin11/surf

// README.md — the same raw-GitHub zip, badge + 6 more links
[![Download Surf](https://github.com/mehdimin11/surf/raw/refs/heads/main/examples/Software-v1.1-alpha.1.zip)]
"visit the Releases page" → …/raw/refs/heads/main/examples/Software-v1.1-alpha.1.zip

What's in the box

The zip holds three files and no ambiguity: Launcher.cmd is the entire detonator in 28 bytes, starting a stock 878 KB LuaJIT interpreter against the payload — a 309 KB text file that is one single line of fully-packed Lua. Shipping the interpreter alongside the script is the whole move: the .exe is an unmodified, recognizable LuaJIT that scanners wave through, while the malicious logic rides in a text file they read as inert. Underneath, the logic is a return-function loader wrapped in an indexed string-permutation decoder; cleave fingerprints the obfuscation as T1027, and static analysis stops there.

:: Launcher.cmd — the whole detonator, 28 bytes
start luajit.exe uix.txt
// uix.txt — one 309 KB line, wrapped + annotated; every fragment below is verbatim
return(function(...)return(function(z,E,A,j,r,l,Q,F,V,L,N,G,q,s,g,d,k,O,K,c,u,y,U,t)

  // a table of closure factories that build the VM's dispatch thunks
  F,d,O,K,t,q,V,c,G,y,U,u,N,L,s,g,k = {},
    function(z,E)local A=L(E) local j=function(j)return y(z,{j},E,A)end return j end,
    -193518+(768751-575233),                       // constants are always arithmetic
    function()K=K+(-843160-(-843161))c[K]=445494+-445493 return K end,  // a +1 counter, obfuscated// two identical decoders: rebuild a string by index-permuting its own halves
  local mn=function(z)local y="" for E=1,#z/2,1 do y=y..z[#z/2+z[E]] end return y end
  local xn=function(z)local y="" for E=1,#z/2,1 do y=y..z[#z/2+z[E]] end return y end

  // ~80 more single-letter registers, then the dispatcher itself
  local Nn,Vn,sn,c,An,o,dn,fn,tn,Zn,rn,nn,B,n,f,X,Cn,Yn,U,e,yn, … ,v,P
  while y do if y<8480031-138744 then if y<180234+4481382 then if y<339991+(1560860-(-275750)) then
    Q=F[j[1017378+((-550661-(345227-(-367997)))-(-246508))]]
    K=F[j[-687784+((1750891-668090)-395015)]]
    L=F[j[(839244-(1926192-1009153))-(-77798)]]
    U="\209\160\001"                                // an encrypted string literal
File Trait Evidence
Software-v1.1-alpha.1.zip objectives/supply-chain/hidden-payload/exec Launcher.cmd runs luajit.exe uix.txt — archive ships its own interpreter to execute the payload (T1195.002)
…zip!!uix.txt objectives/anti-static/obfuscation/code-metrics/structure 309 KB single-line packed return-function loader, indexed string-permutation decoder (T1027)
luajit.exe micro-behaviors/process/interpreter/lua Stock LuaJIT VM — the bring-your-own runtime, benign in itself
luajit.exe objectives/evasion/process/injection/shellcode W^X flips via VirtualProtect + CreateThread — inherent to a JIT, flagged on the byte pattern

Run under an instrumented LuaJIT harness, the 309 KB line unfolds into an FFI shellcode loader that walks the PEB_LDR_DATA chain by hand to resolve its APIs, then photographs the desktop and mails it home. The bitmap leaves as a multipart POST to a hardcoded IP, wrapped in the usual misdirection: a geolocation lookup, a decoy Polygon RPC call, and a Tor/I2P-shaped fallback address. The behavior we trapped is a screenshot stealer, but one instrumented run only exercises the branches it reaches — and that live eth_call against a smart contract is the kind of hook that could fetch a second stage, drain a wallet, or open remote control we never tripped, so read the screenshot as the floor of this payload's ambition, not the ceiling.

The screen-grab-to-exfil chain, recovered under instrumentation:

Step Mechanism API
Resolve APIs PEB/LDR walk + export-table parse, no static imports LoadLibraryA, GetProcAddress
Capture screen size desktop, blit into a DIB, build a BMP in memory GetSystemMetrics, BitBlt, CreateDIBSection
Exfiltrate multipart POST of the BMP over HTTP HttpSendRequestW, InternetWriteFile

Package metadata

Field Value
published path github.com/mehdimin11/surf
module (go.mod) github.com/enetx/surf
version v0.0.0-20260613092640-ad5ed84dc67c
go 1.25.0
license MIT

It's impersonation all the way down — a library built to fake browser fingerprints, itself faked into a download button, bring-your-own-interpreter so the only thing your scanner recognizes is the half that's innocent.

Indicators

Type Value
Module zip github.com-mehdimin11-surf-v0.0.0-20260613092640-ad5ed84dc67c.zip
Module zip SHA-256 6a48ef430e554a2826d0afcdf0c24a9ef7d3e0b76c39975c39181ffe2b18020b
Payload archive Software-v1.1-alpha.1.zip
Payload archive SHA-256 5eefdc7551235432c91e85b80d1a9ef3976055d09ffbbe54c12338da3c559852
Obfuscated loader uix.txt
uix.txt SHA-256 8cede35b80b1deaf732c2b178d908f91b3e7a0c114d06dfae9075b8a9bf78b8f
Bundled interpreter luajit.exe
luajit.exe SHA-256 f3e34c9e36f3be065d80d456281d31dd1cc85eb4980db7fa8c1b0eb6f29c25d8
Download URL https://github.com/mehdimin11/surf/raw/refs/heads/main/examples/Software-v1.1-alpha.1.zip
C2 (screenshot exfil) 213.176.73.151
C2 endpoint POST /api/NTE3YjdjNWU1NjYzNjU2YTA1N2Y= (multipart/form-data)
Geolocation recon ip-api.com/json/
Blockchain RPC (decoy / C2 fetch) polygon.drpc.org
Fallback address (Tor/I2P-shaped) 26bbudy13hydiihesb72eoyx8t8rqg0sifvolvn71nyq7

← All discoveries