Jump to content

2021年11月3日,Zero Day Initiative Pwn2Own宣布,NCC Group EDG(Exploit Development Group)在MC3224i打印機固件中發現了一個遠程漏洞,攻擊者可以利用該漏洞獲得該設備的完全控制權限。請注意,這裡的打印機運行的是最新版本的固件CXLBL.075.272。

Lexmark MC3224i是一款廣受歡迎的多合一彩色激光打印機,在各個電商網站上都賣的很火,所以,Austin 2021 Pwn2Own也將其列為安全測試對象之一。

目前,該漏洞已經得到了修復。在下一篇文章中,我們將詳細介紹該漏洞的詳情以及相應的利用方法。

由於Lexmark公司對提供給消費者的固件更新包進行了加密處理,這無疑提高了相關二進制代碼的分析難度。由於我們的研究時間只有一個多月,而且關於目標的參考資料基本為零,所以,我們決定拆下閃存,並使用編程器來提取其中的固件,因為我們(正確地)估計固件是以未加密的形式存儲的。這樣做的好處是,可以避開固件更新包加密所造成的障礙。提取固件後,可以對二進製文件進行逆向分析,以檢查其中是否存在遠程代碼執行漏洞。

從閃存中提取固件PCB概述我們發現,主印刷電路板(PCB)位於打印機的左側。該設備由專為打印機行業設計的Marvell 88PA6220-BUX2片上系統(SoC)進行供電的,而固件則存儲在Micron MT29F2G08ABAGA NAND閃存(2Gb,即256MB)中。並且,NAND閃存可以很容易地在PCB的左下方找到:

1.png

串行輸出之後,我們很快就找到了UART連接器,因為它在PCB上被標為JRIP1,具體如下圖所示:

1.png

之後,我們焊接了三根線,分別用於:

查看啟動日誌,通過觀察設備的分區信息了解閃存的佈局情況。

掃描啟動日誌,看看是否有跡象表明打印機進行了軟件簽名驗證。

希望能在引導加載程序(U-Boot)或操作系統(Linux)中獲得一個shell。

打印機啟動過程中,串行輸出(115200波特)的內容如下所示:

SiGe2-RevB3.3.22-9h121425

TIME=TueMar1021:02:362020;COMMIT=863d60b

uidc

FailureEnablingAVSworkaroundon88PG870

settingAVSVoltageto1050

Bank5Reg2=0x0000381E,VoltBin=0,efuseEscape=0

AVSefuseValues:

EfuseProgramed=1

LowVDDLimit=32

HighVDDLimit=32

TargetDRO=65535

SelectVsense0=0

a

CallingConfigure_Flashes@0xFFE010A812FE13E0026800

fves

DDR3400MHz1x164Gbit

rSHAcomparePassed0

SHAcomparePassed0

l

LaunchAPCore0@0x00100000

U-Boot2018.07-AUTOINC+761a3261e9(Feb282020-23:26:43+0000)

DRAM:512MiB

NAND:256MiB

MMC:mv_sdh:0,mv_sdh:1,mv_sdh:2

lxk_gen2_eeprom_probe:123:Nopaneleepromoptionfound.

lxk_panel_notouch_probe_gen2:283:paneluicctype68,hwvers19,panelid98,displaytype11,firmwarev4.5,lvds4

foundsmpndisplayTM024HDH49/ILI9341default

lcd_lvds_pll_init:Requestingdotclk=40000000Hz

foundsmpndisplayYeebo2.8B

ubi0:defaultfastmappoolsize:100

ubi0:defaultfastmapWLpoolsize:50

ubi0:attachingmtd1

ubi0:attachedbyfastmap

ubi0:fastmappoolsize:100

ubi0:fastmapWLpoolsize:50

ubi0:attachedmtd1(name'mtd=1',size253MiB)

ubi0:PEBsize:131072bytes(128KiB),LEBsize:126976bytes

ubi0:min./max.I/Ounitsizes:2048/2048,sub-pagesize2048

