Translated ['src/generic-methodologies-and-resources/phishing-methodolog

This commit is contained in:
Translator 2025-04-27 16:32:46 +00:00
parent c78c8acf88
commit 36b1104b5f
5 changed files with 234 additions and 73 deletions

View File

@ -31,6 +31,7 @@ additional-js = [
"theme/tabs.js", "theme/tabs.js",
"theme/ht_searcher.js", "theme/ht_searcher.js",
"theme/sponsor.js", "theme/sponsor.js",
"theme/ai.js"
] ]
no-section-label = true no-section-label = true
preferred-dark-theme = "hacktricks-dark" preferred-dark-theme = "hacktricks-dark"

View File

@ -561,6 +561,7 @@
- [CSRF (Cross Site Request Forgery)](pentesting-web/csrf-cross-site-request-forgery.md) - [CSRF (Cross Site Request Forgery)](pentesting-web/csrf-cross-site-request-forgery.md)
- [Dangling Markup - HTML scriptless injection](pentesting-web/dangling-markup-html-scriptless-injection/README.md) - [Dangling Markup - HTML scriptless injection](pentesting-web/dangling-markup-html-scriptless-injection/README.md)
- [SS-Leaks](pentesting-web/dangling-markup-html-scriptless-injection/ss-leaks.md) - [SS-Leaks](pentesting-web/dangling-markup-html-scriptless-injection/ss-leaks.md)
- [DApps - Decentralized Applications](pentesting-web/dapps-DecentralizedApplications.md)
- [Dependency Confusion](pentesting-web/dependency-confusion.md) - [Dependency Confusion](pentesting-web/dependency-confusion.md)
- [Deserialization](pentesting-web/deserialization/README.md) - [Deserialization](pentesting-web/deserialization/README.md)
- [NodeJS - \_\_proto\_\_ & prototype Pollution](pentesting-web/deserialization/nodejs-proto-prototype-pollution/README.md) - [NodeJS - \_\_proto\_\_ & prototype Pollution](pentesting-web/deserialization/nodejs-proto-prototype-pollution/README.md)
@ -625,6 +626,7 @@
- [Regular expression Denial of Service - ReDoS](pentesting-web/regular-expression-denial-of-service-redos.md) - [Regular expression Denial of Service - ReDoS](pentesting-web/regular-expression-denial-of-service-redos.md)
- [Reset/Forgotten Password Bypass](pentesting-web/reset-password.md) - [Reset/Forgotten Password Bypass](pentesting-web/reset-password.md)
- [Reverse Tab Nabbing](pentesting-web/reverse-tab-nabbing.md) - [Reverse Tab Nabbing](pentesting-web/reverse-tab-nabbing.md)
- [RSQL Injection](pentesting-web/rsql-injection.md)
- [SAML Attacks](pentesting-web/saml-attacks/README.md) - [SAML Attacks](pentesting-web/saml-attacks/README.md)
- [SAML Basics](pentesting-web/saml-attacks/saml-basics.md) - [SAML Basics](pentesting-web/saml-attacks/saml-basics.md)
- [Server Side Inclusion/Edge Side Inclusion Injection](pentesting-web/server-side-inclusion-edge-side-inclusion-injection.md) - [Server Side Inclusion/Edge Side Inclusion Injection](pentesting-web/server-side-inclusion-edge-side-inclusion-injection.md)

View File

