Análisis de cifrado de Ruishu (documento de sentencia) js

El documento del árbitro que había sido capturado durante más de medio año se detuvo repentinamente. Después de revisar el registro, encontré que el código de respuesta HTTP era todo 202. La página de análisis encontró que el sitio web original estaba encriptado en la cookie. Después del análisis, el proceso de solicitud correcto es el siguiente:
1. Solicitar a la página de inicio, volver dos cabeceras de respuesta Set-Cookie, el nombre de la cookie es FSSBBIl1UgzbN7N80SFSSBBIl1UgzbN7N80Ty el código de respuesta es 202. Hay tres códigos desconocidos en el cuerpo de respuesta

 

 

<html> 
<head> 
<meta http-equiv = "Content-Type" content = "text / html; charset = utf-8"> 
</head> 
<! - 1. meta ['content'] 意义 不明 - ->
<Body> <meta name = contenido "procesador" = "webkit"> <id = contenido meta "9DhefwqGPrzGxEp9hPaoag" = "{{qqqqqr0.YUQeBgClraT6.dA28Ej3I1UKSbXlJ8NIUKxHc2UmGAqqkknLqqqqhvefgTey0JSvqkxJqkqLWqqqqk130r0HMvpQpmzDUlrUpmQ83oYJA2wDVVYY1mrWY0f3s2ptVlNp1aqqqqt1073741856VIJRJg9UB18SE49UPp8fH8h5lq2Y8psLYtXTCqAg2iGacfA3Tt9GOFqTs8GTGDCQwHGz5oKqQHAe4oO7h8mEBKcWqqqqhTrB4MbBgeoxqKGKR2zkG8iNtX0bwqfYF_uGc80qqqqr0qqDe5191aqqqqhb6rmecbj1Zrr4q WYozbAKrlMPE9mOVpEiZygKFg3hxX_u.SidW64uobWi227DF_MXET5u6NJI74gv.axiEcePMvHIL_qqqqYDUvHttHp6whZmAp7KOQtW} {tPgKp0pIU0AtpYJcsagEUlJps2JlmANpsGAEYVpUpYTioaNA1TxHIAGCFAr7xGAWwAl8k67pUumcIlQ.D6xnhSSgVfzZDmg4DfzoD6a5KcrDWfEURnTmlm9dRn2PWS7TAOwGmVRXKcx9JULM3rRrHpLfrkJrlK3DlPwMrl9cF0gtD07cUaLUV20v8YShrYWSMSRZD90l8OSvxbzc8saEAG | qqU7aWVE0A9yYlv7CMX9T4THtVhmE4UX0Fjfh_vIdsiNUgCd3JJSPa6hZsZfcgVuLJzRXgkkhAWrC4uoKF82V.vbXKITtyVsSpIJvy9.SY5zYeuUlWzJG_lkrJwYoaucRQQg6y96Ch5rsnbU71HJ.nkkD85xjOoscMWRbGCiHUXy74mDJRW00e0.UV8f7ZDsBQIzOfuDJYjeBdCbXMhlCyCjW8hzMPlbWR_w.LKKFwywal7445 "> <- [if lt IE 9]> <script> document.createElement! (" Sección ") </ script> <! [Endif] ->!
<! - 2. El archivo js con significado ambiguo -> 
<script type = "text / javascript" src = "4QbVtADbnLVIc / c.FxJzG50F.js% 3FD9PVtGL = e5191a"> </script> 
<script type = "text / javascript "> 
<! - 3. El código js de cifrado principal, omitido -> 
</script> 
<script type = 'text / javascript' r = 'm'> 
_ $ hr ('ZoA8'); 
</script> 
<! - 4. La función de este código es solicitar la página actual nuevamente y comentar la 
<script type = "text / javascript"> window.οnlοad = function () {var _ $ n6 = document.getElementById (_ $ fv ('ondoal')); _ $ nR (_ $ n6.nombre, _ $ vD (_ $ n6, _ $ zb ('HvzED2ExKa')));}; </script> 
-> 
</body> 
</html> 
<script type = "text / javascript"> _ $ e3 (); </script>

 

