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