0x00 前言

PHP里头比较常见的Web图像处理库有GDImageMagick

GD里头比较常用的有imagecopyresizedimagecopyresampled 怎么调戏这两个函数的变换已经有很多文章和代码了,比如Github gist

今天简单看看ImageMagick里头有没有什么可以保留数据的方法

0x01 开始

我们的Imagemagick版本

stonemoe@desktop ~/Desktop> magick --version
Version: ImageMagick 7.0.10-6 Q16 x86_64 2020-04-07 https://imagemagick.org
Copyright: © 1999-2020 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI Modules OpenMP(4.5) 
Delegates (built-in): bzlib cairo fontconfig freetype heic jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png raqm raw rsvg tiff webp wmf x xml zlib

随便找张图片看看都有哪些属性

stonemoe@desktop ~/Desktop> magick identify -verbose payload.jpg
Image: payload.jpg
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Mime type: image/jpeg
  Class: DirectClass
  Geometry: 139x97+0+0
  Units: Undefined
  Colorspace: sRGB
  Type: TrueColor
  Base type: Undefined
  Endianess: Undefined
  Depth: 8-bit
  Channel depth:
    Red: 8-bit
    Green: 8-bit
    Blue: 8-bit
  Channel statistics:
    Pixels: 13483
    Red:
      min: 0  (0)
      max: 255 (1)
      mean: 93.4788 (0.366584)
      standard deviation: 69.5047 (0.272567)
      kurtosis: -0.519383
      skewness: 1.02065
      entropy: 0.839877
    Green:
      min: 0  (0)
      max: 255 (1)
      mean: 110.394 (0.43292)
      standard deviation: 41.334 (0.162094)
      kurtosis: 2.00187
      skewness: 1.46048
      entropy: 0.816047
    Blue:
      min: 0  (0)
      max: 244 (0.956863)
      mean: 118.63 (0.465215)
      standard deviation: 75.5964 (0.296456)
      kurtosis: -1.50408
      skewness: -0.589183
      entropy: 0.805144
  Image statistics:
    Overall:
      min: 0  (0)
      max: 255 (1)
      mean: 107.501 (0.421573)
      standard deviation: 62.145 (0.243706)
      kurtosis: -1.06579
      skewness: 0.236057
      entropy: 0.820356
  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)
  Matte color: grey74
  Background color: white
  Border color: srgb(223,223,223)
  Transparent color: none
  Interlace: None
  Intensity: Undefined
  Compose: Over
  Page geometry: 139x97+0+0
  Dispose: Undefined
  Iterations: 0
  Compression: JPEG
  Quality: 72
  Orientation: Undefined
  Convex hull: 0,0 138,0 138,95 136,96 0,96 0,0 
  Minimum bounding box: 138,0 138,96 0,96 -5.8783e-15,0 
  Properties:
    date:create: 2020-04-17T19:39:25+00:00
    date:modify: 2020-04-17T19:39:17+00:00
    jpeg:colorspace: 2
    jpeg:sampling-factor: 1x1,1x1,1x1
    minimum-bounding-box:angle: 0
    minimum-bounding-box:area: 13248
    minimum-bounding-box:height: 138
    minimum-bounding-box:unrotate: 0
    minimum-bounding-box:width: 96
    signature: d40b11152ffe08839367333e59c2e375970adb7e05457af8022092ac1147bc21
  Artifacts:
    verbose: true
  Tainted: False
  Filesize: 4311B
  Number pixels: 13483
  Pixels per second: 18.1345MP
  User time: 0.000u
  Elapsed time: 0:01.000
  Version: ImageMagick 7.0.10-6 Q16 x86_64 2020-04-07 https://imagemagick.org

0x02 jpeg comment

首先看看jpeg comment如何,我们使用exiv2写一下

stonemoe@desktop ~/Desktop> exiv2 -c im_so_happy modify payload.jpg
stonemoe@desktop ~/Desktop> magick identify -verbose payload.jpg
...省略...(只贴Properties部分,下同)
 Properties:
    comment: im_so_happy
    date:create: 2020-04-17T19:47:54+00:00
    date:modify: 2020-04-17T19:47:54+00:00
    jpeg:colorspace: 2
    jpeg:sampling-factor: 1x1,1x1,1x1
...省略...

