Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86373455

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

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