Skip to content

ioncodes/sogen-sandbox-escape

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

Sogen Sandbox Escape

TLDR: arbitrary r/w in vulkan host can lead to sandbox escape, tested on commit 8e6e74adccb7bcd125bd8f351cb841ee12c3bcb7

alt text

Vuln

vulkan_host::upload_memory / download_memory forward the offset (guest controlled) straight to vkMapMemory(offset, size) and never validate it against the allocation size

// not verbatim ofc i copied the relevant lines out

// allocate_memory(): the guest chooses the size
memories.emplace(id, memory_data{ .handle = memory, .allocation_size = size /* e.g. 64 */ });

// but upload_memory()/download_memory() never validate allocation_size:
const size_t copy_bytes = std::min(static_cast<size_t>(size), data_size);
dev->second.map_memory(dev->second.handle, mem->second.handle, offset, size, 0, &mapped); // mapped = base + offset
std::memcpy(mapped, data, copy_bytes); // copies past the end of the allocation

Allocating that memory makes the analyzer map it into its own address space at some host address, I call it base. Every transfer then reads/writes at base + offset, and we fully control offset. So to hit any address target we want, we just send offset = target - base (wraps ofc).

From R/W to Sandbox Escape

Disclaimer: I poked at this for lunch with limited time so I disabled ASLR lol

callbacks.on_stdout is a libstdc++ std::function the analyzer calls on every guest stdout write. Calling it runs _M_invoker(&_M_functor, ...). The exploit uses the write primitive to set:

  • _M_functor (offset 0, the 16-byte inline storage) <- the command string "id>/tmp/SGN",
  • _M_invoker (offset 24) <- &system,

then printf's "PEWPEWPEW". Since rdi is &_M_functor, the analyzer runs system("id>/tmp/SGN") on the host. The command lives inline in that 16-byte functor, so it has to be <= 15 chars + NULL.

on_stdout, system and &handle_stdout are all at fixed addresses (ASLR off), so those are just hardcoded. base is the one value that changes each run, so the guest bruteforces it: pick a candidate base, then use the read primitive to read what should be sitting at on_stdout. Its functor holds &handle_stdout, so the guess that makes that read come back as &handle_stdout is the real base.

I tested this on Fedora with the propriatary Nvidia drivers installed, I suppose depending on OS, drivers and whether validation layers are activated or not the PoC could change but idk have not looked at it in depth.

PEW PEW PEW

x86_64-w64-mingw32-gcc -O2 -Wall exploit.c -o /path/to/emu-root/root/filesys/c/exploit.exe

rm -f /tmp/SGN
setarch "$(uname -m)" -R ./build/release/artifacts/analyzer -e emu-root/root -s c:/exploit.exe

cat /tmp/SGN
# uid=1000(layle) gid=1000(layle) groups=1000(layle),10(wheel),963(docker) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

About

sogen vulkan host arbitrary r/w into sandbox escape

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages