内容简介:作者:Hcamael@知道创宇404实验室最近出了一个libSSH认证绕过漏洞,刚开始时候看的感觉这洞可能挺厉害的,然后很快github上面就有PoC了,msf上很快也添加了exp,但是在使用的过程中发现无法getshell,对此,我进行了深入的分析研究。搞了0.7.5和0.7.6两个版本的源码:
作者:Hcamael@知道创宇404实验室
时间:2018年10月19日
最近出了一个libSSH认证绕过漏洞,刚开始时候看的感觉这洞可能挺厉害的,然后很快github上面就有PoC了,msf上很快也添加了exp,但是在使用的过程中发现无法getshell,对此,我进行了深入的分析研究。
前言
搞了0.7.5和0.7.6两个版本的源码:
360发了一篇分析文章,有getshell的图:
Python版本的PoC到Github上搜一下就有了:
环境
libSSH-0.7.5源码下载地址:
PS: 缺啥依赖自己装,没有当初的编译记录了,也懒得再来一遍
$ tar -xf libssh-0.7.5.tar.xz $ cd libssh-0.7.5 $ mkdir build $ cd build $ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug .. $ make
主要用两个,一个是SSH服务端Demo: examples/ssh_server_fork
, 一个是SSH客户端Demo: ./examples/samplessh
服务端启动命令: sudo examples/ssh_server_fork -p 22221 127.0.0.1 -v
客户端使用命令: ./examples/samplessh -p 22221 myuser@127.0.0.1
PS: 用户那随便填,我使用myuser,只是为了对比正常认证请求和bypass请求有啥区别,正常情况下SSH服务端是使用账户密码认证,账户是: myuser, 密码是: mypassword
修改 ../src/auth.c
的 ssh_userauth_xxxx
函数,我修改的是 ssh_userauth_password
:
根据360的分析文章和我自己的研究结果,修改了上图箭头所示的三处地方,这样 ./examples/samplessh
就会成为了验证该漏洞的PoC
PS: 修改完源码后记得再执行一次make
漏洞分析
根据服务端输出的调试信息,可以找到 ssh_packet_process
函数, 看到第1211行:
1121 r=cb->callbacks[type - cb->start](session,type,session->in_buffer,cb->user);
然后追踪到 callbacks
数组等于 default_packet_handlers
正常情况下,发送 SSH2_MSG_USERAUTH_REQUEST
请求,进入的是 ssh_packet_userauth_request
函数,而该漏洞的利用点就是,发送 SSH2_MSG_USERAUTH_SUCCESS
请求,从而进入 ssh_packet_userauth_success
函数
PS: 我们可以进入该数组中的任意函数,但是看了下其他函数,也没法getshell
正常情况下的执行路径是:
ssh_packet_userauth_request -> ssh_message_queue -> ssh_execute_server_callbacks -> ssh_execute_server_request -> rc = session->server_callbacks->auth_password_function(session, msg->auth_request.username, msg->auth_request.password, session->server_callbacks->userdata);
找找这个函数,发现在服务端Demo中进行了设置:
// examples/ssh_server_fork.c ...... 514 struct ssh_server_callbacks_struct server_cb = { 515 .userdata = &sdata, 516 .auth_password_function = auth_password, 517 .channel_open_request_session_function = channel_open, 518 }; 519 ssh_callbacks_init(&server_cb); 520 ssh_callbacks_init(&channel_cb); 521 ssh_set_server_callbacks(session, &server_cb); ......
找到了 auth_password
函数,由服务端的编写者设置的:
// examples/ssh_server_fork.c static int auth_password(ssh_session session, const char *user, const char *pass, void *userdata) { struct session_data_struct *sdata = (struct session_data_struct *) userdata; (void) session; if (strcmp(user, USER) == 0 && strcmp(pass, PASS) == 0) { sdata->authenticated = 1; return SSH_AUTH_SUCCESS; } sdata->auth_attempts++; return SSH_AUTH_DENIED; }
认证成功后的路径:
ssh_message_auth_reply_success -> ssh_auth_reply_success: 994 session->session_state = SSH_SESSION_STATE_AUTHENTICATED; 995 session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;
正常情况下,在SSH登录成功后,libSSH给session设置了认证成功的状态,SSH服务端编写的人给自己定义的标志位设置为1: sdata->authenticated = 1;
利用该漏洞绕过验证,服务端的流程:
ssh_packet_userauth_success: SSH_LOG(SSH_LOG_DEBUG, "Authentication successful"); SSH_LOG(SSH_LOG_TRACE, "Received SSH_USERAUTH_SUCCESS"); session->auth_state=SSH_AUTH_STATE_SUCCESS; session->session_state=SSH_SESSION_STATE_AUTHENTICATED; session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;
可以成功的把libSSH的session设置为认证成功的状态,但是却不会进入 auth_password
函数,所以用户定义的标志位 sdata->authenticated
仍然等于0
我们在网上看到别人PoC验证成功的图,就是由 ssh_packet_userauth_success
函数输出的 Authentication successful
研究不能getshell之谜
很多人复现该漏洞的时候肯定都发现了,服务端调试的信息都输出了认证成功,但是在getshell的时候却一直无法成功,根据上面的代码,发现session已经被设置成认证成功了,但是为啥还无法获取 shell 权限呢?对此,我又继续深入研究。
根据服务端的调试信息,我发现都能成功打开 channel
,但是在下一步 pty-req channel_request
我服务端显示的信息是被拒绝:
所以我继续跟踪代码执行的流程,跟踪到了 ssh_execute_server_request
函数:
166 case SSH_REQUEST_CHANNEL: channel = msg->channel_request.channel; if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY && ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)) { rc = channel->callbacks->channel_pty_request_function(session, channel, msg->channel_request.TERM, msg->channel_request.width, msg->channel_request.height, msg->channel_request.pxwidth, msg->channel_request.pxheight, channel->callbacks->userdata); if (rc == 0) { ssh_message_channel_request_reply_success(msg); } else { ssh_message_reply_default(msg); } return SSH_OK;
接着发现 ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)
检查失败,所以没有进入到该分支,导致请求被拒绝。
然后回溯 channel->callbacks
,回溯到了SSH服务端 ssh_server_fork.c
:
530 ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); 531 ssh_event_add_session(event, session); 532 n = 0; 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);
在libSSH中没有任何设置channel的回调函数的代码,只要在服务端中,由开发者手动设置,比如上面的545行的代码
然后我们又看到了 sdata.authenticated
,该变量再之前说了,该漏洞绕过的认证,只能把session设置为认证状态,却无法修改SSH服务端开发者定义的 sdata.authenticated
变量,所以该循环将不会跳出,直到 n = 100
的情况下,reutrn结束该函数。这就导致了我们无法getshell。
如果想getshell,有两种修改方式:
1.删除 sdata.authenticated
变量
533 while (sdata.channel == NULL) { ...... 544 }
2.把channel添加回调函数的代码移到循环之前
530 ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); 531 ssh_event_add_session(event, session); 532 ssh_set_channel_callbacks(sdata.channel, &channel_cb); 533 n = 0; 534 while (sdata.authenticated == 0 || sdata.channel == NULL) { ......
在修改了服务端代码后,我也能成功getshell:
总结
之后我看了审计了一下 ssh_execute_server_request
函数的其他分支,发现 SSH_REQUEST_CHANNEL
分支下所有的分支:
SSH_CHANNEL_REQUEST_PTY SSH_CHANNEL_REQUEST_SHELL SSH_CHANNEL_REQUEST_X11 SSH_CHANNEL_REQUEST_WINDOW_CHANGE SSH_CHANNEL_REQUEST_EXEC SSH_CHANNEL_REQUEST_ENV SSH_CHANNEL_REQUEST_SUBSYSTEM
都是调用 channel
的回调函数,所以在回调函数未注册的情况下,是无法成功getshell。
最后得出结论,CVE-2018-10933并没有想象中的危害大,而且网上说的几千个使用libssh的ssh目标,根据banner,我觉得都是libssh官方Demo中的ssh服务端,存在漏洞的版本的确可以绕过认证,但是却无法getshell。
引用
- https://0x48.pw/libssh/
- https://www.anquanke.com/post/id/162225
- https://github.com/search?utf8=%E2%9C%93&q=CVE-2018-10933&type=
- https://www.libssh.org/files/0.7/libssh-0.7.5.tar.xz
- https://0x48.pw/libssh/libssh_0.7.6/src/packet.c.html#ssh_packet_process
- https://0x48.pw/libssh/libssh_0.7.5/src/packet.c.html#default_packet_handlers
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Influxdb 认证绕过漏洞预警
- Apache Dubbo漏洞补丁绕过
- Ghostscript 多个-dSAFER 沙箱绕过漏洞
- ghostscript沙箱绕过远程命令执行漏洞预警
- Shiro权限绕过漏洞分析(CVE-2020-2957)
- Shiro身份验证绕过漏洞(CVE-2020-11989)细节
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP典型模块与项目实战大全
杨宇 / 清华大学出版社 / 2012-1 / 79.00元
《PHP典型模块与项目实战大全》以实战开发为原则,以PHP典型模块和项目开发为主线,通过12个高质量的PHP典型模块和6个PHP大型应用,向读者揭示了Web开发的整体结构,并详尽地介绍PHP开发与建站的技术要点。《PHP典型模块与项目实战大全》附带1张DVD,内容是作者为《PHP典型模块与项目实战大全》录制的全程多媒体语音教学视频及《PHP典型模块与项目实战大全》所涉及的源代码。《PHP典型模块与......一起来看看 《PHP典型模块与项目实战大全》 这本书的介绍吧!