可以看到在Properies下出现了comment属性

然后再使用imagemagick resize和thumbnail处理一下图片

stonemoe@desktop ~/Desktop> convert -size 110x110 payload.jpg payload_resize.png
stonemoe@desktop ~/Desktop> convert -thumbnail 110x110 payload.jpg payload_thumb.png

看看resize的结果

stonemoe@desktop ~/Desktop> magick identify -verbose payload_resize.png
...省略...
 Properties:
    comment: im_so_happy
    date:create: 2020-04-17T19:50:02+00:00
    date:modify: 2020-04-17T19:50:02+00:00
    minimum-bounding-box:angle: 0
    minimum-bounding-box:area: 13248
    minimum-bounding-box:height: 138
    minimum-bounding-box:unrotate: 0
    minimum-bounding-box:width: 96
    png:bKGD: chunk was found (see Background color, above)
    png:cHRM: chunk was found (see Chromaticity, above)
    png:gAMA: gamma=0.45455 (See Gamma, above)
    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: 139, 97
    png:sRGB: intent=0 (Perceptual Intent)
    png:text: 3 tEXt/zTXt/iTXt chunks were found
    signature: d40b11152ffe08839367333e59c2e375970adb7e05457af8022092ac1147bc21
...省略...

然后看看thumbnail的结果

stonemoe@desktop ~/Desktop> magick identify -verbose payload_thumb.png
...省略...
  Properties:
    date:create: 2020-04-17T19:51:41+00:00
    date:modify: 2020-04-17T19:51:41+00:00
    minimum-bounding-box:angle: 0
    minimum-bounding-box:area: 8284
    minimum-bounding-box:height: 109
    minimum-bounding-box:unrotate: 0
    minimum-bounding-box:width: 76
    png:bKGD: chunk was found (see Background color, above)
    png:cHRM: chunk was found (see Chromaticity, above)
    png:gAMA: gamma=0.45455 (See Gamma, above)
    png:IHDR.bit-depth-orig: 8
    png:IHDR.bit_depth: 8
    png:IHDR.color-type-orig: 6
    png:IHDR.color_type: 6 (RGBA)
    png:IHDR.interlace_method: 0 (Not interlaced)
    png:IHDR.width,height: 110, 77
    png:sRGB: intent=0 (Perceptual Intent)
    png:text: 10 tEXt/zTXt/iTXt chunks were found
    png:tIME: 2020-04-17T19:51:41Z
    signature: 0649a56200e5c9888c93a78b9b2951b3d1f64db16e824fa90fc865bd9a83450d
    software: https://imagemagick.org
    Thumb::Document::Pages: 1
    Thumb::Image::Height: 97
    Thumb::Image::Width: 139
    Thumb::Mimetype: image/jpeg
    Thumb::MTime: 1587152874
    Thumb::Size: 4327B
    Thumb::URI: file://payload.jpg
...省略...

可以看到resize保留了comment,而thumbnail把comment去掉了

0x03 EXIF

再看 EXIF,EXIF的定义很多,可以参考Exiv2 Exif Tags Reference

我们先选取部分String类型的数据添加一下

exiv2 -M "add Exif.GPSInfo.GPSMeasureMode fuzz" modify payload.jpg
exiv2 -M "add Exif.GPSInfo.GPSStatus fuzz" modify payload.jpg
exiv2 -M "add Exif.GPSInfo.GPSStatus fuzz" modify payload.jpg
exiv2 -M "add Exif.GPSInfo.GPSSatellites fuzz" modify payload.jpg
exiv2 -M "add Exif.GPSInfo.GPSLongitudeRef fuzz" modify payload.jpg
exiv2 -M "add Exif.GPSInfo.GPSLatitudeRef fuzz" modify payload.jpg
exiv2 -M "add Exif.Iop.RelatedImageFileFormat fuzz" modify payload.jpg
exiv2 -M "add Exif.Iop.InteroperabilityIndex fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.LensSerialNumber fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.LensSerialNumber fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.LensModel fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.LensMake fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.BodySerialNumber fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.CameraOwnerName fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.ImageUniqueID fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.RelatedSoundFile fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.SubSecTimeDigitized fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.SubSecTimeOriginal fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.SubSecTime fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.DateTimeDigitized fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.DateTimeOriginal fuzz" modify payload.jpg
exiv2 -M "add Exif.Photo.SpectralSensitivity fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.CameraLabel fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.ReelName fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.PreviewDateTime fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.CameraSerialNumber fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.UniqueCameraModel fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.ImageHistory fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.SecurityClassification fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.DateTimeOriginal fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.SpectralSensitivity fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.Copyright fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.ImageID fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.TargetPrinter fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.InkNames fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.HostComputer fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.Artist fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.DateTime fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.Software fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.Model fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.Make fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.ProcessingSoftware fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.DocumentName fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.ImageDescription fuzz" modify payload.jpg
exiv2 -M "add Exif.Image.ProcessingSoftware fuzz" modify payload.jpg

