Bug 17351 - ruby new security issue CVE-2015-7551
Summary: ruby new security issue CVE-2015-7551
Status: RESOLVED FIXED
Alias: None
Product: Mageia
Classification: Unclassified
Component: Security (show other bugs)
Version: 5
Hardware: i586 Linux
Priority: Normal normal
Target Milestone: ---
Assignee: QA Team
QA Contact: Sec team
URL: http://lwn.net/Vulnerabilities/668314/
Whiteboard: MGA5-64-OK MGA5-32-OK has_procedure ...
Keywords: validated_update
Depends on:
Blocks:
 
Reported: 2015-12-17 17:14 CET by David Walser
Modified: 2016-01-12 10:14 CET (History)
4 users (show)

See Also:
Source RPM: ruby-2.0.0.p645-1.mga5.src.rpm
CVE:
Status comment:


Attachments
Minimal PoC for ruby Fiddle class (213 bytes, text/plain)
2016-01-10 18:23 CET, Len Lawrence
Details
Minimal PoC for ruby Fiddle class (210 bytes, text/plain)
2016-01-10 18:35 CET, Len Lawrence
Details
JPEG display test (2.78 KB, application/x-ruby)
2016-01-10 18:47 CET, Len Lawrence
Details

Description David Walser 2015-12-17 17:14:58 CET
Upstream has issued an advisory on December 16:
https://www.ruby-lang.org/en/news/2015/12/16/unsafe-tainted-string-usage-in-fiddle-and-dl-cve-2015-7551/

The issue is fixed upstream in 2.2.4 and 2.0.0-p648:
https://www.ruby-lang.org/en/news/2015/12/16/ruby-2-0-0-p648-released/
https://www.ruby-lang.org/en/news/2015/12/16/ruby-2-2-4-released/

Note that 2.0.0 will be EOL in February, so we really should upgrade Mageia 5 to 2.2.x if possible.

Reproducible: 

Steps to Reproduce:
David Walser 2015-12-17 17:15:07 CET

Whiteboard: (none) => MGA5TOO

Comment 1 David Walser 2016-01-08 01:57:34 CET
Updated package uploaded for Cauldron by Pascal.

Updated package uploaded for Mageia 5.

Advisory:
========================

Updated ruby packages fix security vulnerability:

There is an unsafe tainted string vulnerability in Fiddle and DL. This issue
was originally reported and fixed with CVE-2009-5147 in DL, but reappeared
after DL was reimplemented using Fiddle and libffi (CVE-2015-7551).

References:
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-7551
https://www.ruby-lang.org/en/news/2015/12/16/unsafe-tainted-string-usage-in-fiddle-and-dl-cve-2015-7551/
https://www.ruby-lang.org/en/news/2015/12/16/ruby-2-0-0-p648-released/
========================

Updated packages in core/updates_testing:
========================
ruby-2.0.0.p648-1.mga4
libruby2.0-2.0.0.p648-1.mga4
ruby-doc-2.0.0.p648-1.mga4
ruby-devel-2.0.0.p648-1.mga4
ruby-tk-2.0.0.p648-1.mga4
ruby-irb-2.0.0.p648-1.mga4

from ruby-2.0.0.p648-1.mga4.src.rpm

Whiteboard: MGA5TOO => (none)
Source RPM: ruby-2.2.3-10.mga6.src.rpm => ruby-2.0.0.p645-1.mga5.src.rpm
Assignee: pterjan => qa-bugs
Version: Cauldron => 5
CC: (none) => pterjan

Comment 2 Len Lawrence 2016-01-08 14:36:42 CET
mga5  x86_64  Mate

[lcl@vega ~]$ sudo urpmi lib64ruby2.0
Package lib64ruby2.0-2.0.0.p645-1.mga5.x86_64 is already installed

I have been trying to establish a proof of concept for this but have not got very far.
Looking at the CVEs it appears that the security problem lies in the area of
Fiddle and DL.
First of all I tried to find out what fiddle is all about
From CVE-2015-7551
handle = Fiddle::Handle.new(dangerous_user_input)

handle = Fiddle::Handle.new(some_library)
function_pointer = handle[dangerous_user_input]
-----------------------------------------------
From Ruby doc site:
standardlib = "/lib64/libc.so.6"
=> "/lib64/libc.so.6"
@handle = Fiddle::Handle.new( standardlib )
=> #<Fiddle::Handle:0x00000000d69ef8>
# return symbol for address of method
strcpy_ptr = @handle['strcpy']
# alternative
strcpy_ptr = @handle.sym( 'strcpy' )
-----------------------------------------------
According to CVE-2015-7551 the fault affects "All ruby 2.0 versions prior to ruby 2.0.0 patchlevel 648", which means we are affected.
From https://www.ruby-lang.org/en/news/2013/05/14/taint-bypass-dl-fiddle-cve-2013-2065/
I extracted some code and scripted it:

