
Anatomía de un script malicioso: cómo un sitio web puede apoderarse de su navegador

A estas alturas, todos sabemos que los grandes gigantes tecnológicos como Facebook o Google saben todo sobre nuestras vidas, incluida la frecuencia con la que vamos al baño (de ahí todos los anuncios de medicamentos para la próstata que siguen apareciendo, incluso en sitios de noticias). ). de buena reputación). Después de todo, les hemos dado permiso para hacerlo leyendo páginas y páginas de jerga legal en sus páginas de TC (todos lo hicimos, ¿no?) y haciendo clic en el botón “Aceptar”.
Pero, ¿qué puede hacerle un sitio a usted oa su dispositivo sin su consentimiento explícito? ¿Qué sucede cuando visita un sitio levemente “incorrecto” o un sitio “adecuado” que visitó incluye algún script de terceros que no se ha verificado minuciosamente?
¿Le ha pasado alguna vez que su navegador está secuestrado y aparecen innumerables ventanas emergentes y parece que no puede cerrarlas sin salir del navegador por completo o hacer clic 25 veces en el botón “Atrás”? Te sientes en peligro cuando eso sucede, ¿no?
Siguiendo el aporte de Chris aquí en , decidí buscar un script que haga exactamente eso y ver qué sucede bajo el capó. Parecía una tarea bastante desalentadora, pero aprendí bastantes cosas y al final me divertí mucho haciéndola. Espero poder compartir algo de la diversión contigo.
La búsqueda del guión
La idea era, en palabras de Chris, “fragmentos de JavaScript que harían buscar cosas sorprendentemente aterradoras”.
Lo primero que hice fue configurar una máquina virtual con Virtual Box en mi PC principal de desarrollo de Ubuntu. De esta manera, si los sitios que visité y los scripts contenidos en ellos intentarían hacerle algo aterrador a mi computadora, solo necesitaría borrar la VM sin comprometer mi preciosa computadora portátil. Instale la última versión de Ubuntu en la VM, abra el navegador y sal a buscar.
Una de las cosas que estaba buscando era el uso de una variación de la infame Evercookie (también conocida como “cookie no eliminable”) que sería una clara señal de técnicas de seguimiento turbias.
¿Dónde buscar un guión así? Intenté encontrar uno de los anuncios intrusivos antes mencionados en sitios web legítimos, pero no pude encontrar ninguno. Supongo que parece que las empresas que suministran anuncios han mejorado mucho en la detección de scripts sospechosos al automatizar el proceso de investigación.
Probé algunos sitios de noticias de buena reputación para ver si había algo interesante, pero todo lo que encontré fueron toneladas y toneladas de scripts de seguimiento estándar (y errores de JavaScript en los registros de la consola). En estos casos, la mayor parte de lo que hacen los scripts es enviar datos a un servidor, y dado que hay pocas formas de saber qué está haciendo realmente el servidor con los datos, habría sido muy difícil analizarlos.
Entonces pensé que el mejor lugar para buscar cosas “aterradoras” serían los sitios cuyos propietarios no se arriesgarían a una acción legal si hicieran algo “aterrador” a sus usuarios. Lo que significa, básicamente, sitios donde el usuario intenta hacer algo que rosa lo ilegal para empezar.
Analicé algunos servidores proxy de Pirate Bay, pero no tuve suerte. Luego decidió pasar a sitios que ofrecían enlaces a transmisiones ilegales de eventos deportivos. Revisado un par de sitios y observado con atención los scripts que incluían con las herramientas de desarrollo de Chromium.
En un sitio que ofrece, entre otros, streaming ilegal de partidos de tenis de mesa, noté (en la lista de JavaScripts en la pestaña DevTools Network) entre bibliotecas de terceros, scripts de interfaz de usuario estándar y la muy frecuente inclusión duplicada de Google. Biblioteca de análisis (¡ay!), un script con un nombre extraño sin extensión .js y solo un número como URL.
Eché un vistazo al aparentemente infinito par de líneas de código ofuscado que constituían la mayor parte del código del script y encontré cadenas como ,, chromePDFPopunderNew
etiquetas escapadas e incluso una cadena que contenía un PDF en línea. Esto me pareció algo interesante. ¡La caza había terminado! Descargué el script en mi computadora y comencé a intentar encontrarle algún sentido.adblockPopup
flashFileUrl
script
No estoy revelando claramente los dominios involucrados en esta operación, ya que lo que nos interesa aquí es el pecado, no el pecador. Sin embargo, dejó deliberadamente una forma de determinar al menos la URL principal a la que el script envía a los usuarios. Si logras resolver el acertijo, envíame un mensaje privado y te diré si acertaste.
El script: desofuscar y descubrir los parámetros de configuración
Cómo se ve el guión
El script está ofuscado, tanto por motivos de seguridad como para garantizar una descarga más rápida. Está compuesto por una gran IIFE ( expresión de función invocada inmediatamente ), que es una técnica utilizada para aislar un fragmento de código JavaScript de su entorno. El contexto no se mezcla con otros scripts y no hay riesgo de conflicto de espacio de nombres entre funciones o nombres de variables en diferentes scripts.
Aquí está el comienzo del guión. Tenga en cuenta el comienzo del PDF codificado en base64 en la última línea:
Y aquí está el final:
La única acción llevada a cabo en el contexto global, aparentemente, es establecer la variable global zfgloadedpopup
en verdadero, presumiblemente para decirle a otros scripts que pertenecen a la misma “familia” que este ya ha sido cargado. Esta variable se usa solo una vez, por lo que el script en sí no verifica si se ha cargado. Entonces, si el sitio que estás visitando lo incluye dos veces por error, obtendrás el doble de ventanas emergentes por el mismo precio. ¡Afortunado!
El gran IFEE espera dos parámetros, llamados options
y lary
. De hecho, revisé el nombre del segundo parámetro para ver qué podría significar y el único significado que encontré fue “agresivo, antisocial” en la jerga británica. “Entonces estamos siendo agresivos aquí”, pensé. “Interesante.”
El options
parámetro es claramente un objeto con claves y valores, aunque sean totalmente ininteligibles. El lary
parámetro es una cadena de algún tipo. Para que esto tuviera sentido, la única opción era desofuscar todo el guión. Sigue leyendo y todo te será explicado.
Desconsolando el guion
Primero intenté recurrir a herramientas existentes, pero ninguna de las herramientas en línea disponibles parecía estar haciendo lo que esperaban que hicieran. La mayor parte de lo que hicieron fue imprimir el código, algo que mi IDE puede hacer con bastante facilidad por sí solo. Leí sobre JSDetox, que es un software de computadora real y debería ser muy útil para purificar este tipo de script. Sin embargo, intenté instalarlo en dos versiones diferentes de Ubuntu y terminé en el infierno de dependencia de Ruby GEM en ambos casos. JSDetox es bastante antiguo y supongo que ahora es prácticamente un software abandonado. La única opción que quedaba era realizar las cosas principalmente a mano o mediante sustituciones de expresiones regulares manuales o semiautomáticas. Tienes que seguir varios pasos para descifrar completamente el guión.
Aquí hay un GIF animado que muestra la misma sección de código en varias etapas de descifrado:
El primer paso fue bastante sencillo: requirió reformatear el código del script para agregar espacios y saltos de línea. Me quedé con el código con la sangría adecuada, pero aún estaba lleno de cosas ilegibles, como las siguientes:
JSON parsing it g = mapByFunction(Options, function (W) { w = secondHalfOfLary['indexOf'](W); return w !== -1 ? firstHalfOfLary[w] : W; })['join'](''); if (window['JSON'] window['JSON']['parse']) { try { return window['JSON']['parse'](g); } catch (W) { return eval(h + g + p); } } return eval(h + g + p);}
Primero establece un par de variables que contienen paréntesis de apertura y cierre, que se utilizarán más adelante:
var p = ')', h = '(',
Entonces nuestro argumento se divide lary
en dos:
halfLaryLength = lary.length / 2,firstHalfOfLary = lary['substr'](0, halfLaryLength),secondHalfOfLary = lary['substr'](halfLaryLength),
A continuación, mapea la Options
cadena, letra por letra, con esta función:
function (W) { w = secondHalfOfLary['indexOf'](W); return w !== -1 ? firstHalfOfLary[w] : W;}
Si la letra actual está presente en la segunda mitad del lary
argumento, devuelve la letra correspondiente en el alfabeto en minúsculas en la primera parte del mismo argumento. De lo contrario, devuelve la letra actual, sin cambios. Esto significa que el parámetro de opciones está sólo medio cifrado, por así decirlo.
Una vez que se ha realizado el mapeo, la matriz resultante de letras descifradas g
(recuerde, mapByFunction
siempre vuelve una matriz) se convierte nuevamente en una cadena:
g['join']('')
La configuración es inicialmente un objeto JSON, por lo que el script intenta utilizar la función nativa JSON.parse del navegador para convertirlo en un objeto literal. Si el objeto JSON no está disponible (IE7 o anterior, Firefox y Safari 3 o anterior), recurra a ponerlo entre paréntesis y evalúelo:
if (window['JSON'] window['JSON']['parse']) { try { return window['JSON']['parse'](g); } catch (W) { return eval(h + g + p); }}return eval(h + g + p);
Este es otro caso en el que el script es extremadamente compatible con varios navegadores, hasta el punto de admitir navegadores con más de 10 años de antigüedad. Intentaré explicar por qué en breve.
Ahora la options
variable ha sido descifrada. Aquí está en todo su esplendor descifrado, aunque con las URL originales omitidas:
let options = { SS: true, adblockPopup: true, adblockPopupLink: null, adblockPopupTimeout: null, addOverlay: false, addOverlayOnMedia: true, aggressive: false, backClickAd: false, backClickNoHistoryOnly: false, backClickZone: null, chromePDFPopunder: false, chromePDFPopunderNew: false, clickAnywhere: true, desktopChromeFixPopunder: false, desktopPopunderEverywhere: false, desktopPopunderEverywhereLinks: false, disableChromePDFPopunderEventPropagation: false, disableOnMedia: false, disableOpenViaMobilePopunderAndFollowLinks: false, disableOpenViaMobilePopunderAndPropagateEvents: false, disablePerforamnceCompletely: false, dontFollowLink: false, excludes: [], excludesOpenInPopunder: false, excludesOpenInPopunderCapping: null, expiresBackClick: null, getOutFromIframe: false, iOSChromeSwapPopunder: false, iOSClickFix: true, iframeTimeout: 30000, imageToTrackPerformanceOn: "", /* URL OMITTED */ includes: [], interstitialUrl: "", /* URL OMITTED */ isOnclickDisabledInKnownWebView: false, limLo: false, mahClicks: true, mobilePopUpTargetBlankLinks: false, mobilePopunderTargetBlankLinks: false, notificationEnable: false, openPopsWhenInIframe: false, openViaDesktopPopunder: false, openViaMobilePopunderAndPropagateFormSubmit: false, partner: "pa", performanceUrl: "", /* URL OMITTED */ pomc: false, popupThroughAboutBlankForAdBlock: false, popupWithoutPropagationAnywhere: false, ppuClicks: 0, ppuQnty: 3, ppuTimeout: 25, prefetch: "", resetCounters: false, retargetingFrameUrl: "", scripts: [], sessionClicks: 0, sessionTimeout: 1440, smartOverlay: true, smartOverlayMinHeight: 100, smartOverlayMinWidth: 450, startClicks: 0, startTimeout: 0, url: "", /* URL OMITTED */ waitForIframe: true, zIndex: 2000, zoneId: 1628975}
Me pareció muy interesante el hecho de que existía una aggressive
opción, aunque lamentablemente no se utiliza en el código. Teniendo en cuenta todo lo que este script hace en el navegador, tenía mucha curiosidad por saber qué habría hecho si hubiera sido más “agresivo”.
No todas las opciones pasadas al script se utilizan realmente en el script; y no todas las opciones que verifican el script están presentes en el options
argumento pasado en esta versión. Supongo que algunas de las opciones que no están presentes en la configuración del script se usan en versiones implementadas en otros sitios, especialmente en los casos en que este script se usa en Múltiples dominios. Algunas opciones también pueden estar ahí por razones heredadas y simplemente ya no están en uso. Al script le quedan algunas funciones vacías, que probablemente utilizaron algunas de las opciones que faltan.
¿Qué hace realmente el guión?
Con solo leer el nombre de las opciones anteriores, puedes adivinar mucho de lo que hace este script: abrir un archivo smartOverlay
, incluso usando un archivo adblockPopup
. Si es así clickAnywhere
, se abrirá un archivo url
. En nuestra versión específica del guión, no lo hará openPopsWhenInIframe
, y no lo hará getOutFromIframe
, aunque aplicará un archivo iOSClickFix
. Contará las ventanas emergentes y guardará el valor en ppuCount
, e incluso realizará un seguimiento del rendimiento utilizando un imageToTrackPerformanceOn
(que puedo decirles, incluso si omití la URL, está alojada en un CDN). Realizará un seguimiento ppuClicks
(clics en ventanas emergentes, supongo) y se limitará con cautela a ppuQnty
(probablemente una cantidad de ventanas emergentes).
Al leer el código, pude descubrir mucho más, obviamente. Veamos qué hace el guión y sigamos su lógica. Intentaré describir todas las cosas interesantes que puedes hacer, incluidas aquellas que no se activan con el conjunto de opciones que pude descifrar.
El objetivo principal de este script es dirigir al usuario a una URL que está almacenada en su configuración como options['url']
. La URL en la configuración que encontré me redirigió a un sitio web con mucho spam, por lo que a partir de ahora me referiré a esta URL como Sitio con spam, para mayor claridad.
1. ¡Quiero salir de este iFrame!
Lo primero que hace este script es intentar obtener una referencia a la ventana superior si el script en sí se ejecuta desde dentro en un iFrame y, si la configuración actual lo requiere, lo establece como la ventana principal en la que operar y configura todos referencia al elemento documento y agente de usuario a los de la ventana superior:
if (options['getOutFromIframe'] iframeStatus === 'InIframeCanExit') { while (myWindow !== myWindow.top) { myWindow = myWindow.top; } myDocument = myWindow['document']; myDocumentElement = myWindow['document']['documentElement']; myUserAgent = myWindow['navigator']['userAgent'];}
2. ¿Cuál es tu navegador preferido?
La segunda cosa que hace es una detección muy minuciosa del navegador actual, la versión del navegador y el sistema operativo mediante el análisis de la cadena del agente de usuario. Detecta si el usuario está usando Chrome y su versión específica, Firefox, Firefox para Android, UC Browser, Opera Mini, Yandex o si el usuario está usando la aplicación de Facebook. Algunas comprobaciones son muy específicas:
isYandexBrowser = /YaBrowser/['test'](myUserAgent),isChromeNotYandex = chromeVersion !isYandexBrowser,
Veremos por qué más tarde.
3. Todos sus navegadores son de nuestra propiedad.
Lo primero que molesta que hace el script es comprobar la presencia de la history.pushState()
función y, si está presente, el script inyecta una entrada falsa en el historial con el título de la URL actual. Esto le permite interceptar eventos de retroceso (usando el popstate
evento) y enviar al usuario al sitio spam en lugar de a la página anterior que el usuario realmente visitó. Si no hubiera agregado primero una entrada falsa al historial, esta técnica no funcionaría.
function addBackClickAd(options) { if (options['backClickAd'] options['backClickZone'] typeof window['history']['pushState'] === 'function') { if (options['backClickNoHistoryOnly'] window['history'].length 1) { return false; } // pushes a fake history state with the current doc title window['history']['pushState']({exp: Math['random']()}, document['title'], null); var createdAnchor = document['createElement']('a'); createdAnchor['href'] = options['url']; var newURL = 'http://' + createdAnchor['host'] + '/afu.php?zoneid=' + options['backClickZone'] + 'var=' + options['zoneId']; setTimeout(function () { window['addEventListener']('popstate', function (W) { window['location']['replace'](newURL); }); }, 0); }}
Esta técnica se utiliza sólo fuera del contexto de un iFrame y no en Chrome iOS ni en el navegador UC.
4. Este navegador necesita más scripts
Si un script malicioso no es lo suficientemente fuerte, el script intentará inyectar más scripts, según la configuración. Todos los scripts se adjuntan al head
documento y pueden incluir algo que se llama intersticial, control deslizante o pushup, que supongo que son varias formas de anuncios intrusivos que se muestran en el navegador. No pude averiguarlo porque, en el caso de nuestro script, la configuración no contenía ninguno de ellos, excepto uno que era una URL inactiva cuando lo revisé.
5. Ataque del interceptor de clic
A continuación, el script adjunta una función de “interceptor de clics” a todos los tipos de eventos de clic en el documento, incluidos los eventos táctiles en dispositivos móviles. Esta función intercepta todos los clics o toques del usuario en
Deja una respuesta