mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-13 06:42:54 +00:00
603 lines
30 KiB
Markdown
603 lines
30 KiB
Markdown
# Angular
|
|
|
|
## Lista de verificación
|
|
|
|
Lista de verificación [desde aquí](https://lsgeurope.com/post/angular-security-checklist).
|
|
|
|
* [ ] Angular se considera un marco del lado del cliente y no se espera que proporcione protección del lado del servidor
|
|
* [ ] El sourcemap para scripts está deshabilitado en la configuración del proyecto
|
|
* [ ] La entrada de usuario no confiable siempre se interpola o se sanea antes de ser utilizada en plantillas
|
|
* [ ] El usuario no tiene control sobre las plantillas del lado del servidor o del lado del cliente
|
|
* [ ] La entrada de usuario no confiable se sanea utilizando un contexto de seguridad apropiado antes de ser confiada por la aplicación
|
|
* [ ] No se utilizan métodos `BypassSecurity*` con entrada no confiable
|
|
* [ ] La entrada de usuario no confiable no se pasa a clases de Angular como `ElementRef`, `Renderer2` y `Document`, u otros puntos de fuga de JQuery/DOM
|
|
|
|
## ¿Qué es Angular
|
|
|
|
Angular es un marco de **front-end** **potente** y **de código abierto** mantenido por **Google**. Utiliza **TypeScript** para mejorar la legibilidad y depuración del código. Con mecanismos de seguridad sólidos, Angular previene vulnerabilidades comunes del lado del cliente como **XSS** y **redirecciones abiertas**. También se puede utilizar en el **lado del servidor**, por lo que las consideraciones de seguridad son importantes desde **ambos ángulos**.
|
|
|
|
## Arquitectura del marco
|
|
|
|
Para comprender mejor los conceptos esenciales de Angular, veamos su arquitectura básica.
|
|
|
|
Un proyecto Angular común suele tener esta apariencia:
|
|
```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
|
|
```
|
|
Según la documentación, cada aplicación Angular tiene al menos un componente, el componente raíz (`AppComponent`) que conecta una jerarquía de componentes con el DOM. Cada componente define una clase que contiene datos y lógica de la aplicación, y está asociado con una plantilla HTML que define una vista a ser mostrada en un entorno objetivo. El decorador `@Component()` identifica la clase inmediatamente debajo de él como un componente, y proporciona la plantilla y metadatos específicos del componente relacionados. El `AppComponent` está definido en el archivo `app.component.ts`.
|
|
|
|
Los NgModules de Angular declaran un contexto de compilación para un conjunto de componentes dedicado a un dominio de aplicación, un flujo de trabajo, o un conjunto de capacidades estrechamente relacionadas. Cada aplicación Angular tiene un módulo raíz, convencionalmente llamado `AppModule`, que proporciona el mecanismo de arranque que inicia la aplicación. Una aplicación típicamente contiene muchos módulos funcionales. El `AppModule` está definido en el archivo `app.module.ts`.
|
|
|
|
El NgModule `Router` de Angular proporciona un servicio que te permite definir una ruta de navegación entre los diferentes estados de la aplicación y jerarquías de vistas en tu aplicación. El `RouterModule` está definido en el archivo `app-routing.module.ts`.
|
|
|
|
Para datos o lógica que no están asociados con una vista específica, y que deseas compartir entre componentes, creas una clase de servicio. La definición de una clase de servicio es inmediatamente precedida por el decorador `@Injectable()`. El decorador proporciona los metadatos que permiten que otros proveedores sean inyectados como dependencias en tu clase. La inyección de dependencias (DI) te permite mantener tus clases de componentes livianas y eficientes. No obtienen datos del servidor, validan la entrada del usuario, o registran directamente en la consola; delegan tales tareas a los servicios.
|
|
|
|
## Configuración de Sourcemap
|
|
|
|
El framework Angular traduce archivos TypeScript a código JavaScript siguiendo las opciones de `tsconfig.json` y luego construye un proyecto con la configuración de `angular.json`. Al observar el archivo `angular.json`, notamos una opción para habilitar o deshabilitar un sourcemap. Según la documentación de Angular, la configuración predeterminada tiene un archivo sourcemap habilitado para scripts y no está oculto por defecto:
|
|
```json
|
|
"sourceMap": {
|
|
"scripts": true,
|
|
"styles": true,
|
|
"vendor": false,
|
|
"hidden": false
|
|
}
|
|
```
|
|
Generalmente, los archivos de sourcemap se utilizan con fines de depuración ya que mapean archivos generados a sus archivos originales. Por lo tanto, no se recomienda usarlos en un entorno de producción. Si los sourcemaps están habilitados, mejoran la legibilidad y ayudan en el análisis de archivos replicando el estado original del proyecto Angular. Sin embargo, si están deshabilitados, un revisor aún puede analizar un archivo JavaScript compilado manualmente buscando patrones anti-seguridad.
|
|
|
|
Además, un archivo JavaScript compilado con un proyecto Angular se puede encontrar en las herramientas de desarrollo del navegador → Fuentes (o Depurador y Fuentes) → \[id].main.js. Dependiendo de las opciones habilitadas, este archivo puede contener la siguiente línea al final `//# sourceMappingURL=[id].main.js.map` o puede que no, si la opción **hidden** está establecida en **true**. Sin embargo, si el sourcemap está deshabilitado para **scripts**, las pruebas se vuelven más complejas y no podemos obtener el archivo. Además, el sourcemap se puede habilitar durante la construcción del proyecto como `ng build --source-map`.
|
|
|
|
## Vinculación de datos
|
|
|
|
La vinculación se refiere al proceso de comunicación entre un componente y su vista correspondiente. Se utiliza para transferir datos hacia y desde el framework Angular. Los datos pueden pasarse a través de varios medios, como eventos, interpolación, propiedades o a través del mecanismo de vinculación bidireccional. Además, los datos también se pueden compartir entre componentes relacionados (relación padre-hijo) y entre dos componentes no relacionados utilizando la función de Servicio.
|
|
|
|
Podemos clasificar la vinculación por flujo de datos:
|
|
|
|
* Fuente de datos al objetivo de vista (incluye _interpolación_, _propiedades_, _atributos_, _clases_ y _estilos_); se puede aplicar utilizando `[]` o `{{}}` en la plantilla;
|
|
* Objetivo de vista a fuente de datos (incluye _eventos_); se puede aplicar utilizando `()` en la plantilla;
|
|
* Bidireccional; se puede aplicar utilizando `[()]` en la plantilla.
|
|
|
|
La vinculación se puede realizar en propiedades, eventos y atributos, así como en cualquier miembro público de una directiva fuente:
|
|
|
|
| TIPO | OBJETIVO | EJEMPLOS |
|
|
| --------- | -------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
| Propiedad | Propiedad del elemento, Propiedad del componente, Propiedad de la directiva | \<img \[alt]="hero.name" \[src]="heroImageUrl"> |
|
|
| Evento | Evento del elemento, Evento del componente, Evento de la directiva | \<button type="button" (click)="onSave()">Guardar |
|
|
| Bidireccional | Evento y propiedad | \<input \[(ngModel)]="name"> |
|
|
| Atributo | Atributo (la excepción) | \<button type="button" \[attr.aria-label]="help">ayuda |
|
|
| Clase | Propiedad de clase | \<div \[class.special]="isSpecial">Especial |
|
|
| Estilo | Propiedad de estilo | \<button type="button" \[style.color]="isSpecial ? 'red' : 'green'"> |
|
|
|
|
## Modelo de seguridad de Angular
|
|
|
|
El diseño de Angular incluye la codificación o sanitización de todos los datos de forma predeterminada, lo que hace cada vez más difícil descubrir y explotar vulnerabilidades XSS en proyectos de Angular. Hay dos escenarios distintos para el manejo de datos:
|
|
|
|
1. Interpolación o `{{entrada_usuario}}` - realiza una codificación sensible al contexto e interpreta la entrada del usuario como texto;
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
test = "<script>alert(1)</script><h1>prueba</h1>";
|
|
|
|
//app.component.html
|
|
{{test}}
|
|
```
|
|
|
|
Resultado: `<script>alert(1)</script><h1>prueba</h1>`
|
|
2. Vinculación a propiedades, atributos, clases y estilos o `[atributo]="entrada_usuario"` - realiza una sanitización basada en el contexto de seguridad proporcionado.
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
test = "<script>alert(1)</script><h1>prueba</h1>";
|
|
|
|
//app.component.html
|
|
<div [innerHtml]="test"></div>
|
|
```
|
|
|
|
Resultado: `<div><h1>prueba</h1></div>`
|
|
|
|
Hay 6 tipos de `SecurityContext` :
|
|
|
|
* `None`;
|
|
* `HTML` se utiliza al interpretar el valor como HTML;
|
|
* `STYLE` se utiliza al vincular CSS en la propiedad `style`;
|
|
* `URL` se utiliza para propiedades de URL, como `<a href>`;
|
|
* `SCRIPT` se utiliza para código JavaScript;
|
|
* `RESOURCE_URL` como una URL que se carga y se ejecuta como código, por ejemplo, en `<script src>`.
|
|
|
|
## Vulnerabilidades
|
|
|
|
### Métodos de confianza de seguridad de bypass
|
|
|
|
Angular introduce una lista de métodos para evitar su proceso de sanitización predeterminado e indicar que un valor se puede utilizar de forma segura en un contexto específico, como en los siguientes cinco ejemplos:
|
|
|
|
1. `bypassSecurityTrustUrl` se utiliza para indicar que el valor dado es una URL de estilo segura:
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedUrl = this.sanitizer.bypassSecurityTrustUrl('javascript:alert()');
|
|
|
|
//app.component.html
|
|
<a class="e2e-trusted-url" [href]="trustedUrl">Haz clic aquí</a>
|
|
|
|
//resultado
|
|
<a _ngcontent-pqg-c12="" class="e2e-trusted-url" href="javascript:alert()">Haz clic aquí</a>
|
|
```
|
|
2. `bypassSecurityTrustResourceUrl` se utiliza para indicar que el valor dado es una URL de recurso segura:
|
|
|
|
```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>
|
|
|
|
//resultado
|
|
<img _ngcontent-nre-c12="" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
|
|
```
|
|
3. `bypassSecurityTrustHtml` se utiliza para indicar que el valor dado es HTML seguro. Tenga en cuenta que insertar elementos `script` en el árbol DOM de esta manera no hará que se ejecute el código JavaScript contenido, debido a cómo se agregan estos elementos al árbol DOM.
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedHtml = this.sanitizer.bypassSecurityTrustHtml("<h1>etiqueta html</h1><svg onclick=\"alert('bypassSecurityTrustHtml')\" style=display:block>blah</svg>");
|
|
|
|
//app.component.html
|
|
<p style="border:solid" [innerHtml]="trustedHtml"></p>
|
|
|
|
//resultado
|
|
<h1>etiqueta html</h1>
|
|
<svg onclick="alert('bypassSecurityTrustHtml')" style="display:block">blah</svg>
|
|
```
|
|
4. `bypassSecurityTrustScript` se utiliza para indicar que el valor dado es JavaScript seguro. Sin embargo, encontramos que su comportamiento es impredecible, ya que no pudimos ejecutar código JS en plantillas utilizando este método.
|
|
|
|
```jsx
|
|
//app.component.ts
|
|
this.trustedScript = this.sanitizer.bypassSecurityTrustScript("alert('bypass Security TrustScript')");
|
|
|
|
//app.component.html
|
|
<script [innerHtml]="trustedScript"></script>
|
|
|
|
//resultado
|
|
-
|
|
```
|
|
5. `bypassSecurityTrustStyle` se utiliza para indicar que el valor dado es CSS seguro. El siguiente ejemplo ilustra la inyección de CSS:
|
|
|
|
```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">
|
|
|
|
//resultado
|
|
URL de solicitud: GET example.com/exfil/a
|
|
```
|
|
|
|
Angular proporciona un método `sanitize` para sanear los datos antes de mostrarlos en las vistas. Este método emplea el contexto de seguridad proporcionado y limpia la entrada en consecuencia. Sin embargo, es crucial utilizar el contexto de seguridad correcto para los datos y el contexto específicos. Por ejemplo, aplicar un sanitizador con `SecurityContext.URL` en contenido HTML no proporciona protección contra valores HTML peligrosos. En tales escenarios, el uso incorrecto del contexto de seguridad podría llevar a vulnerabilidades XSS.
|
|
|
|
### Inyección de HTML
|
|
|
|
Esta vulnerabilidad ocurre cuando la entrada del usuario se vincula a cualquiera de las tres propiedades: `innerHTML`, `outerHTML` o `srcdoc` de `iframe`. Si bien la vinculación a estas atributos interpreta el HTML tal como es, la entrada se sanea utilizando `SecurityContext.HTML`. Por lo tanto, la inyección de HTML es posible, pero no lo es el scripting entre sitios (XSS).
|
|
|
|
Ejemplo de uso de `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>
|
|
```
|
|
El resultado es `<div><h1>test</h1></div>`.
|
|
|
|
### Inyección de plantillas
|
|
|
|
#### Representación del lado del cliente (CSR)
|
|
|
|
Angular aprovecha plantillas para construir páginas dinámicamente. El enfoque implica encerrar expresiones de plantilla para que Angular las evalúe dentro de dobles llaves (`{{}}`). De esta manera, el marco ofrece funcionalidades adicionales. Por ejemplo, una plantilla como `{{1+1}}` se mostraría como 2.
|
|
|
|
Normalmente, Angular escapa la entrada del usuario que puede confundirse con expresiones de plantilla (por ejemplo, caracteres como \`< > ' " \`\`). Esto significa que se requieren pasos adicionales para evitar esta restricción, como utilizar funciones que generen objetos de cadena de JavaScript para evitar el uso de caracteres en la lista negra. Sin embargo, para lograr esto, debemos considerar el contexto de Angular, sus propiedades y variables. Por lo tanto, un ataque de inyección de plantillas puede aparecer de la siguiente manera:
|
|
```jsx
|
|
//app.component.ts
|
|
const _userInput = '{{constructor.constructor(\'alert(1)\'()}}'
|
|
@Component({
|
|
selector: 'app-root',
|
|
template: '<h1>title</h1>' + _userInput
|
|
})
|
|
```
|
|
Como se muestra arriba: `constructor` se refiere al alcance de la propiedad `constructor` del objeto, lo que nos permite invocar el constructor de String y ejecutar un código arbitrario.
|
|
|
|
#### Renderizado del Lado del Servidor (SSR)
|
|
|
|
A diferencia de CSR, que ocurre en el DOM del navegador, Angular Universal es responsable del SSR de archivos de plantilla. Estos archivos son luego entregados al usuario. A pesar de esta distinción, Angular Universal aplica los mismos mecanismos de sanitización utilizados en CSR para mejorar la seguridad de SSR. Una vulnerabilidad de inyección de plantillas en SSR puede ser detectada de la misma manera que en CSR, porque el lenguaje de plantillas utilizado es el mismo.
|
|
|
|
Por supuesto, también existe la posibilidad de introducir nuevas vulnerabilidades de inyección de plantillas al emplear motores de plantillas de terceros como Pug y Handlebars.
|
|
|
|
### XSS
|
|
|
|
#### Interfaces del DOM
|
|
|
|
Como se mencionó anteriormente, podemos acceder directamente al DOM utilizando la interfaz _Document_. Si la entrada del usuario no se valida previamente, puede dar lugar a vulnerabilidades de scripting entre sitios (XSS).
|
|
|
|
Utilizamos los métodos `document.write()` y `document.createElement()` en los ejemplos a continuación:
|
|
```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);
|
|
}
|
|
}
|
|
```
|
|
#### Clases de Angular
|
|
|
|
Existen algunas clases que se pueden utilizar para trabajar con elementos del DOM en Angular: `ElementRef`, `Renderer2`, `Location` y `Document`. Una descripción detallada de las dos últimas clases se encuentra en la sección **Redirecciones abiertas**. La principal diferencia entre las dos primeras es que la API de `Renderer2` proporciona una capa de abstracción entre el elemento del DOM y el código del componente, mientras que `ElementRef` simplemente mantiene una referencia al elemento. Por lo tanto, según la documentación de Angular, la API de `ElementRef` solo debe utilizarse como último recurso cuando se necesita acceso directo al DOM.
|
|
|
|
* `ElementRef` contiene la propiedad `nativeElement`, que se puede utilizar para manipular los elementos del DOM. Sin embargo, el uso incorrecto de `nativeElement` puede resultar en una vulnerabilidad de inyección de XSS, como se muestra a continuación:
|
|
|
|
```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);
|
|
}
|
|
}
|
|
```
|
|
* A pesar de que `Renderer2` proporciona una API que se puede utilizar de forma segura incluso cuando no se admite el acceso directo a los elementos nativos, aún tiene algunas fallas de seguridad. Con `Renderer2`, es posible establecer atributos en un elemento HTML utilizando el método `setAttribute()`, que no tiene mecanismos de prevención de XSS.
|
|
|
|
```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()">¡Haz clic aquí!</button>
|
|
```
|
|
* Para establecer la propiedad de un elemento del DOM, se puede utilizar el método `Renderer2.setProperty()` y desencadenar un ataque XSS:
|
|
|
|
```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()">¡Haz clic aquí!</button>
|
|
```
|
|
|
|
Durante nuestra investigación, también examinamos el comportamiento de otros métodos de `Renderer2`, como `setStyle()`, `createComment()` y `setValue()`, en relación con las inyecciones de XSS y CSS. Sin embargo, no pudimos encontrar vectores de ataque válidos para estos métodos debido a sus limitaciones funcionales.
|
|
|
|
#### jQuery
|
|
|
|
jQuery es una biblioteca de JavaScript rápida, pequeña y rica en funciones que se puede utilizar en el proyecto de Angular para ayudar con la manipulación de objetos del DOM HTML. Sin embargo, como se sabe, los métodos de esta biblioteca pueden ser explotados para lograr una vulnerabilidad de XSS. Para discutir cómo algunos métodos vulnerables de jQuery pueden ser explotados en proyectos de Angular, agregamos esta subsección.
|
|
|
|
* El método `html()` obtiene el contenido HTML del primer elemento en el conjunto de elementos coincidentes o establece el contenido HTML de cada elemento coincidente. Sin embargo, por diseño, cualquier constructor o método de jQuery que acepte una cadena HTML potencialmente puede ejecutar código. Esto puede ocurrir mediante la inyección de etiquetas `<script>` o el uso de atributos HTML que ejecutan código, como se muestra en el ejemplo.
|
|
|
|
```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>Haz clic</button>
|
|
<p>algún texto aquí</p>
|
|
```
|
|
* El método `jQuery.parseHTML()` utiliza métodos nativos para convertir la cadena en un conjunto de nodos del DOM, que luego se pueden insertar en el documento.
|
|
|
|
```tsx
|
|
jQuery.parseHTML(data [, context ] [, keepScripts ])
|
|
```
|
|
|
|
Como se mencionó anteriormente, la mayoría de las API de jQuery que aceptan cadenas HTML ejecutarán scripts que se incluyen en el HTML. El método `jQuery.parseHTML()` no ejecuta scripts en el HTML analizado a menos que `keepScripts` sea explícitamente `true`. Sin embargo, aún es posible en la mayoría de los entornos ejecutar scripts de forma indirecta; por ejemplo, a través del atributo `<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>Haz clic</button>
|
|
<p id="palias">algún texto</p>
|
|
```
|
|
|
|
### Redirecciones abiertas
|
|
|
|
#### Interfaces del DOM
|
|
|
|
Según la documentación de W3C, los objetos `window.location` y `document.location` se tratan como alias en los navegadores modernos. Es por eso que tienen una implementación similar de algunos métodos y propiedades, lo que podría causar una redirección abierta y XSS del DOM con ataques de esquema `javascript://` como se menciona a continuación.
|
|
|
|
* `window.location.href` (y `document.location.href`)
|
|
|
|
La forma canónica de obtener el objeto de ubicación del DOM actual es utilizando `window.location`. También se puede utilizar para redirigir el navegador a una nueva página. Como resultado, tener control sobre este objeto nos permite explotar una vulnerabilidad de redirección abierta.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.location.href = "https://google.com/about"
|
|
}
|
|
}
|
|
|
|
//app.component.html
|
|
<button type="button" (click)="goToUrl()">¡Haz clic aquí!</button>
|
|
```
|
|
|
|
El proceso de explotación es idéntico para los siguientes escenarios.
|
|
* `window.location.assign()` (y `document.location.assign()`)
|
|
|
|
Este método hace que la ventana cargue y muestre el documento en la URL especificada. Si tenemos control sobre este método, podría ser un punto de ataque para una redirección abierta.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.location.assign("https://google.com/about")
|
|
}
|
|
}
|
|
```
|
|
* `window.location.replace()` (y `document.location.replace()`)
|
|
|
|
Este método reemplaza el recurso actual con el que se encuentra en la URL proporcionada.
|
|
|
|
La diferencia con el método `assign()` es que después de usar `window.location.replace()`, la página actual no se guardará en el Historial de sesión. Sin embargo, también es posible explotar una vulnerabilidad de redirección abierta cuando tenemos control sobre este método.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.location.replace("http://google.com/about")
|
|
}
|
|
}
|
|
```
|
|
* `window.open()`
|
|
|
|
El método `window.open()` toma una URL y carga el recurso que identifica en una nueva pestaña o ventana, ya sea nueva o existente. Tener control sobre este método también podría ser una oportunidad para desencadenar una vulnerabilidad de XSS o redirección abierta.
|
|
|
|
```tsx
|
|
//app.component.ts
|
|
...
|
|
export class AppComponent {
|
|
goToUrl(): void {
|
|
window.open("https://google.com/about", "_blank")
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Clases de Angular
|
|
|
|
* Según la documentación de Angular, `Document` de Angular es igual al documento del DOM, lo que significa que es posible utilizar vectores comunes para el documento del DOM para explotar vulnerabilidades del lado del cliente en Angular. Las propiedades y métodos de `Document.location` podrían ser puntos de ataque para ataques exitosos de redirección abierta, como se muestra en el ejemplo:
|
|
|
|
```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()">¡Haz clic aquí!</button>
|
|
```
|
|
* Durante la fase de investigación, también revisamos la clase `Location` de Angular en busca de vulnerabilidades de redirección abierta, pero no se encontraron vectores válidos. `Location` es un servicio de Angular que las aplicaciones pueden utilizar para interactuar con la URL actual del navegador. Este servicio tiene varios métodos para manipular la URL dada: `go()`, `replaceState()` y `prepareExternalUrl()`. Sin embargo, no podemos utilizarlos para redirigir al dominio externo. Por ejemplo:
|
|
|
|
```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"));
|
|
}
|
|
}
|
|
```
|
|
|
|
Resultado: `http://localhost:4200/http://google.com/about`
|
|
* La clase `Router` de Angular se utiliza principalmente para navegar dentro del mismo dominio y no introduce vulnerabilidades adicionales a la aplicación:
|
|
|
|
```jsx
|
|
//app-routing.module.ts
|
|
const routes: Routes = [
|
|
{ path: '', redirectTo: 'https://google.com', pathMatch: 'full' }]
|
|
```
|
|
|
|
Resultado: `http://localhost:4200/https:`
|
|
|
|
Los siguientes métodos también navegan dentro del alcance del dominio:
|
|
|
|
```jsx
|
|
const routes: Routes = [ { path: '', redirectTo: 'ROUTE', pathMatch: 'prefix' } ]
|
|
this.router.navigate(['PATH'])
|
|
this.router.navigateByUrl('URL')
|
|
```
|
|
|
|
## Referencias
|
|
|
|
* [Angular](https://angular.io/)
|
|
* [Angular Security: The Definitive Guide (Part 1)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-1)
|
|
* [Angular Security: The Definitive Guide (Part 2)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-2)
|
|
* [Angular Security: The Definitive Guide (Part 3)](https://lsgeurope.com/post/angular-security-the-definitive-guide-part-3)
|
|
* [Angular Security: Checklist](https://lsgeurope.com/post/angular-security-checklist)
|
|
* [Workspace and project file structure](https://angular.io/guide/file-structure)
|
|
* [Introduction to components and templates](https://angular.io/guide/architecture-components)
|
|
* [Source map configuration](https://angular.io/guide/workspace-config#source-map-configuration)
|
|
* [Binding syntax](https://angular.io/guide/binding-syntax)
|
|
* [Angular Context: Easy Data-Binding for Nested Component Trees and the Router Outlet](https://medium.com/angular-in-depth/angular-context-easy-data-binding-for-nested-component-trees-and-the-router-outlet-a977efacd48)
|
|
* [Sanitization and security contexts](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 and 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 Example: Manipulating DOM in Angular - TekTutorialsHub](https://www.tektutorialshub.com/angular/renderer2-angular/)
|
|
* [Documentación de la API de jQuery](http://api.jquery.com/)
|
|
* [Cómo usar jQuery con Angular (cuando es absolutamente necesario)](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)
|