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

This commit is contained in:
Translator 2025-04-27 16:33:02 +00:00
parent 0bc6fd356c
commit 055eee725c
5 changed files with 257 additions and 96 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

@ -6,11 +6,11 @@
1. 被害者の調査
1. **被害者のドメイン**を選択します。
2. 被害者が使用している**ログインポータル**を探すために基本的なウェブ列挙を行い、**なりすます**ポータルを**決定**します。
2. 被害者が使用している**ログインポータル**を探すために基本的なウェブ列挙を行い、**なりすます**ポータルを**決定**します。
3. **OSINT**を使用して**メールアドレス**を**見つけます**
2. 環境の準備
1. フィッシング評価に使用する**ドメインを購入**します。
2. 関連する**メールサービス**のレコードを**設定**しますSPF、DMARC、DKIM、rDNS
2. 関連するメールサービスのレコードを**設定**しますSPF、DMARC、DKIM、rDNS
3. **gophish**でVPSを設定します。
3. キャンペーンの準備
1. **メールテンプレート**を準備します。
@ -24,12 +24,12 @@
- **キーワード**: ドメイン名は元のドメインの重要な**キーワード**を**含みます**(例: zelster.com-management.com
- **ハイフン付きサブドメイン**: サブドメインの**ドットをハイフンに変更**します(例: www-zelster.com
- **新しいTLD**: 同じドメインを使用して**新しいTLD**を使用します(例: zelster.org
- **ホモグリフ**: ドメイン名の文字を**似たような文字に置き換えます**(例: zelfser.com
- **ホモグリフ**: ドメイン名の文字を**似た文字に置き換えます**(例: zelfser.com
- **転置**: ドメイン名内の**2つの文字を入れ替えます**(例: zelsetr.com
- **単数化/複数化**: ドメイン名の末尾に「s」を追加または削除します例: zeltsers.com
- **省略**: ドメイン名から**1つの文字を削除します**(例: zelser.com
- **繰り返し**: ドメイン名の**1つの文字を繰り返します**(例: zeltsser.com
- **置換**: ホモグリフのようですが、あまりステルスではありません。ドメイン名の1つの文字を、元の文字の近くにあるキーボードの文字に置き換えます例: zektser.com
- **繰り返し**: ドメイン名の**1つの文字を繰り返します**(例: zeltsser.com
- **置換**: ホモグリフに似ていますが、より目立ちます。ドメイン名内の1つの文字を、元の文字の近くにあるキーボードの文字に置き換えます例: zektser.com
- **サブドメイン化**: ドメイン名内に**ドットを挿入します**(例: ze.lster.com
- **挿入**: ドメイン名に**文字を挿入します**(例: zerltser.com
- **ドットの欠落**: ドメイン名にTLDを追加します例: zelstercom.com
@ -47,15 +47,15 @@
### ビットフリッピング
**通信中に保存されたビットの1つが自動的に反転する可能性があります**。これは、太陽フレア、宇宙線、またはハードウェアエラーなどのさまざまな要因によるものです。
いくつかのビットが保存または通信中に**自動的に反転する可能性があります**。これは、太陽フレア、宇宙線、またはハードウェアエラーなどのさまざまな要因によるものです。
この概念が**DNSリクエストに適用されると**、**DNSサーバーによって受信されたドメイン**が、最初にリクエストされたドメインと同じでない可能性があります。
例えば、ドメイン「windows.com」の1ビットの変更により、「windnws.com」に変わることがあります。
例えば、ドメイン「windows.com」の1ビットの変更は「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/)をお読みください
詳細については、[こちらを読んでください](https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/)。
### 信頼できるドメインを購入する
@ -73,8 +73,8 @@
- [https://hunter.io/](https://hunter.io)
- [https://anymailfinder.com/](https://anymailfinder.com)
**より多くの**有効なメールアドレスを**発見するか、すでに発見したものを**確認するために、被害者のSMTPサーバーをブルートフォース攻撃できるか確認できます。[メールアドレスの確認/発見方法についてはこを学んでください](../../network-services-pentesting/pentesting-smtp/index.html#username-bruteforce-enumeration)。\
さらに、ユーザーが**メールにアクセスするためのウェブポータルを使用している場合**、それが**ユーザーネームのブルートフォース攻撃に対して脆弱であるかどうかを確認し、可能であればその脆弱性を悪用することを忘れないでください**。
**より多くの**有効なメールアドレスを**発見するか、すでに発見したものを確認するために**、被害者のSMTPサーバーをブルートフォース攻撃できるか確認できます。[メールアドレスの確認/発見方法についてはこちらを学んでください](../../network-services-pentesting/pentesting-smtp/index.html#username-bruteforce-enumeration)。\
さらに、ユーザーが**メールにアクセスするためのウェブポータルを使用している場合**、それが**ユーザーブルートフォースに対して脆弱であるかどうかを確認し、可能であればその脆弱性を悪用することを忘れないでください**。
## GoPhishの設定
@ -124,9 +124,9 @@ cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" /opt/gophish/ssl_keys/key.crt
最後に、ファイル **`/etc/hostname`** と **`/etc/mailname`** をあなたのドメイン名に変更し、**VPSを再起動します。**
次に、**DNS Aレコード** `mail.<domain>` をVPSの**IPアドレス**にポイントさせ、**DNS MX**レコードを `mail.<domain>` にポイントさせます。
次に、**DNS Aレコード**`mail.<domain>` に設定し、**VPSのIPアドレス**を指すようにし、**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
@ -208,7 +208,7 @@ case $1 in
start|stop|status) "$1" ;;
esac
```
サービスの設定を完了し、次のことを確認します:
サービスの設定を完了し、次の操作を行って確認します:
```bash
mkdir /var/log/gophish
chmod +x /etc/init.d/gophish
@ -225,9 +225,9 @@ service gophish stop
ドメインが古いほど、スパムとして検出される可能性は低くなります。そのため、フィッシング評価の前にできるだけ長く少なくとも1週間待つべきです。さらに、評判の良い分野に関するページを作成すれば、得られる評判はより良くなります。
1週間待たなければならない場合でも、今すぐにすべての設定を完了させることができます。
1週間待たなければならない場合でも、今すぐにすべての設定を終えることができます。
### リバースDNSrDNSレコードの設定
### 逆引きDNSrDNSレコードの設定
VPSのIPアドレスをドメイン名に解決するrDNSPTRレコードを設定します。
@ -247,7 +247,7 @@ v=spf1 mx a ip4:ip.ip.ip.ip ?all
新しいドメインのために**DMARCレコードを設定する必要があります**。DMARCレコードが何か分からない場合は、[**このページを読んでください**](../../network-services-pentesting/pentesting-smtp/index.html#dmarc)。
ホスト名 `_dmarc.<domain>` を指す新しいDNS TXTレコードを作成し、以下の内容を含める必要があります:
次の内容でホスト名 `_dmarc.<domain>` を指す新しいDNS TXTレコードを作成する必要があります:
```bash
v=DMARC1; p=none
```
@ -255,10 +255,10 @@ v=DMARC1; p=none
新しいドメインのために**DKIMを設定する必要があります**。DMARCレコードが何か分からない場合は、[**このページを読んでください**](../../network-services-pentesting/pentesting-smtp/index.html#dkim)。
このチュートリアルは次に基づいています: [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)
このチュートリアルは[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)に基づいています。
> [!NOTE]
> DKIMキーが生成する両方のB64値を連結する必要があります:
> DKIMキーが生成する両方のB64値を連結する必要があります
>
> ```
> v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0wPibdqPtzYk81njjQCrChIcHzxOp8a1wjbsoNtka2X9QXCZs+iXkvw++QsWDtdYu3q0Ofnr0Yd/TmG/Y2bBGoEgeE+YTUG2aEgw8Xx42NLJq2D1pB2lRQPW4IxefROnXu5HfKSm7dyzML1gZ1U0pR5X4IZCH0wOPhIq326QjxJZm79E1nTh3xj" "Y9N/Dt3+fVnIbMupzXE216TdFuifKM6Tl6O/axNsbswMS1TH812euno8xRpsdXJzFlB9q3VbMkVWig4P538mHolGzudEBg563vv66U8D7uuzGYxYT4WS8NVm3QBMg0QKPWZaKp+bADLkOSB9J2nUpk4Aj9KB5swIDAQAB
@ -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を開く**必要があり、メールをrootとして送信した場合はファイル _/var/mail/root_応答を確認します)。\
あなたはまた、**メール設定を確認する**ために `check-auth@verifier.port25.com` にメールを送信し、**レスポンスを読む**ことができます(これには、**ポート25を開く**必要があり、メールをrootとして送信した場合はファイル _/var/mail/root_レスポンスを確認します)。\
すべてのテストに合格していることを確認してください:
```bash
==========================================================
@ -283,7 +283,7 @@ DKIM check: pass
Sender-ID check: pass
SpamAssassin check: ham
```
あなたはまた、**あなたの管理下にあるGmailにメッセージを送信**、Gmailの受信トレイで**メールのヘッダー**を確認することができます。`dkim=pass``Authentication-Results`ヘッダー欄に存在する必要があります。
あなたはまた、**あなたの管理下にあるGmailにメッセージを送信**、Gmailの受信トレイで**メールのヘッダー**を確認することができます。`dkim=pass``Authentication-Results`ヘッダー欄に存在する必要があります。
```
Authentication-Results: mx.google.com;
spf=pass (google.com: domain of contact@example.com designates --- as permitted sender) smtp.mail=contact@example.com;
@ -295,7 +295,7 @@ dkim=pass header.i=@example.com;
### Microsoftのブラックリストからの削除
あなたのドメイン/IPの削除をリクエストすることができます: [https://sender.office.com/](https://sender.office.com).
あなたのドメイン/IPの削除をリクエストすることができます [https://sender.office.com/](https://sender.office.com).
## GoPhishキャンペーンの作成と開始
@ -303,18 +303,18 @@ 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) (1) (1) (1) (10) (15) (2).png>)
> [!NOTE]
> すべてが正常に動作していることを確認するために、"**テストメールを送信**"機能を使用することをお勧めします。\
> テストを行う際にブラックリストに載らないように、**テストメールを10分メールアドレスに送信することをお勧めします**
> "**テストメールを送信**"機能を使用して、すべてが正常に動作していることをテストすることをお勧めします。\
> テストを行う際にブラックリストに載らないように、**テストメールを10分メールアドレスに送信する**ことをお勧めします。
### メールテンプレート
- テンプレートを識別するための**名前を設定**します
- 次に、**件名**を書きます(特に奇妙なことはなく、通常のメールで読むことができる内容にします
- 次に、**件名**を書きます(奇妙なものではなく、通常のメールで読むことができるもの
- "**トラッキング画像を追加**"にチェックを入れていることを確認してください
- **メールテンプレート**を書きます(以下の例のように変数を使用できます):
```html
@ -335,7 +335,7 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
</body>
</html>
```
注意してほしいのは、**メールの信頼性を高めるために**、クライアントからのメールの署名を使用することが推奨されるということです。提案:
注意してください。**メールの信頼性を高めるために**、クライアントからのメールの署名を使用することをお勧めします。提案:
- **存在しないアドレス**にメールを送信し、返信に署名が含まれているか確認します。
- info@ex.comやpress@ex.com、public@ex.comのような**公開メール**を探し、メールを送信して返信を待ちます。
@ -344,13 +344,13 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
![](<../../images/image (80).png>)
> [!NOTE]
> Email Templateは、**送信するファイルを添付することも可能です**。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,11 +360,11 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
> HTML用に**静的リソース**おそらくCSSやJSページを使用する必要がある場合は、_**/opt/gophish/static/endpoint**_に保存し、_**/static/\<filename>**_からアクセスできます。
> [!NOTE]
> リダイレクトについては、**被害者の正当なメインウェブページにユーザーをリダイレクトする**か、例えば_/static/migration.html_にリダイレクトし、5秒間**スピニングホイール****[https://loading.io/](https://loading.io)**)を表示し、その後プロセスが成功したことを示すことができます。
> リダイレクトは、**被害者の正当なメインウェブページにユーザーをリダイレクトする**か、例えば_/static/migration.html_にリダイレクトし、5秒間**スピニングホイール****[https://loading.io/](https://loading.io)**)を表示し、その後プロセスが成功したことを示すことができます。
### ユーザーとグループ
- 名前を設定
- 名前を設定します。
- **データをインポート**します(例のテンプレートを使用するには、各ユーザーの名、姓、メールアドレスが必要です)。
![](<../../images/image (163).png>)
@ -373,12 +373,12 @@ WRITE HERE SOME SIGNATURE OF SOMEONE FROM THE COMPANY
最後に、名前、メールテンプレート、ランディングページ、URL、送信プロファイル、グループを選択してキャンペーンを作成します。URLは被害者に送信されるリンクになります。
**送信プロファイルは、最終的なフィッシングメールがどのように見えるかを確認するためにテストメールを送信することを許可します**
**送信プロファイルは、最終的なフィッシングメールがどのように見えるかを確認するためにテストメールを送信できます**
![](<../../images/image (192).png>)
> [!NOTE]
> テストメールを**10分メールアドレス**に送信することをお勧めします。テストを行う際にブラックリストに載るのを避けるためです。
> テストメールは**10分メールアドレスに送信することをお勧めします**。テスト中にブラックリストに載るのを避けるためです。
すべてが準備できたら、キャンペーンを開始してください!
@ -403,19 +403,19 @@ phishing-documents.md
### プロキシMitM経由
前述の攻撃は非常に巧妙で、実際のウェブサイトを偽装し、ユーザーが設定した情報を収集しています。残念ながら、ユーザーが正しいパスワードを入力しなかった場合や、偽装したアプリケーションが2FAで設定されている場合、**この情報では騙されたユーザーを偽装することはできません**。
前述の攻撃は非常に巧妙で、実際のウェブサイトを偽装し、ユーザーによって設定された情報を収集しています。残念ながら、ユーザーが正しいパスワードを入力しなかった場合や、偽装したアプリケーションが2FAで構成されている場合、**この情報では騙されたユーザーを偽装することはできません**。
ここで、[**evilginx2**](https://github.com/kgretzky/evilginx2)**、** [**CredSniper**](https://github.com/ustayready/CredSniper)および[**muraena**](https://github.com/muraenateam/muraena)のようなツールが役立ちます。このツールは、MitMのような攻撃を生成することを可能にします。基本的に、攻撃は次のように機能します
ここで、[**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を実行している間のすべてのインタラクションの情報をキャプチャ**します。
2. ユーザーは**資格情報**を偽のページに**送信**し、ツールはそれを実際のウェブページに送信し、**資格情報が機能するか確認します**。
3. アカウントが**2FA**で構成されている場合、MitMページはそれを要求し、**ユーザーが入力**すると、ツールはそれを実際のウェブページに送信します。
4. ユーザーが認証されると、あなた(攻撃者)は**資格情報、2FA、クッキー、およびツールがMitMを実行している間のすべてのインタラクションの情報をキャプチャします**
### VNC経由
もし**被害者を元のページと同じ外観の悪意のあるページに送信する代わりに、実際のウェブページに接続されたブラウザを持つVNCセッションに送信したらどうなるでしょうか**彼が何をしているかを見ることができ、パスワード、使用されるMFA、クッキーを盗むことができます...\
これを[**EvilnVNC**](https://github.com/JoelGMSec/EvilnoVNC)行うことができます。
もし**被害者を元のページと同じ外観の悪意のあるページに送信する代わりに、実際のウェブページに接続されたブラウザ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);
}
})();