2. El navegador vuelve a solicitar la página web original con dos cookies, el código de respuesta es 200 y se obtiene la página normal. El valor de cookieFSSBBIl1UgzbN7N80T en esta solicitud es diferente del valor establecido en el encabezado de respuesta, y se actualizará automáticamente después de un período de tiempo. Además, modifique la respuesta y elimine Set-Cookie: FSSBBIl1UgzbN7N80T = ..., el navegador seguirá teniendo una cookie FSSBBIl1UgzbN7N80T en la nueva solicitud. Se supone que el valor de esta cookie lo calcula js y se actualizará periódicamente.

 

En otras palabras, simplemente descifre el algoritmo de la cookie FSSBBIl1UgzbN7N80T.

Además, algunas personas en Internet dicen que esto es un producto de Ruishu. Hay un párrafo de este tipo en la introducción

> A través de la transformación dinámica continua del código subyacente de la página web del servidor, el atacante no puede leer el objetivo del ataque.

 

En otras palabras, este producto solo proporciona una ofuscación dinámica y el sitio web de origen implementa el código de cifrado real. Es decir, simplemente extraiga la lógica de cifrado original.

 

## Listo para trabajar

### Construye un entorno de análisis fuera de línea

Debido a que el nombre de la variable es diferente cada vez que lo solicita el código Js cifrado, el análisis en línea será muy inconveniente, por lo que es necesario descargar el archivo sin conexión al local y luego analizarlo.

wget --mirror --page-requisites --adjust-extension --no-parent --convert-links http://wenshu.court.gov.cn/Index

Python -m SimpleHTTPServer 8081

Luego visite http: // localhost: 8081 en el navegador para su análisis.

 

### Modificar página

Para la conveniencia del análisis, extraemos los js cifrados en un archivo separado (guárdelo como externo.js y agregamos el depurador al principio del archivo; punto de interrupción debajo del código) y comentamos el código de salto. El código Index.html modificado es el siguiente

<! DOCTYPE html PUBLIC "- // W3C // DTD XHTML 1.0 Transitional // EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html> 
<head> 
<meta http-equiv = "Content-Type" content = "text / html; charset = utf-8"> 
</head>
<Body> <meta name = contenido "procesador" = "webkit"> <id = contenido meta "9DhefwqGPrzGxEp9hPaoag" = "{{qqqqqr0.YUQeBgClraT6.dA28Ej3I1UKSbXlJ8NIUKxHc2UmGAqqkknLqqqqhvefgTey0JSvqkxJqkqLWqqqqk130r0HMvpQpmzDUlrUpmQ83oYJA2wDVVYY1mrWY0f3s2ptVlNp1aqqqqt1073741856VIJRJg9UB18SE49UPp8fH8h5lq2Y8psLYtXTCqAg2iGacfA3Tt9GOFqTs8GTGDCQwHGz5oKqQHAe4oO7h8mEBKcWqqqqhTrB4MbBgeoxqKGKR2zkG8iNtX0bwqfYF_uGc80qqqqr0qqDe5191aqqqqhb6rmecbj1Zrr4q WYozbAKrlMPE9mOVpEiZygKFg3hxX_u.SidW64uobWi227DF_MXET5u6NJI74gv.axiEcePMvHIL_qqqqYDUvHttHp6whZmAp7KOQtW} {tPgKp0pIU0AtpYJcsagEUlJps2JlmANpsGAEYVpUpYTioaNA1TxHIAGCFAr7xGAWwAl8k67pUumcIlQ.D6xnhSSgVfzZDmg4DfzoD6a5KcrDWfEURnTmlm9dRn2PWS7TAOwGmVRXKcx9JULM3rRrHpLfrkJrlK3DlPwMrl9cF0gtD07cUaLUV20v8YShrYWSMSRZD90l8OSvxbzc8saEAG |! QqU7aWVE0A9yYlv7CMX9T4THtVhmE4UX0Fjfh_vIdsiNUgCd3JJSPa6hZsZfcgVuLJzRXgkkhAWrC4uoKF82V.vbXKITtyVsSpIJvy9.SY5zYeuUlWzJG_lkrJwYoaucRQQg6y96Ch5rsnbU71HJ.nkkD85xjOoscMWRbGCiHUXy74mDJRW00e0.UV8f7ZDsBQIzOfuDJYjeBdCbXMhlCyCjW8hzMPlbWR_w.LKKFwywal7445 "> <! - [if lt IE 9]> <script> document.createElement (" sección ") </ script> <! [Endif] -> < script type = "text / javascript" src = "4QbVtADbnLVIc / c.FxJzG50F.js% 3FD9PVtGL = e5191a"> </script>sección ") </script> <! [endif] -> <script type =" text / javascript "src =" 4QbVtADbnLVIc / c.FxJzG50F.js% 3FD9PVtGL = e5191a "> </script>sección ") </script> <! [endif] -> <script type =" text / javascript "src =" 4QbVtADbnLVIc / c.FxJzG50F.js% 3FD9PVtGL = e5191a "> </script>
<script type = "text / javascript" src = "outer.js"> 
</script> 
<script type = 'text / javascript' r = 'm'> 
_ $ hr ('ZoA8'); 
</script> 
<! - 
<script type = "text / javascript"> window.οnlοad = function () {var _ $ n6 = document.getElementById (_ $ fv ('ondoal')); _ $ nR (_ $ n6.name, _ $ vD (_ $ n6, _ $ zb ('HvzED2ExKa')));}; </script> 
-> 
</body> 
</html> 
<script type = "text / javascript" > _ $ e3 (); </script>

 

