|
| 1 | +# Public key authentication bypass in libssh (CVE-2023-2283) |
| 2 | + |
| 3 | +[CVE-2023-2283](https://securitylab.github.com/advisories/GHSL-2023-085_libssh/) |
| 4 | +is an authentication bypass vulnerability in |
| 5 | +[libssh](https://www.libssh.org/), which, under certain conditions, may |
| 6 | +enable a remote attacker to gain unauthorized access to another user’s |
| 7 | +account via ssh login. |
| 8 | + |
| 9 | +This demo uses docker to simulate two computers, named "libssh-server" |
| 10 | +and "libssh-attacker". On libssh-server, we run `ssh_server_pthread`, |
| 11 | +which is a simple ssh server application that is [included as an |
| 12 | +example](https://gitlab.com/libssh/libssh-mirror/-/blob/e8322817a9e5aaef0698d779ddd467a209a85d85/examples/ssh_server.c) |
| 13 | +with the libssh source code. The server is configured to allow public |
| 14 | +key authentication with an ED25519 key, but the attacker does not know the |
| 15 | +private key. The attacker instead authenticates by triggering the vulnerability. |
| 16 | + |
| 17 | +The vulnerability is triggered when `ssh_server_pthread` hits an |
| 18 | +out-of-memory condition at precisely the right moment. If libssh is |
| 19 | +running on a 64-bit server with plenty of RAM then it is very unlikely |
| 20 | +that an attacker will be able to generate enough memory pressure to |
| 21 | +cause an out-of-memory error, which means that the vulnerability is |
| 22 | +unlikely to be exploitable. The goal of this demo is, instead, to show |
| 23 | +that the vulnerability is exploitable if libssh is running in a |
| 24 | +memory-constrained environment such as a [memory-constrained |
| 25 | +container](https://docs.docker.com/config/containers/resource_constraints/), |
| 26 | +which we believe is a realistic scenario for a real-life libssh deployment. |
| 27 | +The demo uses `ulimit` to set a 256MB memory limit on the ssh server. |
| 28 | + |
| 29 | +## Network setup |
| 30 | + |
| 31 | +Create a docker network bridge, to simulate a network with two separate computers. |
| 32 | + |
| 33 | +``` |
| 34 | +docker network create -d bridge --subnet 172.18.0.0/16 libssh-demo-network |
| 35 | +``` |
| 36 | + |
| 37 | +## Server setup |
| 38 | + |
| 39 | +Build the docker image: |
| 40 | + |
| 41 | +``` |
| 42 | +docker build server -t libssh-server --build-arg UID=`id -u` |
| 43 | +``` |
| 44 | + |
| 45 | +Start the container: |
| 46 | + |
| 47 | +``` |
| 48 | +docker run --rm --network libssh-demo-network --ip=172.18.0.10 -it libssh-server |
| 49 | +``` |
| 50 | + |
| 51 | +If you want to be able to debug the libssh server, then you need to start the container with some extra command line arguments: |
| 52 | + |
| 53 | +``` |
| 54 | +docker run --rm --network libssh-demo-network --ip=172.18.0.10 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it libssh-server |
| 55 | +``` |
| 56 | + |
| 57 | +Inside the container, run these commands to create ssh keys for the server: |
| 58 | + |
| 59 | +``` |
| 60 | +mkdir ~/testkeys |
| 61 | +ssh-keygen -P "" -t ecdsa -f ~/testkeys/id_ecdsa |
| 62 | +ssh-keygen -P "" -t rsa -f ~/testkeys/id_rsa |
| 63 | +``` |
| 64 | + |
| 65 | +Start the server: |
| 66 | + |
| 67 | +``` |
| 68 | +ulimit -v 262144 # 256MB |
| 69 | +~/libssh/build/examples/ssh_server_pthread -p 2022 -r ~/testkeys/id_rsa -e ~/testkeys/id_ecdsa -a ~/.ssh/authorized_keys 0.0.0.0 |
| 70 | +``` |
| 71 | + |
| 72 | +Note: ssh servers normally listen on port 22, but root privileges are required to listen on 22, so this demo uses port 2022 instead. Use `sudo` if you want to change the port number to 22. The `sudo` password in this docker container is "x". |
| 73 | + |
| 74 | +## Attacker setup |
| 75 | + |
| 76 | +Build the docker image: |
| 77 | + |
| 78 | +``` |
| 79 | +docker build attacker -t libssh-attacker --build-arg UID=`id -u` |
| 80 | +``` |
| 81 | + |
| 82 | +Start the container: |
| 83 | + |
| 84 | +``` |
| 85 | +docker run --rm --network libssh-demo-network --ip=172.18.0.11 -it libssh-attacker |
| 86 | +``` |
| 87 | + |
| 88 | +If you want to be able to debug the client, then you need to start the container with some extra command line arguments: |
| 89 | + |
| 90 | +``` |
| 91 | +docker run --rm --network libssh-demo-network --ip=172.18.0.11 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it libssh-attacker |
| 92 | +``` |
| 93 | + |
| 94 | +The attacker uses a modified version of libssh. The modifications are in the file named `diff.txt` and are applied during the `docker build` step. |
| 95 | + |
| 96 | +Run the malicious client like this: |
| 97 | + |
| 98 | +``` |
| 99 | +~/libssh/build/examples/ssh-client -p 2022 victim@172.18.0.10 ~/id_ed25519.pub |
| 100 | +``` |
| 101 | + |
| 102 | +The vulnerability is triggered when the ssh server has an out-of-memory error at the exact right moment, which means that the PoC is unreliable. It runs in a loop until it's successful, which can often take several minutes. You may also need to run several instance of the PoC simultaneously to generate enough memory pressure on the server. I suggest using `tmux` to open three terminals and start 3 instances of the PoC. When one of the PoCs succeeds, it creates a file named "success.txt", which notifies the other instances that they should stop. |
| 103 | + |
| 104 | +Note: the PoC sometimes accidentally triggers a SIGSEGV in the server due to an unrelated [null-pointer dereference bug](https://gitlab.com/libssh/libssh-mirror/-/merge_requests/381). If this happens, you will need to restart the `ssh_server_pthread` process. |
0 commit comments