目录

  1. 1. 前言
  2. 2. 实战场景赛
    1. 2.1. 企业安全 (Unsolved)
    2. 2.2. 威胁检测
      1. 2.2.1. flag1
  3. 3. CTF
    1. 3.1. Web
      1. 3.1.1. bookstore
      2. 3.1.2. Deprecated (Unsolved)
      3. 3.1.3. web3 (Unsolved)
      4. 3.1.4. chained_identity (Unsolved)
      5. 3.1.5. ezwebsite (Unsolved)

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

CCB 2nd 决赛

2025/5/6 线下赛
  |     |   总文章阅读量:

前言

80多名被打爆了。。

倒也正常,我们这个队伍配置就没法赢,二进制打烂的题我们零解,我们有躺赢狗(


实战场景赛

企业安全 (Unsolved)

本次靶场模拟了一起供应链攻击事件,目标是测试企业在供应链安全防护中的关键环节。
在该场景中,Stellarion 作为一家专注于可重复使用航天器、先进电推进技术和深空通信系统的高科技太空探索企业,依赖 SafeGuard 提供的服务器安全监控产品。SafeGuard 负责入侵检测、防火墙管理、日志分析、威胁响应等安全防护措施,并对 Stellarion 的核心 IT 设施进行持续监测与风险评估。
攻击者为窃取 Stellarion 的内部敏感文件,利用 SafeGuard 供应链漏洞 进行 投毒攻击,最终渗透 Stellarion 内部系统,导致机密数据泄露。参赛选手需复盘此次攻击,分析攻击路径、关键突破点,并协助双方制定有效的防御策略,提升供应链安全防护能力。
提示:在该场景中,两个企业的部分机器之间存在一条 192.168.60.0/24 网段的专线。

.13

/downloads/ 泄露 jar 包

UserRealm

package com.solidrock.security.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class UserRealm extends AuthorizingRealm {
    public UserRealm() {
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = (String)authenticationToken.getPrincipal();
        String password = new String((char[])((char[])((char[])authenticationToken.getCredentials())));
        if (username.equals("admin@solidrock.com") && password.equals("123456")) {
            return new SimpleAuthenticationInfo(username, password, this.getName());
        } else {
            throw new IncorrectCredentialsException("Username or password is incorrect.");
        }
    }
}

但是 80 端口是个静态页面只有一个 jar 包下载,扫不出其他端口了


.14

[*] alive ports len is: 218
start vulscan
[*] WebTitle http://172.18.167.14:8080 code:302 len:0      title:None 跳转url: http://172.18.167.14:8080/login;jsessionid=2FE97C536C7B8C646A0A291D28C8A030
[*] WebTitle http://172.18.167.14:8080/login;jsessionid=2FE97C536C7B8C646A0A291D28C8A030 code:200 len:15013  title:Solid Rock | Enterprise OA System - Login
Target: http://172.18.167.14:8080/

[11:14:35] Scanning: 
[11:14:36] 400 -   435B - /.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd            
[11:14:39] 302 -     0B - /;json/  ->  http://172.18.167.14:8080/login;jsessionid=57D2116D70C4A47139EB0ABDA2DF774B
[11:14:39] 302 -     0B - /;login/  ->  http://172.18.167.14:8080/login;jsessionid=D39C86CAE3F101F819994B1DE62F6FD0
[11:14:39] 302 -     0B - /;admin/  ->  http://172.18.167.14:8080/login;jsessionid=72ED8E7799391F2FA9727700E415EBC4
[11:14:39] 200 -   15KB - /;/login                                         
[11:14:40] 400 -   435B - /\..\..\..\..\..\..\..\..\..\etc\passwd          
[11:14:40] 400 -   435B - /a%5c.aspx                                       
[11:14:44] 401 -    49B - /api/2/explore/                                  
i[11:14:44] 401 -    49B - /api
[11:14:44] 401 -    49B - /api/                                            
[11:14:44] 401 -    49B - /api/__swagger__/                                
[11:14:44] 401 -    49B - /api/api
[11:14:44] 401 -    49B - /api/2/issue/createmeta
[11:14:44] 401 -    49B - /api/_swagger_/
[11:14:44] 401 -    49B - /api/api-docs                                    
[11:14:44] 401 -    49B - /api/apidocs
[11:14:44] 401 -    49B - /api/apidocs/swagger.json
[11:14:44] 401 -    49B - /api/application.wadl
[11:14:44] 401 -    49B - /api/cask/graphql
[11:14:44] 401 -    49B - /api/batch
[11:14:44] 401 -    49B - /api/config.json
[11:14:44] 401 -    49B - /api/chat
[11:14:44] 401 -    49B - /api/config
[11:14:44] 401 -    49B - /api/create
[11:14:44] 401 -    49B - /api/copy
[11:14:44] 401 -    49B - /api/database.json
[11:14:44] 401 -    49B - /api/credentials.json
[11:14:44] 401 -    49B - /api/delete
[11:14:44] 401 -    49B - /api/docs
[11:14:44] 401 -    49B - /api/credential.json
[11:14:44] 401 -    49B - /api/docs/
[11:14:44] 401 -    49B - /api/embeddings
[11:14:44] 401 -    49B - /api/embed
[11:14:44] 401 -    49B - /api/generate
[11:14:44] 401 -    49B - /api/error_log
[11:14:44] 401 -    49B - /api/index.html
[11:14:44] 401 -    49B - /api/jsonws
[11:14:44] 401 -    49B - /api/jsonws/invoke
[11:14:44] 401 -    49B - /api/login.json
[11:14:44] 401 -    49B - /api/package_search/v4/documentation
[11:14:44] 401 -    49B - /api/proxy
[11:14:44] 401 -    49B - /api/profile
[11:14:44] 401 -    49B - /api/ps
[11:14:44] 401 -    49B - /api/pull
[11:14:44] 401 -    49B - /api/push
[11:14:44] 401 -    49B - /api/show
[11:14:44] 401 -    49B - /api/snapshots
[11:14:44] 401 -    49B - /api/spec/swagger.json
[11:14:44] 401 -    49B - /api/swagger-ui.html
[11:14:44] 401 -    49B - /api/swagger
[11:14:44] 401 -    49B - /api/swagger.json
[11:14:44] 401 -    49B - /api/swagger.yaml
[11:14:44] 401 -    49B - /api/swagger/index.html
[11:14:44] 401 -    49B - /api/swagger/static/index.html
[11:14:44] 401 -    49B - /api/swagger/swagger
[11:14:44] 401 -    49B - /api/swagger/ui/index
[11:14:44] 401 -    49B - /api/tags
[11:14:44] 401 -    49B - /api/timelion/run
[11:14:44] 401 -    49B - /api/users.json
[11:14:44] 401 -    49B - /api/user.json
[11:14:44] 401 -    49B - /api/v1
[11:14:44] 401 -    49B - /api/swagger.yml
[11:14:44] 401 -    49B - /api/v1/
[11:14:44] 401 -    49B - /api/v1/swagger.yaml
[11:14:44] 401 -    49B - /api/v2
[11:14:44] 401 -    49B - /api/v1/swagger.json
[11:14:44] 401 -    49B - /api/v2/
[11:14:44] 401 -    49B - /api/v2/helpdesk/discover
[11:14:44] 401 -    49B - /api/v2/swagger.yaml
[11:14:44] 401 -    49B - /api/v2/swagger.json
[11:14:44] 401 -    49B - /api/v3
[11:14:44] 401 -    49B - /api/v4
[11:14:44] 401 -    49B - /api/vendor/phpunit/phpunit/phpunit
[11:14:44] 401 -    49B - /api/whoami
[11:14:44] 401 -    49B - /api/version
[11:14:46] 500 -   118B - /downloads/dom.php                               
[11:14:47] 500 -    73B - /error                                           
[11:14:47] 500 -    73B - /error/                                          
[11:14:47] 302 -     0B - /home/  ->  http://172.18.167.14:8080/login      
[11:14:47] 302 -     0B - /home  ->  http://172.18.167.14:8080/login       
[11:14:48] 302 -     0B - /index  ->  http://172.18.167.14:8080/login      
[11:14:49] 200 -   15KB - /login                                           
[11:14:49] 200 -   15KB - /login/                                          
[11:14:53] 302 -     0B - /swagger-ui  ->  http://172.18.167.14:8080/swagger-ui/index.html
[11:14:53] 200 -    92B - /swagger-resources                               
[11:14:55] 200 -   55KB - /v2/api-docs                                      
[11:14:55] 200 -   64KB - /v3/api-docs

有一个swagger

选择 /api/user 获取全部账密

{
  "code": 200,
  "message": "All users retrieved",
  "data": [
    {
      "email": "david.miller.07@solidrock.com",
      "password": "@0QYiCo0&if7",
      "role": "ADMIN"
    },
    {
      "email": "alex.turner.01@solidrock.com",
      "password": "Sol1dr0ck_Alex",
      "role": "ADMIN"
    },
    {
      "email": "john.smith.03@solidrock.com",
      "password": "zMu3k!o%Hl!H",
      "role": "USER"
    },
    {
      "email": "sarah.connor.04@solidrock.com",
      "password": "z40e+Zu!RalC",
      "role": "USER"
    },
    {
      "email": "emma.watson.02@solidrock.com",
      "password": "O0rvSDbU3sO",
      "role": "MANAGER"
    },
    {
      "email": "michael.jackson.05@solidrock.com",
      "password": "9lZTsn?dYs=n",
      "role": "MANAGER"
    },
    {
      "email": "taylor.swift.10@solidrock.com",
      "password": "7$phW^03m&DZ",
      "role": "USER"
    },
    {
      "email": "lisa.ray.06@solidrock.com",
      "password": "sWY*1%cGOIfP",
      "role": "USER"
    },
    {
      "email": "robert.pattinson.09@solidrock.com",
      "password": "A=@q0XUlyo7P",
      "role": "MANAGER"
    },
    {
      "email": "jennifer.lopez.08@solidrock.com",
      "password": "T8fnq%Ig8VkW",
      "role": "USER"
    }
  ]
}

登录后只能下载一个 SolidRockSecurityCheckAgent.exe,应该是投毒的供应链软件

但是逆不出东西,只有一个 cmd pause 的操作


威胁检测

flag1

用tcpdump蹲着。

101.212.78.52:36543

flag{3ba612c1fc28bea3465bd0b2695ba265}


CTF

Web

bookstore

登录后就可以管理图书了。

/backup 获取源码

from flask import Flask, request, \
    session, g, redirect, url_for, \
    abort, render_template, flash, \
    render_template_string
import os
from settings import *
import pickle
import re
import traceback

app = Flask(__name__)
app.config.update(dict(
    DATABASE='',
    DEBUG=True,
    SECRET_KEY=os.environ.get('SECRET_KEY'),
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)

class ErrorHandler():
    def __init__(self):
        self.notfound = "Oops! That page doesn't exist."
        self.badreqyest = "Your Rquest We Could Not Understand"

@app.errorhandler(404)
def page_not_found(error):

    template = '''
    <div class="center-content error">
        <h1>{error.notfound} !!!</h1>
        <h2>''' + request.url + '''</h2>
    </div>
    '''
    error = ErrorHandler()
    return template.format(error = error), 404


def get_books(book_name=None):
    if book_name:
        try:
            with open('./books/' + book_name, 'rb') as f:
                book = pickle.load(f)
            return book
        except:
            return None
    else:
        books = []
        dirs = os.listdir("./books/")
        for book_name in dirs:
            try:
                with open('./books/' + book_name, 'rb') as f:
                    book = pickle.load(f)
            except:
                continue
            books.append(book)
        return books


def save_book(book_name, book_bio, book_img, book_price, book_num):
    book = pickle.dumps((book_name, book_img, book_bio, book_price, book_num))
    with open('./books/' + book_name, 'wb') as f:
        f.write(book)
        

@app.route("/bookAdd",methods=['POST','GET'])
def upload():
    if session.get('logged_in', None) and session.get('name', None) == 'admin':
        if request.method == 'POST':
            try:
                book_name = request.form.get('book_name')
                book_bio = request.form.get('book_bio')
                book_price = int(request.form.get('book_price'))
                book_num = int(request.form.get('book_num'))

                f = request.files['myfile']
                book_img = f.filename
                save_book(book_name, book_bio, book_img, book_price, book_num)
                f.save("./static/img/"+f.filename)
            except Exception as e:
                traceback.print_exc()
                return "Something Wrong!!!"
            return "Book Add Success"
        else:
            return render_template('tmpl/bookAdd.html')
    return 'You are not login'

# 主页
@app.route('/')
@app.route('/index')
def index():
    if session.get('logged_in', None):
        name = session.get('name')
        if name == 'admin':
            return render_template('index.html')
    else:
        session['logged_in'] = 0
        session['name'] = 'Anonymous'
        msg = 'Please Login First, {} '

    return render_template('index.html', msg=msg.format(session.get('name')))

@app.route('/login',methods=['POST','GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        username = request.form.get('username')
        password = request.form.get('password')
        if username == ADMIN_USER and password == ADMIN_PASSWORD:
            session['logged_in'] = 1
            session['name'] = 'admin'
        msg = 'Please Login First, {} '
        return render_template('index.html', msg=msg.format(session.get('name')))    

@app.route('/logout')
def logout():
    session['logged_in'] = 0
    session['name'] = 'Anonymous'
    msg = 'Please Login First, {} '
    return render_template('index.html', msg=msg.format(session.get('name')))  

@app.route('/bookDetail/<string:book_name>')
def book_detail(book_name):
    book = get_books(book_name)
    return render_template('tmpl/bookDetail.html', book=book)

@app.route('/backup')
def hint():
    return open(__file__).read()

@app.route('/bookList')
def book_list():
    books = get_books()
    return render_template('tmpl/bookList.html', books=books)


if __name__ == '__main__':
    app.run(host='0.0.0.0',debug=False,port=8080)

目标是打pickle

def get_books(book_name=None):
    if book_name:
        try:
            with open('./books/' + book_name, 'rb') as f:
                book = pickle.load(f)
            return book
        except:
            return None

首先要获取 admin 账密,观察 404 的处理

class ErrorHandler():
    def __init__(self):
        self.notfound = "Oops! That page doesn't exist."
        self.badreqyest = "Your Rquest We Could Not Understand"

@app.errorhandler(404)
def page_not_found(error):

    template = '''
    <div class="center-content error">
        <h1>{error.notfound} !!!</h1>
        <h2>''' + request.url + '''</h2>
    </div>
    '''
    error = ErrorHandler()
    return template.format(error = error), 404

此处可以注入模板字符串

{error.__init__.__globals__}

得到账密

admin:nishibukenengcaichulaide12345678

然后目录穿越打pickle

访问 bookDetail/444 触发回显外带


Deprecated (Unsolved)

不要见到登录就爆破,sql注入你也可以考虑,但是你需要知道后端用的是什么数据库,另外JWT can be flexible in algorithm。

const db = require('better-sqlite3')('./database.db',{readonly : false});

module.exports = {
    getUser(username){
        let result=db.prepare('SELECT * FROM users WHERE username = ?').get(username);
        return result;
    },

    checkUser(username){
        let result=db.prepare('SELECT * FROM users WHERE username = ?').get(username);
        return (result === undefined);
    },

    createUser(username, password){
        let query = 'INSERT INTO users(username, password) VALUES(?,?)';
        db.prepare(query).run(username,password);
    },

    attemptLogin(username, password){
        let result=db.prepare(`SELECT * FROM users WHERE username = ? AND password = ?`).get(username,password);
        return (result !== undefined);
    },
    sendFeedback(message){
        db.prepare(`INSERT INTO messages VALUES('${message}')`).run();
    }
}

一眼注入点在 insert

注意waf:

const badwordCheck= (data) => {
    data=data.toLowerCase();
    let badwords = /system|blob|exec|date|rand|char|regexp|unicode|load/g;
    return !!data.match(badwords);
};

let message=req.body.message.replace(/'/g,"\\'").replace(/"/g,"\\\"");
if(badwordCheck(message)){
    return res.send('Forbidden word in message.');
}
db.sendFeedback(message);

构造 payload:

1\')-- 

但是不能堆叠注入,利用有限

JWTutil

const jwt = require('jsonwebtoken');
const fs = require('fs');

const publicKey  = fs.readFileSync('./publickey.pem', 'utf8');
const privateKey = fs.readFileSync('./privatekey.pem', 'utf8');

module.exports = {
    async sign(data) {
        data = Object.assign(data);
        return (await jwt.sign(data, privateKey, { algorithm:'RS256'}))
    },
    async decode(token) {
        return (await jwt.verify(token, publicKey, { algorithms: ['RS256','HS256'] }));
    }
}

一眼 RS256 和 HS256 的转换,如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名

但是没实践过,不清楚具体的攻击流程,hacktricks 也没讲明白


web3 (Unsolved)

最后一小时上了道题?

这个Web网站没有看出啥问题,但好像DNS域名中隐藏了啥信息,我来挖一挖。

/cgi-bin/readdb.py

 409-56-7008 	Bennet 	Abraham 	415 658-9932 	6223 Bateman St. 	Berkeley 	CA 	94705 	1
213-46-8915 	Green 	Marjorie 	415 986-7020 	309 63rd St. #411 	Oakland 	CA 	94618 	1
238-95-7766 	Carson 	Cheryl 	415 548-7723 	589 Darwin Ln. 	Berkeley 	CA 	94705 	1
998-72-3567 	Ringer 	Albert 	801 826-0752 	67 Seventh Av. 	Salt Lake City 	UT 	84152 	1
899-46-2035 	Ringer 	Anne 	801 826-0752 	67 Seventh Av. 	Salt Lake City 	UT 	84152 	1
722-51-5454 	DeFrance 	Michel 	219 547-9982 	3 Balding Pl. 	Gary 	IN 	46403 	1
807-91-6654 	Panteley 	Sylvia 	301 946-8853 	1956 Arlington Pl. 	Rockville 	MD 	20853 	1
893-72-1158 	McBadden 	Heather 	707 448-4982 	301 Putnam 	Vacaville 	CA 	95688 	0
724-08-9931 	Stringer 	Dirk 	415 843-2991 	5420 Telegraph Av. 	Oakland 	CA 	94609 	0
274-80-9391 	Straight 	Dean 	415 834-2919 	5420 College Av. 	Oakland 	CA 	94609 	1
756-30-7391 	Karsen 	Livia 	415 534-9219 	5720 McAuley St. 	Oakland 	CA 	94609 	1
724-80-9391 	MacFeather 	Stearns 	415 354-7128 	44 Upland Hts. 	Oakland 	CA 	94612 	1
427-17-2319 	Dull 	Ann 	415 836-7128 	3410 Blonde St. 	Palo Alto 	CA 	94301 	1
672-71-3249 	Yokomoto 	Akiko 	415 935-4228 	3 Silver Ct. 	Walnut Creek 	CA 	94595 	1
267-41-2394 	O'Leary 	Michael 	408 286-2428 	22 Cleveland Av. #14 	San Jose 	CA 	95128 	1
472-27-2349 	Gringlesby 	Burt 	707 938-6445 	PO Box 792 	Covelo 	CA 	95428 	3
527-72-3246 	Greene 	Morningstar 	615 297-2723 	22 Graybar House Rd. 	Nashville 	TN 	37215 	0
172-32-1176 	White 	Johnson 	408 496-7223 	10932 Bigge Rd. 	Menlo Park 	CA 	94025 	1
712-45-1867 	del Castillo 	Innes 	615 996-8275 	2286 Cram Pl. #86 	Ann Arbor 	MI 	48105 	1
846-92-7186 	Hunter 	Sheryl 	415 836-7128 	3410 Blonde St. 	Palo Alto 	CA 	94301 	1
486-29-1786 	Locksley 	Charlene 	415 585-4620 	18 Broadway Av. 	San Francisco 	CA 	94130 	1
648-92-1872 	Blotchet-Halls 	Reginald 	503 745-6402 	55 Hillsdale Bl. 	Corvallis 	OR 	97330 	1
341-22-1782 	Smith 	Meander 	913 843-0462 	10 Mississippi Dr. 	Lawrence 	KS 	66044 	0
309-68-290 	Cricket 	Barlab 	415-439-7815 	4279 warker Street 	Oaklang 	CA 	94809 	1

chained_identity (Unsolved)

某公司实习生开发了一个留言板内容,该公司非常注重安全性,不过留言板似乎可以查看数据内容,貌似可以看到数据库的东西,你能帮忙看看有什么问题吗,好像可以污染一下。


ezwebsite (Unsolved)

小明为了窥探别人隐私,特意用网上的CMS搭建了一个网站并且去掉了网站原有的加密,在内网环境中,搭建了一个dtale服务进行数据分析。进后台RCE关注内网服务进行提权