### función gancho

Hay algunas cadenas muy largas y desordenadas en js, supongo que puede ser código js cifrado. Generalmente, el código js para liberar el cifrado es a través de la función eval, así que necesitamos conectarlo. Ejecute el siguiente código en el panel de la consola

 

orig = window.eval; 
window.eval = function (str) {debugger; orig (str);} 
// Este código evita la detección de anti-gancho 
window.eval.toString = function () (return orig.toString (); }

 

Además, para analizar el atributo de contenido del meta # 9DhefwqGPrzGxEp9hPaoag, ejecute el siguiente código para agregar un punto de interrupción

var dom_get_element = document.getElementById; 
document.getElementById = function (id) {if (id == "9DhefwqGPrzGxEp9hPaoag") depurador; dom_get_element (id);}

 

### Supervisión de cambios en las cookies

Agregue document.cookie al panel Watch para monitorear los cambios de cookies.

 

## Proceso de análisis

### Tabla de sustitución de variables de lanzamiento

Abra http: // localhost: 8081 / wenshu.court.gov.cn / en el navegador y ejecute la primera línea de out.js para activar un punto de interrupción en el depurador. Seguimiento de un solo paso, la función eval se activa en la línea 18

 

_ $ tU (_, $ xH (101, 118, 97, 108)] `allcdlwvt` phnt`moOacienmrtnCop`JehTdmPo`tlsston`epvTls $ lt #` _omatSeooaee` ___ suger @ eeaaultv`.hprrtSgetcijeIntmeeb``SOBe`nr`ao``rctttxp``: $ M`tt`bos`uo`tcilg`fnorCsatCedo`rphl``opli``tsneltNfNizeU liBhScFiutoe`a5`Za_lgrCp0aneafXn7tjoLeba9 (P {Gn8tQvFc3dK] V`1xAzi5WFtqEcDlt ___ `rgmrvtIlec`l_sc_a`sayretcIneo`euwe3ixtysgmbd6aV`_owtCc`latce '), _ $ fv ('fMXn49KE26OfLRGSmXasroZMmPl16kBsoQjPPs8oqAhSbglTjrijvsmkkafjulimrcbkhgmmemcnavgjgplhmafjpiqkahdk')));

 

Esto lanzará el siguiente código en el navegador.

 

var _ $ mM = "FSSBBIl1UgzbN7N", _ $ gn = "longitud", _ $ k9 = "piso", _ $ cE = "charCodeAt", _ $ m6 = "cadena", _ $ lf = "corte", _ $ uO = "uu", _ $ i2 = "substr", _ $ rK = "_ $", _ $ b4 = "Math", _ $ hX = "log", _ $ mf = "toString", _ $ jZ = "fromCharCode", _ $ ir = "aplicar", _ $ va = "dividir", _ $ mm = "afirmación fallida con condición:", _ $ kG = "pila", _ $ fL = "pop", _ $ jR = "indexOf", _ $ aS = "Object.InjectedScript.evaluar", _ $ kX = "@ debugger", _ $ ss = "evaluar", _ $ jo = "charAt", _ $ rM = " getTime ", _ $ el =" number ", _ $ cb =" FSSBA ", _ $ ah =" Array ", _ $ gq =" prototype ", _ $ g8 =" execScript ", _ $ lP =" eval " , _ $ hs = "llamar", _ $ po = "reemplazar", _ $ jA = "functioneval () {[nativecode]} ", _ $ vS =" FxJzG50F ", _ $ ng =" qrcklmDoExthWJiHAp1sVYKU3RFMQw8IGfPO92bvLNj.7zXBaSnu0TC6gy_4Ze5d ", _ $ mT $", kP = "documento aleatorio" ", _ $ kk =" setTimeout ", _ $ is =" setInterval ", _ $ jQ =" $ _ ts ", _ $ aP =" localStorage ", _ $ mj =" ___ ts ___ ", _ $ fo =" removeItem " , _ $ pB = "__ # classType", _ $ q6 = "consola", _ $ al = "wP3dxhyJgpbC6tVm_ewcCO", _ $ dm = "coincidencia";removeItem ", _ $ pB =" __ # classType ", _ $ q6 =" consola ", _ $ al =" wP3dxhyJgpbC6tVm_ewcCO ", _ $ dm =" coincidencia ";removeItem ", _ $ pB =" __ # classType ", _ $ q6 =" consola ", _ $ al =" wP3dxhyJgpbC6tVm_ewcCO ", _ $ dm =" coincidencia ";

