GitHub Enterprise is the on-premises version of GitHub.com that you can deploy a whole GitHub service in your private network for businesses. You can get 45-days free trial and download the VM from enterprise.github.com.
After you deployed, you will see like bellow:
Now, I have all the GitHub environment in a VM. It’s interesting, so I decided to look deeper into VM :P
Environment
The beginning of everything is Port Scanning. After using our good friend - Nmap, we found that there are 6 exposed ports on VM.
1 2 3 4 5 6 7 8 9 10 11
$ 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
With a little knocking and service grabbing, it seems like:
22/tcp and 9418/tcp seem like haproxy and it forwards connections to a backend service called babeld
80/tcp and 443/tcp are the main GitHub services
122/tcp is just a SSH service
8443/tcp is management console of GitHub
By the way, GitHub management console need a password to login. Once you got the password, you can add your SSH key and connect into VM through 122/tcp
With SSH into VM, we examined the whole system and found that the service code base looks like under directory of /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
Change directory to /data/ and try to review the source code, but it seems encrypted :(
GitHub uses a custom library to obfuscate their source code. If you search ruby_concealer.so on Google, you will find a kind man write a snippet on this gist.
It simply replace rb_f_eval to rb_f_putsin ruby_concealer.so and it’s work.
But to be a hacker. We can’t just use it without knowing how it works. So, let’s open IDA Pro!
As you can see. It just uses Zlib::Inflate::inflate to decompress data and XOR with following key:
This obfuscation is intended to discourage GitHub Enterprise customers from making modifications to the VM. We know this ‘encryption’ is easily broken.
So we can easily implement it by our-self!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
require'zlib'
defdecrypt(s) key = "This obfuscation is intended to discourage GitHub Enterprise customers from making modifications to the VM. We know this 'encryption' is easily broken. " i, plaintext = 0, ''
Zlib::Inflate.inflate(s).each_byte do |c| plaintext << (c ^ key[i%key.length].ord).chr i += 1 end plaintext end
$ ./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 are written in Ruby (Ruby on Rails and Sinatra).
/data/github/ looks like the application run under port 80/tcp443/tcp and it looks like the real code base of github.com, gist.github.com and api.github.com
/data/render/ looks like real code base of render.githubusercontent.com
/data/enterprise-manage/ seems like the application run under port 8443/tcp
GitHub Enterprise uses enterprise? and dotcom? to check whether the application is running under Enterprise Mode or GitHub dot com mode.
Vulnerability
I use about one week to find this vulnerability, I am not familiar with Ruby. But just learning from doing :P
This is my rough schedule of the week.
Day 1 - Setting VM
Day 2 - Setting VM
Day 3 - Learning Rails by code reviewing
Day 4 - Learning Rails by code reviewing
Day 5 - Learning Rails by code reviewing
Day 6 - Yeah, I found a SQL Injection!
That SQL Injection vulnerability is found under GitHub Enterprise PreReceiveHookTarget model.
The root cause is in /data/github/current/app/model/pre_receive_hook_target.rb line 45
Although There is built-in ORM(called ActiveRecord in Rails) in Rails and prevent you from SQL Injection. But there are so many misuse of ActiveRecord may cause SQL Injection.
More examples you can check Rails-sqli.org. It’s good to learn about SQL Injection on Rails.
In this case, if we can control the parameter of method order we can inject our malicious payload into SQL.
OK, let’s trace up! sorted_by is called by /data/github/current/app/api/org_pre_receive_hooks.rb in line 61.
You can see that params[:sort] is passed to scope.sorted_by . So, we can inject our malicious payload into params[:sort].
Before you trigger this vulnerability, you need a valid access_token with admin:pre_receive_hook scope to access API. Fortunately, it can be obtained by following command: