php imagecreatefrom* 系列函式之 png

wyzsk發表於2020-08-19
作者: janes · 2016/05/20 9:48

0x00 簡介


這篇文章主要分析 php 使用 GD 庫的 imagecreatefrompng() 函式重建 png 圖片可能導致的本地檔案包含漏洞。

當系統存在檔案包含的點,能包含圖片檔案; 另外系統存在圖片上傳,上傳的圖片使用 imagecreatefrompng() 函式重建圖片並儲存在本地,則很可能出現檔案包含的漏洞。

通常,系統在實現圖片上傳功能時,為了防範使用者上傳含有惡意 php 程式碼的圖片,可採用 gd 庫重建圖片,gd 庫重建圖片的一系列函式 imagecreatefrom*,會檢查圖片規範,驗證圖片合法性,以此抵禦圖片中含有惡意 php 程式碼的攻擊。

那麼, imagecreatefrom* 系列函式是否能完全抵禦圖片中插入 php 程式碼的攻擊呢,本文以 imagecreatefrompng() 函式作為研究物件,探討實現重建 png 格式的圖片中包含惡意 php 程式碼的可能性,以及所需要滿足的條件。

png 檔案格式, imagecreatefrompng 函式解析, 修改圖片, 上傳, 檔案包含 ...

0x01 png 圖片格式

要實現重建的 png 圖片中仍包含有惡意的 php 程式碼, 首先要對 png 圖片格式有基本的瞭解。png 支援三種影像型別:索引彩色影像(index-color images),灰度影像(grayscale images),真彩色影像(true-color images), 其中索引彩色影像也稱為基於調色盤影像(Palette-based images)。

標準的 png 檔案結構由一個 png 標識頭連線多個 png 資料塊組成,如: png signature | png chunk | png chunk | ... | png chunk.

png 標識

png 標識作為 png 圖片的頭部,為固定的 8 位元組,如下

89 50 4E 47 OD 0A 1A 0A

png 資料塊

png 定義了兩種型別的資料塊,一種是稱為關鍵資料塊(critical chunk),標準的資料塊; 另一種叫做輔助資料塊(ancillary chunks),可選的資料塊。關鍵資料塊定義了3個標準資料塊,每個 png 檔案都必須包含它們。3個標準資料塊為: IHDR, IDAT, IEND.

這裡介紹4個資料塊:IHDR, PLTE, IDAT, IEND

png 資料塊結構

png 檔案中,每個資料塊由4個部分組成 length | type(name) | data | CRC, 說明如下

length: 4 bytes, just length of the data, not include type and CRC  
type: 4 bytes, ASCII letters([A-Z,a-z])
CRC: 4bytes

CRC(cyclic redundancy check)域中的值是對Chunk Type Code域和Chunk Data域中的資料進行計算得到的。CRC具體演算法定義在ISO 3309和ITU-T V.42中,其值按下面的CRC碼生成多項式進行計算: x+x+x+x+x+x+x+x+x+x+x+x+x+x+1

  • IHDR

檔案頭資料塊IHDR(header chunk):它包含有PNG檔案中儲存的影像資料的基本資訊,並要作為第一個資料塊出現在PNG資料流中,而且一個PNG資料流中只能有一個檔案頭資料塊。

檔案頭資料塊由13位元組組成,它的如下所示

域的名稱 位元組數 說明
Width 4 bytes 影像寬度,以畫素為單位
Height 4 bytes 影像高度,以畫素為單位
Bit depth 1 byte 影像深度. 索引彩色影像: 1,2,4或8 灰度影像: 1,2,4,8或16 真彩色影像: 8或16
ColorType 1 byte 顏色型別. 0:灰度影像, 1,2,4,8或16 2:真彩色影像,8或16 3:索引彩色影像,1,2,4或8 4:帶α通道資料的灰度影像,8或16 6:帶α通道資料的真彩色影像,8或16
Compression method 1 byte 壓縮方法(LZ77派生演算法)
Filter method 1 byte 濾波器方法
Interlace method 1 byte 隔行掃描方法. 0:非隔行掃描 1: Adam7(由Adam M. Costello開發的7遍隔行掃描方法)
  • PLTE