Guardamos este código publicado en el archivo reference_map.js.

 

### Liberar inner.js

Elimine la línea 18 del código de lanzamiento en out.js y cite el reference_map.js obtenido en Index.html. Continuar con la depuración, hay el siguiente código en la línea 180

var _ $ n6 = _ $ zk [_ $ vS]; 
si (_ $ n6) { 
    _ $ zk [_ $ vS] = falso; 
    _ $ tM (_ $ n6); 
}

El valor de _ $ n6 es la cadena larga en 4QbVtADbnLVIc / c.FxJzG50F.js. Supongo que la función _ $ tM descifrará y liberará esta cadena.

En 440 líneas de código (en la función _ $ tM) _ $ mc (_ $ eG (_ $ qh.join (''))); después de la ejecución, encontrará que el valor de la cookie ha sido actualizado.

El análisis encontró que el valor de retorno de _ $ eG (_ $ qh.join ('')) es una parte del código js en forma de texto, y la función de _ $ mc es ejecutar esta parte de js. Guardamos este js en el archivo inner.js.

 

### Depurar inner.js

Introduzca inner.js en Index.html, elimine el punto de interrupción de outside.js y comente 440 líneas de código, depure inner.js y descubra que inner.js libera una tabla de sustitución de variables más grande, donde la cookie hace referencia a _ $ f0 = "cookie", buscando la variable en inner.js, estableciendo puntos de interrupción en todos los lugares donde se cambia la cookie y depurando para averiguar la lógica de cálculo de la cookie.

El valor de la cookie se obtiene descifrando el atributo de contenido del meta # 9DhefwqGPrzGxEp9hPaoag.

Existe el siguiente código en la función _ $ oc en la línea 804

 

