YAML modules get some security fixes

Update your YAML modules to change the default behavior to not bless data structures. But, test your code to ensure that you don’t depend on that feature or break a bunch of things through the upgrade. If you depend on the old default behavior, you can to set a variable to enable the old behavior. The original announcement has more details.

Previously I’ve written about Perl’s Storable security problem. In short, inflating a serialized object could load an arbitrary class and potentially run damaging and unintended code. YAML also has a way to inflate data into class and can have the same problems.

Here’s a program to convert a Mojo::URL object to YAML:

use v5.10;

use Mojo::URL;
use YAML;

my $url = Mojo::URL->new( 'http://www.example.com' );

say Dump( $url );

The YAML includes a the type of “object” it is, which is basically a blessed Perl reference. Unserializable things, such as filehandes or code references, will have problems. Otherwise, it’s straightforward:

--- !!perl/hash:Mojo::URL
host: www.example.com
path: !!perl/hash:Mojo::Path
  charset: UTF-8
  leading_slash: ''
  parts: []
  trailing_slash: ''
scheme: http

Now reverse that. This program starts with the YAML text (in an indented here doc) and loads it. Notice that I did not load the Mojo::URL module but I end up with a blessed reference. This uses YAML 1.29 (because a later version will do it differently):

use v5.26;

use YAML qw(Load); # This is v1.29

my $string = <<~"HERE";
	--- !!perl/hash:Mojo::URL
	host: www.example.com
	path: !!perl/hash:Mojo::Path
	  charset: UTF-8
	  leading_slash: ''
	  parts: []
	  trailing_slash: ''
	scheme: http
	HERE

my $data = Load( $string );

say 'Version: ', YAML->VERSION;
say Dumper( $data ); use Data::Dumper;

With an older YAML, I get a Mojo::URL object back, even though
I haven’t loaded that module
:

Version: 1.29
$VAR1 = bless( {
                 'scheme' => 'http',
                 'host' => 'www.example.com',
                 'path' => bless( {
                                    'charset' => 'UTF-8',
                                    'leading_slash' => '',
                                    'parts' => [],
                                    'trailing_slash' => ''
                                  }, 'Mojo::Path' )
               }, 'Mojo::URL' );

The rest of the attack is the same as that for Storable. If I know ahead of time that the DESTROY method will do something, I can construct the serialization of that object to get it to act on my data. With File::Temp, I can get it to try to remove files.

This feature was reported as a bug in Debian #862373 , and then as a GitHub issue for the YAML module.

With YAML 1.30 (released on January 28, 2020) changes this behavior. Previously (back several versions) it blessed inflated objects by default. Now I get back a simple, non-blessed hash:

Version: 1.30
$VAR1 = {
          'path' => {
                      'trailing_slash' => '',
                      'leading_slash' => '',
                      'charset' => 'UTF-8',
                      'parts' => []
                    },
          'scheme' => 'http',
          'host' => 'www.example.com'
        };

I can set $YAML::LoadBlessed to a true value to get the old behavior. I don’t really want to set a global variable because I don’t know what else I’ll mess up far away in the program. I can do it in a block with local to limit the damage:

#!perl
use v5.26;

use YAML qw(Load);

my $string = <<~"HERE";
	--- !!perl/hash:Mojo::URL
	host: www.example.com
	path: !!perl/hash:Mojo::Path
	  charset: UTF-8
	  leading_slash: ''
	  parts: []
	  trailing_slash: ''
	scheme: http
	HERE

my $url = do {
	local $YAML::LoadBlessed = 1;
	Load( $string );
	};

say Dumper( $url ); use Data::Dumper;