Hacking con Go — HTTPS con mTLS

Vay3t
6 min readApr 5, 2023

--

Que es el mTLS?

mTLS es el acrónimo de “Mutual Transport Layer Security” (seguridad mutua de la capa de transporte), que es una forma de autenticación y encriptación que se utiliza en las comunicaciones a través de redes informáticas.

mTLS se basa en el protocolo TLS y requiere que tanto el cliente como el servidor se autentiquen entre sí mediante certificados digitales. Esto significa que, a diferencia del SSL Pinning que el cliente valida de que el certificado del servidor sea legítimo. En el caso de mTLS ambos extremos deben proporcionar pruebas de identidad antes de que se establezca la conexión.

Al requerir la autenticación mutua, mTLS proporciona una mayor seguridad en las comunicaciones en comparación con el TLS convencional, que solo autentica al servidor. Esto hace que mTLS sea especialmente útil en situaciones en las que se requiere un alto nivel de seguridad, como en la transferencia de datos financieros o de salud.

Diagrama de una conexion mTLS robada de Cloudflare.

Para que se usa en el Hacking el mTLS?

Por lo general, uno de los casos de uso es cuando necesitas programar un agente (reverse shell, backdoor, etc.) y quieres que este no sea interceptado por el área de DFIR/SOC/NOC, de esta forma es más difícil saber qué está pasando en la conexión entre el agente y el servidor.

Que es el SSL Pinning?

El SSL Pinning es una técnica de seguridad que permite a una aplicación móvil o de escritorio verificar la identidad del servidor al que se conecta mediante la validación de su certificado SSL/TLS, esto sirve para evitar ataques man-in-the-middle en donde se utilizarían certificados no legítimos. Algunas de las validaciones que se pueden hacer con el SSL Pinning son:

  • Verificar que el certificado del servidor es válido y emitido por una autoridad de certificación confiable.
  • Verificar que el nombre del dominio en el certificado coincide con el nombre del dominio al que se está conectando.
  • Verificar que el certificado no ha caducado y que no ha sido revocado.
  • Verificar que el certificado es para el servidor correcto y no ha sido falsificado.

mTLS vs SSL Pinning

La diferencia principal entre mTLS y SSL Pinning es que mTLS requiere que ambos extremos de la conexión tengan un certificado TLS y una clave privada, mientras que SSL Pinning solo requiere que el cliente tenga una copia del certificado o la clave pública del servidor. Esto hace que mTLS sea más complejo y menos escalable que SSL Pinning, pero también más seguro.

Cliente y servidor HTTPS con mTLS

Forkeando el proyecto mTLS de haoel es posible modificar el aspecto base y obtener un resultado más personalizado.

En este caso, usando el fork, creé el repositorio example-mtls, dejando solo lo necesario y modificando algunos programas del repositorio original, también añadiendo nuevos.

Clonando el repositorio para su posterior uso:

git clone https://gitlab.com/vay3t/example-mtls

Creación de certificados SSL/TLS

Crearemos un certificado SSL/TLS autofirmado tanto para el cliente como para el servidor, para eso usaremos el siguiente archivo:

go run certs.go

Este código en Go genera un certificado de autoridad de certificación (CA) y dos certificados de servidor y cliente. Estos certificados se utilizan para establecer una conexión segura (SSL/TLS) entre el servidor y el cliente. El código crea una estructura de datos pkix.Name que contiene información sobre el propietario del certificado, como el país, la organización, la unidad organizativa, etc. A continuación, se utiliza la biblioteca estándar de Go crypto para generar claves privadas y públicas RSA y certificados X.509.

En la función makeCA(), el código crea un certificado de autoridad de certificación (CA) que se utiliza para firmar todos los certificados posteriores. En la función makeCert(), el código crea dos certificados para el servidor y el cliente respectivamente y los firma con el certificado de autoridad de certificación generado anteriormente.

En general, este código genera tres certificados que se utilizan para establecer conexiones seguras entre servidores y clientes en una red.

Levantando el servidor HTTPS

Se ejecuta el servidor HTTPS usando los certificados generados por certs.go:

go run server.go

Ejecución de cliente modificado con mTLS

