Este artículo detalla el proceso de ingeniería inversa para desencriptar los datos devueltos por la API de weibotop.cn, específicamente el endpoint https://api.weibotop.cn/currentitems. La respuesta de este endpoint se presenta como una cadena de texto cifrada que requiere un proceso de descifrado específico para acceder a la información subyacente.
Proceso de Análisis y Descubrimiento
1. Bypassing el Debugger del Navegador
Al acceder a la URL principla (https://www.weibotop.cn/2.0/) y abrir las herramientas de desarrollador, la página a menudo entra en modo de depuración de forma automática. Para evitar esto y facilitar la inspección del tráfico de red, se puede desactivar el punto de interrupción del depurador haciendo clic derecho sobre la línea de código donde se detiene y seleccionando la opción "Nunca pausar aquí" o "Never pause here". Tras recargar la página, el navegador ya no se detendrá en ese punto, permitiendo una captura de paquetes más fluida.
2. Identificación de la Carga Útil Cifrada
El objetivo principal es el endpoint currentitems. Al inspeccionar las solicitudes de red, se observa que la respuesta a esta solicitud es una cadena cifrada. El siguiente paso es localizar la lógica de descifrado dentro del código JavaScript del sitio.
A diferencia de otros casos donde se podría buscar en interceptors (patrón común en frameworks como Axios), en esta ocasión esa búsqueda no arrojó resultados relevantes. La estrategia alternativa fue buscar la URL del endpoint (currentitems) directamente en el código fuente de la página, lo que condujo a una única ocurrencia relevante.
3. Localización del Algoritmo de Cifrado/Descifrado
Navegando al contexto de la función donde se encontró la referencia a currentitems, se pudo identificar rápidamente el código responsable del cifrado y descifrado de los datos. Se trata de una implementación del algoritmo AES (Advanced Encryption Standard).
4. Determinación de Parámetros AES (Key, Mode, Padding)
Para descifrar los datos, es fundamental identificar la clave (key), el modo de operación (mode) y el esquema de relleno (padding). Se estableció un punto de interrupción en la sección de código encargada del descifrado y se recargó la página para depurar paso a paso:
- La cadena cifrada inicial se asigna a una variable temporal.
- Esta cadena se decodifica de Base64 utilizando
CryptoJS.enc.Base64.parse(). - La clave de descifrado se obtiene de una variable predefinida. Se descubrió que esta clave se genera a partir del valor SHA1 de una cadena fija ("tSdGtmwh49BcR1irt18mxG41dGsBuGKS"), de la cual se toman los primeros 32 caracteres en formato hexadecimal.
- El modo de operación se identifica como
CryptoJS.mode.ECB(Electronic Codebook). - El esquema de relleno es
CryptoJS.pad.Pkcs7. - No se utiliza un IV (Initialization Vector) explícito, lo cual es coherente con el modo ECB.
La estructura de la llamada a la función de descifrado en JavaScript era la siguiente:
n.AES.decrypt({
ciphertext: o // Datos cifrados en Base64
}, r, { // Clave AES
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8)
Integración de la Lógica de Descifrado en Python
Inicialmente, se intentó replicar el proceso de descifrado directamente en Python. Sin embargo, se encontraron inconsistencias en la decodificación Base64 entre la implementación de JavaScript (crypto-js) y las librerías estándar de Python, lo que impedía un descifrado exitoso. La solución más robusta y directa fue ejecutar el código JavaScript de descifrado directamente desde Python.
Código JavaScript para Descifrado
Se extrajo el código JavaScript relevante y se adaptó para ser reutilizable. Este script define la generación de la clave SHA1 y una función que toma la cadena cifrada y devuelve los datos descifrados en formatto JSON.
var CryptoJS = require("crypto-js");
// Derivación de la clave AES
let claveSHA1 = CryptoJS.SHA1(CryptoJS.enc.Utf8.parse("tSdGtmwh49BcR1irt18mxG41dGsBuGKS"));
let claveAES = CryptoJS.enc.Hex.parse(claveSHA1.toString(CryptoJS.enc.Hex).substring(0, 32));
/**
* Función para descifrar datos cifrados en Base64 utilizando AES (ECB, Pkcs7).
* @param {string} textoCifradoBase64 - La cadena cifrada en formato Base64.
* @returns {object} - Los datos descifrados como un objeto JSON.
*/
function descifrarDatos(textoCifradoBase64) {
let bytesCifrados = CryptoJS.enc.Base64.parse(textoCifradoBase64);
let datosDescifrados = CryptoJS.AES.decrypt({
ciphertext: bytesCifrados
}, claveAES, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
return JSON.parse(datosDescifrados);
}
Código Python para la Obtención y Descifrado de Datos
El script de Python utiliza la librería requests para obtener la respuesta cifrada y execjs para ejecutar el código JavaScript y realizar el descifrado. Se incluye una configuración para manejar correctamente la codificación en los procesos secundarios de subprocess.
from functools import partial
import subprocess
import requests
import execjs
# Configuración para asegurar la codificación UTF-8 en subprocess
subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
# URL del endpoint API
url_api = "https://api.weibotop.cn/currentitems"
# Realizar la solicitud HTTP para obtener los datos cifrados
cabeceras = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
}
respuesta_api = requests.get(url_api, headers=cabeceras)
texto_cifrado_respuesta = respuesta_api.text
# Cargar el código JavaScript desde un archivo
with open("desencriptador.js", mode="r", encoding="utf-8") as fichero_js:
codigo_js = fichero_js.read()
# Compilar y ejecutar el contexto JavaScript
contexto_js = execjs.compile(codigo_js)
# Llamar a la función JavaScript de descifrado
datos_desencriptados = contexto_js.call("descifrarDatos", texto_cifrado_respuesta)
# Imprimir los datos descifrados
print(datos_desencriptados)
Al ejecutar el script de Python, se obtiene la salida con los datos completamente descifrados, confirmando el éxito de la integración y el proceso de ingeniería inversa.