調色盤資料塊PLTE(palette chunk)包含有與索引彩色影像(indexed-color image)相關的彩色變換資料,它僅與索引彩色影像有關,而且要放在影像資料塊(image data chunk)之前。

PLTE資料塊是定義影像的調色盤資訊,PLTE可以包含1~256個調色盤資訊,每一個調色盤資訊由3個位元組組成:

顏色 位元組 意義
Red 1 byte 0 = 黑色, 255 = 紅
Green 1 byte 0 = 黑色, 255 = 綠色
Blue 1 byte 0 = 黑色, 255 = 藍色

因此,調色盤的長度應該是3的倍數,否則,這將是一個非法的調色盤。顏色數 = length/3

對於索引影像,調色盤資訊是必須的,調色盤的顏色索引從0開始編號,然後是1、2……,調色盤的顏色數不能超過色深中規定的顏色數(如影像色深為4的時候,調色盤中的顏色數不可以超過2^4=16),否則,這將導致PNG影像不合法。

  • IDAT

影像資料塊IDAT(image data chunk):它儲存實際的資料,在資料流中可包含多個連續順序的影像資料塊。IDAT存放著影像真正的資料資訊

  • IEND

影像結束資料IEND(image trailer chunk):它用來標記PNG檔案或者資料流已經結束,並且必須要放在檔案的尾部。

正常情況下, png 檔案的結尾為如下12個字元:

00 00 00 00 49 45 4E 44 AE 42 60 82

由於資料塊結構的定義,IEND資料塊的長度總是0(00 00 00 00,除非人為加入資訊),資料標識總是IEND(49 45 4E 44),因此,CRC碼也總是AE 42 60 82

0x02 php imagecreatefrompng() 函式


有了對 png 圖片格式的基本瞭解,可以幫助我們更好的理解 imagecreatefrompng() 函式的底層實現。分析 php 原始碼(php 5.6.20)可知, php imagecreatefrompng() 函式實現重建圖片,核心是 gd 庫的 gdImageCreateFromPngCtx() 函式。

分析 gd 庫中的 gdImageCreateFromPngCtx() 函式可知,函式首先會檢測 png signature, 不合法則返回NULL。然後會讀原始的 png 圖片檔案給 png_ptr, 再從 png_ptr 中讀圖片資訊到 info_ptr,再之後就是獲取 IHDR 資訊,讀 IDAT 資料等,這裡不一一討論。這裡僅討論 png_read_info() 函式中對讀 PLTE 資料庫的驗證處理。

#!c
gd_png.c/gdImageCreateFromPngCtx
{
...
if (png_sig_cmp(sig, 0, 8) != 0) { /* bad signature */
        return NULL;        /* bad signature */
}   

... 

png_set_read_fn (png_ptr, (void *) infile, gdPngReadData);
png_read_info (png_ptr, info_ptr);  /* read all PNG info up to image data */
...
}

要了解 png_read_info() 的內部實現,可以透過讀 libpng 的原始碼(libpng 1.6.21)進行了解。當圖片型別是索引影像時,png_read_info() 讀到 PLTE chunk 時會呼叫 png_handle_PLTE 函式進行 CRC 校驗

#!c
pngread.c/png_read_info
{
...
else if (chunk_name == png_PLTE)
         png_handle_PLTE(png_ptr, info_ptr, length);
...
}   

pngrutil.c/png_handle_PLTE 
{
...
#ifndef PNG_READ_OPT_PLTE_SUPPORTED
   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
#endif
   {
      png_crc_finish(png_ptr, (int) length - num * 3);
   }
...
}

分析底層原始碼可知, png signature 是不可能插入 php 程式碼的; IHDR 儲存的是 png 的圖片資訊,有固定的長度和格式,程式會提取圖片資訊資料進行驗證,很難插入 php 程式碼;而 PLTE 主要進行了 CRC 校驗和顏色數合法性校驗等簡單的校驗,那麼很可能在 data 域插入 php 程式碼。