ubi0:VIDheaderoffset:2048(aligned2048),dataoffset:4096

ubi0:goodPEBs:2018,badPEBs:8,corruptedPEBs:0

ubi0:uservolume:7,internalvolumes:1,max.volumescount:128

ubi0:max/meanerasecounter:2/1,WLthreshold:4096,imagesequencenumber:0

ubi0:availablePEBs:0,totalreservedPEBs:2018,PEBsreservedforbadPEBhandling:32

Loadingfile'/shared/pm/softoff'toaddr0x1f6545d4.

UnmountingUBIFSvolumeInternalStorage!

Carddidnotrespondtovoltageselect!

bootcmd:setenvcramfsaddr0x1e900000;ubiread0x1e900000Kernel0xa67208;sha256verify0x1e9000000x1f3670001;cramfsload0x100000/main.img;source0x100000;loop.l0xd00000001

Read10908168bytesfromvolumeKernelto1e900000

Codeauthenticationsuccess

###CRAMFSloadcomplete:2165bytesloadedto0x100000

##Executingscriptat00100000

###CRAMFSloadcomplete:4773416bytesloadedto0xa00000

###CRAMFSloadcomplete:4331046bytesloadedto0x1600000

##BootingkernelfromLegacyImageat00a00000.

ImageName:Linux-4.17.19-yocto-standard-74b

ImageType:ARMLinuxKernelImage(uncompressed)

DataSize:4773352Bytes=4.6MiB

LoadAddress:00008000

EntryPoint:00008000

##LoadinginitRamdiskfromLegacyImageat01600000.

ImageName:initramfs-image-granite2-2020063

ImageType:ARMLinuxRAMDiskImage(uncompressed)

DataSize:4330982Bytes=4.1MiB

LoadAddress:00000000

EntryPoint:00000000

##FlattenedDeviceTreeblobat01500000

Bootingusingthefdtblobat0x1500000

LoadingKernelImage.OK

UsingDeviceTreeinplaceat01500000,end01516aff

UPDATINGDEVICETREEWITHst:1fec4000sz:12c000

Startingkernel.

BootingLinuxonphysicalCPU0xffff00

Linuxversion4.17.19-yocto-standard-74b7175b2a3452f756ffa76f750e50db(oe-user@oe-host)(gccversion7.3.0(GCC))#1SMPPREEMPTMonJun2919:46:01UTC2020

CPU:ARMv7Processor[410fd034]revision4(ARMv7),cr=30c5383d

CPU:divinstructionsavailable:patchingdivisioncode

CPU:PIPT/VIPTnonaliasingdatacache,VIPTaliasinginstructioncache

OF:fdt:Machinemodel:mv6220Lionfish00dL

earlycon:early_pxa0atMMIO320x00000000d4030000(options'')

bootconsole[early_pxa0]enabled

FIXignoringexception0xa11addr=fb7ffffeswapper/0:1

.根據我們在審查其他設備方面的經驗來說,有時可以通過訪問UART引腳獲得完整的Linux shell。不過,在MC3224i設備上,UART RX引腳似乎沒有被啟用,因此,我們只能查看引導日誌,而無法與系統進行交互。它可能是通過SoC上的E型保險絲來禁用引腳的。或者,零歐姆電阻器可能已經從生產設備的PCB上移除,在這種情況下,我們就有機會重新啟用它。由於我們的主要目標是拆除閃存並提取固件,所以,我們沒有進一步研究這個引腳。

從閃存中轉儲固件拆除和轉儲(或重新編程)閃存比大多數人想像中的要容易得多,而且這樣做的好處有很多:它通常允許我們啟用調試功能,獲得對Shell的訪問權,讀取敏感密鑰,並在某些情況下繞過固件簽名驗證。在我們的案例中,我們的目標是提取文件系統,並對二進製文件進行逆向工程,因為Pwn2Own規則明確規定,只有遠程執行的漏洞才能被接受。不過,對exploit開發工作來說,則沒有任何限制。重要的是,要把exploit的開發和執行看作是不同的工作。雖然執行過程決定了攻擊的可擴展性和攻擊者的成本,但利用代碼的開發過程(或NRE)只需要一次,因此,即使我們在後者上面消耗了大量的時間和設備,也不會對執行工作造成影響。而防御者的工作,就增加exploit的執行難度。

借助於熱風機之類的工具,我們可以輕鬆將閃存拆下來。在清洗完引腳後,我們使用帶有TSOP-48適配器的TNM5000編程器來讀取閃存的內容。首先,我們要確保閃存正確連接到適配器上,選擇正確的閃存標識符,然後,我們就可以讀取閃存的全部內容,並將其保存到文件中了。重新安裝閃存時,也需要仔細進行,以確保設備的功能不受影響。整個過程大約花了一個小時,包括在顯微鏡下測試連接。萬幸的是,打印機成功地啟動了! 好了,這部分工作就大功告成了……

1.png

轉儲的閃存映像的長度正好是285,212,672字節,很明顯,這比256MB(268,435,456字節)還要長。這是因為,在讀取閃存的原始讀取時,還包括備用區域,也被稱為頁面OOB(帶外)數據區域。下面的內容來自Micron公司的說明文檔:

內部ECC對於主要區域的每528字節(x8)和備用區域的每16字節(x8)提供了9位檢測碼和8位校正碼。 [.]

在PROGRAM操作過程中,在頁面被寫入NAND Flash陣列之前,設備會在緩存寄存器中的2k頁面上計算ECC代碼。 ECC代碼被存儲在頁面的備用區域。

在讀操作中,頁面數據從陣列中被讀到緩存寄存器中,在那裡ECC代碼被計算出來,並與從陣列中讀取的ECC代碼值進行比較。如果檢測出1-8位的錯誤,將通過高速緩存寄存器進行糾正。只有經過糾正的數據,才會在I/O總線上輸出。

NAND閃存是以內存頁為單位進行編程和讀取的。一個內存頁由2048字節的可用存儲空間和128字節的OOB組成,後者用於存儲糾錯代碼和壞塊管理的標誌,也就是說,頁面的總長度為2176字節。不過,對於擦除操作來說,則是以塊為單位進行的。根據Micron公司的文檔,對於這個閃存部分,一個塊由64頁組成,總共有128KB的可用數據。該閃存由兩個面組成,每個麵包含1024個塊,因此:

2planes*1024blocks/plane*64pages/block*(2048+128)bytes/page=285,212,672由於備用區域僅用於閃存管理,並且不包含有用的用戶數據,因此,我們編寫了一個小腳本,將每個2048字節的頁面後面128字節的OOB數據刪除,結果文件長度正好是256MB。

分析轉儲的固件提取Marvell映像還記得我們說過該打印機是由Marvell芯片組供電的嗎?這時,這些信息就排上用場了。雖然88PA6220是專門為打印機行業設計的,但其固件映像的格式看起來與其他Marvell SoC是一樣的。因此,GitHub上有許多類似處理器的文件或代碼,可以作為參考。例如,我們看到該映像以TIM(可信映像模塊)頭部開始。該頭部包含大量關於其他映像的信息,其中一些信息被用來提取單個映像:

1.png

TIM頭部的格式如下面最後一個結構體所示(顯然,它假定OOB數據已經被刪除):

typedefstruct{

unsignedintVersion;

unsignedintIdentifier;

unsignedintTrusted;

unsignedintIssueDate;

unsignedintOEMUniqueID;

}VERSION_I;

typedefstruct{

unsignedintReserved[5];

unsignedintBootFlashSign;

}FLASH_I,*pFLASH_I;

//Constantpartoftheheader