看看图片现在的状态

Image: payload.jpg
  Format: JPEG (Joint Photographic Experts Group JFIF format)
  Mime type: image/jpeg
  Class: DirectClass
  Geometry: 139x97+0+0
  Units: Undefined
  Colorspace: sRGB
  Type: TrueColor
  Base type: Undefined
  Endianess: Undefined
  Depth: 8-bit
  Channel depth:
    Red: 8-bit
    Green: 8-bit
    Blue: 8-bit
  Channel statistics:
    Pixels: 13483
    Red:
      min: 0  (0)
      max: 255 (1)
      mean: 93.4788 (0.366584)
      standard deviation: 69.5047 (0.272567)
      kurtosis: -0.519383
      skewness: 1.02065
      entropy: 0.839877
    Green:
      min: 0  (0)
      max: 255 (1)
      mean: 110.394 (0.43292)
      standard deviation: 41.334 (0.162094)
      kurtosis: 2.00187
      skewness: 1.46048
      entropy: 0.816047
    Blue:
      min: 0  (0)
      max: 244 (0.956863)
      mean: 118.63 (0.465215)
      standard deviation: 75.5964 (0.296456)
      kurtosis: -1.50408
      skewness: -0.589183
      entropy: 0.805144
  Image statistics:
    Overall:
      min: 0  (0)
      max: 255 (1)
      mean: 107.501 (0.421573)
      standard deviation: 62.145 (0.243706)
      kurtosis: -1.06579
      skewness: 0.236057
      entropy: 0.820356
  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)
  Matte color: grey74
  Background color: white
  Border color: srgb(223,223,223)
  Transparent color: none
  Interlace: None
  Intensity: Undefined
  Compose: Over
  Page geometry: 139x97+0+0
  Dispose: Undefined
  Iterations: 0
  Compression: JPEG
  Quality: 72
  Orientation: Undefined
  Convex hull: 0,0 138,0 138,95 136,96 0,96 0,0 
  Minimum bounding box: 138,0 138,96 0,96 -5.8783e-15,0 
  Profiles:
    Profile-exif: 884 bytes
  Properties:
    comment: im_so_happy
    date:create: 2020-04-17T20:04:46+00:00
    date:modify: 2020-04-17T20:04:46+00:00
    exif:Artist: fuzz
    exif:BodySerialNumber: fuzz
    exif:CameraOwnerName: fuzz
    exif:Copyright: fuzz
    exif:DateTime: fuzz
    exif:DateTimeDigitized: fuzz
    exif:DateTimeOriginal: fuzz
    exif:DocumentName: fuzz
    exif:ExifOffset: 452
    exif:GPSInfo: 764
    exif:GPSLatitudeRef: fuzz
    exif:GPSLongitudeRef: fuzz
    exif:GPSMeasureMode: fuzz
    exif:GPSSatellites: fuzz
    exif:GPSStatus: fuzz
    exif:ImageDescription: fuzz
    exif:ImageHistory: fuzz
    exif:ImageID: fuzz
    exif:ImageUniqueID: fuzz
    exif:InkNames: fuzz
    exif:InteroperabilityOffset: 722
    exif:LensMake: fuzz
    exif:LensModel: fuzz
    exif:LensSerialNumber: fuzz
    exif:Make: fuzz
    exif:Model: fuzz
    exif:RelatedSoundFile: fuzz
    exif:SecurityClassification: fuzz
    exif:Software: fuzz
    exif:SpectralSensitivity: fuzz
    exif:SubSecTime: fuzz
    exif:SubSecTimeDigitized: fuzz
    exif:SubSecTimeOriginal: fuzz
    exif:TargetPrinter: fuzz
    exif:thumbnail:InteroperabilityIndex: fuzz
    exif:thumbnail:RelatedImageFileFormat: fuzz
    jpeg:colorspace: 2
    jpeg:sampling-factor: 1x1,1x1,1x1
    minimum-bounding-box:angle: 0
    minimum-bounding-box:area: 13248
    minimum-bounding-box:height: 138
    minimum-bounding-box:unrotate: 0
    minimum-bounding-box:width: 96
    signature: d40b11152ffe08839367333e59c2e375970adb7e05457af8022092ac1147bc21
    unknown: fuzz
  Artifacts:
    verbose: true
  Tainted: False
  Filesize: 5215B
  Number pixels: 13483
  Pixels per second: 26.032MP
  User time: 0.000u
  Elapsed time: 0:01.000
  Version: ImageMagick 7.0.10-6 Q16 x86_64 2020-04-07 https://imagemagick.org