@ -10,7 +10,7 @@
3. Usar **OSINT** para **encontrar correos electrónicos**. 3. Usar **OSINT** para **encontrar correos electrónicos**.
2. Preparar el entorno 2. Preparar el entorno
1. **Comprar el dominio** que vas a usar para la evaluación de phishing. 1. **Comprar el dominio** que vas a usar para la evaluación de phishing.
2. **Configurar el servicio de correo** registros relacionados (SPF, DMARC, DKIM, rDNS). 2. **Configurar el servicio de correo** relacionado (SPF, DMARC, DKIM, rDNS).
3. Configurar el VPS con **gophish**. 3. Configurar el VPS con **gophish**.
3. Preparar la campaña 3. Preparar la campaña
1. Preparar la **plantilla de correo electrónico**. 1. Preparar la **plantilla de correo electrónico**.
@ -32,7 +32,7 @@
- **Reemplazo**: Como homoglyph pero menos sigiloso. Reemplaza una de las letras en el nombre de dominio, quizás con una letra cercana a la letra original en el teclado (por ejemplo, zektser.com). - **Reemplazo**: Como homoglyph pero menos sigiloso. Reemplaza una de las letras en el nombre de dominio, quizás con una letra cercana a la letra original en el teclado (por ejemplo, zektser.com).
- **Subdominado**: Introducir un **punto** dentro del nombre de dominio (por ejemplo, ze.lster.com). - **Subdominado**: Introducir un **punto** dentro del nombre de dominio (por ejemplo, ze.lster.com).
- **Inserción**: **Inserta una letra** en el nombre de dominio (por ejemplo, zerltser.com). - **Inserción**: **Inserta una letra** en el nombre de dominio (por ejemplo, zerltser.com).
- **Punto faltante**: Adjuntar el TLD al nombre de dominio. (por ejemplo, zelstercom.com) - **Punto faltante**: Agregar el TLD al nombre de dominio. (por ejemplo, zelstercom.com)
**Herramientas Automáticas** **Herramientas Automáticas**
@ -74,7 +74,7 @@ Para asegurarte de que el dominio expirado que vas a comprar **ya tiene un buen
- [https://anymailfinder.com/](https://anymailfinder.com) - [https://anymailfinder.com/](https://anymailfinder.com)
Para **descubrir más** direcciones de correo electrónico válidas o **verificar las que ya has descubierto**, puedes comprobar si puedes forzar por fuerza bruta los servidores smtp de la víctima. [Aprende cómo verificar/descubrir direcciones de correo electrónico aquí](../../network-services-pentesting/pentesting-smtp/index.html#username-bruteforce-enumeration).\ Para **descubrir más** direcciones de correo electrónico válidas o **verificar las que ya has descubierto**, puedes comprobar si puedes forzar por fuerza bruta los servidores smtp de la víctima. [Aprende cómo verificar/descubrir direcciones de correo electrónico aquí](../../network-services-pentesting/pentesting-smtp/index.html#username-bruteforce-enumeration).\
Además, no olvides que si los usuarios utilizan **cualquier portal web para acceder a sus correos**, puedes verificar si es vulnerable a **fuerza bruta de nombres de usuario**, y explotar la vulnerabilidad si es posible. Además, no olvides que si los usuarios utilizan **cualquier portal web para acceder a sus correos**, puedes verificar si es vulnerable a **fuerza bruta de nombres de usuario** y explotar la vulnerabilidad si es posible.
## Configurando GoPhish ## Configurando GoPhish
@ -82,7 +82,7 @@ Además, no olvides que si los usuarios utilizan **cualquier portal web para acc
Puedes descargarlo de [https://github.com/gophish/gophish/releases/tag/v0.11.0](https://github.com/gophish/gophish/releases/tag/v0.11.0) Puedes descargarlo de [https://github.com/gophish/gophish/releases/tag/v0.11.0](https://github.com/gophish/gophish/releases/tag/v0.11.0)
Descarga y descomprime dentro de `/opt/gophish` y ejecuta `/opt/gophish/gophish`\ Descarga y descomprime en `/opt/gophish` y ejecuta `/opt/gophish/gophish`\
Se te dará una contraseña para el usuario administrador en el puerto 3333 en la salida. Por lo tanto, accede a ese puerto y usa esas credenciales para cambiar la contraseña del administrador. Puede que necesites tunelizar ese puerto a local: Se te dará una contraseña para el usuario administrador en el puerto 3333 en la salida. Por lo tanto, accede a ese puerto y usa esas credenciales para cambiar la contraseña del administrador. Puede que necesites tunelizar ese puerto a local:
```bash ```bash
ssh -L 3333:127.0.0.1:3333 <user>@<ip> ssh -L 3333:127.0.0.1:3333 <user>@<ip>
@ -126,7 +126,7 @@ Finalmente, modifica los archivos **`/etc/hostname`** y **`/etc/mailname`** a tu
Ahora, crea un **registro A de DNS** de `mail.<domain>` apuntando a la **dirección IP** del VPS y un **registro MX de DNS** apuntando a `mail.<domain>` Ahora, crea un **registro A de DNS** de `mail.<domain>` apuntando a la **dirección IP** del VPS y un **registro MX de DNS** apuntando a `mail.<domain>`
Ahora probemos enviar un correo electrónico: Ahora probemos enviar un correo:
```bash ```bash
apt install mailutils apt install mailutils
echo "This is the body of the email" | mail -s "This is the subject line" test@email.com echo "This is the body of the email" | mail -s "This is the subject line" test@email.com
@ -161,7 +161,7 @@ Modifica `/opt/gophish/config.json` a lo siguiente (nota el uso de https):
``` ```
**Configurar el servicio gophish** **Configurar el servicio gophish**
Para crear el servicio gophish de modo que se pueda iniciar automáticamente y gestionar como un servicio, puedes crear el archivo `/etc/init.d/gophish` con el siguiente contenido: Para crear el servicio gophish para que se inicie automáticamente y se gestione como un servicio, puedes crear el archivo `/etc/init.d/gophish` con el siguiente contenido:
```bash ```bash
#!/bin/bash #!/bin/bash
# /etc/init.d/gophish # /etc/init.d/gophish
@ -208,7 +208,7 @@ case $1 in
start|stop|status) "$1" ;; start|stop|status) "$1" ;;
esac esac
``` ```
Termina de configurar el servicio y verifica que funcione: Termina de configurar el servicio y verifica su funcionamiento haciendo:
```bash ```bash
mkdir /var/log/gophish mkdir /var/log/gophish
chmod +x /etc/init.d/gophish chmod +x /etc/init.d/gophish
@ -231,7 +231,7 @@ Ten en cuenta que incluso si tienes que esperar una semana, puedes terminar de c
Configura un registro rDNS (PTR) que resuelva la dirección IP del VPS al nombre de dominio. Configura un registro rDNS (PTR) que resuelva la dirección IP del VPS al nombre de dominio.
### Registro de Sender Policy Framework (SPF) ### Registro de Marco de Políticas de Remitente (SPF)
Debes **configurar un registro SPF para el nuevo dominio**. Si no sabes qué es un registro SPF [**lee esta página**](../../network-services-pentesting/pentesting-smtp/index.html#spf). Debes **configurar un registro SPF para el nuevo dominio**. Si no sabes qué es un registro SPF [**lee esta página**](../../network-services-pentesting/pentesting-smtp/index.html#spf).
@ -243,7 +243,7 @@ Este es el contenido que debe establecerse dentro de un registro TXT dentro del
```bash ```bash
v=spf1 mx a ip4:ip.ip.ip.ip ?all v=spf1 mx a ip4:ip.ip.ip.ip ?all
``` ```
### Registro de Autenticación, Informe y Conformidad de Mensajes Basado en Dominio (DMARC) ### Registro de Autenticación de Mensajes Basado en Dominio, Informes y Conformidad (DMARC)
Debes **configurar un registro DMARC para el nuevo dominio**. Si no sabes qué es un registro DMARC [**lee esta página**](../../network-services-pentesting/pentesting-smtp/index.html#dmarc). Debes **configurar un registro DMARC para el nuevo dominio**. Si no sabes qué es un registro DMARC [**lee esta página**](../../network-services-pentesting/pentesting-smtp/index.html#dmarc).
@ -297,7 +297,7 @@ La página [www.mail-tester.com](https://www.mail-tester.com) puede indicarte si
Puedes solicitar la eliminación de tu dominio/IP en [https://sender.office.com/](https://sender.office.com). Puedes solicitar la eliminación de tu dominio/IP en [https://sender.office.com/](https://sender.office.com).
## Crear y lanzar campaña GoPhish ## Crear y lanzar campaña de GoPhish
### Perfil de envío ### Perfil de envío
@ -308,8 +308,8 @@ La página [www.mail-tester.com](https://www.mail-tester.com) puede indicarte si
![](<../../images/image (253) (1) (2) (1) (1) (2) (2) (3) (3) (5) (3) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (10) (15) (2).png>) ![](<../../images/image (253) (1) (2) (1) (1) (2) (2) (3) (3) (5) (3) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (10) (15) (2).png>)
> [!NOTE] > [!NOTE]
> Se recomienda utilizar la funcionalidad "**Enviar correo de prueba**" para comprobar que todo está funcionando.\ > Se recomienda utilizar la funcionalidad "**Enviar correo de prueba**" para verificar que todo esté funcionando.\
> Recomendaría **enviar los correos de prueba a direcciones de 10min** para evitar ser incluido en la lista negra al hacer pruebas. > Recomendaría **enviar los correos de prueba a direcciones de 10min** para evitar ser incluido en la lista negra durante las pruebas.
### Plantilla de correo electrónico ### Plantilla de correo electrónico
@ -337,7 +337,7 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
``` ```
Nota que **para aumentar la credibilidad del correo electrónico**, se recomienda usar alguna firma de un correo del cliente. Sugerencias: Nota que **para aumentar la credibilidad del correo electrónico**, se recomienda usar alguna firma de un correo del cliente. Sugerencias:
- Envía un correo a una **dirección no existente** y verifica si la respuesta tiene alguna firma. - Envía un correo a una **dirección inexistente** y verifica si la respuesta tiene alguna firma.
- Busca **correos públicos** como info@ex.com o press@ex.com o public@ex.com y envíales un correo y espera la respuesta. - Busca **correos públicos** como info@ex.com o press@ex.com o public@ex.com y envíales un correo y espera la respuesta.
- Intenta contactar **algún correo válido descubierto** y espera la respuesta. - Intenta contactar **algún correo válido descubierto** y espera la respuesta.
@ -349,7 +349,7 @@ Nota que **para aumentar la credibilidad del correo electrónico**, se recomiend
### Página de Aterrizaje ### Página de Aterrizaje
- Escribe un **nombre** - Escribe un **nombre**
- **Escribe el código HTML** de la página web. Ten en cuenta que puedes **importar** páginas web. - **Escribe el código HTML** de la página web. Nota que puedes **importar** páginas web.
- Marca **Capturar Datos Enviados** y **Capturar Contraseñas** - Marca **Capturar Datos Enviados** y **Capturar Contraseñas**
- Establece una **redirección** - Establece una **redirección**
@ -357,7 +357,7 @@ Nota que **para aumentar la credibilidad del correo electrónico**, se recomiend
> [!NOTE] > [!NOTE]
> Generalmente necesitarás modificar el código HTML de la página y hacer algunas pruebas en local (quizás usando algún servidor Apache) **hasta que te gusten los resultados.** Luego, escribe ese código HTML en el cuadro.\ > Generalmente necesitarás modificar el código HTML de la página y hacer algunas pruebas en local (quizás usando algún servidor Apache) **hasta que te gusten los resultados.** Luego, escribe ese código HTML en el cuadro.\
> Ten en cuenta que si necesitas **usar algunos recursos estáticos** para el HTML (quizás algunas páginas CSS y JS) puedes guardarlos en _**/opt/gophish/static/endpoint**_ y luego acceder a ellos desde _**/static/\<filename>**_ > Nota que si necesitas **usar algunos recursos estáticos** para el HTML (quizás algunas páginas CSS y JS) puedes guardarlos en _**/opt/gophish/static/endpoint**_ y luego acceder a ellos desde _**/static/\<filename>**_
> [!NOTE] > [!NOTE]
> Para la redirección podrías **redirigir a los usuarios a la página web principal legítima** de la víctima, o redirigirlos a _/static/migration.html_ por ejemplo, poner alguna **rueda giratoria (**[**https://loading.io/**](https://loading.io)**) durante 5 segundos y luego indicar que el proceso fue exitoso**. > Para la redirección podrías **redirigir a los usuarios a la página web principal legítima** de la víctima, o redirigirlos a _/static/migration.html_ por ejemplo, poner alguna **rueda giratoria (**[**https://loading.io/**](https://loading.io)**) durante 5 segundos y luego indicar que el proceso fue exitoso**.
@ -365,13 +365,13 @@ Nota que **para aumentar la credibilidad del correo electrónico**, se recomiend
### Usuarios y Grupos ### Usuarios y Grupos
- Establece un nombre - Establece un nombre
- **Importa los datos** (ten en cuenta que para usar la plantilla del ejemplo necesitas el nombre, apellido y dirección de correo electrónico de cada usuario) - **Importa los datos** (nota que para usar la plantilla del ejemplo necesitas el nombre, apellido y dirección de correo electrónico de cada usuario)
![](<../../images/image (163).png>) ![](<../../images/image (163).png>)
### Campaña ### Campaña
Finalmente, crea una campaña seleccionando un nombre, la plantilla de correo, la página de aterrizaje, la URL, el perfil de envío y el grupo. Ten en cuenta que la URL será el enlace enviado a las víctimas. Finalmente, crea una campaña seleccionando un nombre, la plantilla de correo, la página de aterrizaje, la URL, el perfil de envío y el grupo. Nota que la URL será el enlace enviado a las víctimas.
Nota que el **Perfil de Envío permite enviar un correo de prueba para ver cómo se verá el correo de phishing final**: Nota que el **Perfil de Envío permite enviar un correo de prueba para ver cómo se verá el correo de phishing final**:
@ -410,7 +410,7 @@ Aquí es donde herramientas como [**evilginx2**](https://github.com/kgretzky/evi
1. **Suplantas el formulario de inicio de sesión** de la página web real. 1. **Suplantas el formulario de inicio de sesión** de la página web real.
2. El usuario **envía** sus **credenciales** a tu página falsa y la herramienta envía esas credenciales a la página web real, **verificando si las credenciales funcionan**. 2. El usuario **envía** sus **credenciales** a tu página falsa y la herramienta envía esas credenciales a la página web real, **verificando si las credenciales funcionan**.
3. Si la cuenta está configurada con **2FA**, la página MitM lo pedirá y una vez que el **usuario lo introduzca**, la herramienta lo enviará a la página web real. 3. Si la cuenta está configurada con **2FA**, la página MitM lo pedirá y una vez que el **usuario lo introduzca**, la herramienta lo enviará a la página web real.
4. Una vez que el usuario esté autenticado, tú (como atacante) habrás **capturado las credenciales, el 2FA, la cookie y cualquier información** de cada interacción mientras la herramienta está realizando un MitM. 4. Una vez que el usuario esté autenticado, tú (como atacante) habrás **capturado las credenciales, el 2FA, la cookie y cualquier información** de cada interacción mientras la herramienta realiza un MitM.
### A través de VNC ### A través de VNC
@ -422,7 +422,7 @@ Puedes hacer esto con [**EvilnVNC**](https://github.com/JoelGMSec/EvilnoVNC)
Obviamente, una de las mejores maneras de saber si te han descubierto es **buscar tu dominio en listas negras**. Si aparece listado, de alguna manera tu dominio fue detectado como sospechoso.\ Obviamente, una de las mejores maneras de saber si te han descubierto es **buscar tu dominio en listas negras**. Si aparece listado, de alguna manera tu dominio fue detectado como sospechoso.\
Una forma fácil de verificar si tu dominio aparece en alguna lista negra es usar [https://malwareworld.com/](https://malwareworld.com) Una forma fácil de verificar si tu dominio aparece en alguna lista negra es usar [https://malwareworld.com/](https://malwareworld.com)
Sin embargo, hay otras formas de saber si la víctima está **buscando activamente actividad de phishing sospechosa en la naturaleza**, como se explica en: Sin embargo, hay otras formas de saber si la víctima está **buscando activamente actividad de phishing sospechosa en la naturaleza** como se explica en:
{{#ref}} {{#ref}}
detecting-phising.md detecting-phising.md

141
theme/ai.js Normal file
View File

@ -0,0 +1,141 @@
/**
* HackTricks AI Chat Widget v1.14 animated typing indicator
* ------------------------------------------------------------------------
* Replaces the static placeholder with a threedot **bouncing** loader
* while waiting for the assistants response.
* ------------------------------------------------------------------------
*/
(function () {
const LOG = "[HackTricks-AI]";
/* ---------------- Usertunable constants ---------------- */
const MAX_CONTEXT = 3000; // highlightedtext char limit
const MAX_QUESTION = 500; // question char limit
const TOOLTIP_TEXT =
"💡 Highlight any text on the page,\nthen click to ask HackTricks AI about it";
const API_BASE = "https://www.hacktricks.ai/api/assistants/threads";
const BRAND_RED = "#b31328"; // HackTricks brand
/* ------------------------------ State ------------------------------ */
let threadId = null;
let isRunning = false;
const $ = (sel, ctx = document) => ctx.querySelector(sel);
if (document.getElementById("ht-ai-btn")) { console.warn(`${LOG} Widget already injected.`); return; }
(document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", init) : init());
/* ==================================================================== */
async function init() {
console.log(`${LOG} Injecting widget… v1.14`);
await ensureThreadId();
injectStyles();
const btn = createFloatingButton();
createTooltip(btn);
const panel = createSidebar();
const chatLog = $("#ht-ai-chat");
const sendBtn = $("#ht-ai-send");
const inputBox = $("#ht-ai-question");
const resetBtn = $("#ht-ai-reset");
const closeBtn = $("#ht-ai-close");
/* ------------------- Selection snapshot ------------------- */
let savedSelection = "";
btn.addEventListener("pointerdown", () => { savedSelection = window.getSelection().toString().trim(); });
/* ------------------- Helpers ------------------------------ */
function addMsg(text, cls) {
const b = document.createElement("div");
b.className = `ht-msg ${cls}`;
b.textContent = text;
chatLog.appendChild(b);
chatLog.scrollTop = chatLog.scrollHeight;
return b;
}
const LOADER_HTML = '<span class="ht-loading"><span></span><span></span><span></span></span>';
function setInputDisabled(d) { inputBox.disabled = d; sendBtn.disabled = d; }
function clearThreadCookie() { document.cookie = "threadId=; Path=/; Max-Age=0"; threadId = null; }
function resetConversation() { chatLog.innerHTML=""; clearThreadCookie(); panel.classList.remove("open"); }
/* ------------------- Panel open / close ------------------- */
btn.addEventListener("click", () => {
if (!savedSelection) { alert("Please highlight some text first to then ask Hacktricks AI about it."); return; }
if (savedSelection.length > MAX_CONTEXT) { alert(`Highlighted text is too long (${savedSelection.length} chars). Max allowed: ${MAX_CONTEXT}.`); return; }
chatLog.innerHTML=""; addMsg(savedSelection, "ht-context"); panel.classList.add("open"); inputBox.focus();
});
closeBtn.addEventListener("click", resetConversation);
resetBtn.addEventListener("click", resetConversation);
/* --------------------------- Messaging --------------------------- */
async function sendMessage(question, context=null) {
if (!threadId) await ensureThreadId();
if (isRunning) { addMsg("Please wait until the current operation completes.", "ht-ai"); return; }
isRunning = true; setInputDisabled(true);
const loadingBubble = addMsg("", "ht-ai");
loadingBubble.innerHTML = LOADER_HTML;
const content = context ? `### Context:\n${context}\n\n### Question to answer:\n${question}` : question;
try {
const res = await fetch(`${API_BASE}/${threadId}/messages`, { method:"POST", credentials:"include", headers:{"Content-Type":"application/json"}, body:JSON.stringify({content}) });
if (!res.ok) {
let err=`Unknown error: ${res.status}`;
try { const e=await res.json(); if(e.error) err=`Error: ${e.error}`; else if(res.status===429) err="Rate limit exceeded. Please try again later."; } catch(_){}
loadingBubble.textContent = err; return; }
const data = await res.json();
loadingBubble.remove();
if (Array.isArray(data.response)) data.response.forEach(p=>{ addMsg( p.type==="text"&&p.text&&p.text.value ? p.text.value : JSON.stringify(p), "ht-ai"); });
else if (typeof data.response === "string") addMsg(data.response, "ht-ai");
else addMsg(JSON.stringify(data,null,2), "ht-ai");
} catch (e) { console.error("Error sending message:",e); loadingBubble.textContent="An unexpected error occurred."; }
finally { isRunning=false; setInputDisabled(false); chatLog.scrollTop=chatLog.scrollHeight; }
}
async function handleSend(){ const q=inputBox.value.trim(); if(!q)return; if(q.length>MAX_QUESTION){alert(`Your question is too long (${q.length} chars). Max allowed: ${MAX_QUESTION}.`); return;} inputBox.value=""; addMsg(q,"ht-user"); await sendMessage(q,savedSelection||null);}
sendBtn.addEventListener("click", handleSend);
inputBox.addEventListener("keydown", e=>{ if(e.key==="Enter"&&!e.shiftKey){ e.preventDefault(); handleSend(); } });
}
/* ==================================================================== */
async function ensureThreadId(){ const m=document.cookie.match(/threadId=([^;]+)/); if(m&&m[1]){threadId=m[1];return;} try{ const r=await fetch(API_BASE,{method:"POST",credentials:"include"}); const d=await r.json(); if(!r.ok||!d.threadId) throw new Error(`${r.status} ${r.statusText}`); threadId=d.threadId; document.cookie=`threadId=${threadId}; Path=/; Secure; SameSite=Strict; Max-Age=7200`; }catch(e){ console.error("Error creating threadId:",e); alert("Failed to initialise the conversation. Please refresh and try again."); throw e; }}
/* ==================================================================== */
function injectStyles(){ const css=`
#ht-ai-btn{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);width:60px;height:60px;border-radius:50%;background:#1e1e1e;color:#fff;font-size:28px;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:99999;box-shadow:0 2px 8px rgba(0,0,0,.4);transition:opacity .2s}
#ht-ai-btn:hover{opacity:.85}
@media(max-width:768px){#ht-ai-btn{display:none}}
#ht-ai-tooltip{position:fixed;padding:6px 8px;background:#111;color:#fff;border-radius:4px;font-size:13px;white-space:pre-wrap;pointer-events:none;opacity:0;transform:translate(-50%,-8px);transition:opacity .15s ease,transform .15s ease;z-index:100000}
#ht-ai-tooltip.show{opacity:1;transform:translate(-50%,-12px)}
#ht-ai-panel{position:fixed;top:0;right:0;height:100%;width:350px;max-width:90vw;background:#000;color:#fff;display:flex;flex-direction:column;transform:translateX(100%);transition:transform .3s ease;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial,sans-serif}
#ht-ai-panel.open{transform:translateX(0)}
@media(max-width:768px){#ht-ai-panel{display:none}}
#ht-ai-header{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;border-bottom:1px solid #333}
#ht-ai-header .ht-actions{display:flex;gap:8px;align-items:center}
#ht-ai-close,#ht-ai-reset{cursor:pointer;font-size:18px;background:none;border:none;color:#fff;padding:0}
#ht-ai-close:hover,#ht-ai-reset:hover{opacity:.7}
#ht-ai-chat{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;font-size:14px}
.ht-msg{max-width:90%;line-height:1.4;padding:10px 12px;border-radius:8px;white-space:pre-wrap;word-wrap:break-word}
.ht-user{align-self:flex-end;background:${BRAND_RED}}
.ht-ai{align-self:flex-start;background:#222}
.ht-context{align-self:flex-start;background:#444;font-style:italic;font-size:13px}
#ht-ai-input{display:flex;gap:8px;padding:12px 16px;border-top:1px solid #333}
#ht-ai-question{flex:1;min-height:40px;max-height:120px;resize:vertical;padding:8px;border-radius:6px;border:none;font-size:14px}
#ht-ai-send{padding:0 18px;border:none;border-radius:6px;background:${BRAND_RED};color:#fff;font-size:14px;cursor:pointer}
#ht-ai-send:disabled{opacity:.5;cursor:not-allowed}
/* Loader animation */
.ht-loading{display:inline-flex;align-items:center;gap:4px}
.ht-loading span{width:6px;height:6px;border-radius:50%;background:#888;animation:ht-bounce 1.2s infinite ease-in-out}
.ht-loading span:nth-child(2){animation-delay:0.2s}
.ht-loading span:nth-child(3){animation-delay:0.4s}
@keyframes ht-bounce{0%,80%,100%{transform:scale(0);}40%{transform:scale(1);} }
::selection{background:#ffeb3b;color:#000}
::-moz-selection{background:#ffeb3b;color:#000}`;
const s=document.createElement("style"); s.id="ht-ai-style"; s.textContent=css; document.head.appendChild(s);}
function createFloatingButton(){ const d=document.createElement("div"); d.id="ht-ai-btn"; d.textContent="🤖"; document.body.appendChild(d); return d; }
function createTooltip(btn){ const t=document.createElement("div"); t.id="ht-ai-tooltip"; t.textContent=TOOLTIP_TEXT; document.body.appendChild(t); btn.addEventListener("mouseenter",()=>{const r=btn.getBoundingClientRect(); t.style.left=`${r.left+r.width/2}px`; t.style.top=`${r.top}px`; t.classList.add("show");}); btn.addEventListener("mouseleave",()=>t.classList.remove("show")); }
function createSidebar(){ const p=document.createElement("div"); p.id="ht-ai-panel"; p.innerHTML=`<div id="ht-ai-header"><strong>HackTricksAI Chat</strong><div class="ht-actions"><button id="ht-ai-reset" title="Reset">↺</button><span id="ht-ai-close" title="Close">✖</span></div></div><div id="ht-ai-chat"></div><div id="ht-ai-input"><textarea id="ht-ai-question" placeholder="Type your question…"></textarea><button id="ht-ai-send">Send</button></div>`; document.body.appendChild(p); return p; }
})();

View File

@ -1,3 +1,26 @@
/*
Polyfill so requestIdleCallback works everywhere (IE 11/Safari)
*/
if (typeof window.requestIdleCallback !== "function") {
window.requestIdleCallback = function (cb) {
const start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
};
window.cancelIdleCallback = window.clearTimeout;
}
/*
search.js
*/
"use strict"; "use strict";
window.search = window.search || {}; window.search = window.search || {};
(function search(search) { (function search(search) {
@ -471,64 +494,58 @@ window.search = window.search || {};
showResults(true); showResults(true);
} }
(async function loadSearchIndex(lang = window.lang || 'en') { (async function loadSearchIndex(lang = window.lang || "en") {
/* ───────── paths ───────── */ const branch = lang === "en" ? "master" : lang;
const branch = lang === 'en' ? 'master' : lang; const rawUrl =
const baseRemote = `https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/${branch}`; `https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/${branch}/searchindex.js`;
const remoteJson = `${baseRemote}/searchindex.json`; const localJs = "/searchindex.js";
const remoteJs = `${baseRemote}/searchindex.js`; const TIMEOUT_MS = 10_000;
const localJson = './searchindex.json';
const localJs = './searchindex.js';
const TIMEOUT_MS = 5_000;
/* ───────── helpers ───────── */ const injectScript = (src) =>
const fetchWithTimeout = (url, opt = {}) =>
Promise.race([
fetch(url, opt),
new Promise((_, r) => setTimeout(() => r(new Error('timeout')), TIMEOUT_MS))
]);
const loadScript = src =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const s = document.createElement('script'); const s = document.createElement("script");
s.src = src; s.src = src;
s.onload = resolve; s.onload = () => resolve(src);
s.onerror = reject; s.onerror = (e) => reject(e);
document.head.appendChild(s); document.head.appendChild(s);
}); });
/* ───────── 1. remote JSON ───────── */
try { try {
const r = await fetchWithTimeout(remoteJson); /* 1 — download raw JS from GitHub */
if (!r.ok) throw new Error(r.status); const controller = new AbortController();
return init(await r.json()); const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
} catch (e) {
console.warn('Remote JSON failed →', e); const res = await fetch(rawUrl, { signal: controller.signal });
clearTimeout(timer);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
/* 2 — wrap in a Blob so the browser sees application/javascript */
const code = await res.text();
const blobUrl = URL.createObjectURL(
new Blob([code], { type: "application/javascript" })
);
/* 3 — execute it */
await injectScript(blobUrl);
/* PATCH
heavy parsing now deferred to idle time
*/
requestIdleCallback(() => init(window.search));
return; // ✔ UI remains responsive
} catch (eRemote) {
console.warn("Remote JS failed →", eRemote);
} }
/* ───────── 2. remote JS ───────── */ /* ───────── fallback: local copy ───────── */
try { try {
await loadScript(remoteJs); await injectScript(localJs);
return init(window.search);
} catch (e) {
console.warn('Remote JS failed →', e);
}
/* ───────── 3. local JSON ───────── */ /* ───────────── PATCH ───────────── */
try { requestIdleCallback(() => init(window.search));
const r = await fetch(localJson); return;
if (!r.ok) throw new Error(r.status); } catch (eLocal) {
return init(await r.json()); console.error("Local JS failed →", eLocal);
} catch (e) {
console.warn('Local JSON failed →', e);
}
/* ───────── 4. local JS ───────── */
try {
await loadScript(localJs);
return init(window.search);
} catch (e) {
console.error('Local JS failed →', e);
} }
})(); })();