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

This commit is contained in:
Translator 2025-04-27 16:33:13 +00:00
parent 1f6f61c8f5
commit fcb76ebac5
5 changed files with 248 additions and 87 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

@ -2,19 +2,19 @@
{{#include ../../banners/hacktricks-training.md}}
## Методологія
## Methodology
1. Розвідка жертви
1. Виберіть **домен жертви**.
2. Виконайте базову веб-енумерацію **в пошуках порталів для входу**, які використовує жертва, і **вирішіть**, який з них ви будете **імітувати**.
2. Виконайте базову веб-інвентаризацію **в пошуках порталів для входу**, які використовує жертва, і **вирішіть**, який з них ви будете **імітувати**.
3. Використовуйте деякі **OSINT** для **пошуку електронних адрес**.
2. Підготуйте середовище
1. **Купіть домен**, який ви будете використовувати для фішингової оцінки.
2. **Налаштуйте електронну пошту** та пов'язані записи (SPF, DMARC, DKIM, rDNS).
3. Налаштуйте VPS з **gophish**.
1. **Купіть домен**, який ви будете використовувати для фішингової оцінки
2. **Налаштуйте записи** служби електронної пошти (SPF, DMARC, DKIM, rDNS)
3. Налаштуйте VPS з **gophish**
3. Підготуйте кампанію
1. Підготуйте **шаблон електронної пошти**.
2. Підготуйте **веб-сторінку** для крадіжки облікових даних.
1. Підготуйте **шаблон електронної пошти**
2. Підготуйте **веб-сторінку** для крадіжки облікових даних
4. Запустіть кампанію!
## Генерація подібних доменних імен або купівля надійного домену
@ -22,12 +22,12 @@
### Техніки варіації доменних імен
- **Ключове слово**: Доменне ім'я **містить** важливе **ключове слово** оригінального домену (наприклад, zelster.com-management.com).
- **Гіпенізований піддомен**: Змініть **крапку на дефіс** у піддомені (наприклад, www-zelster.com).
- **Новий TLD**: Той самий домен з використанням **нового TLD** (наприклад, zelster.org).
- **Гіпенізований піддомен**: Змініть **крапку на дефіс** піддомену (наприклад, www-zelster.com).
- **Новий TLD**: Той самий домен з використанням **нового TLD** (наприклад, zelster.org)
- **Гомогліф**: Він **замінює** літеру в доменному імені на **літери, які виглядають подібно** (наприклад, zelfser.com).
- **Транспозиція:** Він **міняє місцями дві літери** в доменному імені (наприклад, zelsetr.com).
- **Сингуларизація/Плюралізація**: Додає або видаляє “s” в кінці доменного імені (наприклад, zeltsers.com).
- **Пропуск**: Він **видаляє одну** з літер з доменного імені (наприклад, zelser.com).
- **Виключення**: Він **видаляє одну** з літер з доменного імені (наприклад, zelser.com).
- **Повторення:** Він **повторює одну** з літер у доменному імені (наприклад, zeltsser.com).
- **Замінювання**: Як гомогліф, але менш непомітно. Він замінює одну з літер у доменному імені, можливо, на літеру, що знаходиться поруч з оригінальною літерою на клавіатурі (наприклад, zektser.com).
- **Піддомен**: Введіть **крапку** всередині доменного імені (наприклад, ze.lster.com).
@ -45,15 +45,15 @@
- [https://dnstwister.report/](https://dnstwister.report)
- [https://www.internetmarketingninjas.com/tools/free-tools/domain-typo-generator/](https://www.internetmarketingninjas.com/tools/free-tools/domain-typo-generator/)
### Бітфліпінг
### Bitflipping
Існує **можливість, що один з бітів, що зберігаються або передаються, може автоматично змінитися** через різні фактори, такі як сонячні спалахи, космічні промені або апаратні помилки.
Існує **можливість, що один з деяких бітів, збережених або в комунікації, може автоматично змінитися** через різні фактори, такі як сонячні спалахи, космічні промені або апаратні помилки.
Коли цей концепт **застосовується до DNS-запитів**, можливо, що **домен, отриманий DNS-сервером**, не є тим самим доменом, який спочатку запитувався.
Наприклад, одна зміна біта в домені "windows.com" може змінити його на "windnws.com."
Зловмисники можуть **використовувати це, реєструючи кілька доменів з бітфліпінгом**, які схожі на домен жертви. Їхнє намір — перенаправити легітимних користувачів на свою інфраструктуру.
Зловмисники можуть **використовувати це, реєструючи кілька доменів з битфліпом**, які схожі на домен жертви. Їх намір - перенаправити легітимних користувачів на свою інфраструктуру.
Для отримання додаткової інформації читайте [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/)
@ -117,16 +117,16 @@ cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" /opt/gophish/ssl_keys/key.crt
- **/etc/postfix/transport**
- **/etc/postfix/virtual_regexp**
**Також змініть значення наступних змінних у файлі /etc/postfix/main.cf**
**Також змініть значення наступних змінних у /etc/postfix/main.cf**
`myhostname = <domain>`\
`mydestination = $myhostname, <domain>, localhost.com, localhost`
Нарешті, змініть файли **`/etc/hostname`** та **`/etc/mailname`** на ваше доменне ім'я та **перезавантажте ваш VPS.**
Нарешті, змініть файли **`/etc/hostname`** та **`/etc/mailname`** на ваше ім'я домену та **перезавантажте ваш VPS.**
Тепер створіть **DNS A запис** для `mail.<domain>`, що вказує на **ip-адресу** VPS, та **DNS MX** запис, що вказує на `mail.<domain>`
Тепер давайте протестуємо відправку електронного листа:
Тепер давайте протестуємо відправку електронної пошти:
```bash
apt install mailutils
echo "This is the body of the email" | mail -s "This is the subject line" test@email.com
@ -223,7 +223,7 @@ service gophish stop
### Чекайте та будьте легітимними
Чим старіший домен, тим менше ймовірно, що його сприймуть як спам. Тому вам слід чекати якомога більше часу (принаймні 1 тиждень) перед оцінкою фішингу. Більше того, якщо ви створите сторінку про репутаційний сектор, отримана репутація буде кращою.
Чим старіший домен, тим менше ймовірно, що його сприймуть як спам. Тому вам слід чекати якомога довше (принаймні 1 тиждень) перед оцінкою фішингу. Більше того, якщо ви створите сторінку про репутаційний сектор, отримана репутація буде кращою.
Зверніть увагу, що навіть якщо вам потрібно почекати тиждень, ви можете закінчити налаштування всього зараз.
@ -235,7 +235,7 @@ service gophish stop
Вам потрібно **налаштувати запис SPF для нового домену**. Якщо ви не знаєте, що таке запис SPF [**прочитайте цю сторінку**](../../network-services-pentesting/pentesting-smtp/index.html#spf).
Ви можете використовувати [https://www.spfwizard.net/](https://www.spfwizard.net) для генерації вашої SPF політики (використовуйте IP-адресу машини VPS)
Ви можете використовувати [https://www.spfwizard.net/](https://www.spfwizard.net) для генерації вашої SPF політики (використовуйте IP-адресу VPS машини)
![](<../../images/image (1037).png>)
@ -245,7 +245,7 @@ v=spf1 mx a ip4:ip.ip.ip.ip ?all
```
### Domain-based Message Authentication, Reporting & Conformance (DMARC) Record
Ви повинні **налаштувати запис DMARC для нового домену**. Якщо ви не знаєте, що таке запис DMARC [**прочитайте цю сторінку**](../../network-services-pentesting/pentesting-smtp/index.html#dmarc).
Вам потрібно **налаштувати запис DMARC для нового домену**. Якщо ви не знаєте, що таке запис DMARC [**прочитайте цю сторінку**](../../network-services-pentesting/pentesting-smtp/index.html#dmarc).
Вам потрібно створити новий DNS TXT запис, вказуючи ім'я хоста `_dmarc.<domain>` з наступним вмістом:
```bash
@ -267,11 +267,11 @@ v=DMARC1; p=none
### Test your email configuration score
Ви можете зробити це, використовуючи [https://www.mail-tester.com/](https://www.mail-tester.com)\
Просто перейдіть на сторінку та надішліть електронний лист на адресу, яку вони вам нададуть:
Просто перейдіть на сторінку і надішліть електронний лист на адресу, яку вони вам нададуть:
```bash
echo "This is the body of the email" | mail -s "This is the subject line" test-iimosa79z@srv1.mail-tester.com
```
Ви також можете **перевірити свою конфігурацію електронної пошти**, надіславши електронний лист на `check-auth@verifier.port25.com` та **прочитавши відповідь** (для цього вам потрібно буде **відкрити** порт **25** і побачити відповідь у файлі _/var/mail/root_, якщо ви надішлете електронний лист як root).\
Ви також можете **перевірити конфігурацію вашої електронної пошти**, надіславши електронний лист на `check-auth@verifier.port25.com` та **прочитавши відповідь** (для цього вам потрібно буде **відкрити** порт **25** і переглянути відповідь у файлі _/var/mail/root_, якщо ви надішлете електронний лист як root).\
Перевірте, що ви пройшли всі тести:
```bash
==========================================================
@ -303,9 +303,9 @@ dkim=pass header.i=@example.com;
- Встановіть **ім'я для ідентифікації** профілю відправника
- Вирішіть, з якого облікового запису ви будете надсилати фішингові електронні листи. Пропозиції: _noreply, support, servicedesk, salesforce..._
- Ви можете залишити порожніми ім'я користувача та пароль, але обов'язково перевірте "Ігнорувати помилки сертифіката"
- Ви можете залишити ім'я користувача та пароль порожніми, але обов'язково перевірте "Ігнорувати помилки сертифіката"
![](<../../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) (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]
> Рекомендується використовувати функцію "**Надіслати тестовий електронний лист**", щоб перевірити, чи все працює.\
@ -337,20 +337,20 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
```
Зверніть увагу, що **для підвищення достовірності електронного листа** рекомендується використовувати підпис з електронного листа клієнта. Пропозиції:
- Відправте електронний лист на **неіснуючу адресу** та перевірте, чи є у відповіді підпис.
- Відправте електронний лист на **неіснуючу адресу** та перевірте, чи є у відповіді якась підпис.
- Шукайте **публічні електронні адреси** такі як info@ex.com або press@ex.com або public@ex.com і надішліть їм електронний лист, а потім чекайте на відповідь.
- Спробуйте зв'язатися з **якою-небудь дійсною виявленою** електронною адресою та чекайте на відповідь.
![](<../../images/image (80).png>)
> [!NOTE]
> Шаблон електронного листа також дозволяє **додавати файли для відправки**. Якщо ви також хочете вкрасти NTLM виклики, використовуючи спеціально підготовлені файли/документи [прочитайте цю сторінку](../../windows-hardening/ntlm/places-to-steal-ntlm-creds.md).
> Шаблон електронного листа також дозволяє **додавати файли для відправки**. Якщо ви також хочете вкрасти NTLM виклики, використовуючи спеціально підготовлені файли/документи, [прочитайте цю сторінку](../../windows-hardening/ntlm/places-to-steal-ntlm-creds.md).
### Лендінг Пейдж
- Напишіть **ім'я**
- **Напишіть HTML код** веб-сторінки. Зверніть увагу, що ви можете **імпортувати** веб-сторінки.
- Позначте **Захопити надіслані дані** та **Захопити паролі**
- Позначте **Збирати надіслані дані** та **Збирати паролі**
- Встановіть **перенаправлення**
![](<../../images/image (826).png>)
@ -360,7 +360,7 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
> Зверніть увагу, що якщо вам потрібно **використовувати деякі статичні ресурси** для HTML (можливо, деякі CSS та JS сторінки), ви можете зберегти їх у _**/opt/gophish/static/endpoint**_ і потім отримати до них доступ з _**/static/\<filename>**_
> [!NOTE]
> Для перенаправлення ви можете **перенаправити користувачів на легітимну основну веб-сторінку** жертви або перенаправити їх на _/static/migration.html_, наприклад, поставити **крутячий коло (**[**https://loading.io/**](https://loading.io)**) на 5 секунд, а потім вказати, що процес був успішним**.
> Для перенаправлення ви можете **перенаправити користувачів на легітимну основну веб-сторінку** жертви або перенаправити їх на _/static/migration.html_, наприклад, поставити **крутильне колесо (**[**https://loading.io/**](https://loading.io)**) на 5 секунд, а потім вказати, що процес був успішним**.
### Користувачі та Групи
@ -392,7 +392,7 @@ clone-a-website.md
## Документи та файли з бекдором
У деяких фішингових оцінках (в основному для Red Teams) ви також захочете **надсилати файли, що містять якийсь бекдор** (можливо, C2 або просто щось, що викликає аутентифікацію).\
У деяких фішингових оцінках (в основному для Red Teams) ви також захочете **надсилати файли, що містять якийсь вид бекдору** (можливо, C2 або просто щось, що викликає аутентифікацію).\
Перегляньте наступну сторінку для деяких прикладів:
{{#ref}}
@ -403,18 +403,18 @@ phishing-documents.md
### Через Proxy MitM
Попередня атака досить хитра, оскільки ви підробляєте реальний веб-сайт і збираєте інформацію, введену користувачем. На жаль, якщо користувач не ввів правильний пароль або якщо програма, яку ви підробили, налаштована на 2FA, **ця інформація не дозволить вам видавати себе за обманутого користувача**.
Попередня атака досить хитра, оскільки ви підробляєте реальний веб-сайт і збираєте інформацію, яку вводить користувач. На жаль, якщо користувач не ввів правильний пароль або якщо програма, яку ви підробили, налаштована з 2FA, **ця інформація не дозволить вам видавати себе за обманутого користувача**.
Ось де корисні інструменти, такі як [**evilginx2**](https://github.com/kgretzky/evilginx2)**,** [**CredSniper**](https://github.com/ustayready/CredSniper) та [**muraena**](https://github.com/muraenateam/muraena). Цей інструмент дозволить вам згенерувати атаку типу MitM. В основному, атака працює наступним чином:
1. Ви **підробляєте форму входу** реальної веб-сторінки.
2. Користувач **надсилає** свої **облікові дані** на вашу підроблену сторінку, а інструмент надсилає їх на реальну веб-сторінку, **перевіряючи, чи працюють облікові дані**.
3. Якщо обліковий запис налаштований на **2FA**, сторінка MitM запитає про це, і як тільки **користувач введе** його, інструмент надішле його на реальну веб-сторінку.
4. Як тільки користувач аутентифікований, ви (як зловмисник) отримаєте **захоплені облікові дані, 2FA, куки та будь-яку інформацію** про кожну взаємодію, поки інструмент виконує MitM.
3. Якщо обліковий запис налаштований з **2FA**, сторінка MitM запитає про це, і як тільки **користувач введе** його, інструмент надішле його на реальну веб-сторінку.
4. Як тільки користувач аутентифікований, ви (як атакуючий) будете мати **захоплені облікові дані, 2FA, куки та будь-яку інформацію** про кожну взаємодію, поки інструмент виконує MitM.
### Через VNC
Що, якщо замість того, щоб **надсилати жертву на шкідливу сторінку** з таким же виглядом, як оригінальна, ви надішлете його на **сесію VNC з браузером, підключеним до реальної веб-сторінки**? Ви зможете бачити, що він робить, вкрасти пароль, використане MFA, куки...\
А що, якщо замість **відправлення жертви на шкідливу сторінку** з таким же виглядом, як оригінальна, ви відправите його на **сесію VNC з браузером, підключеним до реальної веб-сторінки**? Ви зможете бачити, що він робить, вкрасти пароль, використане MFA, куки...\
Ви можете зробити це за допомогою [**EvilnVNC**](https://github.com/JoelGMSec/EvilnoVNC)
## Виявлення виявлення
@ -428,7 +428,7 @@ phishing-documents.md
detecting-phising.md
{{#endref}}
Ви можете **придбати домен з дуже схожою назвою** на домен жертви **і/або згенерувати сертифікат** для **піддомену** домену, контрольованого вами, **який містить** **ключове слово** домену жертви. Якщо **жертва** виконає будь-який вид **DNS або HTTP взаємодії** з ними, ви дізнаєтеся, що **він активно шукає** підозрілі домени, і вам потрібно буде бути дуже обережним.
Ви можете **придбати домен з дуже схожою назвою** на домен жертви **і/або згенерувати сертифікат** для **субдомену** домену, контрольованого вами, **який містить** **ключове слово** домену жертви. Якщо **жертва** виконає будь-який вид **DNS або HTTP взаємодії** з ними, ви дізнаєтеся, що **він активно шукає** підозрілі домени, і вам потрібно буде бути дуже обережним.
### Оцінка фішингу

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;
/* ───────── 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;
(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;
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);
}
/* ───────── 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);
await injectScript(localJs);
/* ───────────── PATCH ───────────── */
requestIdleCallback(() => init(window.search));
return;
} catch (eLocal) {
console.error("Local JS failed →", eLocal);
}
})();