CVE-2022-2633 WordPress All-in-One插件SSRF+任意文件下载

环境搭建

存在漏洞的版本:https://downloads.wordpress.org/plugin/all-in-one-video-gallery.2.6.0.zip

安装好wordpress并启用插件

image-20240919173912995

漏洞分析

SSRF

通过漏洞描述知道,漏洞存在~/public/video.php中的download_video函数中

让我们来分析一下这个函数

如果dl不是数字,就会把内容base64解码,存入$file变量,如果文件为空则exit

if ( is_numeric( $_GET['dl'] ) ) {
			$file = get_post_meta( (int) $_GET['dl'], 'mp4', true );
		} else {
			$file = base64_decode( $_GET['dl'] );
		}
if ( empty( $file ) ) {
			die( esc_html__( 'Download file URL is empty.', 'all-in-one-video-gallery' ) );
           	exit;
        }

后面把file里面的空格替换成%20

再之后检查file是url还是一个文件路径,home_url()返回的是WordPress 安装的完整 URL,在file中找这个URL

if ( strpos( $file, home_url() ) !== false ) {
			$is_remote_file = false;
		}	

if ( preg_match( '#http://#', $file ) || preg_match( '#https://#', $file ) ) {
          	$formatted_path = 'url';
        } else {
          	$formatted_path = 'filepath';
        }

然后再判断这个file作为url和文件的可达(读)性

if ( $formatted_path == 'url' ) {
          	$file_headers = @get_headers( $file );
  
          	if ( $file_headers[0] == 'HTTP/1.1 404 Not Found' ) {
           		die( esc_html__( 'File is not readable or not found.', 'all-in-one-video-gallery' ) );
           		exit;
          	}          
        } elseif ( $formatted_path == 'filepath' ) {		
          	if ( ! @is_readable( $file ) ) {
				die( esc_html__( 'File is not readable or not found.', 'all-in-one-video-gallery' ) );
               	exit;
          	}
        }

$is_remote_file默认为true、formatted_path刚刚被赋值为url

判断就会为true从而进入里面执行curl_exec发起请求

测试一下,发起了请求

image-20240920145507274

总结一下,可以看到这个代码编写的时候根本没考虑到SSRF的问题,file变量只要不是home_url,就会对这个file发起curl请求了,另外这个strpos的验证也很不健壮,是很好绕过的

再说读文件,位于这个函数最下面,按理说如果判断为应该发起curl请求以后应该就结束了,但是并没有退出,也没有对读文件的内容和路径做限制,默认以application/octet-stream去读

image-20240920153627959

任意文件下载

同样的payload,url换成文件路径就下载了

image-20240920145809913

这里最后不管怎么样都会执行

$nfile = @fopen( $file, 'rb' );
        while ( ! feof( $nfile ) ) {                 
        	print( @fread( $nfile, $chunk ) );
            @ob_flush();
            @flush();
        }

base64编码里面用file协议,或者不用都无所谓的,进入curl请求的判断是这么写

if ( $is_remote_file && $formatted_path == 'url' )

这个$is_remote_file也可以控制让他不进这个if,但是进了以后也没有die,所以好像也无所谓

修复

这个dl改名为vdl了,另外base64_decode换成wordpress自带的函数

sanitize_text_field,作用是对用户输入进行过滤

Check whether the string is a valid UTF-8 character, and remove all HTML tags

get_transient,用来获取瞬态数据的值,如果瞬态数据不存在、没有值或已过期,则返回值将为false

If the transient does not exist, does not have a value, or has expired, then the return value will be false.

image-20240920152444307

另外,搜不到http就把is_remote_file改成false

image-20240920153016107

会取出file的文件后缀ext,然后进行判断

如果不是指定的媒体文件,mime_type会被赋值为application/octet-stream,从而进入判断exit进程

image-20240920153304031

image-20240920153256128

0%