前言
这个RCE漏洞利用链的实现是由几个逻辑洞的结合而导致的,这几天我花了一些时间复现了一遍,在此记录一下。
固件解压
我下载的是RV345 v1.0.03.24,从官网下载到压缩包解压之后可以看到它的rootfs是ubi格式的img。之前我都是使用kali里的 binwalk 对其进行解压可以直接得到解压之后的文件系统。但是由于前几天我的虚拟机坏了,不得不进行重装,但是我还没有装 kali。故找了一下提取ubi格式的方式。在github上有一个项目:https://github.com/nlitsme/ubidump ,通过里面的ubidump.py可以很轻松的提取出ubi格式的文件。命令如下:
1
| python3 ubidump.py -s . 0.ubi
|
漏洞分析
CVE-2022-20705 Improper Session Management Vulnerability
CVE-2022-20705 Improper Session Management Vulnerability,是由于nginx的配置不当导致的。nginx的配置文件是**/etc/nginx/nginx.conf**,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| user www-data; worker_processes 4;
error_log /dev/null;
events { worker_connections 1024; }
http { access_log off;
upstream jsonrpc { server 127.0.0.1:9000; }
upstream rest { server 127.0.0.1:8008; }
include /var/nginx/conf.d/proxy.websocket.conf; include /var/nginx/sites-enabled/*; }
|
可以发现它又加载了**/var/nginx/conf.d/proxy.websocket.conf和/var/nginx/sites-enabled/,但是固件解压出来的rootfs里的var目录有些问题,所以笔者只能根据别人的文章找一下漏洞发生的配置文件。结合rest.url.conf和proxy.conf**来看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| location /api/ { proxy_pass http://rest; include /var/nginx/conf.d/proxy.conf; }
location /api/operations/ciscosb-file:file-copy { proxy_pass http://rest; include /var/nginx/conf.d/proxy.conf; proxy_read_timeout 3600; proxy_send_timeout 3600; }
location /api/operations/ciscosb-file:form-file-upload { set $deny 1; if ($http_authorization != "") { set $deny "0"; }
if ($deny = "1") { return 403; }
upload_pass /form-file-upload; upload_store /tmp/upload; upload_store_access user:rw group:rw all:rw; upload_set_form_field $upload_field_name.name "$upload_file_name"; upload_set_form_field $upload_field_name.content_type "$upload_content_type"; upload_set_form_field $upload_field_name.path "$upload_tmp_path"; upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5"; upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size"; upload_pass_form_field "^.*$"; upload_cleanup 400 404 499 500-505; upload_resumable on; }
location /restconf/ { proxy_pass http://rest; include /var/nginx/conf.d/proxy.conf; }
location /restconf/operations/ciscosb-file:file-copy { proxy_pass http://rest; include /var/nginx/conf.d/proxy.conf; proxy_read_timeout 3600; proxy_send_timeout 3600; }
|
1 2 3 4 5 6 7 8 9 10
| proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Authorization $http_authorization; proxy_set_header Accept-Encoding ""; proxy_set_header Connection ""; proxy_ssl_session_reuse off; server_name_in_redirect off;
|
如果我们请求中Authorization不为空,此时set $deny “0”,就可以向下调用upload模块。它会在调用**/form-file-upload前,把文件上传到/tmp/upload下。并且由于没有设置level,它的存储格式类似/tmp/upload/0000000001。至此我们可以实现任意文件上传至/tmp/upload**。
我们接着向下分析,可以在rootfs/etc/nginx/conf.d下找到web.upload.conf如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| location /form-file-upload { include uwsgi_params; proxy_buffering off; uwsgi_modifier1 9; uwsgi_pass 127.0.0.1:9003; uwsgi_read_timeout 3600; uwsgi_send_timeout 3600; }
location /upload { set $deny 1;
if (-f /tmp/websession/token/$cookie_sessionid) { set $deny "0"; }
if ($deny = "1") { return 403; }
upload_pass /form-file-upload; upload_store /tmp/upload; upload_store_access user:rw group:rw all:rw; upload_set_form_field $upload_field_name.name "$upload_file_name"; upload_set_form_field $upload_field_name.content_type "$upload_content_type"; upload_set_form_field $upload_field_name.path "$upload_tmp_path"; upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5"; upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size"; upload_pass_form_field "^.*$"; upload_cleanup 400 404 499 500-505; upload_resumable on; }
|
我们可以发现其对**/upload进行了$cookie_sessionid的检验,但是并没有对/form-file-upload进行检验。我们看一下/form-file-upload**的后端处理程序。启动脚本(uwsgi-launcher)如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #!/bin/sh /etc/rc.common
export UWSGI_PLUGIN_DIR=/usr/lib/uwsgi/plugins
start() { uwsgi -m --ini /etc/uwsgi/jsonrpc.ini & uwsgi -m --ini /etc/uwsgi/blockpage.ini & uwsgi -m --ini /etc/uwsgi/upload.ini & }
stop() { killall -9 uwsgi }
|
我们再去找一下**/etc/uwsgi/upload.ini**
1 2 3 4 5 6 7 8 9 10 11 12 13
| [uwsgi] plugins = cgi workers = 1 master = 1 uid = www-data gid = www-data socket=127.0.0.1:9003 buffer-size=4096 cgi = /www/cgi-bin/upload.cgi cgi-allowed-ext = .cgi cgi-allowed-ext = .pl cgi-timeout = 300 ignore-sigpipe = true
|
从上述文件中我们可以知道**/form-file-upload它对应的后端处理程序是/www/cgi-bin/upload.cgi。因此我们可以无条件访问upload.cgi**。
同时上述配置文件中我们可以看到检查了**/tmp/websession/token/$cookie_sessionid文件是否存在。但是存在缺陷,就是这里的$cookie_sessionid是用户在http请求中传进去的一个值,它并没有检查是否存在../../,也就是说我们可以通过跨目录来导致授权绕过。如:我们可以传递../../../etc/firmware_version**。
同时也可以看到在upload.cgi里对sessionid=进行了检查,限制了它的字符,但是并没有考虑到传多个sessionid=的情况。因为这里的sessionid=是遍历HTTP_COOKIE并且取出它最后一个sessionid=作为实际的sessionid=使用,所以我们可以传两个sessionid=。前一个用来绕过web.upload.conf里的判断,后一个当作正常的数据用来通过upload.cgi的判断。这样也可以实现无条件访问upload.cgi。
我们接着看upload.cgi
传入适当的参数可以使得我们有能力任意文件移动到**/tmp/www下,通过这两个漏洞我们也可以伪造出一个session**。
CVE-2022-20707 Command Injection
我们继续查看upload.cgi。
这个漏洞可以使得任意命令执行。
参考链接
https://bestwing.me/Pwning%20a%20Cisco%20RV340%20%20%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%EF%BC%88CVE-2022-20705%20%E5%92%8C%20CVE-2022-20707.html
https://blog.relyze.com/2022/04/pwning-cisco-rv340-with-4-bug-chain.html
https://paper.seebug.org/1890/
https://onekey.com/blog/advisory-cisco-rv34x-authentication-bypass-remote-command-execution/
https://nosec.org/home/detail/4985.html
文章首发
https://mp.weixin.qq.com/s/2joZwexIdVdgc5NL8W3J-A