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

This commit is contained in:
Translator 2025-04-27 16:32:58 +00:00
parent 9f7764f1f2
commit 21763d876d
5 changed files with 244 additions and 83 deletions

View File

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

View File

@ -561,6 +561,7 @@
- [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)
- [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)
- [Deserialization](pentesting-web/deserialization/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)
- [Reset/Forgotten Password Bypass](pentesting-web/reset-password.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 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)

View File

@ -9,7 +9,7 @@
2. Wykonaj podstawową enumerację sieci **szukając portali logowania** używanych przez ofiarę i **zdecyduj**, który z nich będziesz **podrabiać**.
3. Użyj **OSINT**, aby **znaleźć e-maile**.
2. Przygotowanie środowiska
1. **Kup domenę**, której zamierzasz użyć do oceny phishingowej.
1. **Kup domenę**, którą zamierzasz użyć do oceny phishingowej.
2. **Skonfiguruj usługi e-mail** związane z rekordami (SPF, DMARC, DKIM, rDNS).
3. Skonfiguruj VPS z **gophish**.
3. Przygotowanie kampanii
@ -47,20 +47,20 @@
### Bitflipping
Istnieje **możliwość, że jeden z bitów przechowywanych lub w komunikacji może zostać automatycznie zmieniony** z powodu różnych czynników, takich jak burze słoneczne, promieniowanie kosmiczne lub błędy sprzętowe.
Istnieje **możliwość, że jeden z bitów przechowywanych lub w komunikacji może zostać automatycznie zmieniony** z powodu różnych czynników, takich jak wybuchy słoneczne, promieniowanie kosmiczne lub błędy sprzętowe.
Gdy ten koncept jest **stosowany do zapytań DNS**, możliwe jest, że **domena odebrana przez serwer DNS** nie jest taka sama jak domena pierwotnie żądana.
Na przykład, pojedyncza modyfikacja bitu w domenie "windows.com" może zmienić ją na "windnws.com."
Napastnicy mogą **wykorzystać to, rejestrując wiele domen z bit-flipping**, które są podobne do domeny ofiary. Ich intencją jest przekierowanie legalnych użytkowników do własnej infrastruktury.
Atakujący mogą **wykorzystać to, rejestrując wiele domen z bit-flipping**, które są podobne do domeny ofiary. Ich intencją jest przekierowanie legalnych użytkowników do własnej infrastruktury.
Aby uzyskać więcej informacji, przeczytaj [https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/](https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/)
### Kup zaufaną domenę
Możesz poszukać na [https://www.expireddomains.net/](https://www.expireddomains.net) wygasłej domeny, której możesz użyć.\
Aby upewnić się, że wygasła domena, którą zamierzasz kupić, **ma już dobre SEO**, możesz sprawdzić, jak jest sklasyfikowana w:
Możesz poszukać na [https://www.expireddomains.net/](https://www.expireddomains.net) wygasłej domeny, którą mógłbyś wykorzystać.\
Aby upewnić się, że wygasła domena, którą zamierzasz kupić, **ma już dobrą SEO**, możesz sprawdzić, jak jest sklasyfikowana w:
- [http://www.fortiguard.com/webfilter](http://www.fortiguard.com/webfilter)
- [https://urlfiltering.paloaltonetworks.com/query/](https://urlfiltering.paloaltonetworks.com/query/)
@ -73,14 +73,14 @@ Aby upewnić się, że wygasła domena, którą zamierzasz kupić, **ma już dob
- [https://hunter.io/](https://hunter.io)
- [https://anymailfinder.com/](https://anymailfinder.com)
Aby **odkryć więcej** ważnych adresów e-mail lub **zweryfikować te, które** już odkryłeś, możesz sprawdzić, czy możesz przeprowadzić brute-force na serwerach smtp ofiary. [Dowiedz się, jak zweryfikować/odkryć adres e-mail tutaj](../../network-services-pentesting/pentesting-smtp/index.html#username-bruteforce-enumeration).\
Aby **odkryć więcej** ważnych adresów e-mail lub **zweryfikować te**, które już odkryłeś, możesz sprawdzić, czy możesz przeprowadzić brute-force na serwerach smtp ofiary. [Dowiedz się, jak zweryfikować/odkryć adres e-mail tutaj](../../network-services-pentesting/pentesting-smtp/index.html#username-bruteforce-enumeration).\
Ponadto nie zapomnij, że jeśli użytkownicy korzystają z **jakiegokolwiek portalu internetowego do dostępu do swoich e-maili**, możesz sprawdzić, czy jest on podatny na **brute force nazwy użytkownika** i wykorzystać tę podatność, jeśli to możliwe.
## Konfigurowanie GoPhish
### Instalacja
Możesz pobrać go z [https://github.com/gophish/gophish/releases/tag/v0.11.0](https://github.com/gophish/gophish/releases/tag/v0.11.0)
Możesz go pobrać z [https://github.com/gophish/gophish/releases/tag/v0.11.0](https://github.com/gophish/gophish/releases/tag/v0.11.0)
Pobierz i rozpakuj go w `/opt/gophish` i uruchom `/opt/gophish/gophish`\
Otrzymasz hasło dla użytkownika admin na porcie 3333 w wyjściu. Dlatego uzyskaj dostęp do tego portu i użyj tych danych logowania, aby zmienić hasło administratora. Może być konieczne tunelowanie tego portu do lokalnego:
@ -109,7 +109,7 @@ cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" /opt/gophish/ssl_keys/key.crt
```
**Konfiguracja poczty**
Zacznij od instalacji: `apt-get install postfix`
Rozpocznij instalację: `apt-get install postfix`
Następnie dodaj domenę do następujących plików:
@ -117,14 +117,14 @@ Następnie dodaj domenę do następujących plików:
- **/etc/postfix/transport**
- **/etc/postfix/virtual_regexp**
**Zmień także wartości następujących zmiennych w /etc/postfix/main.cf**
**Zmień również wartości następujących zmiennych w /etc/postfix/main.cf**
`myhostname = <domena>`\
`mydestination = $myhostname, <domena>, localhost.com, localhost`
`myhostname = <domain>`\
`mydestination = $myhostname, <domain>, localhost.com, localhost`
Na koniec zmodyfikuj pliki **`/etc/hostname`** i **`/etc/mailname`** na swoją nazwę domeny i **zrestartuj swój VPS.**
Teraz stwórz **rekord A DNS** dla `mail.<domena>` wskazujący na **adres IP** VPS oraz **rekord MX DNS** wskazujący na `mail.<domena>`
Teraz stwórz **rekord A DNS** dla `mail.<domain>` wskazujący na **adres IP** VPS oraz **rekord MX DNS** wskazujący na `mail.<domain>`
Teraz przetestuj wysyłanie e-maila:
```bash
@ -223,11 +223,11 @@ service gophish stop
### Czekaj i bądź legitny
Im starsza domena, tym mniej prawdopodobne, że zostanie uznana za spam. Dlatego powinieneś czekać jak najdłużej (przynajmniej 1 tydzień) przed oceną phishingu. Co więcej, jeśli umieścisz stronę o reputacyjnym sektorze, uzyskana reputacja będzie lepsza.
Im starsza domena, tym mniej prawdopodobne, że zostanie uznana za spam. Powinieneś więc czekać jak najdłużej (przynajmniej 1 tydzień) przed oceną phishingu. Co więcej, jeśli umieścisz stronę o reputacyjnym sektorze, uzyskana reputacja będzie lepsza.
Zauważ, że nawet jeśli musisz czekać tydzień, możesz teraz zakończyć konfigurację wszystkiego.
### Skonfiguruj rekord Reverse DNS (rDNS)
### Skonfiguruj rekord odwrotnego DNS (rDNS)
Ustaw rekord rDNS (PTR), który rozwiązuje adres IP VPS na nazwę domeny.
@ -243,7 +243,7 @@ To jest zawartość, która musi być ustawiona wewnątrz rekordu TXT w domenie:
```bash
v=spf1 mx a ip4:ip.ip.ip.ip ?all
```
### Rekord uwierzytelniania wiadomości oparty na domenie, raportowanie i zgodność (DMARC)
### Rekord uwierzytelniania wiadomości opartego na domenie, raportowania i zgodności (DMARC)
Musisz **skonfigurować rekord DMARC dla nowej domeny**. Jeśli nie wiesz, czym jest rekord DMARC [**przeczytaj tę stronę**](../../network-services-pentesting/pentesting-smtp/index.html#dmarc).
@ -253,7 +253,7 @@ v=DMARC1; p=none
```
### DomainKeys Identified Mail (DKIM)
Musisz **skonfigurować DKIM dla nowej domeny**. Jeśli nie wiesz, co to jest rekord DMARC [**przeczytaj tę stronę**](../../network-services-pentesting/pentesting-smtp/index.html#dkim).
Musisz **skonfigurować DKIM dla nowej domeny**. Jeśli nie wiesz, czym jest rekord DMARC [**przeczytaj tę stronę**](../../network-services-pentesting/pentesting-smtp/index.html#dkim).
Ten samouczek oparty jest na: [https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-dkim-with-postfix-on-debian-wheezy)
@ -271,7 +271,7 @@ Po prostu wejdź na stronę i wyślij e-mail na adres, który ci podadzą:
```bash
echo "This is the body of the email" | mail -s "This is the subject line" test-iimosa79z@srv1.mail-tester.com
```
Możesz również **sprawdzić swoją konfigurację e-mail** wysyłając wiadomość e-mail na `check-auth@verifier.port25.com` i **czytając odpowiedź** (w tym celu będziesz musiał **otworzyć** port **25** i zobaczyć odpowiedź w pliku _/var/mail/root_, jeśli wyślesz e-mail jako root).\
Możesz również **sprawdzić swoją konfigurację e-mail** wysyłając wiadomość e-mail na `check-auth@verifier.port25.com` i **czytając odpowiedź** (w tym celu musisz **otworzyć** port **25** i zobaczyć odpowiedź w pliku _/var/mail/root_, jeśli wysyłasz e-mail jako root).\
Sprawdź, czy przeszedłeś wszystkie testy:
```bash
==========================================================
@ -289,29 +289,29 @@ Authentication-Results: mx.google.com;
spf=pass (google.com: domain of contact@example.com designates --- as permitted sender) smtp.mail=contact@example.com;
dkim=pass header.i=@example.com;
```
### Usuwanie z czarnej listy Spamhouse
### Usuwanie z Czarnej Listy Spamhouse
Strona [www.mail-tester.com](https://www.mail-tester.com) może wskazać, czy Twoja domena jest blokowana przez spamhouse. Możesz poprosić o usunięcie swojej domeny/IP pod adresem: [https://www.spamhaus.org/lookup/](https://www.spamhaus.org/lookup/)
### Usuwanie z czarnej listy Microsoftu
### Usuwanie z Czarnej Listy Microsoft
Możesz poprosić o usunięcie swojej domeny/IP pod adresem [https://sender.office.com/](https://sender.office.com).
## Utwórz i uruchom kampanię GoPhish
## Utwórz i Uruchom Kampanię GoPhish
### Profil wysyłania
### Profil Wysyłający
- Ustaw **nazwę identyfikującą** profil nadawcy
- Zdecyduj, z którego konta będziesz wysyłać phishingowe e-maile. Sugestie: _noreply, support, servicedesk, salesforce..._
- Możesz pozostawić puste pole z nazwą użytkownika i hasłem, ale upewnij się, że zaznaczyłeś Ignoruj błędy certyfikatu
- Zdecyduj, z którego konta będziesz wysyłać e-maile phishingowe. Sugestie: _noreply, support, servicedesk, salesforce..._
- Możesz pozostawić puste nazwę użytkownika i hasło, ale upewnij się, że zaznaczyłeś Ignoruj błędy certyfikatu
![](<../../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]
> Zaleca się korzystanie z funkcji "**Wyślij e-mail testowy**", aby sprawdzić, czy wszystko działa.\
> Zalecałbym **wysyłanie e-maili testowych na adresy 10min mail**, aby uniknąć dodania do czarnej listy podczas testów.
> Zalecałbym **wysyłanie e-maili testowych na adresy 10min**, aby uniknąć dodania do czarnej listy podczas testów.
### Szablon e-maila
### Szablon E-mail
- Ustaw **nazwę identyfikującą** szablon
- Następnie napisz **temat** (nic dziwnego, po prostu coś, co mógłbyś oczekiwać w zwykłym e-mailu)
@ -335,7 +335,7 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
</body>
</html>
```
Zauważ, że **aby zwiększyć wiarygodność e-maila**, zaleca się użycie jakiegoś podpisu z e-maila od klienta. Sugestie:
Zauważ, że **aby zwiększyć wiarygodność e-maila**, zaleca się użycie jakiegoś podpisu z e-maila klienta. Sugestie:
- Wyślij e-mail na **nieistniejący adres** i sprawdź, czy odpowiedź zawiera jakiś podpis.
- Szukaj **publicznych e-maili** takich jak info@ex.com lub press@ex.com lub public@ex.com i wyślij im e-mail, a następnie czekaj na odpowiedź.
@ -357,7 +357,7 @@ Zauważ, że **aby zwiększyć wiarygodność e-maila**, zaleca się użycie jak
> [!NOTE]
> Zazwyczaj będziesz musiał zmodyfikować kod HTML strony i przeprowadzić kilka testów lokalnie (może używając jakiegoś serwera Apache) **aż do uzyskania zadowalających wyników.** Następnie wpisz ten kod HTML w polu.\
> Zauważ, że jeśli potrzebujesz **użyć jakichś zasobów statycznych** dla HTML (może jakieś strony CSS i JS), możesz je zapisać w _**/opt/gophish/static/endpoint**_ i następnie uzyskać do nich dostęp z _**/static/\<filename>**_
> Zauważ, że jeśli potrzebujesz **użyć jakichś statycznych zasobów** dla HTML (może jakieś strony CSS i JS), możesz je zapisać w _**/opt/gophish/static/endpoint**_ i następnie uzyskać do nich dostęp z _**/static/\<filename>**_
> [!NOTE]
> W przypadku przekierowania możesz **przekierować użytkowników na legitną główną stronę internetową** ofiary lub przekierować ich na _/static/migration.html_, na przykład, umieścić jakiś **kręcący się kółko (**[**https://loading.io/**](https://loading.io)**) przez 5 sekund, a następnie wskazać, że proces zakończył się sukcesem**.
@ -414,12 +414,12 @@ Tutaj przydatne są narzędzia takie jak [**evilginx2**](https://github.com/kgre
### Poprzez VNC
Co jeśli zamiast **wysyłać ofiarę na złośliwą stronę** o takim samym wyglądzie jak oryginalna, wyślesz go do **sesji VNC z przeglądarką połączoną z prawdziwą stroną internetową**? Będziesz mógł zobaczyć, co robi, ukraść hasło, używaną MFA, ciasteczka...\
Co jeśli zamiast **wysyłać ofiarę na złośliwą stronę** o takim samym wyglądzie jak oryginalna, wyślesz go na **sesję VNC z przeglądarką połączoną z prawdziwą stroną internetową**? Będziesz mógł zobaczyć, co robi, ukraść hasło, używane MFA, ciasteczka...\
Możesz to zrobić za pomocą [**EvilnVNC**](https://github.com/JoelGMSec/EvilnoVNC)
## Wykrywanie wykrycia
Oczywiście jednym z najlepszych sposobów, aby dowiedzieć się, czy zostałeś wykryty, jest **sprawdzenie swojej domeny w czarnych listach**. Jeśli pojawi się na liście, w jakiś sposób twoja domena została wykryta jako podejrzana.\
Oczywiście jednym z najlepszych sposobów, aby dowiedzieć się, czy zostałeś złapany, jest **sprawdzenie swojej domeny w czarnych listach**. Jeśli pojawi się na liście, w jakiś sposób twoja domena została wykryta jako podejrzana.\
Jednym z łatwych sposobów, aby sprawdzić, czy twoja domena pojawia się w jakiejkolwiek czarnej liście, jest użycie [https://malwareworld.com/](https://malwareworld.com)
Jednak istnieją inne sposoby, aby dowiedzieć się, czy ofiara **aktywnie szuka podejrzanej aktywności phishingowej w sieci**, jak wyjaśniono w:

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";
window.search = window.search || {};
(function search(search) {
@ -471,64 +494,58 @@ window.search = window.search || {};
showResults(true);
}
(async function loadSearchIndex(lang = window.lang || 'en') {
/* ───────── paths ───────── */
const branch = lang === 'en' ? 'master' : lang;
const baseRemote = `https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/${branch}`;
const remoteJson = `${baseRemote}/searchindex.json`;
const remoteJs = `${baseRemote}/searchindex.js`;
const localJson = './searchindex.json';
const localJs = './searchindex.js';
const TIMEOUT_MS = 5_000;
(async function loadSearchIndex(lang = window.lang || "en") {
const branch = lang === "en" ? "master" : lang;
const rawUrl =
`https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/${branch}/searchindex.js`;
const localJs = "/searchindex.js";
const TIMEOUT_MS = 10_000;
/* ───────── helpers ───────── */
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) => {
const s = document.createElement('script');
s.src = src;
s.onload = resolve;
s.onerror = reject;
const injectScript = (src) =>
new Promise((resolve, reject) => {
const s = document.createElement("script");
s.src = src;
s.onload = () => resolve(src);
s.onerror = (e) => reject(e);
document.head.appendChild(s);
});
});
/* ───────── 1. remote JSON ───────── */
try {
const r = await fetchWithTimeout(remoteJson);
if (!r.ok) throw new Error(r.status);
return init(await r.json());
} catch (e) {
console.warn('Remote JSON failed →', e);
/* 1 — download raw JS from GitHub */
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
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 {
await loadScript(remoteJs);
return init(window.search);
} catch (e) {
console.warn('Remote JS failed →', e);
}
await injectScript(localJs);
/* ───────── 3. local JSON ───────── */
try {
const r = await fetch(localJson);
if (!r.ok) throw new Error(r.status);
return init(await r.json());
} 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);
/* ───────────── PATCH ───────────── */
requestIdleCallback(() => init(window.search));
return;
} catch (eLocal) {
console.error("Local JS failed →", eLocal);
}
})();