本環(huán)境是蛇矛實驗室基于"火天網(wǎng)演攻防演訓(xùn)靶場"進行搭建,通過火天網(wǎng)演中的環(huán)境構(gòu)建模塊,可以靈活的對目標(biāo)網(wǎng)絡(luò)進行設(shè)計和配置,并且可以快速進行場景搭建和復(fù)現(xiàn)驗證工作。
環(huán)境準(zhǔn)備
圖片馬想要執(zhí)行需要的條件:
1.圖片可以上傳到目標(biāo)服務(wù)器上。
2.圖片可解析為PHP代碼。
要滿足第一個條件,可能的方式有文件上傳、遠程文件下載、SSRF等方式。
要滿足第二個條件可能的方式有上傳比如png格式木馬但是可以改名為php,從而解析為php;或者可以修改.htacces文件來控制這個php解析類型,使其支持解析png為php代碼執(zhí)行;或者是只能上傳png格式圖片,但是可以文件包含這個png來執(zhí)行php代碼。
當(dāng)然以上描述的情況這個跟目標(biāo)環(huán)境有關(guān)系。為了方便測試,我制作了一個docker鏡像。鏡像中web服務(wù)支持文件上傳,上傳時會檢查MIME類型是否為圖片,但是不檢查上傳文件的擴展名。這表示可以上傳一個圖片格式的.php文件到服務(wù)器上。在靶場中利用靶機拉取docker鏡像。
dockerpullordar/astrolock dockerrun-d-p80:80ordar/astrolock
PNG結(jié)構(gòu)
PNG的基本構(gòu)成為:
8字節(jié)頭文件+4字節(jié)數(shù)據(jù)長度+4字節(jié)數(shù)據(jù)標(biāo)識符+數(shù)據(jù)塊數(shù)據(jù)+4字節(jié)CRC校驗碼
PNG圖片由很多數(shù)據(jù)塊組成,每個數(shù)據(jù)塊包含了不同的信息。PNG定義了兩種類型的數(shù)據(jù)塊:
一種是稱為關(guān)鍵數(shù)據(jù)塊(critical chunk),這是標(biāo)準(zhǔn)的數(shù)據(jù)塊,每個PNG文件必須包含。
另一種叫做輔助數(shù)據(jù)塊(ancillary chunks),這是可選的數(shù)據(jù)塊。
關(guān)鍵數(shù)據(jù)塊
關(guān)鍵數(shù)據(jù)塊包括:文件頭數(shù)據(jù)塊、調(diào)色板數(shù)據(jù)塊、圖像數(shù)據(jù)塊和圖像結(jié)束數(shù)據(jù)塊
“89 50 4E 47 00 DA 1A 0A” png文件的標(biāo)識符
文件頭數(shù)據(jù)塊IHDR
調(diào)色板數(shù)據(jù)塊PLTE
圖像數(shù)據(jù)塊IDAT
圖像結(jié)束數(shù)據(jù)塊IEND
輔助數(shù)據(jù)塊
其余 18 個塊類型稱為輔助塊類型, 編碼器可以生成哪些,解碼器可以解釋。
透明度信息:tRNS色彩空間信息:cHRM、gAMA、iCCP、sBIT、sRGB、cICP文本信息:iTXt,tEXt,zTXt雜項信息:bKGD、hIST、pHYs、sPLT、eXIf時間信息:tIME動畫信息:acTL,fcTL,fdAT
部分?jǐn)?shù)據(jù)塊如下:
在PNG中注入PHP代碼
first-part 簡單插入
查看/first-part的源代碼,這非常簡單:網(wǎng)絡(luò)表單需要標(biāo)題、描述和有效的PNG文件。然后,應(yīng)用程序?qū)纳蟼鞯膱D像文件中獲取數(shù)據(jù),從原始文件名構(gòu)建一個唯一的文件名,并將文件簡單地存儲在文件系統(tǒng)中(suits_thumbnails目錄中,可公開訪問)。
# 創(chuàng)建“suit”對象并將其鏈接到Symfony表單 $suit = newSuit(); $form = $this->createForm(SuitType::class, $suit); $form->handleRequest($request); # 如果用戶定義了標(biāo)題、描述并上傳了JPG/PNG MIME類型的文件,則表單將提交并有效 if($form->isSubmitted() && $form->isValid()) { # 獲取通過表單上傳的文件,從原始文件名構(gòu)建一個唯一的文件名 $suitFile = $form->get('suit')->getData(); $originalFilename = $suitFile->getClientOriginalName(); $newFilename = uniqid().'_'.$originalFilename; # 將上傳的文件用唯一文件名存儲在Web服務(wù)器上的縮略圖目錄/suits_thumbnails/中 try{ $suitFile->move( $this->getParameter('thumbnails_directory'), $newFilename ); $suit->setSuitFilename($newFilename); } catch(FileException $e) { returnnewResponse("File exception"); } # 將“suit”對象存儲在數(shù)據(jù)庫中以在應(yīng)用程序中顯示 $entityManager->persist($suit); $entityManager->flush(); return$this->redirectToRoute('app_first_part'); }
方法一 直接拼接
網(wǎng)上搜索圖片馬制作的方法,很容易就能找到的方法就是這種。以下方法的效果是等效的。
1.文本方式打開圖片,然后末尾粘貼一句話木馬;
2.cmd中 copy 1.png /b + 2.php 3.png;
3.使用echo命令追加代碼到png圖片末尾。
這種方法已經(jīng)爛大街了,這里就不多說了,懂得都懂。在png圖像結(jié)束塊IEND后面緊跟著phpinfo。
方法二 預(yù)定義文本塊注入
PNG 圖像格式允許向文件添加注釋以存儲一些meta data數(shù)據(jù)。通過查閱PNG文檔可以找到已經(jīng)定義的文本塊關(guān)鍵字。
當(dāng)然這些關(guān)鍵字是可以修改的,通過下載工具https://exiftool.org/來設(shè)置這些預(yù)定義關(guān)鍵字。
當(dāng)然它是可以成功解析的,可以看到文本塊copyright后面緊接著就是phpinfo。
PHP-GD庫的壓縮圖像
大多數(shù)時候,圖像文件不會像/first-part所假設(shè)的那樣按原樣存儲在服務(wù)器上。而是使用一些標(biāo)準(zhǔn)的PHP 庫(如 PHP-GD)將圖像調(diào)整大小、壓縮或編碼為特定的文件格式。
/second-part 路徑為攻擊者實現(xiàn)了一個稍微更現(xiàn)實、也更困難的場景。在存儲用戶上傳的文件之前,應(yīng)用程序?qū)⑹褂?PHP-GD 函數(shù) imagepng 壓縮所有上傳到 Web 服務(wù)器的圖像。
# 創(chuàng)建“suit”對象并將其鏈接到Symfony表單 $suit = newSuit(); $form = $this->createForm(SuitType::class, $suit); $form->handleRequest($request); # 如果用戶定義了標(biāo)題、描述并上傳了JPG/PNG MIME類型的文件,則表單將提交并有效 if($form->isSubmitted() && $form->isValid()) { # 從表單中獲取上傳文件 $suitFile = $form->get('suit')->getData(); # 從原始文件名生成唯一的文件名 $originalFilename = $suitFile->getClientOriginalName(); $newFilename = uniqid().'_'.$originalFilename; try{ # 壓縮上傳的PNG(gzip庫的第9級)并保存 $source = imagecreatefrompng($suitFile->getPathName()); imagepng($source, $this->getParameter('thumbnails_directory').'/'.$newFilename, 9); $suit->setSuitFilename($newFilename); } catch(FileException $e) { returnnewResponse("Exception in image processing"); } # 將“suit”對象存儲在數(shù)據(jù)庫中以在應(yīng)用程序中顯示 $entityManager->persist($suit); $entityManager->flush(); return$this->redirectToRoute('app_second_part'); }
方法三 PLTE塊注入
壓縮 PNG 文件時,PHP-GD將刪除輔助塊以減小輸出文件的大小。這就是為什么我們在第一部分中注入的PHP payload無法在壓縮過程中幸存下來。
但是,如果我們可以將我們的payload注入到 PNG 文件的關(guān)鍵塊中呢?當(dāng)然,這些塊在壓縮圖像時不會被破壞。執(zhí)行這種注入的完美候選者是 PLTE 塊,這是一個包含 PNG 圖像的“調(diào)色板”的關(guān)鍵塊,即顏色列表。根據(jù) PNG 規(guī)范:
PLTE 塊包含 1 到 256 個調(diào)色板條目,每個條目都是三字節(jié)的形式:
Red: 1byte(0= black, 255= red) Green: 1byte(0= black, 255= green) Blue: 1byte(0= black, 255= blue)
使用PLTE塊,我們可能有256*3字節(jié)可用于將我們的payload注入到這樣一個關(guān)鍵塊中,這應(yīng)該綽綽有余。唯一的限制是有效載荷的長度必須能被3整除。
使用下面的腳本,可以輕松創(chuàng)建PLTE塊注入payload的png圖像。
執(zhí)行腳本:
php gen.php '' png-plte-inject.php
使用PLTE注入可以繞過PHP-GD庫的壓縮,可以成功上傳并且成功解析。
PHP-GD庫調(diào)整圖像大小
Web 應(yīng)用程序在處理圖像時執(zhí)行的另一個標(biāo)準(zhǔn)操作是調(diào)整它們的大小以標(biāo)準(zhǔn)化它們的格式。為此,應(yīng)用程序可以例如使用PHP-GD庫imagecopyresized函數(shù)或 imagecopyresampled函數(shù)。
/third-part路由實現(xiàn)了目標(biāo)應(yīng)用程序在存儲輸入PNG 文件之前對其進行壓縮和調(diào)整大小的場景。
# 創(chuàng)建“suit”對象并將其鏈接到Symfony表單 $suit = newSuit(); $form = $this->createForm(SuitType::class, $suit); $form->handleRequest($request); # 如果用戶定義了標(biāo)題、描述并上傳了JPG/PNG MIME類型的文件,則表單將提交并有效 if($form->isSubmitted() && $form->isValid()) { # 從表單中獲取上傳文件 $suitFile = $form->get('suit')->getData(); # 從原始文件名生成唯一的文件名 $originalFilename = $suitFile->getClientOriginalName(); $newFilename = uniqid().'_'.$originalFilename; try{ # 壓縮上傳的PNG(gzip庫的第9級),調(diào)整大小并保存 $filename = $suitFile->getPathName(); list($width, $height) = getimagesize($filename); $source = imagecreatefrompng($filename); $thumb = imagecreatetruecolor(55, 55); imagecopyresampled($thumb, $source, 0, 0, 0, 0, 55, 55, $width, $height); imagepng($thumb, $this->getParameter('thumbnails_directory').'/'.$newFilename); $suit->setSuitFilename($newFilename); } catch(FileException $e) { returnnewResponse("Exception in image processing"); } # 將“suit”對象存儲在數(shù)據(jù)庫中以在應(yīng)用程序中顯示 $entityManager->persist($suit); $entityManager->flush(); return$this->redirectToRoute('app_third_part'); }
方法四 IDAT塊注入
調(diào)整圖像大小時,即使是 PLTE關(guān)鍵塊的內(nèi)容也會被破壞,我們的payload也會隨之破壞。這些函數(shù)實際上只使用原始文件中的像素數(shù)據(jù)來創(chuàng)建一個全新的圖像。關(guān)鍵數(shù)據(jù)塊或輔助數(shù)據(jù)塊中包含的數(shù)據(jù)都可能會被忽略。
將PHP載荷注入PNG文件的一種相當(dāng)復(fù)雜但有效的方法是將其編碼為PNG的IDAT塊。
當(dāng)將原始圖像保存為PNG時,圖像的每一行都按字節(jié)進行過濾,并且該行前綴有一個數(shù)字,該數(shù)字描述了所使用的過濾器類型(0x01到0x05),不同的行可以使用不同的過濾器。這背后的基本原理是提高壓縮比。過濾完所有行后,它們都使用 DEFLATE 算法壓縮以形成 IDAT 塊。
要生成一個包含有效PHP代碼的IDAT 塊,應(yīng)該找到原始像素的精確組合,這些像素一旦被 PNG線過濾器和DEFLATE算法處理,就會輸出所需的有效載荷。
字符串不能包含任何重復(fù)的代碼塊,否則它們將被壓縮。
雖然很難,但確實是可以實現(xiàn)的。
可以使用以下腳本來生成110x110的PNG圖像,一旦調(diào)整為55x55,IDAT匯總將包含 PHP 代碼:=$_GET[0]($_POST[1]);?>
此腳本生成惡意 PNG 圖像:
phpgenerate_idat_png.php> png-idat-inject.php
這時候?qū)⑸傻膱D像通過/third-part路由上傳,就可以觸發(fā)webshell。
通過發(fā)送POST請求來觸發(fā)php執(zhí)行。
IMAGICK庫調(diào)整圖像大小
除了PHP-GD之外,最流行的圖像處理庫之一是Imagick,它是ImageMagick的PHP實現(xiàn)。/fourth-part路由說明了一個應(yīng)用程序,它使用thumbnailImage功能調(diào)整用戶上傳的文件的大小。此函數(shù)用于產(chǎn)生較小的low-cost縮略圖圖像,適合在Web上顯示。
# 創(chuàng)建“suit”對象并將其鏈接到Symfony表單 $suit = newSuit(); $form = $this->createForm(SuitType::class, $suit); $form->handleRequest($request); # 如果用戶定義了標(biāo)題、描述并上傳了JPG/PNG MIME類型的文件,則表單將提交并有效 if($form->isSubmitted() && $form->isValid()) { # 從表單中獲取上傳文件 $suitFile = $form->get('suit')->getData(); # 從原始文件名生成唯一的文件名 if($suitFile) { $originalFilename = $suitFile->getClientOriginalName(); $newFilename = uniqid().'_'.$originalFilename; try{ # 使用Imagick將文件轉(zhuǎn)換為100x100縮略圖 $filename = $suitFile->getPathName(); $imgck = newImagick($filename); $imgck->thumbnailImage(55, 55, true, true); $imgck->writeImage($this->getParameter('thumbnails_directory')."/".$newFilename); $suit->setSuitFilename($newFilename); } catch(Exception$e) { returnNewResponse("Exception in image processing"); } } #將“suit”對象存儲在數(shù)據(jù)庫中以在應(yīng)用程序中顯示 $entityManager->persist($suit); $entityManager->flush(); return$this->redirectToRoute('app_fourth_part'); }
方法五 自定義文本塊注入
當(dāng)Imagick調(diào)整圖像大小時,它實際上會對文本塊執(zhí)行幾個操作:
擦除標(biāo)記為“Comment”的tEXt塊。
覆蓋以下tEXt塊的值(或者如果它們不存在則定義它們):date:create, date:modify, software, Thumb::Pages, Thumb::Height, Thumb::Width, Thumb::Mimetype, Thumb::MTime, Thumb::Size, Thumb::URI。
保留任何其他tEXt塊的原始值(包括不存在預(yù)定義關(guān)鍵字的tEXt塊)。
在方法二的PNG注釋中,我們已經(jīng)知道了預(yù)定義的tEXt塊的預(yù)定義關(guān)鍵字有如下這些:
因為我們修改的是copyright塊,所以是可以繞過thumbnailImage方法的處理的。
使用方法二的圖像,可以成功執(zhí)行。如下圖,可以看到它并不是緊跟在tEXt塊后面執(zhí)行。
除了以上預(yù)定義關(guān)鍵字,我們還可以自定義關(guān)鍵字。通過以下簡單的python代碼可以實現(xiàn)。
# -*- coding: utf-8 -*- # @Author : ordar # @Python: 3.7.5 fromPIL importImage fromPngImagePlugin importPngInfo im = Image.open("png/png.png") p = PngInfo() # PngInfo類有兩個方法add_itxt和add_text,這兩個方法實現(xiàn)的效果是一樣的。 # iTXt是國際文本數(shù)據(jù);iEXt是文本信息數(shù)據(jù)塊 # p.add_itxt(b"Aaaaa", b"") p.add_text(b"Aaaaa", b"") im.save("png/png-text-custom.png",pnginfo=p)
用exiftool工具查看圖片信息,可以發(fā)現(xiàn)Aaaaa文本信息成功添加進去了。
當(dāng)然也可以解析執(zhí)行。
總結(jié)
當(dāng)服務(wù)器可以將圖像文件解釋為PHP時,例如弱擴展檢查、解析漏洞、本地文件包含漏洞、錯誤配置PHP解析擴展等方式,可以使用這些技術(shù)來造成代碼執(zhí)行。
蛇矛實驗室成立于2020年,致力于安全研究、攻防解決方案、靶場對標(biāo)場景仿真復(fù)現(xiàn)及技戰(zhàn)法設(shè)計與輸出等相關(guān)方向。團隊核心成員均由從事安全行業(yè)10余年經(jīng)驗的安全專家組成,團隊目前成員涉及紅藍對抗、滲透測試、逆向破解、病毒分析、工控安全以及免殺等相關(guān)領(lǐng)域。
審核編輯:湯梓紅
-
服務(wù)器
+關(guān)注
關(guān)注
12文章
9338瀏覽量
86159 -
代碼
+關(guān)注
關(guān)注
30文章
4841瀏覽量
69147 -
PHP
+關(guān)注
關(guān)注
0文章
454瀏覽量
26812
原文標(biāo)題:PHP代碼執(zhí)行-PNG注入
文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
什么是PHP?什么是PHPer?
PHP從入門到精通(免費)
PHP實現(xiàn)定時任務(wù)的幾種方法詳解
CVE漏洞—PHPCMS2008 /type.php代碼注入高危漏洞預(yù)警
PHP如何打開一個頁面執(zhí)行另一個頁面的代碼
![<b class='flag-5'>PHP</b>如何打開一個頁面<b class='flag-5'>執(zhí)行</b>另一個頁面的<b class='flag-5'>代碼</b>](https://file.elecfans.com/web1/M00/89/3C/o4YBAFyKHR-ARxZDAAGYt0tpCA8511.png)
PHP程序設(shè)計高級教程的源代碼合集免費下載
![<b class='flag-5'>PHP</b>程序設(shè)計高級教程的源<b class='flag-5'>代碼</b>合集免費下載](https://file.elecfans.com/web1/M00/B1/69/pIYBAF34KKiAFA9LAAN3TQfAWeQ202.png)
評論