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;