從對 PLTE chunk 驗證的分析可知, 當原始圖片格式給索引圖片時,PLTE 資料塊在滿足 png 格式規範的情況下,程式還會進行 CRC 校驗。因此,要將 PHP 程式碼寫入 PLTE 資料塊,不僅要修改 data 域的內容為php程式碼,然後修改 CRC 為正確的 CRC 校驗值,當要填充的程式碼過長時,可以改變 length 域的數值,滿足 length 為3的倍數, 且顏色數不超過色深中規定的顏色數。例如: IHDR 資料塊中 Bit depth 為 08, 則最大的顏色數為 2^8=256, 那麼 PLTE 資料塊 data 的長度不超過 3*256=0x300。 這個長度對寫入 php 一句話木馬或者建立後門檔案足夠了。

那麼是不是所有 png 圖片都可以在 PLTE 資料塊插入 php 程式碼呢?下面透過實驗予以說明。

0x03 實驗驗證


png 支援索引彩色影像(index-color images),灰度影像(grayscale images),真彩色影像(true-color images)三種型別的圖片,而 PLTE 資料塊是索引影像所必須的,因此索引影像極有可能在 PLTE 資料塊插入 php 程式碼。

下面摘錄 gd 庫中 gdImageCreateFromPng() 函式的一段說明

If the PNG image being loaded is a truecolor image, the resulting
gdImagePtr will refer to a truecolor image. If the PNG image being
loaded is a palette or grayscale image, the resulting gdImagePtr
will refer to a palette image.

函式將索引彩色影像和灰度影像轉換為索引彩色影像, 將真彩色影像轉換為真彩色影像。下面分別轉換這三種型別的圖片,測試圖片地址: 圖片 . php程式碼如下

#!php
<?php
$pngfile = 'test.png';
$newpngfile = 'new.png';
$im = imagecreatefrompng($pngfile);
imagepng($im,$newpngfile);
?>
  • 索引影像

索引影像

讀 IHDR 資料塊資訊,色深為8bits, color type=0x03, 為索引影像型別,改變其 PLTE 資料塊如下,修改資料為 <?php phpinfo();?>

修改資料為php程式碼

計算 CRC 過程如下

計算 CRC

imagecreatefrompng() 重建的圖片如下

new png

可以看出重建的圖片中 PLTE 資料塊保留了 php 程式碼,重建也增加了 pHYs 資料塊,對我們所關心的結果並沒有影響。說明插入 php 程式碼成功。

  • 灰度影像

原始影像, 不含 PLTE 資料塊, 如下所示

灰度影像

插入 PLTE 資料塊,並寫入 php 程式碼

add PLTE chunk

重建後的圖片如下

new png

可以看出重建的圖片轉換為 索引影像型別,並且重寫了 PLTE 資料塊,寫入 php 程式碼失敗

  • 真彩色影像

原始影像, 不含 PLTE 資料塊, 如下所示

真彩色影像

插入 PLTE 資料塊,並寫入 php 程式碼

add PLTE chunk

重建後的圖片如下

new png

可以看出真彩色型別圖片重建後的圖片不含 PLTE 資料塊,寫入 php 程式碼失敗

0x04 總結


透過以上分析和實驗可知, imagecreatefrompng() 函式並不能完全防止圖片中插入 php 程式碼, 當圖片型別為索引影像時, 在 PLTE chunk 可以成功插入 php 程式碼, 而另外其他型別的圖片並不能實現 PLTE chunk 中插入 php 程式碼。最後, 一併感謝下面參考資料對我研究的幫助。

附,修改索引影像插入 php 程式碼的地址 github

所附程式碼實現了當 payload 長度大於 PLTE 資料長度時, 會重寫 PLTE 資料塊。然而 在實驗過程中發現,imagecreatepng()函式重建的圖片 PLTE 資料塊的長度仍為原始的長度,即並不能隨意擴充 PLTE 資料塊的長度,具體原因還需深入分析原始碼, 也就是說要載入的 payload 不能超過 PLTE 資料塊所給的長度。

通常情況下, PLTE 資料塊所給的長度可以滿足我們插入基本的 php 後門程式碼,存在了那麼一個點,是不是可以撬動地球了呢

當然本文還有許多不足之處,望大家批評指正

0x05 資料參考


1.github libgd

2.gd2.0.33 manual

3.libgd home

4.png book

5.png chunk

6.png 檔案格式解析

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章