这里不知道为啥写了个unknown属性进去,不过无伤大雅

这些内容在文件中数据长这样 2020-04-17T20:08:46.png 这里可以看到数据被写在了文件的头部

再resize和thumbnail一下试试

stonemoe@desktop ~/Desktop> convert -size 110x110 payload.jpg payload_resize.png
stonemoe@desktop ~/Desktop> magick convert -thumbnail 110x110 payload.jpg payload_thumb.png

先看看resize的结果

stonemoe@desktop ~/Desktop> magick identify -verbose payload_resize.png
...省略...
  Properties:
    comment: im_so_happy
    date:create: 2020-04-17T20:13:41+00:00
    date:modify: 2020-04-17T20:13:41+00:00
    exif:Artist: fuzz
    exif:BodySerialNumber: fuzz
    exif:CameraOwnerName: fuzz
    exif:Copyright: fuzz
    exif:DateTime: fuzz
    exif:DateTimeDigitized: fuzz
    exif:DateTimeOriginal: fuzz
    exif:DocumentName: fuzz
    exif:ExifOffset: 452
    exif:GPSInfo: 764
    exif:GPSLatitudeRef: fuzz
    exif:GPSLongitudeRef: fuzz
    exif:GPSMeasureMode: fuzz
    exif:GPSSatellites: fuzz
    exif:GPSStatus: fuzz
    exif:ImageDescription: fuzz
    exif:ImageHistory: fuzz
    exif:ImageID: fuzz
    exif:ImageUniqueID: fuzz
    exif:InkNames: fuzz
    exif:InteroperabilityOffset: 722
    exif:LensMake: fuzz
    exif:LensModel: fuzz
    exif:LensSerialNumber: fuzz
    exif:Make: fuzz
    exif:Model: fuzz
    exif:RelatedSoundFile: fuzz
    exif:SecurityClassification: fuzz
    exif:Software: fuzz
    exif:SpectralSensitivity: fuzz
    exif:SubSecTime: fuzz
    exif:SubSecTimeDigitized: fuzz
    exif:SubSecTimeOriginal: fuzz
    exif:TargetPrinter: fuzz
    exif:thumbnail:InteroperabilityIndex: fuzz
    exif:thumbnail:RelatedImageFileFormat: fuzz
    minimum-bounding-box:angle: 0
    minimum-bounding-box:area: 13248
    minimum-bounding-box:height: 138
    minimum-bounding-box:unrotate: 0
    minimum-bounding-box:width: 96
    png:bKGD: chunk was found (see Background color, above)
    png:cHRM: chunk was found (see Chromaticity, above)
    png:gAMA: gamma=0.45455 (See Gamma, above)
    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: 139, 97
    png:sRGB: intent=0 (Perceptual Intent)
    png:text: 40 tEXt/zTXt/iTXt chunks were found
    signature: d40b11152ffe08839367333e59c2e375970adb7e05457af8022092ac1147bc21
    unknown: fuzz
...省略...

可以看到信息完好无损

并且因为我们resize为png格式,属性相关数据会被移动到文件末尾 2020-04-17T20:19:50.png 相关标准可以参考PNG Chunks Spec以及PNG Options and Extensions

再看看thumnail的结果

