CVE-2022-2633 WordPress All-in-One插件SSRF+任意文件下载
环境搭建
存在漏洞的版本:https://downloads.wordpress.org/plugin/all-in-one-video-gallery.2.6.0.zip
安装好wordpress并启用插件
漏洞分析
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
发起请求
测试一下,发起了请求
总结一下,可以看到这个代码编写的时候根本没考虑到SSRF的问题,file变量只要不是home_url,就会对这个file发起curl请求了,另外这个strpos的验证也很不健壮,是很好绕过的
再说读文件,位于这个函数最下面,按理说如果判断为应该发起curl请求以后应该就结束了,但是并没有退出,也没有对读文件的内容和路径做限制,默认以application/octet-stream去读
任意文件下载
同样的payload,url换成文件路径就下载了
这里最后不管怎么样都会执行
$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.
另外,搜不到http就把is_remote_file改成false
会取出file的文件后缀ext,然后进行判断
如果不是指定的媒体文件,mime_type会被赋值为application/octet-stream,从而进入判断exit进程