20 March 2013

Vulnerability Summary

  • YAML allows the de/serialization of arbitrary objects
  • YAML libraries in Ruby allow the de/serialization of arbitary ruby objects
    • If the Ruby YAML implementation allocates and initializs the Ruby objects upon deserialization
      • Since symbols in Ruby aren’t garbage collected, a hash can be crafted to crash the stack
      • Some system calls can be sent

There are relevant differences in the Syck and Psych implementations I will later expand upon.

Required Reading

Supplemental Reading

Workarounds

SafeYaml

    SafeYAML::OPTIONS[:default_mode] = :unsafe
    SafeYAML::OPTIONS[:default_mode] = :safe

    # Ruby >= 1.9.3
    YAML.load(yaml, filename, :safe => true) # calls safe_load
    YAML.load(yaml, filename, :safe => false) # calls unsafe_load

    # Ruby < 1.9.3
    YAML.load(yaml, :safe => true) # calls safe_load
    YAML.load(yaml, :safe => false) # calls unsafe_load
    # The way that SafeYAML works is by restricting the kinds of objects that can be deserialized via YAML.load. More specifically, only the following types of objects can be deserialized by default:

    # Hashes
    # Arrays
    # Strings
    # Numbers
    # Dates
    # Times
    # Booleans
    # Nils

    # Using Syck (unfortunately, Syck and Psych use different tagging schemes)
    SafeYAML::OPTIONS[:whitelisted_tags] = ["tag:ruby.yaml.org,2002:object:OpenStruct"]

    # Using Psych
    SafeYAML::OPTIONS[:whitelisted_tags] = ["!ruby/object:OpenStruct"]

    # Don't sanitize disallowed types silently
    SafeYAML::OPTIONS[:raise_on_unknown_tag] = true

    # Symbols in Ruby are not garbage-collected; therefore enabling symbol deserialization in your application may leave you vulnerable to DOS attacks.
    # Guard: Uses YAML as a serialization format for notifications. The data serialized uses symbolic keys, so setting `SafeYAML::OPTIONS[:deserialize_symbols] = true` is necessary to allow Guard to work.
    # sidekiq: Uses a YAML configiuration file with symbolic keys, so setting `SafeYAML::OPTIONS[:deserialize_symbols] = true` should allow it to work.

see re: PsychShield

PsychShield

    # By default, Psych Shield allows the following types of objects:

    # Hash Array String Range
    # Numeric Fixnum Integer Bignum Float Rational Complex
    # Time DateTime
    # NilClass TrueClass FalseClass
    # To enable additional classes, add the stringified form using the "add" method:

    PsychShield.add('MyClass::IsAwesome::And::Safe')
    # To disable all classes (even the defaults), use the clear method:

    PsychShield.clear