#!/bin/env/ ruby
require 'dl'
require 'fiddle'
def my_function(user_input)
  handle    = DL.dlopen(nil)
  sys = Fiddle::Function.new(handle['system'],
                             [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
  sys.call user_input
end

$SAFE = 1
my_function "uname -rs".taint
puts my_function

[lcl@vega ~]$ ruby taint.rb
DL is deprecated, please use Fiddle
taint.rb:9:in `call': tainted parameter not allowed (SecurityError)
	from taint.rb:9:in `my_function'
	from taint.rb:13:in `<main>'

I don't know what "tainted" actually means in this context but it looks like the system responds correctly to a "tainted" object but is unable to check
taintedness for some  types of objects.  So I am guessing that the following might be a PoC.

#!/bin/env/ ruby
# unknown.rb
require 'dl'
require 'fiddle'
def my_function(user_input)
  handle    = DL.dlopen(nil)
  sys = Fiddle::Function.new(handle['system'],
                             [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
  sys.call user_input
end
$SAFE = 1
my_function "uname -rs".taint
puts my_function

[lcl@vega ~]$ ruby unknown.rb
DL is deprecated, please use Fiddle
Linux 4.1.15-tmb-desktop-1.mga5
unknown.rb:5:in `my_function': wrong number of arguments (0 for 1) (ArgumentError)
	from unknown.rb:14:in `<main>'


Any advice?

CC: (none) => tarazed25

Comment 3 Len Lawrence 2016-01-08 14:38:09 CET
Sorry, the penultimate line should be:
my_function "uname -rs"
Copy and paste woes.
Comment 4 Pascal Terjan 2016-01-08 14:43:57 CET
You are calling my_function a second time on the last line without argument, you probably want to combine the 2 lines into:

puts my_function "uname -rs"

or 

puts my_function("uname -rs")
Comment 5 Len Lawrence 2016-01-08 15:05:04 CET
Yes, I noticed that - our posts clashed in midair.
#!/bin/env/ ruby
# unknown.rb
require 'dl'
require 'fiddle'
def my_function(user_input)
  handle    = DL.dlopen(nil)
  sys = Fiddle::Function.new(handle['system'],
                             [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
  sys.call user_input
end
$SAFE = 1
my_function "uname -rs"

[lcl@vega ~]$ ruby unknown.rb
DL is deprecated, please use Fiddle
Linux 4.1.15-tmb-desktop-1.mga5

Which proves nothing, leaving the question how to construct a tainted string?
Comment 6 Len Lawrence 2016-01-08 16:06:31 CET
Streamlined the scriptlet to get rid of DL.
#!/bin/env/ ruby
require 'fiddle'
def my_function( user_input )
  libc = Fiddle.dlopen( '/lib64/libc.so.6' ) 
  handle = libc['system'] 
  sys = Fiddle::Function.new( handle,
                              [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT )
  sys.call user_input
end

$SAFE = 1
my_function "uname -rs".taint
Comment 7 Len Lawrence 2016-01-08 19:17:55 CET
Installed the updates as listed and tried the amended test script again.
Having read somewhere that user input is unsafe by default I inserted a line to wait for input of the command from the user, 'uname -rs' in this case.
#!/bin/env ruby
# poc.rb => poc
require 'fiddle'
def my_function( user_input )
  libc = Fiddle.dlopen( '/lib64/libc.so.6' ) 
  handle = libc['system'] 
  sys = Fiddle::Function.new( handle,
                              [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT )
  sys.call user_input
end
$SAFE = 0
command = gets.chomp
my_function command
puts "User input is treated as unsafe" if command.tainted?

$ chmod +x poc
$ ./poc
uname -rs
Linux 4.1.15-tmb-desktop-1.mga5
User input is treated as unsafe

Change the security level to 1 ($SAFE = 1) and run again.

$ ./poc
uname -rs
./poc:8:in `call': tainted parameter not allowed (SecurityError)
	from ./poc:8:in `my_function'
	from ./poc:12:in `<main>'

So, no change in behaviour after the update.  I had been hoping for a SecurityError whichever security level is applied.  The  script was run in
irb as well.  So I am letting go at this point and running general tests of ruby.  The taintedness needs to be introduced into the Fiddle operations somehow else.

Used 'ri Fiddle > fiddler' to dump documentation of the Fiddle extension to a file and tested gem install.
Ran several of my own interactive gui ruby-tk scripts without any problem.

[lcl@vega ~]$ sudo gem install mplayer-ruby
Successfully installed mplayer-ruby-0.2.0
Parsing documentation for mplayer-ruby-0.2.0
Done installing documentation for mplayer-ruby after 0 seconds
1 gem installed

No regressions noted so far so, with reservations regarding the PoC, I am marking this as OK for 64-bits.
Len Lawrence 2016-01-08 19:18:19 CET

Whiteboard: (none) => MGA5-64-OK

Comment 8 Pascal Terjan 2016-01-08 21:21:24 CET
Reading about this, the difference seems to be when using $SAFE > 0

I understand it should raise SecurityErrror if the filename is tainted and you try to dlopen it
Comment 9 Pascal Terjan 2016-01-08 22:38:17 CET
Ah sorry I had missed that in comment 2 you were already getting the security error with $SAFE=1, it's probably a specific case then
Comment 10 Pascal Terjan 2016-01-08 22:46:43 CET
https://github.com/ruby/ruby/commit/073cc5e815fcf5178fe4e515fcde74dc3597adeb adds some tests for it:

 +    def test_safe_handle_open
 +      t = Thread.new do
 +        $SAFE = 1
 +        Fiddle::Handle.new(LIBC_SO.taint)
 +      end
 +      assert_raise(SecurityError) { t.value }
 +    end
 +
 +    def test_safe_function_lookup
 +      t = Thread.new do
 +        h = Fiddle::Handle.new(LIBC_SO)
 +        $SAFE = 1
 +        h["qsort".taint]
 +      end
 +      assert_raise(SecurityError) { t.value }
 +    end
Comment 11 Pascal Terjan 2016-01-08 22:51:53 CET
On Mageia 5:

$ ruby -rfiddle -e 't = Thread.new{ $SAFE = 1; Fiddle::Handle.new("libc.so.6".taint)}; t.value'
$

On cauldron:

$ ruby -rfiddle -e 't = Thread.new{ $SAFE = 1; Fiddle::Handle.new("libc.so.6".taint)}; t.value'
-e:1:in `initialize': Insecure operation - initialize (SecurityError)
	from -e:1:in `new'
	from -e:1:in `block in <main>'
$
Comment 12 Len Lawrence 2016-01-08 23:49:38 CET
Thanks for those pointers.  Shall have a look at these tests on another machine with respect to pre and post updates.  We are not in too much of a hurry for these I hope.
Comment 13 Len Lawrence 2016-01-10 18:16:17 CET
mga5  x86_64  Mate

Before the update the following two commands were accepted, without any security errors.

$ ruby -rfiddle -e 't = Thread.new{ $SAFE = 1; Fiddle::Handle.new("libc.so.6".taint) }; t.value'
$ ruby -rfiddle -e 't = Thread.new{ c = Fiddle::Handle.new("libc.so.6")
  $SAFE = 1; c["qsort".taint] }; t.value'

After the update the unsafe operations were trapped.
For the handle open:
-e:1:in `initialize': Insecure operation - new (SecurityError)
	from -e:1:in `new'
	from -e:1:in `block in <main>'

For the function lookup:
-e:2:in `[]': Insecure operation: -r (SecurityError)
	from -e:2:in `block in <main>'

The two commands can be regarded as a minimal PoC.
Comment 14 Len Lawrence 2016-01-10 18:23:10 CET
Created attachment 7334 [details]
Minimal PoC for ruby Fiddle class

Command line tests supplied with help from Pascal Terjan.
Before the update unsafe input is not trapped.
After the correction the commands should fail with a SecurityError.
Comment 15 Len Lawrence 2016-01-10 18:35:54 CET
Created attachment 7335 [details]
Minimal PoC for ruby Fiddle class

Thanks to Pascal Terjan for these commands.
Before the update neither command should provide any error output.
After the update they both trap the unsafe operations and raise a SecurityError.

Attachment 7334 is obsolete: 0 => 1

Comment 16 Len Lawrence 2016-01-10 18:47:34 CET
Created attachment 7336 [details]
JPEG display test

A simple test for ruby-tk.  tkimg should be installed.
Comment 17 Len Lawrence 2016-01-10 18:54:30 CET
mga5  i586 vbox  Mate

Ran the minimal PoC test before and after the update with the same results as in the 64-bit case.

Installed a gem, used irb and called rubyimage.rb on the command line.
Everything looks OK.
Len Lawrence 2016-01-10 18:56:18 CET

Whiteboard: MGA5-64-OK => MGA5-64-OK MGA5-32-OK has_procedure

Len Lawrence 2016-01-10 18:56:42 CET

CC: (none) => sysadmin-bugs
Keywords: (none) => validated_update

Comment 18 Dave Hodgins 2016-01-12 06:31:00 CET
Advisory added using ruby-2.0.0.p648-1.mga5 source rpm.

CC: (none) => davidwhodgins
Whiteboard: MGA5-64-OK MGA5-32-OK has_procedure => MGA5-64-OK MGA5-32-OK has_procedure advisory

Comment 19 Mageia Robot 2016-01-12 10:14:52 CET
An update for this issue has been pushed to Mageia Updates repository.

http://advisories.mageia.org/MGASA-2016-0007.html

Status: NEW => RESOLVED
Resolution: (none) => FIXED


Note You need to log in before you can comment on or make changes to this bug.