Pwn2Own Austin 2021 Cisco RV34x RCE 漏洞鏈復現

2023-03-07 15:12:20

前言

這個RCE漏洞利用鏈的實現是由幾個邏輯洞的結合而導致的,這幾天我花了一些時間復現了一遍,在此記錄一下。

韌體解壓

我下載的是RV345 v1.0.03.24,從官網下載到壓縮包解壓之後可以看到它的rootfsubi格式的img。之前我都是使用kali裡的 binwalk 對其進行解壓可以直接得到解壓之後的檔案系統。但是由於前幾天我的虛擬機器器壞了,不得不進行重灌,但是我還沒有裝 kali。故找了一下提取ubi格式的方式。在github上有一個專案:https://github.com/nlitsme/ubidump,通過裡面的ubidump.py可以很輕鬆的提取出ubi格式的檔案。命令如下:

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,如下

user www-data;
worker_processes  4;

error_log /dev/null;

events {
    worker_connections  1024;
}

http {
	access_log off;
	#error_log /var/log/nginx/error.log  error;

	upstream jsonrpc {
		server 127.0.0.1:9000;
	}

	upstream rest {
		server 127.0.0.1:8008;
	}

	# For websocket proxy server
	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.confproxy.conf來看。

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;
}
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如下:

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)如下:

#!/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

[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 a Cisco RV340 漏洞分析(CVE-2022-20705 和 CVE-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