安全研究员vakzz于4月7日在hackerone上提交了一个关于gitlab的RCE漏洞,在当时并没有提及是否需要登录gitlab进行授权利用,在10月25日该漏洞被国外安全公司通过日志分析发现未授权的在野利用,并发现了新的利用方式。根据官方漏洞通告页面得知安全的版本为13.10.3、13.9.6和13.8.8。本篇将复现分析携带恶意文件的请求是如何通过gitlab传递到exiftool进行解析的
gitlab介绍GitLab是由GitLabInc.开发,使用MIT许可证的基于网络的Git仓库管理工具,且具有wiki和issue跟踪功能。使用Git作为代码管理工具,并在此基础上搭建起来的web服务。
1、份很多已经买不到的绝版电子书2、30G安全大厂内部的视频资料3、份src文档4、常见安全面试题5、ctf大赛经典题目解析6、全套工具包7、应急响应笔记8、网络安全学习路线
可以看到在gitlab的组成中包含的各种组件,可以通过两个关键入口访问,分别是HTTP/HTTPS(TCP80,)和SSH(TCP22),请求通过nginx转发到Workhorse,然后Workhorse和Puma进行交互,这里我们着重介绍下通过Web访问的组件GitLabWorkhorse。
Puma是一个用于Ruby应用程序的简单、快速、多线程和高度并发的HTTP1.1服务器,用于提供GitLab网页和API。从GitLab13.0开始,Puma成为了默认的Web服务器,替代了之前的Unicorn。而在GitLab14.0中,Unicorn从Linux包中删除,只有Puma可用。
GitLabWorkhorse介绍GitLabWorkhorse是一个使用go语言编写的敏捷反向代理。在gitlab_features说明中可以总结大概的内容为,它会处理一些大的HTTP请求,比如文件上传、文件下载、Gitpush/pull和Git包下载。其它请求会反向代理到GitLabRails应用。可以在GitLab的项目路径lib/support/nginx/gitlab中的nginx配置文件内看到其将请求转发给了GitLabWorkhorse。默认采用了unixsocket进行交互。
这篇文档还写到,GitLabWorkhorse在实现上会起到以下作用:
理论上所有向gitlab-Rails的请求首先通过上游代理,例如NGINX或Apache,然后将到达gitlab-Workhorse。
workhorse能处理一些无需调用Rails组件的请求,例如静态的js/css资源文件,如以下的路由注册:
u.route( "",`^/assets/`,//匹配路由//处理静态文件 static.ServeExisting( u.URLPrefix, staticpages.CacheExpireMax, assetsNotFoundHandler, ), withoutTracing(),//Tracingonassetsisverynoisy)
workhorse能修改Rails组件发来的响应。例如:假设你的Rails组件使用send_file,那么gitlab-workhorse将会打开磁盘中的文件然后把文件内容作为响应体返回给客户端。
gitlab-workhorse能接管向Rails组件询问操作权限后的请求,例如处理gitclone之前得确认当前客户的权限,在向Rails组件询问确认后workhorse将继续接管的请求,如以下的路由注册:
u.route("GET",gitProjectPattern+`info/refs\z`,git.GetInfoRefsHandler(api)),u.route("POST",gitProjectPattern+`git-upload-pack\z`,contentEncodingHandler(git.UploadPack(api)),withMatcher(isContentType("application/x-git-upload-pack-request"))),u.route("POST",gitProjectPattern+`git-receive-pack\z`,contentEncodingHandler(git.ReceivePack(api)),withMatcher(isContentType("application/x-git-receive-pack-request"))),u.route("PUT",gitProjectPattern+`gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`,lfs.PutStore(api,signingProxy,preparers.lfs),withMatcher(isContentType("application/octet-stream")))
workhorse能修改发送给Rails组件之前的请求信息。例如:当处理GitLFS上传时,workhorse首先向Rails组件询问当前用户是否有执行权限,然后它将请求体储存在一个临时文件里,接着它将修改过后的包含此临时文件路径的请求体发送给Rails组件。
workhorse能管理与Rails组件通信的长时间存活的websocket连接,代码如下:
//Terminalwebsocket u.wsRoute(projectPattern+`-/environments/[0-9]+/terminal.ws\z`,channel.Handler(api)), u.wsRoute(projectPattern+`-/jobs/[0-9]+/terminal.ws\z`,channel.Handler(api)),
使用ps-aux
grep"workhorse"命令可以看到gitlab-workhorse的默认启动参数
go语言前置知识我会简要介绍一下漏洞涉及的相关语言前置知识,这样才能够更深入的理解该漏洞,并将相关知识点串联起来,达到举一反三。
函数、方法和接口
在golang中函数和方法的定义是不同的,看下面一段代码
packagemain//Person接口typePersoninterface{ isAdult()bool}//Boy结构体typeBoystruct{ Namestring Ageint}//函数funcNewBoy(namestring,ageint)*Boy{ returnBoy{ Name:name, Age:age, }}//方法func(p*Boy)isAdult()bool{ returnp.Age18}funcmain(){ //结构体调用 b:=NewBoy("Star",18) println(b.isAdult()) //将接口赋值b,使用接口调用 varpPerson=b println(p.isAdult())//false}
其中NewBoy为函数,isAdult为方法。他们的区别是方法在func后面多了一个接收者参数,这个接受者可以是一个结构体或者接口,你可以把他当做某一个"类",而就是实现了该类的方法。
通过取地址操作可以将一个结构体实例化,相当于new,可以看到在中函数封装了这种操作。在main函数中通过调用函数实例化Boy结构体,并调用了其方法。
关于接口的实现在Go语言中是隐式的。两个类型之间的实现关系不需要在代码中显式地表示出来。Go语言中没有类似于implements的关键字。Go编译器将自动在需要的时候检查两个类型之间的实现关系。**在类型中添加与接口签名一致的方法就可以实现该方法。**如的参数和返回值均与接口Person中的方法一致。所以在main函数中可以直接将定义的接口p赋值为实例结构体b。并进行调用。
net/