typedefstruct{

{

VERSION_IVersionBind;

FLASH_IFlashInfo;

unsignedintNumImages;

unsignedintNumKeys;

unsignedintSizeOfReserved;

}CTIM,*pCTIM;

typedefstruct{

uint32_tImageID;//IndicatewhichImage

uint32_tNextImageID;//Indicatenextimageinthechain

uint32_tFlashEntryAddr;//BlocknumbersforNAND

uint32_tLoadAddr;

uint32_tImageSize;

uint32_tImageSizeToHash;

HASHALGORITHMID_THashAlgorithmID;//SeeHASHALGORITHMID_T

uint32_tHash[16];//Reserve512bitsforthehash

uint32_tPartitionNumber;

}IMAGE_INFO_3_4_0,*pIMAGE_INFO_3_4_0;//0x60bytes

typedefstruct{

unsignedintKeyID;

unsignedintHashAlgorithmID;

unsignedintModulusSize;

unsignedintPublicKeySize;

unsignedintRSAPublicExponent[64];

unsignedintRSAModulus[64];

unsignedintKeyHash[8];

}KEY_MOD,*pKEY_MOD;

typedefstruct{

pCTIMpConsTIM;//Constantpart

pIMAGE_INFOpImg;//PointertoImages(v3.4.0)

pKEY_MODpKey;//PointertoKeys

unsignedint*pReserved;//PointertoReservedArea

pPLAT_DSpTBTIM_DS;//PointertoDigitalSignature

}TIM;正如下文所詳述的那樣,該處理器的保護措施是由Lexmark團隊提供的,所以,讓我們先來看看有助於提取映像的相關字段。關於每個字段的完整描述,請參考這裡的參考手冊:

VERSION_I:常規TIM頭部信息。

Version (0x00030400):TIM頭部的版本(3.4.0)。它對以後確定使用哪個版本的映像信息結構(IMAGE_INFO_3_4_0)非常有用。

Identifier (0x54494D48):其值總是ASCII字符串“TIMH”,用於識別有效頭部。

Trusted (0x00000001):0代表不安全的處理器,1代表安全。該處理器已經被Lexmark保護起來,因此,只有經過簽名的固件才允許在這些設備上運行。

FLASH_I:啟動閃存屬性。

NumImages (0x00000004):表示頭部中有四個結構體,描述構成固件的映像。

NumKeys (0x00000001):這個頭部中有一個密鑰信息結構體。

SizeOfReserved (0x00000000):在TIM頭部末尾的簽名之前,OEM可以保留最多4KB(sizeof(TIMH))空間供其使用。 Lexmark沒有使用這個功能。

IMAGE_INFO_3_4_0:映像1的信息。

ImageID (0x54494D48):映像的ID('TIMH'),本例中為TIM頭部。

NextImageID (0x4F424D49):下一個映像的ID('OBMI'),OEM啟動模塊映像。

FlashEntryAddr (0x00000000):TIM頭部對應的閃存索引。

ImageSize (0x00000738):映像的大小,1,848字節被頭部佔用。

IMAGE_INFO_3_4_0 :映像2的信息。

ImageID (0x4F424D49):映像的ID('OBMI'),OEM啟動模塊映像。 OBM由Marvell公司提供,負責啟動打印機所需的任務。看一下UART的啟動日誌,在U-Boot啟動信息之前顯示的所有內容,都是由OBM代碼顯示的。至於功能方面,OBM將設置DDR和應用處理器核心0,並對隨後加載的固件(U-Boot)進行了固件簽名驗證。

NextImageID (0x4F534C4F):下一個映像的ID('OSLO')。

FlashEntryAddr (0x00001000):OBMI的閃存索引。

ImageSize (0x0000FD40):映像的大小,OBMI長度為64,832字節。

IMAGE_INFO_3_4_0:映像3的信息。

ImageID (0x4F534C4F):映像的ID('OSLO'),包含U-Boot代碼。

NextImageID (0x54524458):下一個映像的ID('TRDX')。

FlashEntryAddr (0x000C0000):O

0 Comments

Recommended Comments

There are no comments to display.

Guest
Add a comment...