mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-24 12:03:37 +00:00
601 lines
30 KiB
Markdown
601 lines
30 KiB
Markdown
# Angular
|
|
|
|
## Die Checkliste
|
|
|
|
Checkliste [von hier](https://lsgeurope.com/post/angular-security-checklist).
|
|
|
|
* [ ] Angular wird als Client-seitiges Framework betrachtet und wird nicht erwartet, serverseitigen Schutz zu bieten
|
|
* [ ] Sourcemap für Skripte ist in der Projektkonfiguration deaktiviert
|
|
* [ ] Nicht vertrauenswürdige Benutzereingaben werden immer interpoliert oder bereinigt, bevor sie in Vorlagen verwendet werden
|
|
* [ ] Der Benutzer hat keine Kontrolle über serverseitige oder clientseitige Vorlagen
|
|
* [ ] Nicht vertrauenswürdige Benutzereingaben werden vor der Verwendung durch die Anwendung mit einem geeigneten Sicherheitskontext bereinigt
|
|
* [ ] `BypassSecurity*`-Methoden werden nicht mit nicht vertrauenswürdigen Eingaben verwendet
|
|
* [ ] Nicht vertrauenswürdige Benutzereingaben werden nicht an Angular-Klassen wie `ElementRef`, `Renderer2` und `Document` oder andere JQuery/DOM-Senken übergeben
|
|
|
|
## Was ist Angular
|
|
|
|
Angular ist ein **leistungsstarkes** und **Open-Source**-Front-End-Framework, das von **Google** gepflegt wird. Es verwendet **TypeScript**, um die Lesbarkeit und das Debugging des Codes zu verbessern. Mit starken Sicherheitsmechanismen verhindert Angular gängige Client-seitige Sicherheitslücken wie **XSS** und **offene Weiterleitungen**. Es kann auch auf der **Serverseite** verwendet werden, wodurch Sicherheitsüberlegungen von **beiden Seiten** wichtig sind.
|
|
|
|
## Framework-Architektur
|
|
|
|
Um die Grundlagen von Angular besser zu verstehen, gehen wir durch seine wesentlichen Konzepte.
|
|
|
|
Ein typisches Angular-Projekt sieht normalerweise so aus:
|
|
```bash
|
|
my-workspace/
|
|
├── ... #workspace-wide configuration files
|
|
├── src
|
|
│ ├── app
|
|
│ │ ├── app.module.ts #defines the root module, that tells Angular how to assemble the application
|
|
│ │ ├── app.component.ts #defines the logic for the application's root component
|
|
│ │ ├── app.component.html #defines the HTML template associated with the root component
|
|
│ │ ├── app.component.css #defines the base CSS stylesheet for the root component
|
|
│ │ ├── app.component.spec.ts #defines a unit test for the root component
|
|
│ │ └── app-routing.module.ts #provides routing capability for the application
|
|
│ ├── lib
|
|
│ │ └── src #library-specific configuration files
|
|
│ ├── index.html #main HTML page, where the component will be rendered in
|
|
│ └── ... #application-specific configuration files
|
|
├── angular.json #provides workspace-wide and project-specific configuration defaults
|
|
└── tsconfig.json #provides the base TypeScript configuration for projects in the workspace
|
|
```
|
|
Gemäß der Dokumentation hat jede Angular-Anwendung mindestens eine Komponente, die Wurzelkomponente (`AppComponent`), die eine Komponentenhierarchie mit dem DOM verbindet. Jede Komponente definiert eine Klasse, die Anwendungsdaten und -logik enthält und mit einer HTML-Vorlage verknüpft ist, die eine Ansicht definiert, die in einer Zielumgebung angezeigt werden soll. Der `@Component()`-Dekorator kennzeichnet die unmittelbar darunter liegende Klasse als Komponente und stellt die Vorlage und die zugehörigen komponentenspezifischen Metadaten bereit. Die `AppComponent` ist in der Datei `app.component.ts` definiert.
|
|
|
|
Angular NgModules deklarieren einen Kompilierungskontext für eine Gruppe von Komponenten, die einem Anwendungsbereich, einem Arbeitsablauf oder einem eng verwandten Satz von Funktionen gewidmet sind. Jede Angular-Anwendung hat ein Root-Modul, das konventionell `AppModule` genannt wird und den Bootstrap-Mechanismus bereitstellt, der die Anwendung startet. Eine Anwendung enthält in der Regel viele funktionale Module. Das `AppModule` ist in der Datei `app.module.ts` definiert.
|
|
|
|
Das Angular `Router` NgModule stellt einen Dienst bereit, mit dem Sie einen Navigationspfad zwischen den verschiedenen Anwendungszuständen und Ansichtshierarchien in Ihrer Anwendung definieren können. Das `RouterModule` ist in der Datei `app-routing.module.ts` definiert.
|
|
|
|
Für Daten oder Logik, die nicht mit einer bestimmten Ansicht verknüpft sind und die Sie über Komponenten hinweg teilen möchten, erstellen Sie eine Serviceklasse. Die Definition einer Serviceklasse wird unmittelbar vom `@Injectable()`-Dekorator vorangestellt. Der Dekorator stellt Metadaten bereit, die es anderen Providern ermöglichen, als Abhängigkeiten in Ihre Klasse injiziert zu werden. Die Dependency Injection (DI) ermöglicht es Ihnen, Ihre Komponentenklassen schlank und effizient zu halten. Sie rufen keine Daten vom Server ab, validieren keine Benutzereingaben und protokollieren nicht direkt in die Konsole; diese Aufgaben werden an Services delegiert.
|
|
|
|
## Sourcemap-Konfiguration
|
|
|
|
Das Angular-Framework übersetzt TypeScript-Dateien in JavaScript-Code, indem es die Optionen der `tsconfig.json` befolgt und dann ein Projekt mit der Konfiguration der `angular.json` erstellt. Beim Betrachten der Datei `angular.json` haben wir eine Option zur Aktivierung oder Deaktivierung einer Sourcemap festgestellt. Gemäß der Angular-Dokumentation ist die Standardkonfiguration für Skripte eine aktivierte Sourcemap-Datei, die standardmäßig nicht ausgeblendet ist:
|
|
```json
|
|
"sourceMap": {
|
|
"scripts": true,
|
|
"styles": true,
|
|
"vendor": false,
|
|
"hidden": false
|
|
}
|
|
```
|
|
Generell werden Sourcemap-Dateien zu Debugging-Zwecken verwendet, da sie generierte Dateien ihren Originaldateien zuordnen. Daher wird nicht empfohlen, sie in einer Produktionsumgebung zu verwenden. Wenn Sourcemaps aktiviert sind, verbessert dies die Lesbarkeit und erleichtert die Dateianalyse, indem der ursprüngliche Zustand des Angular-Projekts repliziert wird. Wenn sie jedoch deaktiviert sind, kann ein Überprüfer eine kompilierte JavaScript-Datei immer noch manuell analysieren, indem er nach Anti-Sicherheitsmustern sucht.
|
|
|
|
Darüber hinaus kann eine kompilierte JavaScript-Datei mit einem Angular-Projekt in den Browser-Entwicklertools → Quellen (oder Debugger und Quellen) → \[id].main.js gefunden werden. Abhängig von den aktivierten Optionen kann diese Datei die folgende Zeile am Ende enthalten `//# sourceMappingURL=[id].main.js.map` oder auch nicht, wenn die **hidden**-Option auf **true** gesetzt ist. Wenn die Sourcemap jedoch für **Skripte** deaktiviert ist, wird das Testen komplexer und wir können die Datei nicht erhalten. Darüber hinaus kann die Sourcemap während des Projektbuilds aktiviert werden, z.B. mit `ng build --source-map`.
|
|
|
|
## Datenbindung
|
|
|
|
Binding bezieht sich auf den Prozess der Kommunikation zwischen einer Komponente und ihrer entsprechenden Ansicht. Es wird verwendet, um Daten zwischen dem Angular-Framework zu übertragen. Daten können auf verschiedene Weise übertragen werden, z.B. durch Ereignisse, Interpolation, Eigenschaften oder durch den Mechanismus der bidirektionalen Bindung. Darüber hinaus können Daten zwischen verwandten Komponenten (Eltern-Kind-Beziehung) und zwischen zwei nicht verwandten Komponenten mithilfe der Service-Funktion geteilt werden.
|
|
|
|
Wir können die Bindung nach Datenfluss klassifizieren:
|
|
|
|
* Datenquelle zum Ansichtsziel (umfasst _Interpolation_, _Eigenschaften_, _Attribute_, _Klassen_ und _Styles_); kann mit `[]` oder `{{}}` in der Vorlage angewendet werden;
|
|
* Ansichtsziel zur Datenquelle (umfasst _Ereignisse_); kann mit `()` in der Vorlage angewendet werden;
|
|
* Bidirektional; kann mit `[()]` in der Vorlage angewendet werden.
|
|
|
|
Binding kann auf Eigenschaften, Ereignisse und Attribute angewendet werden, sowie auf jedes öffentliche Element einer Quellrichtlinie:
|
|
|
|
| TYP | ZIEL | BEISPIELE |
|
|
| --------- | -------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
| Eigenschaft | Elementeigenschaft, Komponenteneigenschaft, Direktiveigenschaft | \<img \[alt]="hero.name" \[src]="heroImageUrl"> |
|
|
| Ereignis | Elementereignis, Komponentenereignis, Direktiveereignis | \<button type="button" (click)="onSave()">Speichern |
|
|
| Bidirektional | Ereignis und Eigenschaft | \<input \[(ngModel)]="name"> |
|
|
| Attribut | Attribut (die Ausnahme) | \<button type="button" \[attr.aria-label]="Hilfe">Hilfe |
|
|
| Klasse | Klasseneigenschaft | \<div \[class.special]="isSpecial">Spezial |
|
|
| Style | Styleigenschaft | \<button type="button" \[style.color]="isSpecial ? 'red' : 'green'"> |
|
|
|
|
## Angular-Sicherheitsmodell
|
|
|
|
Das Design von Angular umfasst standardmäßig die Codierung oder Säuberung aller Daten, was es zunehmend schwierig macht, XSS-Sicherheitslücken in Angular-Projekten zu entdecken und auszunutzen. Es gibt zwei verschiedene Szenarien für die Datenverarbeitung:
|
|
|
|
1. Interpolation oder `{{user_input}}` - führt eine kontextsensitive Codierung durch und interpretiert die Benutzereingabe als Text;
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
test = "<script>alert(1)</script><h1>test</h1>";
|
|
|
|
//app.component.html
|
|
{{test}}
|
|
```
|
|
|
|
Ergebnis: `<script>alert(1)</script><h1>test</h1>`
|
|
2. Bindung an Eigenschaften, Attribute, Klassen und Styles oder `[attribute]="user_input"` - führt eine Säuberung basierend auf dem bereitgestellten Sicherheitskontext durch.
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
test = "<script>alert(1)</script><h1>test</h1>";
|
|
|
|
//app.component.html
|
|
<div [innerHtml]="test"></div>
|
|
```
|
|
|
|
Ergebnis: `<div><h1>test</h1></div>`
|
|
|
|
Es gibt 6 Arten von `SecurityContext` :
|
|
|
|
* `None`;
|
|
* `HTML` wird verwendet, wenn der Wert als HTML interpretiert wird;
|
|
* `STYLE` wird verwendet, um CSS in die `style`-Eigenschaft einzubinden;
|
|
* `URL` wird für URL-Eigenschaften wie `<a href>` verwendet;
|
|
* `SCRIPT` wird für JavaScript-Code verwendet;
|
|
* `RESOURCE_URL` als URL, die geladen und als Code ausgeführt wird, z.B. in `<script src>`.
|
|
|
|
## Schwachstellen
|
|
|
|
### Umgehung der Sicherheitsvertrauensmethoden
|
|
|
|
Angular führt eine Liste von Methoden ein, um seinen standardmäßigen Säuberungsprozess zu umgehen und anzuzeigen, dass ein Wert sicher in einem bestimmten Kontext verwendet werden kann, wie in den folgenden fünf Beispielen:
|
|
|
|
1. `bypassSecurityTrustUrl` wird verwendet, um anzugeben, dass der angegebene Wert eine sichere Style-URL ist:
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');
|
|
|
|
//app.component.html
|
|
<a class="e2e-trusted-url" [href]="trustedUrl">Klick mich</a>
|
|
|
|
//Ergebnis
|
|
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Klick mich</a>
|
|
```
|
|
2. `bypassSecurityTrustResourceUrl` wird verwendet, um anzugeben, dass der angegebene Wert eine sichere Ressourcen-URL ist:
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedResourceUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png");
|
|
|
|
//app.component.html
|
|
<iframe [src]="trustedResourceUrl"></iframe>
|
|
|
|
//Ergebnis
|
|
<img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
|
|
```
|
|
3. `bypassSecurityTrustHtml` wird verwendet, um anzugeben, dass der angegebene Wert sicherer HTML-Code ist. Beachten Sie, dass das Einfügen von `script`-Elementen auf diese Weise nicht dazu führt, dass der darin enthaltene JavaScript-Code ausgeführt wird, aufgrund der Art und Weise, wie diese Elemente dem DOM-Baum hinzugefügt werden.
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml("<h1>html tag</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
|
|
|
|
//app.component.html
|
|
<p style="border:solid" [innerHtml]="trustedHtml"></p>
|
|
|
|
//Ergebnis
|
|
<h1>html tag</h1>
|
|
<svg onclick="alert('bypassSecurityTrustHtml')" style="display:block">blah</svg>
|
|
```
|
|
4. `bypassSecurityTrustScript` wird verwendet, um anzugeben, dass der angegebene Wert sicherer JavaScript-Code ist. Wir haben jedoch festgestellt, dass sich das Verhalten dieses Codes unvorhersehbar verhält, da wir keinen JS-Code in Vorlagen ausführen konnten, der diese Methode verwendet.
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");
|
|
|
|
//app.component.html
|
|
<script [innerHtml]="trustedScript"></script>
|
|
|
|
//Ergebnis
|
|
-
|
|
```
|
|
5. `bypassSecurityTrustStyle` wird verwendet, um anzugeben, dass der angegebene Wert sicherer CSS-Code ist. Das folgende Beispiel veranschaulicht die CSS-Injektion:
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedStyle = this.sanitizer.bypassSecurityTrustStyle('background-image: url(https://example.com/exfil/a)');
|
|
|
|
//app.component.html
|
|
<input type="password" name="pwd" value="01234" [style]="trustedStyle">
|
|
|
|
//Ergebnis
|
|
Request URL: GET example.com/exfil/a
|
|
```
|
|
|
|
Angular bietet eine `sanitize`-Methode, um Daten vor der Anzeige in Ansichten zu säubern. Diese Methode verwendet den bereitgestellten Sicherheitskontext und bereinigt die Eingabe entsprechend. Es ist jedoch wichtig, den richtigen Sicherheitskontext für die spezifischen Daten und den Kontext zu verwenden. Wenn beispielsweise ein Säuberer mit `SecurityContext.URL` auf HTML-Inhalte angewendet wird, bietet dies keinen Schutz gegen gefährliche HTML-Werte. In solchen Szenarien kann ein Missbrauch des Sicherheitskontexts zu XSS-Sicherheitslücken führen.
|
|
### HTML-Injektion
|
|
|
|
Diese Schwachstelle tritt auf, wenn Benutzereingaben an eine der drei Eigenschaften gebunden werden: `innerHTML`, `outerHTML` oder `iframe` `srcdoc`. Während die Bindung an diese Attribute HTML wie es ist interpretiert, wird die Eingabe mit `SecurityContext.HTML` bereinigt. Daher ist eine HTML-Injektion möglich, aber kein Cross-Site Scripting (XSS).
|
|
|
|
Beispiel für die Verwendung von `innerHTML`:
|
|
```jsx
|
|
//app.component.ts
|
|
import { Component} from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html'
|
|
})
|
|
export class AppComponent{
|
|
//define a variable with user input
|
|
test = "<script>alert(1)</script><h1>test</h1>";
|
|
}
|
|
|
|
//app.component.html
|
|
<div [innerHTML]="test"></div>
|
|
```
|
|
Das Ergebnis ist `<div><h1>test</h1></div>`.
|
|
|
|
### Template-Injektion
|
|
|
|
#### Clientseitiges Rendern (CSR)
|
|
|
|
Angular verwendet Vorlagen, um Seiten dynamisch zu erstellen. Der Ansatz besteht darin, Vorlagenausdrücke, die von Angular ausgewertet werden sollen, in doppelte geschweifte Klammern (`{{}}`) einzuschließen. Auf diese Weise bietet das Framework zusätzliche Funktionalität. Zum Beispiel würde eine Vorlage wie `{{1+1}}` als 2 angezeigt.
|
|
|
|
Normalerweise maskiert Angular Benutzereingaben, die mit Vorlagenausdrücken verwechselt werden können (z. B. Zeichen wie \`< > ' " \`\`). Das bedeutet, dass zusätzliche Schritte erforderlich sind, um diese Einschränkung zu umgehen, z. B. die Verwendung von Funktionen, die JavaScript-Zeichenkettenobjekte generieren, um gesperrte Zeichen zu vermeiden. Um dies jedoch zu erreichen, müssen wir den Angular-Kontext, seine Eigenschaften und Variablen berücksichtigen. Daher kann ein Angriff auf die Template-Injektion wie folgt aussehen:
|
|
```jsx
|
|
//app.component.ts
|
|
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
|
|
@Component({
|
|
selector: 'app-root',
|
|
template: '<h1>title</h1>' + _userInput
|
|
})
|
|
```
|
|
Wie oben gezeigt, bezieht sich `constructor` auf den Gültigkeitsbereich der Eigenschaft `constructor` des Objekts, was es uns ermöglicht, den String-Konstruktor aufzurufen und beliebigen Code auszuführen.
|
|
|
|
#### Serverseitiges Rendern (SSR)
|
|
|
|
Im Gegensatz zu CSR, das im DOM des Browsers stattfindet, ist Angular Universal für das SSR von Vorlagendateien verantwortlich. Diese Dateien werden dann an den Benutzer geliefert. Trotz dieser Unterscheidung wendet Angular Universal die gleichen Säuberungsmechanismen an, die auch bei CSR zur Verbesserung der SSR-Sicherheit verwendet werden. Eine Vorlageneinschleusungsschwachstelle in SSR kann auf die gleiche Weise wie in CSR erkannt werden, da die verwendete Vorlagensprache die gleiche ist.
|
|
|
|
Natürlich besteht auch die Möglichkeit, neue Vorlageneinschleusungsschwachstellen einzuführen, wenn Drittanbieter-Vorlagenengines wie Pug und Handlebars verwendet werden.
|
|
|
|
### XSS
|
|
|
|
#### DOM-Schnittstellen
|
|
|
|
Wie bereits erwähnt, können wir über die _Document_-Schnittstelle direkt auf den DOM zugreifen. Wenn die Benutzereingabe nicht zuvor validiert wird, kann dies zu Cross-Site Scripting (XSS)-Schwachstellen führen.
|
|
|
|
In den folgenden Beispielen haben wir die Methoden `document.write()` und `document.createElement()` verwendet:
|
|
```jsx
|
|
//app.component.ts 1
|
|
import { Component} from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
template: ''
|
|
})
|
|
export class AppComponent{
|
|
constructor () {
|
|
document.open();
|
|
document.write("<script>alert(document.domain)</script>");
|
|
document.close();
|
|
}
|
|
}
|
|
|
|
//app.component.ts 2
|
|
import { Component} from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
template: ''
|
|
})
|
|
export class AppComponent{
|
|
constructor () {
|
|
var d = document.createElement('script');
|
|
var y = document.createTextNode("alert(1)");
|
|
d.appendChild(y);
|
|
document.body.appendChild(d);
|
|
}
|
|
}
|
|
|
|
//app.component.ts 3
|
|
import { Component} from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
template: ''
|
|
})
|
|
export class AppComponent{
|
|
constructor () {
|
|
var a = document.createElement('img');
|
|
a.src='1';
|
|
a.setAttribute('onerror','alert(1)');
|
|
document.body.appendChild(a);
|
|
}
|
|
}
|
|
```
|
|
#### Angular-Klassen
|
|
|
|
Es gibt einige Klassen, die in Angular verwendet werden können, um mit DOM-Elementen zu arbeiten: `ElementRef`, `Renderer2`, `Location` und `Document`. Eine detaillierte Beschreibung der letzten beiden Klassen finden Sie im Abschnitt **Open Redirects**. Der Hauptunterschied zwischen den ersten beiden besteht darin, dass die `Renderer2`-API eine Abstraktionsschicht zwischen dem DOM-Element und dem Komponentencode bietet, während `ElementRef` lediglich eine Referenz auf das Element enthält. Daher sollte die `ElementRef`-API gemäß der Angular-Dokumentation nur als letzter Ausweg verwendet werden, wenn direkter Zugriff auf das DOM erforderlich ist.
|
|
|
|
* `ElementRef` enthält die Eigenschaft `nativeElement`, die verwendet werden kann, um die DOM-Elemente zu manipulieren. Eine unsachgemäße Verwendung von `nativeElement` kann jedoch zu einer XSS-Injektionslücke führen, wie im folgenden Beispiel gezeigt:
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css']
|
|
})
|
|
export class AppComponent {
|
|
...
|
|
constructor(private elementRef: ElementRef) {
|
|
const s = document.createElement('script');
|
|
s.type = 'text/javascript';
|
|
s.textContent = 'alert("Hello World")';
|
|
this.elementRef.nativeElement.appendChild(s);
|
|
}
|
|
}
|
|
```
|
|
* Trotz der Tatsache, dass `Renderer2` eine API bereitstellt, die sicher verwendet werden kann, auch wenn kein direkter Zugriff auf native Elemente unterstützt wird, weist sie dennoch einige Sicherheitslücken auf. Mit `Renderer2` ist es möglich, Attribute an einem HTML-Element mit der Methode `setAttribute()` festzulegen, die keine XSS-Präventionsmechanismen enthält.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css']
|
|
})
|
|
export class AppComponent {
|
|
|
|
public constructor (
|
|
private renderer2: Renderer2
|
|
){}
|
|
@ViewChild("img") img!: ElementRef;
|
|
|
|
addAttribute(){
|
|
this.renderer2.setAttribute(this.img.nativeElement, 'src', '1');
|
|
this.renderer2.setAttribute(this.img.nativeElement, 'onerror', 'alert(1)');
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<img #img>
|
|
<button (click)="setAttribute()">Click me!</button>
|
|
```
|
|
* Um die Eigenschaft eines DOM-Elements festzulegen, können Sie die Methode `Renderer2.setProperty()` verwenden und einen XSS-Angriff auslösen:
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import {Component, Renderer2, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css']
|
|
})
|
|
export class AppComponent {
|
|
|
|
public constructor (
|
|
private renderer2: Renderer2
|
|
){}
|
|
@ViewChild("img") img!: ElementRef;
|
|
|
|
setProperty(){
|
|
this.renderer2.setProperty(this.img.nativeElement, 'innerHTML', '<img src=1 onerror=alert(1)>');
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<a #a></a>
|
|
<button (click)="setProperty()">Click me!</button>
|
|
```
|
|
|
|
Während unserer Recherche haben wir auch das Verhalten anderer `Renderer2`-Methoden wie `setStyle()`, `createComment()` und `setValue()` in Bezug auf XSS- und CSS-Injektionen untersucht. Aufgrund ihrer funktionalen Einschränkungen konnten wir jedoch keine gültigen Angriffsvektoren für diese Methoden finden.
|
|
|
|
#### jQuery
|
|
|
|
jQuery ist eine schnelle, kleine und funktionsreiche JavaScript-Bibliothek, die in Angular-Projekten zur Manipulation von HTML-DOM-Objekten verwendet werden kann. Wie bekannt ist, können die Methoden dieser Bibliothek ausgenutzt werden, um eine XSS-Schwachstelle zu erreichen. Um zu diskutieren, wie einige anfällige jQuery-Methoden in Angular-Projekten ausgenutzt werden können, haben wir diesen Unterabschnitt hinzugefügt.
|
|
|
|
* Die Methode `html()` gibt den HTML-Inhalt des ersten Elements im Satz der übereinstimmenden Elemente zurück oder legt den HTML-Inhalt jedes übereinstimmenden Elements fest. Jedoch kann jede jQuery-Konstruktor- oder -Methode, die einen HTML-String akzeptiert, potenziell Code ausführen. Dies kann durch das Einfügen von `<script>`-Tags oder die Verwendung von HTML-Attributen, die Code ausführen, wie im Beispiel gezeigt, geschehen.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import { Component, OnInit } from '@angular/core';
|
|
import * as $ from 'jquery';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css']
|
|
})
|
|
export class AppComponent implements OnInit
|
|
{
|
|
ngOnInit()
|
|
{
|
|
$("button").on("click", function()
|
|
{
|
|
$("p").html("<script>alert(1)</script>");
|
|
});
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<button>Click me</button>
|
|
<p>some text here</p>
|
|
```
|
|
* Die Methode `jQuery.parseHTML()` verwendet native Methoden, um den String in eine Reihe von DOM-Knoten umzuwandeln, die dann in das Dokument eingefügt werden können.
|
|
|
|
```tsx
|
|
jQuery.parseHTML(data [, context ] [, keepScripts ])
|
|
```
|
|
|
|
Wie zuvor erwähnt, werden die meisten jQuery-APIs, die HTML-Strings akzeptieren, Skripte ausführen, die im HTML enthalten sind. Die Methode `jQuery.parseHTML()` führt keine Skripte in dem analysierten HTML aus, es sei denn, `keepScripts` ist explizit `true`. Es ist jedoch immer noch möglich, Skripte indirekt auszuführen, zum Beispiel über das Attribut `<img onerror>`.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import { Component, OnInit } from '@angular/core';
|
|
import * as $ from 'jquery';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css']
|
|
})
|
|
export class AppComponent implements OnInit
|
|
{
|
|
ngOnInit()
|
|
{
|
|
$("button").on("click", function()
|
|
{
|
|
var $palias = $("#palias"),
|
|
str = "<img src=1 onerror=alert(1)>",
|
|
html = $.parseHTML(str),
|
|
nodeNames = [];
|
|
$palias.append(html);
|
|
});
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<button>Click me</button>
|
|
<p id="palias">some text</p>
|
|
```
|
|
|
|
### Open Redirects
|
|
|
|
#### DOM-Schnittstellen
|
|
|
|
Gemäß der W3C-Dokumentation werden die Objekte `window.location` und `document.location` in modernen Browsern als Aliase behandelt. Aus diesem Grund haben sie eine ähnliche Implementierung einiger Methoden und Eigenschaften, die eine Open-Redirect- und DOM-XSS mit `javascript://`-Schema-Angriffe verursachen können, wie unten beschrieben.
|
|
|
|
* `window.location.href`(und `document.location.href`)
|
|
|
|
Der kanonische Weg, das aktuelle DOM-Location-Objekt zu erhalten, besteht darin, `window.location` zu verwenden. Es kann auch verwendet werden, um den Browser zu einer neuen Seite umzuleiten. Daher ermöglicht uns die Kontrolle über dieses Objekt, eine Open-Redirect-Schwachstelle auszunutzen.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.location.href = "https://google.com/about"
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<button type="button" (click)="goToUrl()">Click me!</button>
|
|
```
|
|
|
|
Der Ausbeutungsprozess ist für die folgenden Szenarien identisch.
|
|
* `window.location.assign()`(und `document.location.assign()`)
|
|
|
|
Diese Methode bewirkt, dass das Fenster das Dokument an der angegebenen URL lädt und anzeigt. Wenn wir die Kontrolle über diese Methode haben, könnte sie ein Angriffsziel für einen Open-Redirect-Angriff sein.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.location.assign("https://google.com/about")
|
|
}
|
|
}
|
|
```
|
|
* `window.location.replace()`(und `document.location.replace()`)
|
|
|
|
Diese Methode ersetzt die aktuelle Ressource durch diejenige unter der angegebenen URL.
|
|
|
|
Der Unterschied zur Methode `assign()` besteht darin, dass nach Verwendung von `window.location.replace()` die aktuelle Seite nicht im Sitzungsverlauf gespeichert wird. Es ist jedoch auch möglich, eine Open-Redirect-Schwachstelle auszunutzen, wenn wir die Kontrolle über diese Methode haben.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.location.replace("http://google.com/about")
|
|
}
|
|
}
|
|
```
|
|
* `window.open()`
|
|
|
|
Die Methode `window.open()` nimmt eine URL entgegen und lädt die Ressource, die sie identifiziert, in einem neuen oder vorhandenen Tab oder Fenster. Die Kontrolle über diese Methode könnte auch eine Möglichkeit sein, eine XSS- oder Open-Redirect-Schwachstelle auszulösen.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.open("https://google.com/about", "_blank")
|
|
}
|
|
}
|
|
```
|
|
#### Angular-Klassen
|
|
|
|
* Laut der Angular-Dokumentation ist die Angular-Klasse `Document` dasselbe wie das DOM-Dokument, was bedeutet, dass gängige Vektoren für das DOM-Dokument verwendet werden können, um clientseitige Schwachstellen in Angular auszunutzen. Die Eigenschaften und Methoden von `Document.location` können als Angriffsvektoren für erfolgreiche Open-Redirect-Angriffe dienen, wie im folgenden Beispiel gezeigt:
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import { Component, Inject } from '@angular/core';
|
|
import { DOCUMENT } from '@angular/common';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css']
|
|
})
|
|
export class AppComponent {
|
|
constructor(@Inject(DOCUMENT) private document: Document) { }
|
|
|
|
goToUrl(): void {
|
|
this.document.location.href = 'https://google.com/about';
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<button type="button" (click)="goToUrl()">Klick mich!</button>
|
|
```
|
|
* Während der Recherche haben wir auch die Angular-Klasse `Location` auf Open-Redirect-Schwachstellen überprüft, aber es wurden keine gültigen Vektoren gefunden. `Location` ist ein Angular-Service, den Anwendungen verwenden können, um mit der aktuellen URL des Browsers zu interagieren. Dieser Service verfügt über mehrere Methoden zur Manipulation der angegebenen URL - `go()`, `replaceState()` und `prepareExternalUrl()`. Wir können sie jedoch nicht zur Umleitung auf eine externe Domain verwenden. Zum Beispiel:
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
import { Component, Inject } from '@angular/core';
|
|
import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common';
|
|
|
|
@Component({
|
|
selector: 'app-root',
|
|
templateUrl: './app.component.html',
|
|
styleUrls: ['./app.component.css'],
|
|
providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}],
|
|
})
|
|
export class AppComponent {
|
|
location: Location;
|
|
constructor(location: Location) {
|
|
this.location = location;
|
|
}
|
|
goToUrl(): void {
|
|
console.log(this.location.go("http://google.com/about"));
|
|
}
|
|
}
|
|
```
|
|
|
|
Ergebnis: `http://localhost:4200/http://google.com/about`
|
|
* Die Angular-Klasse `Router` wird hauptsächlich zum Navigieren innerhalb der gleichen Domäne verwendet und führt keine zusätzlichen Schwachstellen in der Anwendung ein:
|
|
|
|
```jsx
|
|
//app-routing.module.ts
|
|
const routes: Routes = [
|
|
{ path: '', redirectTo: 'https://google.com', pathMatch: 'full' }]
|
|
```
|
|
|
|
Ergebnis: `http://localhost:4200/https:`
|
|
|
|
Die folgenden Methoden navigieren ebenfalls innerhalb des Domänenbereichs:
|
|
|
|
```jsx
|
|
const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ]
|
|
this.router.navigate(['PATH'])
|
|
this.router.navigateByUrl('URL')
|
|
```
|
|
|
|
## Referenzen
|
|
|
|
* [Angular](https://angular.io/)
|
|
* [Angular Security: Der ultimative Leitfaden (Teil 1)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-1)
|
|
* [Angular Security: Der ultimative Leitfaden (Teil 2)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-2)
|
|
* [Angular Security: Der ultimative Leitfaden (Teil 3)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-3)
|
|
* [Angular Security: Checkliste](https://lsgeurope.com/post/angular-security-checklist)
|
|
* [Workspace- und Projektdateistruktur](https://angular.io/guide/file-structure)
|
|
* [Einführung in Komponenten und Vorlagen](https://angular.io/guide/architecture-components)
|
|
* [Konfiguration der Quellkarten](https://angular.io/guide/workspace-config#source-map-configuration)
|
|
* [Bindungssyntax](https://angular.io/guide/binding-syntax)
|
|
* [Angular-Kontext: Einfache Datenbindung für verschachtelte Komponentenbäume und den Router Outlet](https://medium.com/angular-in-depth/angular-context-easy-data-binding-for-nested-component-trees-and-the-router-outlet-a977efacd48)
|
|
* [Sanktionierung und Sicherheitskontexte](https://angular.io/guide/security#sanitization-and-security-contexts)
|
|
* [GitHub - angular/dom\_security\_schema.ts](https://github.com/angular/angular/blob/main/packages/compiler/src/schema/dom\_security\_schema.ts)
|
|
* [XSS in Angular und AngularJS](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/XSS%20in%20Angular.md)
|
|
* [Angular Universal](https://angular.io/guide/universal)
|
|
* [DOM XSS](https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/dom-xss)
|
|
* [Angular ElementRef](https://angular.io/api/core/ElementRef)
|
|
* [Angular Renderer2](https://angular.io/api/core/Renderer2)
|
|
* [Renderer2-Beispiel: Manipulation des DOM in Angular - TekTutorialsHub](https://www.tektutorialshub.com/angular/renderer2-angular/)
|
|
* [jQuery API-Dokumentation](http://api.jquery.com/)
|
|
* [Wie man jQuery mit Angular verwendet (wenn es unbedingt erforderlich ist)](https://blog.bitsrc.io/how-to-use-jquery-with-angular-when-you-absolutely-have-to-42c8b6a37ff9)
|
|
* [Angular Document](https://angular.io/api/common/DOCUMENT)
|
|
* [Angular Location](https://angular.io/api/common/Location)
|
|
* [Angular Router](https://angular.io/api/router/Router)
|