CVE-2018-10933 libssh authentication bypass 漏洞浅析
本月19号和21号看了一下CVE-2018-10933 libssh的authentication bypass这个漏洞,记录一下整个过程。
0x01 漏洞环境
libssh的环境手工安装过程如下。
# keys for samplesshd-cb
ssh-keygen -t rsa
ssh-keygen -t dsa
# dependencies
apt-get install cmake libssl-dev
apt-get install libkrb5-dev # for samplesshd-cb see in examples/CMakeLists.txt
# download
wget https://www.libssh.org/files/0.7/libssh-0.7.4.tar.xz
tar xvf libssh-0.7.4.tar.xz
cd libssh-0.7.4
# compile
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug ..
make && make install
cd examples/
./samplesshd-cb --dsakey /root/.ssh/id_dsa --rsakey /root/.ssh/id_rsa 127.0.0.1 -p 22 -v
环境安装的过程中,有一个坑踩了一下,如果没有samplesshd-cb(libssh 官方给的example中的demo程序)多半是没有安装libkrb5-dev,具体可以参考example中CMakeLists.txt脚本。
0x02 漏洞验证
git clone https://github.com/blacknbunny/libSSH-Authentication-Bypass.git
pip install -r requirement.txt
python libsshauthbypass.py --host 172.17.0.4 -p22
用samplesshd-cb验证时,可以看到Authentication成功了,但是却不能getshell。输出:
0x03 漏洞分析
为什么只能打开channel,不能getshell?exploit repo中提到了这个example有明确的auth handler。
People trying to reproduce libssh bug: the sample code (samplesshd-cb) is not vuln because it has explicit auth handlers. You can open a channel but nothing will happen.
https://github.com/blacknbunny/libSSH-Authentication-Bypass/issues/7
到这里还是不太明白原因,得看代码了。这时候发现网上已经有了分析文章[7]。整理关键部分如下:
漏洞的本质原因是攻击者可以构造SSH2_MSG_USERAUTH_SUCCESS
的请求,从而可以让libssh的session直接被设置为session->auth_state=SSH_AUTH_STATE_SUCCESS;
。
libssh中正常的验证过程如下:
- 发送
SSH2_MSG_USERAUTH_REQUEST
请求,进入的是ssh_packet_userauth_request
函数 - 调用session->server_callbacks->auth_password_function函数指针,指向用户基于libssh自己实现的auth函数
- 用户自己实现的函数(比如examples/ssh_server_fork.c)中auth_password实现验证,验证成功会修改session_data_struct *sdata中的sdata->authenticated = 1;。这样验证成功。
poc中是构造SSH2_MSG_USERAUTH_SUCCESS
的请求,libssh中的执行流如下:
- 直接进入ssh_packet_userauth_success函数,修改了
session->auth_state=SSH_AUTH_STATE_SUCCESS;
- 由于不会进入auth_password中,因此sdata->authenticated没有修改。
修改了session->auth_state=SSH_AUTH_STATE_SUCCESS;
有用吗?有用。session验证过了之后,会和服务端打开channel,但是在ssh_server_fork实现时,还校验了sdata.authenticated ,因此不能getshell。
533 while (sdata.authenticated == 0 || sdata.channel == NULL) {
534 /* If the user has used up all attempts, or if he hasn't been able to
535 * authenticate in 10 seconds (n * 100ms), disconnect. */
536 if (sdata.auth_attempts >= 3 || n >= 100) {
537 return;
538 }
539 if (ssh_event_dopoll(event, 100) == SSH_ERROR) {
540 fprintf(stderr, "%s\n", ssh_get_error(session));
541 return;
542 }
543 n++;
544 }
545 ssh_set_channel_callbacks(sdata.channel, &channel_cb);//getshell
vulhub[8]上有了环境,测试了可以rce。进到docker中看一下是用的ssh_server_fork这个程序。
root@476516fbb426:/usr/src/build/examples# lsof -i:22
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh_serve 9 root 3u IPv4 84938 0t0 TCP *:ssh (LISTEN)
root@476516fbb426:/usr/src/build/examples# ps -aux |grep 9
root 9 0.0 0.1 20548 2796 ? SL 05:20 0:00 /usr/src/build/examples/ssh_server_fork --hostkey=/etc/ssh/ssh_host_rsa_key --ecdsakey=/etc/ssh/ssh_host_ecdsa_key --dsakey=/etc/ssh/ssh_host_dsa_key --rsakey=/etc/ssh/ssh_host_rsa_key -p 22 0.0.0.0
root 19 0.0 0.1 18188 3240 pts/0 Ss 07:25 0:00 /bin/bash
root 94 0.0 0.1 36636 2892 pts/0 R+ 08:20 0:00 ps -aux
root 95 0.0 0.0 11112 976 pts/0 S+ 08:20 0:00 grep 9
根目录下告诉了我们漏洞环境的patch,是ssh_server_fork这个程序,也是通过删除sdata.authenticated == 0 来做的。
root@476516fbb426:/# cat ssh_server_fork.patch
--- ssh_server_fork.c 2018-08-10 17:06:03.000000000 +0800
+++ ssh_server_fork_patch.c 2018-10-19 13:44:07.000000000 +0800
@@ -531,7 +531,7 @@
ssh_event_add_session(event, session);
n = 0;
- while (sdata.authenticated == 0 || sdata.channel == NULL) {
+ while (sdata.channel == NULL) {
/* If the user has used up all attempts, or if he hasn't been able to
* authenticate in 10 seconds (n * 100ms), disconnect. */
if (sdata.auth_attempts >= 3 || n >= 100) {
0x04 总结
这个漏洞的本质上是由于libssh实现的问题导致了authentication bypass,可以通过发送数据包来修改本次session->auth_state
变量。
如果要getshell还是要看具体基于libssh的程序实现,比如基于libssh官方demo中ssh_server_fork还校验了sdata.authenticated变量,在没有注册handler的情况是无法getshell的,因此漏洞危害没有那么大。其实github也用到了libssh,但是官方Github Security twitter上也说了,他们有自己的实现,事实上也是不受影响的。
We use a custom version of libssh; SSH2_MSG_USERAUTH_SUCCESS with libssh server is not relied upon for pubkey-based auth, which is what we use the library for. Patches have been applied out of an abundance of caution, but GHE was never vulnerable to CVE-2018-10933.
REF
- https://www.seebug.org/vuldb/ssvid-97614
- libssh repo https://github.com/simonsj/libssh/commits/master
- poc0 https://gist.github.com/mlosapio/2062ebf943485a7289d226e0d00498e7
- poc1 https://github.com/SoledaD208/CVE-2018-10933
- poc2 https://github.com/blacknbunny/libSSH-Authentication-Bypass/
- 360 cert analysis https://cert.360.cn/report/detail?id=a407dddd655dba34405688b1498c3aa1
- hcamael analysis https://0x48.pw/2018/10/19/0x49/#jump3
- Vulhub https://github.com/vulhub/vulhub/tree/master/libssh/CVE-2018-10933
- zoomeye https://zoomeye.org/searchResult?q="SSH-2.0-libssh"
- mygist https://gist.github.com/thinkycx/6ec27dc470de03fb16c2f447dbbbd070