Class: Msf::Exploit::Remote::HTTP::RailsActiveStorageVips::PngDecoder
- Inherits:
-
Object
- Object
- Msf::Exploit::Remote::HTTP::RailsActiveStorageVips::PngDecoder
- Defined in:
- lib/msf/core/exploit/remote/http/rails_active_storage_vips/png_decoder.rb
Overview
Applies strict resource limits before using ChunkyPNG to decode grayscale representation responses. ChunkyPNG receives only bounded, exactly sized scanlines because its unbounded parser must not process target-controlled PNG data. Format rules: www.w3.org/TR/png-3/
Constant Summary collapse
- PNG_SIGNATURE =
ChunkyPNG::Datastream::SIGNATURE
- MAX_PIXELS =
1_048_576- MAX_DATA_BYTES =
4_194_304- MAX_CHUNKS =
1024- MAX_IDAT_BYTES =
3_145_728
Class Method Summary collapse
-
.decode(data) ⇒ Hash{Symbol => Integer, String}
Decodes a bounded, 8-bit, non-interlaced grayscale PNG image.
Class Method Details
.decode(data) ⇒ Hash{Symbol => Integer, String}
Decodes a bounded, 8-bit, non-interlaced grayscale PNG image.
22 23 24 25 26 27 28 29 30 31 32 33 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 |
# File 'lib/msf/core/exploit/remote/http/rails_active_storage_vips/png_decoder.rb', line 22 def self.decode(data) raise TriggerError, 'Representation body is not a PNG image' unless data.is_a?(String) && data.start_with?(PNG_SIGNATURE) raise TriggerError, 'Representation PNG exceeded the maximum encoded size' if data.bytesize > MAX_DATA_BYTES offset = PNG_SIGNATURE.bytesize idat = String.new.b chunk_count = 0 width = nil height = nil bit_depth = nil color_type = nil compression = nil filter_method = nil interlace = nil saw_iend = false while offset + 12 <= data.bytesize chunk_count += 1 raise TriggerError, 'Representation PNG contained too many chunks' if chunk_count > MAX_CHUNKS length = data.byteslice(offset, 4).unpack1('N') raise TriggerError, 'Representation PNG contains an oversized chunk' if length > data.bytesize - offset - 12 chunk_type = data.byteslice(offset + 4, 4) chunk_data = data.byteslice(offset + 8, length) chunk_crc = data.byteslice(offset + 8 + length, 4).unpack1('N') expected_crc = Zlib.crc32(chunk_type + chunk_data) raise TriggerError, "Representation PNG #{chunk_type.inspect} chunk failed its CRC check" unless chunk_crc == expected_crc case chunk_type when 'IHDR' raise TriggerError, 'Representation PNG contained an invalid IHDR chunk' unless length == 13 && width.nil? width, height, bit_depth, color_type, compression, filter_method, interlace = chunk_data.unpack('NNC5') when 'IDAT' raise TriggerError, 'Representation PNG image data exceeded the compressed size limit' if idat.bytesize + length > MAX_IDAT_BYTES idat << chunk_data when 'IEND' saw_iend = true break end offset += 12 + length end raise TriggerError, 'Representation PNG is missing IHDR data' unless width && height raise TriggerError, 'Representation PNG dimensions were invalid' unless width.positive? && height.positive? && width * height <= MAX_PIXELS raise TriggerError, "Unsupported PNG bit depth #{bit_depth}" unless bit_depth == 8 raise TriggerError, "Unsupported PNG color type #{color_type}" unless color_type == 0 raise TriggerError, "Unsupported PNG compression method #{compression}" unless compression.zero? raise TriggerError, "Unsupported PNG filter method #{filter_method}" unless filter_method.zero? raise TriggerError, 'Interlaced PNG responses are not supported' unless interlace.zero? raise TriggerError, 'Representation PNG is missing image data' if idat.empty? raise TriggerError, 'Representation PNG is missing IEND data' unless saw_iend stride = width expected_size = height * (stride + 1) raw = inflate_limited(idat, expected_size) raise TriggerError, 'Representation PNG scanline data had an unexpected length' unless raw.bytesize == expected_size canvas = ChunkyPNG::Canvas.decode_png_pixelstream( raw, width, height, ChunkyPNG::COLOR_GRAYSCALE, bit_depth, ChunkyPNG::INTERLACING_NONE, nil, nil ) { width: width, height: height, channels: 1, pixels: canvas.to_grayscale_stream } rescue Zlib::Error => e raise TriggerError, "Representation PNG decompression failed: #{e.}" rescue ChunkyPNG::Exception => e raise TriggerError, "Representation PNG decoding failed: #{e.}" end |