Google chrome swiftshader texture allocation integer overflow Vulnerability / Exploit
/
/
/
Exploits / Vulnerability Discovered : 2018-07-19 |
Type : dos |
Platform : multiple
This exploit / vulnerability Google chrome swiftshader texture allocation integer overflow is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
There's a remotely triggerable memory corruption issue in SwiftShader that's reachable from WebGL, resulting from an integer overflow issue.
In the GPU process there is validation on the sizes passed to texture creation functions to ensure that they shouldn't cause overflow. However, in the Swiftshader code there is a separate rounding up of render-target sizes to the next even size, which allows bypassing this validation.
(Note the additional +4, which is also (unexpected by the chrome gpu process) unsafe but in practice shouldn't cause an issue.)
void *Surface::allocateBuffer(int width, int height, int depth, int border, int samples, Format format)
{
// Render targets require 2x2 quads
int width2 = (width + 1) & ~1;
int height2 = (height + 1) & ~1;
// FIXME: Unpacking byte4 to short4 in the sampler currently involves reading 8 bytes,
// and stencil operations also read 8 bytes per four 8-bit stencil values,
// so we have to allocate 4 extra bytes to avoid buffer overruns.
return allocate(size(width2, height2, depth, border, samples, format) + 4);
}
The maximum value for bytes(format) is 16, with something like GL_RGBA32F, and samples is 1.
We can't cause this value to overflow if we have to provide the texture contents, since allocating a sufficiently large Float32Array will be larger than the renderer memory limits, but we can use glTexStorage3D to trigger the overflow.
Then we'll get an integer overflow during size calculation, and end up with a small buffer for a large texture.
If we use the path glTexSubImage3D to initialize the texture, this will zero out (Chrome's expected size) of the texture (~4gig) in the (260 byte) allocation, which may make exploitation awkward, but especially in a context like the GPU process with multiple threads interacting, it's likely possible to exploit this issue. There may also be alternative paths which avoid the wild memset, but I'm reporting now so that work on a fix can start.
Note, it is possible for an attacker to force use of the Swiftshader backend for WebGL rendering by simply crashing the GPU process a few times (for a platform dependent value of 'few'). The attached PoC uses 4 domains and cycles between them to trigger 3 (hardware accelerated) GPU process crashes due to OOM (on my workstation, at least) which will then be followed by the (software accelerated) GPU process hitting this bug. Mileage may vary with different GPU drivers/OpenGL implementations.
Crashes with the PoC will be fairly random - whatever you'd expect for zeroing out your entire heap...
See crash-id d0573792cf03341d for a crash on the current stable branch.
To test using the attached PoC, either run chrome with --disable-gpu to force software rendering, or create 4 aliases to localhost evil0.com, evil1.com, evil2.com and evil3.com in your /etc/hosts file and run ./server.py <port_number> and point your browser to evil0.com:<port_number>.
Proof of Concept:
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/45059.zip There's a remotely triggerable memory corruption issue in SwiftShader that's reachable from WebGL, resulting from an integer overflow issue.
In the GPU process there is validation on the sizes passed to texture creation functions to ensure that they shouldn't cause overflow. However, in the Swiftshader code there is a separate rounding up of render-target sizes to the next even size, which allows bypassing this validation.
(Note the additional +4, which is also (unexpected by the chrome gpu process) unsafe but in practice shouldn't cause an issue.)
void *Surface::allocateBuffer(int width, int height, int depth, int border, int samples, Format format)
{
// Render targets require 2x2 quads
int width2 = (width + 1) & ~1;
int height2 = (height + 1) & ~1;
// FIXME: Unpacking byte4 to short4 in the sampler currently involves reading 8 bytes,
// and stencil operations also read 8 bytes per four 8-bit stencil values,
// so we have to allocate 4 extra bytes to avoid buffer overruns.
return allocate(size(width2, height2, depth, border, samples, format) + 4);
}
The maximum value for bytes(format) is 16, with something like GL_RGBA32F, and samples is 1.
We can't cause this value to overflow if we have to provide the texture contents, since allocating a sufficiently large Float32Array will be larger than the renderer memory limits, but we can use glTexStorage3D to trigger the overflow.
Then we'll get an integer overflow during size calculation, and end up with a small buffer for a large texture.
If we use the path glTexSubImage3D to initialize the texture, this will zero out (Chrome's expected size) of the texture (~4gig) in the (260 byte) allocation, which may make exploitation awkward, but especially in a context like the GPU process with multiple threads interacting, it's likely possible to exploit this issue. There may also be alternative paths which avoid the wild memset, but I'm reporting now so that work on a fix can start.
Note, it is possible for an attacker to force use of the Swiftshader backend for WebGL rendering by simply crashing the GPU process a few times (for a platform dependent value of 'few'). The attached PoC uses 4 domains and cycles between them to trigger 3 (hardware accelerated) GPU process crashes due to OOM (on my workstation, at least) which will then be followed by the (software accelerated) GPU process hitting this bug. Mileage may vary with different GPU drivers/OpenGL implementations.
Crashes with the PoC will be fairly random - whatever you'd expect for zeroing out your entire heap...
See crash-id d0573792cf03341d for a crash on the current stable branch.
To test using the attached PoC, either run chrome with --disable-gpu to force software rendering, or create 4 aliases to localhost evil0.com, evil1.com, evil2.com and evil3.com in your /etc/hosts file and run ./server.py <port_number> and point your browser to evil0.com:<port_number>.
Proof of Concept:
https://github.com/offensive-security/exploitdb-bin-sploits/raw/master/bin-sploits/45059.zip
Google chrome swiftshader texture allocation integer overflow