Después de haber obtenido la contraseña de ecorp_user de la tarea anterior, inicia sesión.

La aplicación usa una forma ligeramente diferente de encriptar la petición y la respuesta, como se muestra en el js /static/form-verify-submit.js.
Cómo Encripta los Datos la Aplicación Web
Antes de modificar la extensión de Burp, vamos a entender cómo el código JS encripta las peticiones de autentificación. Los componentes clave son:
- ROT13 Encoding
Antes de encriptar, el username y secret se ofuscan usando ROT13.
function rot13(message) { const originalAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const cipher = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"; return message.replace(/[a-z]/gi, letter => cipher[originalAlpha.indexOf(letter)]); }
- ROT13 cambia cada carácter 13 lugares en adelante, dando la vuelta si es necesario.
- Ejemplo:
ecorp_user-->rpebc_hfre0007(no tiene efecto porque ROT13 sólo afecta a letras)
- AES Key Generation
Se genera una clave AES aleatoria para cada petición:
const rawAesKey = window.crypto.getRandomValues(new Uint8Array(16)); const aesKey = await getSecretKey(rawAesKey);
getSecretKey()importa la clave aleatoria en modo AES-CBC.
- Encriptar las Credenciales
let rawdata = "username=" + rot13(formDataObj["username"]) + "&secret=" + rot13(formDataObj["secret"]); let data = window.btoa(String.fromCharCode(...new Uint8Array(await encryptMessage(aesKey, enc.encode(rawdata).buffer))));
- Las credenciales ROT13-encoded son:
"username=rpebc_hfre&secret=0007"
- Esta string es encriptada con AES usando la clave random y el valor IV fijado:
"0000000000000000". - Los bytes encriptados son base64-encodeados antes de ser mandados.
- Subir la Petición
body: "mac=" + encodeURIComponent(window.btoa(String.fromCharCode(...rawAesKey))) + "&data=" + encodeURIComponent(data)
mac=contiene la clave AES base64-encodeada.data=contiene las credenciales encriptadas encodeadas en base64.
Cómo Desencripta la Información Nuestra Extensión de Burp
Ahora que sabemos cómo la encriptación del lado del cliente funciona, vamos a examinar la extensión de Burp 102Burp, que intercepta, desencripta y analiza peticiones.
- Inicializar la Extensión de Burp
public class BurpExtender implements BurpExtension, HttpHandler { private MontoyaApi api; private static final String FIXED_IV = "0000000000000000"; // 16-byte IV private Logging logging; @Override public void initialize(MontoyaApi api) { this.api = api; this.logging = api.logging(); api.extension().setName("Burp Decryptor"); // Register the HTTP request handler api.http().registerHttpHandler(this); logging.logToOutput("Extension Loaded Successfully"); } }
Para analizar y desencriptar la petición y respuesta de la aplicación, la extensión de Burp debe interceptar la peticiones HTTP y extraer los valores necesarios antes de que lleguen al servidor. La extensión de Burp comienza la ejecución mediante el método initialize().
Esta función configura el nombre de la extensión a "Burp Decryptor" y registra un handler HTTP. Esto permite que la extensión modifique peticiones y respuestas mientras pasan por Burp.
- Interceptar las Peticiones de Autentificación
@Override public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) { String requestBody = requestToBeSent.body().toString(); // Extract MAC (AES key) and data (encrypted payload) Pattern macPattern = Pattern.compile("mac=([^&]+)"); Pattern dataPattern = Pattern.compile("data=([^&]+)"); Matcher macMatcher = macPattern.matcher(requestBody); Matcher dataMatcher = dataPattern.matcher(requestBody);
Una vez cargada la extensión, monitoriza las peticiones HTTP salientes. Cada petición interceptada es pasada al handleHttpRequestToBeSent(), donde es analizada en busca de la presencia de mac y de parámetros de datos. Estos parámetros contienen la clave AES y credenciales encriptadas.
Aquí, el cuerpo de la petición de recupera como string y se comprueba usando expresiones regulares para localizar la mac y los parámetros. Si esos parámetros son encontrados, son decodeados de base64.
if (macMatcher.find() && dataMatcher.find()) { try { byte[] aesKey = Base64.getDecoder().decode(URLDecoder.decode(macMatcher.group(1), StandardCharsets.UTF_8)); byte[] encryptedData = Base64.getDecoder().decode(URLDecoder.decode(dataMatcher.group(1), StandardCharsets.UTF_8));
La clave AES extraída de la mac está ahora en formato de byte, lista para usarse para la desencriptación. Las credenciales encriptadas, extraídas de información, son también decodeadas en base64 hacia el formato original de array de bytes.
- Desencriptar las Credenciales
// Decrypt AES-CBC encrypted data byte[] decryptedBytes = decryptAES(encryptedData, aesKey); String decryptedData = new String(decryptedBytes, StandardCharsets.UTF_8).trim();
Una vez que la clave AES y la información encriptada son extraídas, la extensión usa la función decryptAES() para invertir la encriptación. Esta usa AES-CBC con pading PKCS5 y el mismo IV que fue usado en el JS.
Se implementa de la siguiente manera:
private byte[] decryptAES(byte[] encryptedData, byte[] key) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec iv = new IvParameterSpec(FIXED_IV.getBytes(StandardCharsets.UTF_8)); SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey, iv); return cipher.doFinal(encryptedData); }
Usando la clave AES obtenida de la petición, esta función invierte exitosamente la encriptación aplicada en el lado del cliente, revelando las credenciales originales en formato encodeado ROT13.
- Invertir el ROT13 Encoding
Como el JavaScript original ofuscaba las credenciales usando ROT13 antes de la encriptación, debemos aplicar ROT13 de nuevo para restaurar los valores originales.
// Apply ROT13 decoding String firstPassRot13 = rot13(decryptedData); String finalDecryptedData = doubleDecodeParameterNames(firstPassRot13);
La función rot13() gira cada letra 13 lugares en el alfabeto, convirtiendo rpebc_hfre en ecorp_user y frperg en secret.
private String rot13(String text) { StringBuilder result = new StringBuilder(); for (char c : text.toCharArray()) { if (Character.isLetter(c)) { if (Character.isUpperCase(c)) { result.append((char) ('A' + (c - 'A' + 13) % 26)); } else { result.append((char) ('a' + (c - 'a' + 13) % 26)); } } else { result.append(c); } } return result.toString(); }
- Registrar la Información Desencriptada
Con las credenciales desencriptadas e invertidas exitosamente, son registradas en el log de eventos de Burp Suite.
// Log fully decoded request logging.logToOutput("\n===== [Decrypted Request] ====="); logging.logToOutput(finalDecryptedData); logging.logToOutput("========================================\n");
- Manejar las Respuestas del Servidor
Mientras la extensión de Burp intercepta y desencripta las peticiones de autentificación, también debe procesar y desencriptar las respuestas del servidor. Cuando la aplicación web recibe una petición de autentificación, procesa las credenciales otorgadas y devuelve la respuesta encriptada.
La respuesta del servidor sigue un patrón de encriptación similar al de la petición. En lugar de transmitir el resultado de la autentificación en texto plano, el servidor encripta la respuesta usando AES-CBC y el mismo IV y manda el parámetro result=. Como la clave AES no se incluye en la respuesta, la extensión de Burp debe recuperarla de la petición original.
Para manejar esto, la extensión usa el método handleHttpResponseReceived(), que es disparado cuando una petición HTTP para por Burp Suite:
@Override public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) { String responseBody = responseReceived.body().toString();
En este punto, la variable responseBody mantiene la respuesta encriptada del servidor como string. El siguiente paso es extraer el parámetro encriptado result= usando una expresión regular:
Pattern resultPattern = Pattern.compile("result=([^&]+)"); Matcher resultMatcher = resultPattern.matcher(responseBody);
Este patrón busca el parámetro result= y captura el valor base64-encodeado.
Sin embargo, antes de que la desencriptación tenga lugar, la extensión necesita recuperar la clave AES de la petición. Esto se hace accediendo al parámetro mac= de la petición:
if (resultMatcher.find()) { try { // Retrieve the AES key from the original request String requestBody = responseReceived.initiatingRequest().body().toString(); Pattern macPattern = Pattern.compile("mac=([^&]+)"); Matcher macMatcher = macPattern.matcher(requestBody); if (!macMatcher.find()) { logging.logToError("Could not retrieve AES key from request."); return ResponseReceivedAction.continueWith(responseReceived); } byte[] aesKey = Base64.getDecoder().decode(URLDecoder.decode(macMatcher.group(1), StandardCharsets.UTF_8));
La extensión extrae la clave AES encodeada en base64 de la petición original, la decodea, y marca un array de bytes. Si no se encuentra parámetro mac= la extensión registra un error y permite que la repuesta pase sin cambio.
Una vez que la clave AES es recuperada, la extensión desencripta el parámetro result=. Primero, la respuesta encriptada es extraída y decodeada de base64.
byte[] encryptedResponse = Base64.getDecoder().decode(URLDecoder.decode(resultMatcher.group(1), StandardCharsets.UTF_8));
Con esta información encriptada, la extensión llama a decryptAES(), función la cual sigue el mismo proceso AES-CBC usado en la desencriptación de la petición.
byte[] decryptedResponseBytes = decryptAES(encryptedResponse, aesKey); String decryptedResponse = new String(decryptedResponseBytes, StandardCharsets.UTF_8).trim();
Ahora que la respuesta encriptada ha sido desencriptada con éxito, la extensión comprueba si la respuesta fue más ofuscada usando ROT13. Si sí, aplica el decoding para obtener el mensaje original.
String finalDecryptedResponse = rot13(decryptedResponse);
Finalmente, la respuesta de autentificación desencriptada se registra en Burp Suite, permitiendo ver la respuesta pública del servidor.
logging.logToOutput("\n===== [Decrypted Response] ====="); logging.logToOutput(finalDecryptedResponse); logging.logToOutput("========================================\n");
Outcome
===== [Decrypted Request] ===== username=ecorp_user&secret=THM{SECRET_FROM_PREVIOUS_TASK} ======================================== ===== [Decrypted Response] ===== Welcome ecorp_user your secret is THM{****************************} ========================================
Después de conseguir desencriptar la respuesta al formulario, trata de cambiar el username de ecorp_user a ecopr_admin.