A partir del archivo client.go que lee en el disco los certificados necesarios para establecer la conexión se realiza una copia y se modifica para que el cliente, en este caso llamado, embedded.go tenga almacenado dentro del binario los certificados utilizados para la conexión mTLS, de esta forma no es necesario tener los archivos por separado, lo que añade una capa de seguridad a la hora de tener que hacer una herramienta o agente (backdoor) y que esta no pueda ser interceptada por medio de un proxy o un sniffer.

go build embedded.go
./embedded

Pero analicemos el código embedded.go

package main

import (
"crypto/tls"
"crypto/x509"
_ "embed" // Importación vacía para cargar archivos embebidos
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)

// Archivos de certificados embebidos
var (
//go:embed certs/ca.crt
caCert []byte
//go:embed certs/client.crt
clientCert []byte
//go:embed certs/client.key
clientKey []byte
)

func main() {
// Crear un nuevo pool de certificados y agregar el certificado de CA
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)

// Cargar los certificados de cliente y clave privada
certificate, err := tls.X509KeyPair(clientCert, clientKey)
if err != nil {
log.Fatalf("could not load certificate: %v", err)
}

// Crear un nuevo cliente HTTP con TLS personalizado
client := http.Client{
Timeout: time.Second * 30,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
Certificates: []tls.Certificate{certificate},
},
},
}

// Realizar una solicitud GET al servidor HTTPS
url := "https://localhost:8443/hello"
r, err := client.Get(url)
if err != nil {
log.Fatalf("error making get request: %v", err)
}
defer r.Body.Close()

// Leer y mostrar el cuerpo de la respuesta HTTP
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Fatalf("error reading response: %v", err)
}
fmt.Printf("%s\n", body)
}

Este código es un ejemplo de cómo usar certificados TLS para establecer una conexión HTTPS segura y hacer una solicitud HTTP en Go. A continuación, se detallan las diferentes partes del código:

  1. Se usan las directivas embed para incrustar en el código los archivos que contienen los certificados necesarios para la conexión TLS: ca.crt, client.crt, y client.key.
  2. En la función main, se crea un nuevo objeto CertPool usando el método NewCertPool() de la biblioteca x509, que contendrá el certificado de la autoridad de certificación (CA) que se usará para verificar el certificado del servidor.
  3. Después, se carga el certificado del cliente usando el método X509KeyPair() de la biblioteca tls. Este certificado será utilizado para autenticar al cliente al servidor.
  4. Luego, se crea un objeto http.Client que se usará para realizar la solicitud HTTP. Este objeto tiene un tiempo de espera de 30 segundos y un transporte personalizado que especifica la configuración TLS. En particular, se establece el objeto tls.Config que contiene la raíz del certificado CA y el par de claves X509 que identifica al cliente.
  5. La variable url almacena la URL a la que se realizará la solicitud HTTPS. En este caso, la solicitud se hará al servidor local en el puerto 8443 y con la ruta /hello.
  6. Se realiza la solicitud HTTP mediante el método Get() del objeto http.Client. Si ocurre algún error, se genera un mensaje de error.
  7. Una vez recibida la respuesta, se lee su cuerpo mediante el método ReadAll() de la biblioteca io/ioutil.
  8. Finalmente, se imprime el cuerpo de la respuesta en la salida estándar mediante el método Printf() de la biblioteca fmt.

En resumen, este código muestra cómo usar certificados TLS para establecer una conexión HTTPS segura y hacer una solicitud HTTP en Go. Es importante destacar que para que este código funcione correctamente, se deben tener los certificados y claves de cliente y servidor correspondientes, y asegurarse de que estén configurados correctamente.

Ideas para implementar

  • Una buena práctica es comprimir y cifrar los archivos antes de compilar el cliente embedded.go y descomprimirlos/descifrarlos en ejecución.
  • Intentar, ofuscar y cifrar URLs, usuarios y contraseñas, tokens, keys, secretos en general que son usados por la herramienta.
  • Se recomienda cifrar la comunicación de datos entre el cliente y el servidor con algoritmos modernos (AES, Chacha20, etc) para garantizar la seguridad. A pesar de la implementación de mTLS, los atacantes pueden intentar evadir esta técnica, por lo que se sugiere añadir una capa extra de cifrado para proteger aún más la información.

--

--

Vay3t
Vay3t

No responses yet