...省略...
  Properties:
    date:create: 2020-04-17T20:13:47+00:00
    date:modify: 2020-04-17T20:13:47+00:00
    exif:Artist: fuzz
    exif:BodySerialNumber: fuzz
    exif:CameraOwnerName: fuzz
    exif:Copyright: fuzz
    exif:DateTime: fuzz
    exif:DateTimeDigitized: fuzz
    exif:DateTimeOriginal: fuzz
    exif:DocumentName: fuzz
    exif:ExifOffset: 452
    exif:GPSInfo: 764
    exif:GPSLatitudeRef: fuzz
    exif:GPSLongitudeRef: fuzz
    exif:GPSMeasureMode: fuzz
    exif:GPSSatellites: fuzz
    exif:GPSStatus: fuzz
    exif:ImageDescription: fuzz
    exif:ImageHistory: fuzz
    exif:ImageID: fuzz
    exif:ImageUniqueID: fuzz
    exif:InkNames: fuzz
    exif:InteroperabilityOffset: 722
    exif:LensMake: fuzz
    exif:LensModel: fuzz
    exif:LensSerialNumber: fuzz
    exif:Make: fuzz
    exif:Model: fuzz
    exif:RelatedSoundFile: fuzz
    exif:SecurityClassification: fuzz
    exif:Software: fuzz
    exif:SpectralSensitivity: fuzz
    exif:SubSecTime: fuzz
    exif:SubSecTimeDigitized: fuzz
    exif:SubSecTimeOriginal: fuzz
    exif:TargetPrinter: fuzz
    exif:thumbnail:InteroperabilityIndex: fuzz
    exif:thumbnail:RelatedImageFileFormat: fuzz
    minimum-bounding-box:angle: 0
    minimum-bounding-box:area: 8284
    minimum-bounding-box:height: 109
    minimum-bounding-box:unrotate: 0
    minimum-bounding-box:width: 76
    png:bKGD: chunk was found (see Background color, above)
    png:cHRM: chunk was found (see Chromaticity, above)
    png:gAMA: gamma=0.45455 (See Gamma, above)
    png:IHDR.bit-depth-orig: 8
    png:IHDR.bit_depth: 8
    png:IHDR.color-type-orig: 6
    png:IHDR.color_type: 6 (RGBA)
    png:IHDR.interlace_method: 0 (Not interlaced)
    png:IHDR.width,height: 110, 77
    png:sRGB: intent=0 (Perceptual Intent)
    png:text: 47 tEXt/zTXt/iTXt chunks were found
    png:tIME: 2020-04-17T20:13:47Z
    signature: 0649a56200e5c9888c93a78b9b2951b3d1f64db16e824fa90fc865bd9a83450d
    software: https://imagemagick.org
    Thumb::Document::Pages: 1
    Thumb::Image::Height: 97
    Thumb::Image::Width: 139
    Thumb::Mimetype: image/jpeg
    Thumb::MTime: 1587153886
    Thumb::Size: 5215B
    Thumb::URI: file://payload.jpg
    unknown: fuzz
...省略...

可以看到jpeg comment没了,但是EXIF还在

看看文件内容长什么样 2020-04-17T20:29:21.png 看起来比resize的结果还少了一部分冗余数据

0x0? 试试效果

写一段人畜无害的php代码进去

stonemoe@desktop ~/Desktop> exiv2 -M "add Exif.Image.Copyright <?php echo \"php_is_happy\"; ?>" modify payload.jpg
stonemoe@desktop ~/Desktop> magick convert -thumbnail 110x110 payload.jpg payload_thumb.png

执行一下

stonemoe@desktop ~/Desktop> mv payload_thumb.png test.php
stonemoe@desktop ~/Desktop> php test.php | xxd | grep -2 php_is
00003bf0: 6572 4e61 6d65 0066 757a 7a31 7bc0 6000  erName.fuzz1{.`.
00003c00: 0000 2c74 4558 7465 7869 663a 436f 7079  ..,tEXtexif:Copy
00003c10: 7269 6768 7400 7068 705f 6973 5f68 6170  right.php_is_hap
00003c20: 7079 f9d4 5fce 0000 0012 7445 5874 6578  py.._.....tEXtex
00003c30: 6966 3a44 6174 6554 696d 6500 6675 7a7a  if:DateTime.fuzz

可以看到可以执行