función _ $ oc () { 
    _ $ fM (_ $ sw, _ $ g9, _ $ u9); 
    _ $ fM (_ $ sw, _ $ da, _ $ v9); 
    _ $ fM (_ $ sw, _ $ kp, _ $ bV); 
    _ $ fM (_ $ sw, _ $ aE, _ $ wV); 
    _ $ fM (_ $ sw, _ $ vL, _ $ xm); 
    _ $ fM (_ $ sw, _ $ sS, _ $ ta); 
    _ $ fM (_ $ sw, _ $ s3, _ $ hb); 
    _ $ fM (_ $ sw, _ $ an, _ $ xx); 
    _ $ fM (_ $ sw, _ $ bJ, _ $ tI); 
    _ $ fM (_ $ sw, _ $ qB, _ $ wE); 
    _ $ fM (_ $ tU, _ $ es, _ $ sz); 
    si (_ $ sw [_ $ bo]) { 
        _ $ fM (_ $ sw, _ $ tf, _ $ sz); 
        _ $ fM (_ $ sw, _ $ cl, _ $ sz); 
        _ $ fM (_ $ sw, _ $ vQ, _ $ sz); 
    } 
    _ $ fM (_ $ tU, _ $ sl, _ $ vu); 
    // window.addEventListener ('cargar', _ $ tk), 即 在 页面 加载 完毕 后 更新 一次 cookie 
    _ $ fM (_ $ tU, _ $ is, _ $ tk);
    // Actualiza la cookie cada 50 segundos 
    _ $ tU [_ $ hD] (function () { 
        _ $ uD (10); 
    }, 50000);

De acuerdo con la tabla de sustitución de variables, estos códigos registran varios eventos de monitoreo y actualizan la cookie regularmente a través del intervalo.

 

#### Parámetro MmEwMD en URL

Hay un parámetro MmEwMD después de cada URL solicitada,

Ejecutar en el panel de la consola

var xhr = nueva ventana.XMLHttpRequest (); 
xhr.open ('POST', '/ hola', verdadero); 
xhr.send ('abc');

Este parámetro se agregará automáticamente después de la URL solicitada.

http: // localhost: 8081 / ¿hola? MmEwMD = 1zXMRFfiFy00rBpdSoJ2cJ4EvS6mQ1dvl5jZLrqB… oRVu5t.x8Y3GVQLmqdG.lmuE2snbEk.YHif4rQamHwOik.

Supongo que el xhr de la página actual está enganchado.

Al buscar y analizar la tabla de sustitución de variables lanzada por inner.js, localizamos las 8107 líneas de código de inner.js

función _ $ xc (_ $ v8, _ $ vz) { 
    var _ $ xB = []; 
    var _ $ mE = _ $ qS (6); 
    si (_ $ mE) { 
        _ $ xB = _ $ xB [_ $ lt] (_ $ vz); 
        _ $ xB.push (_ $ c9 (_ $ v8)? 1: 0); 
        var _ $ vT = _ $ h6 () + _ $ mE + _ $ cR (_ $ xB); 
        _ $ vT + = _ $ tc (_ $ vT); 
        return _ $ y5 + '=' + _ $ vT; 
    } si no 
        devuelve _ $ y5 + '='; 
}

El propósito de este código es calcular el valor del parámetro MmEwMD en la URL de solicitud. Al conectar las funciones de apertura y envío de XMLHttpRequest, este parámetro se agrega automáticamente después de la URL cuando se inicia una solicitud asincrónica.

function _ $ dJ () { 
    var _ $ v8 = _ $ tU [_ $ ld]; 
    var _ $ v8 = window.XMLHttpRequest; 
    if (_ $ v8) { 
        var _ $ vz = _ $ v8 ['prototype'] ; 
        if (_ $ vz) { 
            _ $ jv = _ $ vz ['open']; 
            _ $ ca = _ $ vz ['send']; 
            // función de enganche de apertura, calcula el token al llamar a open y guárdalo en sig Atributos_ 
            $ vz ['abrir'] = función () { 
                argumentos [1] = _ $ aY (argumentos [1]); 
                esto ['sig'] = argumentos [1]; 
                return _ $ jv ['aplicar'] (esto, argumentos); 
            } 
            ; 
            // Al llamar a send para iniciar una solicitud asincrónica, agregue sig a la url. 
            _ $ vz ['enviar'] = función () {
                argumentos [0] = _ $ uG (argumentos [0], esto ['sig']);
                return _ $ ca ['aplicar'] (esto, argumentos); 
            } 
            ; 
        } 
}

## para resumir

La lógica de cifrado del sitio web es aproximadamente la siguiente:

1. external.js libera los parámetros cifrados y descifra el archivo c.FxJzG50F.js para obtener el código js estáticamente ofuscado (inner.js)

2. Ejecute inner.js, descifre el atributo de contenido de la etiqueta meta # 9DhefwqGPrzGxEp9hPaoag para obtener una matriz y agregue los dos parámetros calculados por outside.js para calcular el valor de FSSBBIl1UgzbN7N80T;

Registre el evento de carga para agregar el tiempo de intervalo después de la carga y calcule el nuevo valor de la cookie en función del valor de la cookie existente (mediante la función _ $ tk)

Las funciones de abrir y enviar del gancho XMLHttpRequest agregan automáticamente tokens a las solicitudes en la página.

 

 

Esquema de derivación:

Debido a que la cookie se actualiza automáticamente a intervalos regulares, y el token solicitado se agrega automáticamente cuando la solicitud se inicia a través de un gancho, por lo que si deja que el navegador cargue la página de inicio primero y usa la solicitud asincrónica xhr en la página de inicio, el valor de la cookie y el token solicitados se tomarán automáticamente en. Entonces podemos encontrar un navegador programable.

Supongo que te gusta

Origin blog.csdn.net/zhangge3663/article/details/108402278
Recomendado
Clasificación