mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/pentesting-web/hacking-with-cookies/README.md'] to de
This commit is contained in:
parent
860718e449
commit
cc0f98c336
@ -18,7 +18,7 @@ Die Hosts, die ein Cookie empfangen, werden durch das Attribut `Domain` festgele
|
||||
|
||||
Ein spezifischer URL-Pfad, der in der angeforderten URL vorhanden sein muss, damit der `Cookie`-Header gesendet wird, wird durch das Attribut `Path` angezeigt. Dieses Attribut betrachtet das Zeichen `/` als Verzeichnistrenner, was Übereinstimmungen in Unterverzeichnissen ermöglicht.
|
||||
|
||||
### Reihenfolge-Regeln
|
||||
### Bestimmungsregeln
|
||||
|
||||
Wenn zwei Cookies denselben Namen tragen, wird dasjenige, das zum Senden ausgewählt wird, basierend auf:
|
||||
|
||||
@ -32,7 +32,7 @@ Wenn zwei Cookies denselben Namen tragen, wird dasjenige, das zum Senden ausgew
|
||||
- **Lax**: Erlaubt, dass das Cookie mit GET-Anfragen gesendet wird, die von Drittanbieter-Websites initiiert werden.
|
||||
- **None**: Erlaubt, dass das Cookie von jeder Drittanbieter-Domain gesendet wird.
|
||||
|
||||
Denken Sie daran, dass das Verständnis dieser Attribute bei der Konfiguration von Cookies dazu beitragen kann, sicherzustellen, dass sie in verschiedenen Szenarien wie erwartet funktionieren.
|
||||
Denken Sie daran, dass das Verständnis dieser Attribute bei der Konfiguration von Cookies helfen kann, um sicherzustellen, dass sie in verschiedenen Szenarien wie erwartet funktionieren.
|
||||
|
||||
| **Anfragetyp** | **Beispielcode** | **Cookies gesendet, wenn** |
|
||||
| ---------------- | ---------------------------------- | --------------------------- |
|
||||
@ -58,11 +58,11 @@ Dies verhindert, dass der **Client** auf das Cookie zugreift (zum Beispiel über
|
||||
|
||||
#### **Umgehungen**
|
||||
|
||||
- Wenn die Seite **die Cookies als Antwort** auf eine Anfrage sendet (zum Beispiel auf einer **PHPinfo**-Seite), ist es möglich, XSS auszunutzen, um eine Anfrage an diese Seite zu senden und **die Cookies** aus der Antwort zu **stehlen** (siehe ein Beispiel in [https://hackcommander.github.io/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://hackcommander.github.io/posts/2022/11/12/bypass-httponly-via-php-info-page/)).
|
||||
- Wenn die Seite **die Cookies als Antwort** auf eine Anfrage sendet (zum Beispiel auf einer **PHPinfo**-Seite), ist es möglich, XSS auszunutzen, um eine Anfrage an diese Seite zu senden und **die Cookies** aus der Antwort zu **stehlen** (siehe ein Beispiel in [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/)).
|
||||
- Dies könnte mit **TRACE** **HTTP**-Anfragen umgangen werden, da die Antwort des Servers (wenn diese HTTP-Methode verfügbar ist) die gesendeten Cookies widerspiegelt. Diese Technik wird als **Cross-Site Tracking** bezeichnet.
|
||||
- Diese Technik wird von **modernen Browsern vermieden, indem das Senden einer TRACE**-Anfrage von JS nicht erlaubt wird. Einige Umgehungen dafür wurden jedoch in spezifischer Software gefunden, wie das Senden von `\r\nTRACE` anstelle von `TRACE` an IE6.0 SP2.
|
||||
- Eine andere Möglichkeit ist die Ausnutzung von Zero-Day-Sicherheitsanfälligkeiten der Browser.
|
||||
- Es ist möglich, **HttpOnly-Cookies zu überschreiben**, indem man einen Cookie-Jar-Overflow-Angriff durchführt:
|
||||
- Es ist möglich, **HttpOnly-Cookies** durch einen Cookie-Jar-Overflow-Angriff zu **überschreiben**:
|
||||
|
||||
{{#ref}}
|
||||
cookie-jar-overflow.md
|
||||
@ -81,19 +81,19 @@ Cookies, die mit `__Secure-` beginnen, müssen zusammen mit dem `secure`-Flag vo
|
||||
Für Cookies, die mit `__Host-` beginnen, müssen mehrere Bedingungen erfüllt sein:
|
||||
|
||||
- Sie müssen mit dem `secure`-Flag gesetzt werden.
|
||||
- Sie müssen von einer durch HTTPS gesicherten Seite stammen.
|
||||
- Sie müssen von einer Seite stammen, die durch HTTPS gesichert ist.
|
||||
- Es ist verboten, eine Domain anzugeben, was ihre Übertragung an Subdomains verhindert.
|
||||
- Der Pfad für diese Cookies muss auf `/` gesetzt werden.
|
||||
|
||||
Es ist wichtig zu beachten, dass Cookies, die mit `__Host-` beginnen, nicht an Superdomains oder Subdomains gesendet werden dürfen. Diese Einschränkung hilft, Anwendungscookies zu isolieren. Daher kann die Verwendung des `__Host-`-Präfixes für alle Anwendungscookies als gute Praxis zur Verbesserung der Sicherheit und Isolation angesehen werden.
|
||||
|
||||
### Überschreiben von Cookies
|
||||
### Cookies überschreiben
|
||||
|
||||
Eine der Schutzmaßnahmen von Cookies mit dem Präfix `__Host-` besteht darin, zu verhindern, dass sie von Subdomains überschrieben werden. Dies verhindert beispielsweise [**Cookie Tossing-Angriffe**](cookie-tossing.md). In dem Vortrag [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**Paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)) wird präsentiert, dass es möglich war, \_\_HOST- Cookies von einer Subdomain zu setzen, indem man den Parser täuschte, zum Beispiel, indem man "=" am Anfang oder am Ende hinzufügte...:
|
||||
Eine der Schutzmaßnahmen von mit `__Host-` beginnenden Cookies besteht darin, sie daran zu hindern, von Subdomains überschrieben zu werden. Dies verhindert beispielsweise [**Cookie Tossing-Angriffe**](cookie-tossing.md). In dem Vortrag [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**Paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)) wird präsentiert, dass es möglich war, \_\_HOST- beginnende Cookies von einer Subdomain zu setzen, indem man den Parser täuschte, zum Beispiel, indem man "=" am Anfang oder am Ende hinzufügte...:
|
||||
|
||||
<figure><img src="../../images/image (6) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Oder in PHP war es möglich, **andere Zeichen am Anfang** des Cookie-Namens hinzuzufügen, die durch Unterstrich-Zeichen ersetzt werden sollten, was es ermöglichte, `__HOST-` Cookies zu überschreiben:
|
||||
Oder in PHP war es möglich, **andere Zeichen am Anfang** des Cookie-Namens hinzuzufügen, die durch Unterstriche **ersetzt** werden sollten, was es ermöglichte, `__HOST-` Cookies zu überschreiben:
|
||||
|
||||
<figure><img src="../../images/image (7) (1) (1) (1) (1).png" alt="" width="373"><figcaption></figcaption></figure>
|
||||
|
||||
@ -103,7 +103,7 @@ Wenn ein benutzerdefiniertes Cookie sensible Daten enthält, überprüfen Sie es
|
||||
|
||||
### Dekodierung und Manipulation von Cookies
|
||||
|
||||
Sensible Daten, die in Cookies eingebettet sind, sollten immer überprüft werden. Cookies, die in Base64 oder ähnlichen Formaten codiert sind, können oft dekodiert werden. Diese Sicherheitsanfälligkeit ermöglicht es Angreifern, den Inhalt des Cookies zu ändern und sich als andere Benutzer auszugeben, indem sie ihre modifizierten Daten wieder in das Cookie codieren.
|
||||
Sensible Daten, die in Cookies eingebettet sind, sollten immer überprüft werden. Cookies, die in Base64 oder ähnlichen Formaten kodiert sind, können oft dekodiert werden. Diese Sicherheitsanfälligkeit ermöglicht es Angreifern, den Inhalt des Cookies zu ändern und sich als andere Benutzer auszugeben, indem sie ihre modifizierten Daten wieder in das Cookie kodieren.
|
||||
|
||||
### Sitzungsübernahme
|
||||
|
||||
@ -133,7 +133,7 @@ cookie-tossing.md
|
||||
|
||||
Klicken Sie auf den vorherigen Link, um auf eine Seite zuzugreifen, die mögliche Schwächen in JWT erklärt.
|
||||
|
||||
JSON Web Tokens (JWT), die in Cookies verwendet werden, können ebenfalls Sicherheitsanfälligkeiten aufweisen. Für detaillierte Informationen zu potenziellen Schwächen und deren Ausnutzung wird empfohlen, das verlinkte Dokument über das Hacken von JWT zu konsultieren.
|
||||
JSON Web Tokens (JWT), die in Cookies verwendet werden, können ebenfalls Sicherheitsanfälligkeiten aufweisen. Für detaillierte Informationen zu potenziellen Schwächen und wie man sie ausnutzt, wird empfohlen, das verlinkte Dokument über das Hacken von JWT zu konsultieren.
|
||||
|
||||
### Cross-Site Request Forgery (CSRF)
|
||||
|
||||
@ -147,7 +147,7 @@ document.cookie = "a=v1"
|
||||
document.cookie = "=test value;" // Setting an empty named cookie
|
||||
document.cookie = "b=v2"
|
||||
```
|
||||
Das Ergebnis im gesendeten Cookie-Header ist `a=v1; test value; b=v2;`. Interessanterweise ermöglicht dies die Manipulation von Cookies, wenn ein Cookie mit leerem Namen gesetzt wird, wodurch möglicherweise andere Cookies kontrolliert werden können, indem das leere Cookie auf einen bestimmten Wert gesetzt wird:
|
||||
Das Ergebnis im gesendeten Cookie-Header ist `a=v1; test value; b=v2;`. Interessanterweise ermöglicht dies die Manipulation von Cookies, wenn ein Cookie mit einem leeren Namen gesetzt wird, wodurch möglicherweise andere Cookies kontrolliert werden können, indem das leere Cookie auf einen bestimmten Wert gesetzt wird:
|
||||
```js
|
||||
function setCookie(name, value) {
|
||||
document.cookie = `${name}=${value}`
|
||||
@ -165,9 +165,9 @@ document.cookie = "\ud800=meep"
|
||||
```
|
||||
Dies führt dazu, dass `document.cookie` einen leeren String ausgibt, was auf eine permanente Beschädigung hinweist.
|
||||
|
||||
#### Cookie Smuggling aufgrund von Parsing-Problemen
|
||||
#### Cookie-Smuggling aufgrund von Parsing-Problemen
|
||||
|
||||
(Weitere Details finden Sie in der[originalen Forschung](https://blog.ankursundara.com/cookie-bugs/)) Mehrere Webserver, einschließlich der von Java (Jetty, TomCat, Undertow) und Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), behandeln Cookie-Strings aufgrund veralteter RFC2965-Unterstützung falsch. Sie lesen einen doppelt zitierten Cookie-Wert als einen einzelnen Wert, selbst wenn er Semikolons enthält, die normalerweise Schlüssel-Wert-Paare trennen sollten:
|
||||
(Weitere Details finden Sie in der [originalen Forschung](https://blog.ankursundara.com/cookie-bugs/)) Mehrere Webserver, einschließlich der von Java (Jetty, TomCat, Undertow) und Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), behandeln Cookie-Strings aufgrund veralteter RFC2965-Unterstützung falsch. Sie lesen einen doppelt zitierten Cookie-Wert als einen einzelnen Wert, selbst wenn er Semikolons enthält, die normalerweise Schlüssel-Wert-Paare trennen sollten:
|
||||
```
|
||||
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
|
||||
```
|
||||
@ -175,11 +175,11 @@ RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
|
||||
|
||||
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) Die falsche Analyse von Cookies durch Server, insbesondere Undertow, Zope und solche, die Python's `http.cookie.SimpleCookie` und `http.cookie.BaseCookie` verwenden, schafft Möglichkeiten für Cookie-Injektionsangriffe. Diese Server versäumen es, den Beginn neuer Cookies ordnungsgemäß zu kennzeichnen, was es Angreifern ermöglicht, Cookies zu fälschen:
|
||||
|
||||
- Undertow erwartet ein neues Cookie sofort nach einem zitierten Wert ohne Semikolon.
|
||||
- Undertow erwartet ein neues Cookie unmittelbar nach einem in Anführungszeichen gesetzten Wert ohne Semikolon.
|
||||
- Zope sucht nach einem Komma, um mit der Analyse des nächsten Cookies zu beginnen.
|
||||
- Pythons Cookie-Klassen beginnen die Analyse bei einem Leerzeichen.
|
||||
|
||||
Diese Schwachstelle ist besonders gefährlich in Webanwendungen, die auf cookie-basiertem CSRF-Schutz basieren, da sie es Angreifern ermöglicht, gefälschte CSRF-Token-Cookies einzuschleusen, was potenziell Sicherheitsmaßnahmen umgeht. Das Problem wird durch Pythons Umgang mit doppelten Cookie-Namen verschärft, bei dem das letzte Vorkommen frühere überschreibt. Es wirft auch Bedenken hinsichtlich `__Secure-` und `__Host-` Cookies in unsicheren Kontexten auf und könnte zu Autorisierungsumgehungen führen, wenn Cookies an Backend-Server weitergegeben werden, die anfällig für Spoofing sind.
|
||||
Diese Schwachstelle ist besonders gefährlich in Webanwendungen, die auf cookie-basiertem CSRF-Schutz basieren, da sie Angreifern ermöglicht, gefälschte CSRF-Token-Cookies einzuschleusen, was potenziell Sicherheitsmaßnahmen umgeht. Das Problem wird durch Pythons Umgang mit doppelten Cookie-Namen verschärft, bei dem das letzte Vorkommen frühere überschreibt. Es wirft auch Bedenken hinsichtlich `__Secure-` und `__Host-` Cookies in unsicheren Kontexten auf und könnte zu Autorisierungsumgehungen führen, wenn Cookies an Backend-Server weitergegeben werden, die anfällig für Spoofing sind.
|
||||
|
||||
### Cookies $version
|
||||
|
||||
@ -197,7 +197,7 @@ Laut [**diesem Blogbeitrag**](https://portswigger.net/research/stealing-httponly
|
||||
- Dann wird das legitime Cookie in der Reihenfolge als nächstes kommen
|
||||
- **Erstelle ein Dummy-Cookie, das das doppelte Anführungszeichen** innerhalb seines Wertes schließt
|
||||
|
||||
Auf diese Weise wird das Opfer-Cookie innerhalb der neuen Cookie-Version 1 gefangen und wird immer dann reflektiert, wenn es reflektiert wird.
|
||||
Auf diese Weise wird das Opfer-Cookie in das neue Cookie der Version 1 gefangen und wird immer reflektiert, wenn es reflektiert wird.
|
||||
```javascript
|
||||
document.cookie = `$Version=1;`;
|
||||
document.cookie = `param1="start`;
|
||||
@ -219,11 +219,11 @@ Diese Analyse zeigt an, dass escaped Werte innerhalb der Cookies unescaped werde
|
||||
|
||||
#### Umgehung von Cookie-Namen-Blocklisten
|
||||
|
||||
In der RFC2109 wird angegeben, dass ein **Komma als Trennzeichen zwischen Cookie-Werten verwendet werden kann**. Außerdem ist es möglich, **Leerzeichen und Tabs vor und nach dem Gleichheitszeichen hinzuzufügen**. Daher erzeugt ein Cookie wie `$Version=1; foo=bar, abc = qux` nicht das Cookie `"foo":"bar, admin = qux"`, sondern die Cookies `foo":"bar"` und `"admin":"qux"`. Beachten Sie, wie 2 Cookies erzeugt werden und wie der Raum vor und nach dem Gleichheitszeichen bei admin entfernt wurde.
|
||||
Im RFC2109 wird angegeben, dass ein **Komma als Trennzeichen zwischen Cookie-Werten verwendet werden kann**. Außerdem ist es möglich, **Leerzeichen und Tabs vor und nach dem Gleichheitszeichen hinzuzufügen**. Daher erzeugt ein Cookie wie `$Version=1; foo=bar, abc = qux` nicht das Cookie `"foo":"bar, admin = qux"`, sondern die Cookies `foo":"bar"` und `"admin":"qux"`. Beachten Sie, wie 2 Cookies generiert werden und wie admin das Leerzeichen vor und nach dem Gleichheitszeichen entfernt wurde.
|
||||
|
||||
#### Umgehung der Wertanalyse mit Cookie-Splitting
|
||||
|
||||
Schließlich würden verschiedene Backdoors in einem String verschiedene Cookies zusammenführen, die in verschiedenen Cookie-Headern übergeben werden, wie in:
|
||||
Schließlich würden verschiedene Backdoors in einem String verschiedene Cookies zusammenführen, die in unterschiedlichen Cookie-Headern übergeben werden, wie in:
|
||||
```
|
||||
GET / HTTP/1.1
|
||||
Host: example.com
|
||||
@ -237,27 +237,27 @@ Cookie: comment')
|
||||
|
||||
Resulting cookie: name=eval('test//, comment') => allowed
|
||||
```
|
||||
### Extra verwundbare Cookie-Überprüfungen
|
||||
### Zusätzliche Überprüfungen von anfälligen Cookies
|
||||
|
||||
#### **Grundlegende Überprüfungen**
|
||||
|
||||
- Das **Cookie** ist jedes Mal, wenn Sie sich **anmelden**, **gleich**.
|
||||
- Das **cookie** ist jedes Mal, wenn Sie sich **anmelden**, **gleich**.
|
||||
- Melden Sie sich ab und versuchen Sie, dasselbe Cookie zu verwenden.
|
||||
- Versuchen Sie, sich mit 2 Geräten (oder Browsern) mit demselben Cookie in dasselbe Konto einzuloggen.
|
||||
- Versuchen Sie, sich mit 2 Geräten (oder Browsern) bei demselben Konto mit demselben Cookie anzumelden.
|
||||
- Überprüfen Sie, ob das Cookie Informationen enthält, und versuchen Sie, es zu ändern.
|
||||
- Versuchen Sie, mehrere Konten mit fast demselben Benutzernamen zu erstellen, und überprüfen Sie, ob Sie Ähnlichkeiten sehen können.
|
||||
- Überprüfen Sie die Option "**angemeldet bleiben**", falls vorhanden, um zu sehen, wie sie funktioniert. Wenn sie vorhanden ist und anfällig sein könnte, verwenden Sie immer das Cookie von **angemeldet bleiben** ohne ein anderes Cookie.
|
||||
- Überprüfen Sie die Option "**remember me**", falls vorhanden, um zu sehen, wie sie funktioniert. Wenn sie vorhanden ist und anfällig sein könnte, verwenden Sie immer das Cookie von **remember me** ohne ein anderes Cookie.
|
||||
- Überprüfen Sie, ob das vorherige Cookie funktioniert, selbst nachdem Sie das Passwort geändert haben.
|
||||
|
||||
#### **Erweiterte Cookie-Angriffe**
|
||||
|
||||
Wenn das Cookie beim Anmelden gleich bleibt (oder fast gleich bleibt), bedeutet dies wahrscheinlich, dass das Cookie mit einem Feld Ihres Kontos (wahrscheinlich dem Benutzernamen) verbunden ist. Dann können Sie:
|
||||
|
||||
- Versuchen, viele **Konten** mit sehr **ähnlichen** Benutzernamen zu erstellen und versuchen, **zu erraten**, wie der Algorithmus funktioniert.
|
||||
- Versuchen, viele **Konten** mit sehr **ähnlichen** Benutzernamen zu erstellen und versuchen, zu **erraten**, wie der Algorithmus funktioniert.
|
||||
- Versuchen, den **Benutzernamen zu bruteforcen**. Wenn das Cookie nur als Authentifizierungsmethode für Ihren Benutzernamen gespeichert wird, können Sie ein Konto mit dem Benutzernamen "**Bmin**" erstellen und jeden einzelnen **Bit** Ihres Cookies **bruteforcen**, da eines der Cookies, die Sie versuchen werden, das von "**admin**" sein wird.
|
||||
- Versuchen Sie **Padding** **Oracle** (Sie können den Inhalt des Cookies entschlüsseln). Verwenden Sie **padbuster**.
|
||||
|
||||
**Padding Oracle - Padbuster-Beispiele**
|
||||
**Padding Oracle - Padbuster Beispiele**
|
||||
```bash
|
||||
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
|
||||
# When cookies and regular Base64
|
||||
|
236
theme/ai.js
236
theme/ai.js
@ -1,27 +1,28 @@
|
||||
/**
|
||||
* HackTricks AI Chat Widget v1.15 – Markdown rendering + sanitised
|
||||
* ------------------------------------------------------------------------
|
||||
* • Replaces the static “…” placeholder with a three-dot **bouncing** loader
|
||||
* • Renders assistant replies as Markdown while purging any unsafe HTML
|
||||
* (XSS-safe via DOMPurify)
|
||||
* ------------------------------------------------------------------------
|
||||
* HackTricks AI Chat Widget v1.16 – resizable sidebar
|
||||
* ---------------------------------------------------
|
||||
* ❶ Markdown rendering + sanitised (same as before)
|
||||
* ❷ NEW: drag‑to‑resize panel, width persists via localStorage
|
||||
*/
|
||||
(function () {
|
||||
const LOG = "[HackTricks-AI]";
|
||||
|
||||
/* ---------------- User-tunable constants ---------------- */
|
||||
const MAX_CONTEXT = 3000; // highlighted-text char limit
|
||||
const MAX_QUESTION = 500; // question char limit
|
||||
const LOG = "[HackTricks‑AI]";
|
||||
/* ---------------- User‑tunable constants ---------------- */
|
||||
const MAX_CONTEXT = 3000; // highlighted‑text char limit
|
||||
const MAX_QUESTION = 500; // question char limit
|
||||
const MIN_W = 250; // ← resize limits →
|
||||
const MAX_W = 600;
|
||||
const DEF_W = 350; // default width (if nothing saved)
|
||||
const TOOLTIP_TEXT =
|
||||
"💡 Highlight any text on the page,\nthen click to ask HackTricks AI about it";
|
||||
"💡 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
|
||||
const BRAND_RED = "#b31328";
|
||||
|
||||
/* ------------------------------ State ------------------------------ */
|
||||
let threadId = null;
|
||||
let isRunning = false;
|
||||
|
||||
/* ---------- helpers ---------- */
|
||||
const $ = (sel, ctx = document) => ctx.querySelector(sel);
|
||||
if (document.getElementById("ht-ai-btn")) {
|
||||
console.warn(`${LOG} Widget already injected.`);
|
||||
@ -31,44 +32,37 @@
|
||||
? document.addEventListener("DOMContentLoaded", init)
|
||||
: init());
|
||||
|
||||
/* ==================================================================== */
|
||||
/* 🔗 1. 3rd-party libs → Markdown & sanitiser */
|
||||
/* ==================================================================== */
|
||||
/* =================================================================== */
|
||||
/* 🔗 1. 3rd‑party libs → Markdown & sanitiser */
|
||||
/* =================================================================== */
|
||||
function loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((res, rej) => {
|
||||
const s = document.createElement("script");
|
||||
s.src = src;
|
||||
s.onload = resolve;
|
||||
s.onerror = () => reject(new Error(`Failed to load ${src}`));
|
||||
s.onload = res;
|
||||
s.onerror = () => rej(new Error(`Failed to load ${src}`));
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
async function ensureDeps() {
|
||||
const deps = [];
|
||||
if (typeof marked === "undefined") {
|
||||
if (typeof marked === "undefined")
|
||||
deps.push(loadScript("https://cdn.jsdelivr.net/npm/marked/marked.min.js"));
|
||||
}
|
||||
if (typeof DOMPurify === "undefined") {
|
||||
if (typeof DOMPurify === "undefined")
|
||||
deps.push(
|
||||
loadScript(
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.5/purify.min.js"
|
||||
)
|
||||
);
|
||||
}
|
||||
if (deps.length) await Promise.all(deps);
|
||||
}
|
||||
const mdToSafeHTML = (md) =>
|
||||
DOMPurify.sanitize(marked.parse(md, { mangle: false, headerIds: false }), {
|
||||
USE_PROFILES: { html: true }
|
||||
});
|
||||
|
||||
function mdToSafeHTML(md) {
|
||||
// 1️⃣ Markdown → raw HTML
|
||||
const raw = marked.parse(md, { mangle: false, headerIds: false });
|
||||
// 2️⃣ Purify
|
||||
return DOMPurify.sanitize(raw, { USE_PROFILES: { html: true } });
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
/* =================================================================== */
|
||||
async function init() {
|
||||
/* ----- make sure marked & DOMPurify are ready before anything else */
|
||||
try {
|
||||
await ensureDeps();
|
||||
} catch (e) {
|
||||
@ -76,14 +70,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`${LOG} Injecting widget… v1.15`);
|
||||
console.log(`${LOG} Injecting widget… v1.16`);
|
||||
|
||||
await ensureThreadId();
|
||||
injectStyles();
|
||||
|
||||
const btn = createFloatingButton();
|
||||
createTooltip(btn);
|
||||
const panel = createSidebar();
|
||||
const panel = createSidebar(); // ← panel with resizer
|
||||
const chatLog = $("#ht-ai-chat");
|
||||
const sendBtn = $("#ht-ai-send");
|
||||
const inputBox = $("#ht-ai-question");
|
||||
@ -100,15 +94,8 @@
|
||||
function addMsg(text, cls) {
|
||||
const b = document.createElement("div");
|
||||
b.className = `ht-msg ${cls}`;
|
||||
|
||||
// ✨ assistant replies rendered as Markdown + sanitised
|
||||
if (cls === "ht-ai") {
|
||||
b.innerHTML = mdToSafeHTML(text);
|
||||
} else {
|
||||
// user / context bubbles stay plain-text
|
||||
b.textContent = text;
|
||||
}
|
||||
|
||||
b[cls === "ht-ai" ? "innerHTML" : "textContent"] =
|
||||
cls === "ht-ai" ? mdToSafeHTML(text) : text;
|
||||
chatLog.appendChild(b);
|
||||
chatLog.scrollTop = chatLog.scrollHeight;
|
||||
return b;
|
||||
@ -116,30 +103,28 @@
|
||||
const LOADER_HTML =
|
||||
'<span class="ht-loading"><span></span><span></span><span></span></span>';
|
||||
|
||||
function setInputDisabled(d) {
|
||||
const setInputDisabled = (d) => {
|
||||
inputBox.disabled = d;
|
||||
sendBtn.disabled = d;
|
||||
}
|
||||
function clearThreadCookie() {
|
||||
};
|
||||
const clearThreadCookie = () => {
|
||||
document.cookie = "threadId=; Path=/; Max-Age=0";
|
||||
threadId = null;
|
||||
}
|
||||
function resetConversation() {
|
||||
threadId = null;
|
||||
};
|
||||
const 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.");
|
||||
alert("Please highlight some text first.");
|
||||
return;
|
||||
}
|
||||
if (savedSelection.length > MAX_CONTEXT) {
|
||||
alert(
|
||||
`Highlighted text is too long (${savedSelection.length} chars). Max allowed: ${MAX_CONTEXT}.`
|
||||
);
|
||||
alert(`Highlighted text is too long. Max ${MAX_CONTEXT} chars.`);
|
||||
return;
|
||||
}
|
||||
chatLog.innerHTML = "";
|
||||
@ -157,11 +142,10 @@
|
||||
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 loading = addMsg("", "ht-ai");
|
||||
loading.innerHTML = LOADER_HTML;
|
||||
|
||||
const content = context
|
||||
? `### Context:\n${context}\n\n### Question to answer:\n${question}`
|
||||
@ -178,43 +162,39 @@
|
||||
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.";
|
||||
else if (res.status === 429) err = "Rate limit exceeded.";
|
||||
} catch (_) {}
|
||||
loadingBubble.textContent = err;
|
||||
loading.textContent = err;
|
||||
return;
|
||||
}
|
||||
const data = await res.json();
|
||||
loadingBubble.remove();
|
||||
loading.remove();
|
||||
if (Array.isArray(data.response))
|
||||
data.response.forEach((p) => {
|
||||
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.";
|
||||
loading.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}.`
|
||||
);
|
||||
alert(`Question too long (${q.length}). Max ${MAX_QUESTION}.`);
|
||||
return;
|
||||
}
|
||||
inputBox.value = "";
|
||||
@ -228,9 +208,9 @@
|
||||
handleSend();
|
||||
}
|
||||
});
|
||||
}
|
||||
} /* end init */
|
||||
|
||||
/* ==================================================================== */
|
||||
/* =================================================================== */
|
||||
async function ensureThreadId() {
|
||||
const m = document.cookie.match(/threadId=([^;]+)/);
|
||||
if (m && m[1]) {
|
||||
@ -241,62 +221,67 @@
|
||||
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;
|
||||
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.");
|
||||
alert("Failed to initialise the conversation. Please refresh.");
|
||||
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}`;
|
||||
#ht-ai-btn{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);min-width:60px;height:60px;border-radius:30px;background:linear-gradient(45deg, #b31328, #d42d3f, #2d5db4, #3470e4);background-size:300% 300%;animation:gradientShift 8s ease infinite;color:#fff;font-size:18px;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;padding:0 20px}
|
||||
#ht-ai-btn span{margin-left:8px;font-weight:bold}
|
||||
@keyframes gradientShift{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}
|
||||
#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%;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 */
|
||||
.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}
|
||||
/* NEW: resizer handle */
|
||||
#ht-ai-resizer{position:absolute;left:0;top:0;width:6px;height:100%;cursor:ew-resize;background:transparent}
|
||||
#ht-ai-resizer:hover{background:rgba(255,255,255,.05)}`;
|
||||
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 = "🤖";
|
||||
d.innerHTML = "🤖<span>HackTricksAI</span>";
|
||||
document.body.appendChild(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
function createTooltip(btn) {
|
||||
const t = document.createElement("div");
|
||||
t.id = "ht-ai-tooltip";
|
||||
@ -311,11 +296,16 @@
|
||||
btn.addEventListener("mouseleave", () => t.classList.remove("show"));
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
function createSidebar() {
|
||||
const saved = parseInt(localStorage.getItem("htAiWidth") || DEF_W, 10);
|
||||
const width = Math.min(Math.max(saved, MIN_W), MAX_W);
|
||||
|
||||
const p = document.createElement("div");
|
||||
p.id = "ht-ai-panel";
|
||||
p.style.width = width + "px"; // ← applied width
|
||||
p.innerHTML = `
|
||||
<div id="ht-ai-header"><strong>HackTricks AI Chat</strong>
|
||||
<div id="ht-ai-header"><strong>HackTricks AI Chat</strong>
|
||||
<div class="ht-actions">
|
||||
<button id="ht-ai-reset" title="Reset">↺</button>
|
||||
<span id="ht-ai-close" title="Close">✖</span>
|
||||
@ -326,7 +316,39 @@
|
||||
<textarea id="ht-ai-question" placeholder="Type your question…"></textarea>
|
||||
<button id="ht-ai-send">Send</button>
|
||||
</div>`;
|
||||
/* NEW: resizer strip */
|
||||
const resizer = document.createElement("div");
|
||||
resizer.id = "ht-ai-resizer";
|
||||
p.appendChild(resizer);
|
||||
document.body.appendChild(p);
|
||||
addResizeLogic(resizer, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ---------------- resize behaviour ---------------- */
|
||||
function addResizeLogic(handle, panel) {
|
||||
let startX, startW, dragging = false;
|
||||
|
||||
const onMove = (e) => {
|
||||
if (!dragging) return;
|
||||
const dx = startX - e.clientX; // dragging leftwards ⇒ +dx
|
||||
let newW = startW + dx;
|
||||
newW = Math.min(Math.max(newW, MIN_W), MAX_W);
|
||||
panel.style.width = newW + "px";
|
||||
};
|
||||
const onUp = () => {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
localStorage.setItem("htAiWidth", parseInt(panel.style.width, 10));
|
||||
document.removeEventListener("mousemove", onMove);
|
||||
document.removeEventListener("mouseup", onUp);
|
||||
};
|
||||
handle.addEventListener("mousedown", (e) => {
|
||||
dragging = true;
|
||||
startX = e.clientX;
|
||||
startW = parseInt(window.getComputedStyle(panel).width, 10);
|
||||
document.addEventListener("mousemove", onMove);
|
||||
document.addEventListener("mouseup", onUp);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
Loading…
x
Reference in New Issue
Block a user