A multi-stage PE loader for @carrot_c4k3's CollateralDamage Xbox One exploit.
This project has only been built and tested using x86_64-pc-windows-msvc
on Windows 11. It will likely build on any 64-bit Windows, but has not been tested across different versions.
- Clone this repo and its dependencies:
git clone https://github.com/exploits-forsale/solstice.git
cd solstice
- Ensure rust nightly is installed:
rust toolchain install nightly
- Install
just
: https://github.com/casey/just - Run:
just build-exploit
All necessary outputs will be in the outputs/
directory.
If you would like to test on the PC version of GameScript, running the following command will execute build-exploit
and copy the artifacts to the GameScript directory. This will overwrite its autosave state so all you have to do is click "run code":
just generate
Currently the stage2 shellcode attempts to open and run run.exe
out of GameScript's LocalState
directory. An args.txt
file can be placed in the same directory contaning the command line string to pass to run.exe
.
ENSURE THAT A host_ip.txt
EXISTS IN THE ROOT OF THE REPO THAT POINTS TO THE IP ADDRESS THAT WILL BE HOSTING THE STAGE2
/run.exe
DELIVERY SERVER
- Build necessary stages with:
just generate-dev
- Copy
outputs/gamescript_autosave_network.txt
to GameScript - Run the server:
# Defaults to using the test_rust_program.exe as run.exe
just run-server
# Optionally use the following command to specify a run.exe
just run-server ./path/to/file.exe
If testing on PC, you can build in debug mode to get debug output in WinDbg.
- Build
just generate debug
- Run GameScript
- Attach WinDbg
- Run the exploit
If WinDbg is not attached the application will crash.
solstice uses a 3 stage loader. stage1 and stage2 can be replaced with custom stages and are not unique to Xbox or GameScript.
The stage1 loader is intended to work well with GameScript, and the project is intended to provide a streamlined experience with exploitation against GameScript. You can even use your own, better reflective PE loader, but you may have issues caused by slight differences between the Xbox SystemOS/UWP post-exploitation and a normal Windows exploitation.
For example:
- The PEB on Xbox is readonly (but you can change its permissions)
- kernel32.dll is not loaded by default (kernelbase.dll is!)
- The filesystem is generally more restricted
The 3-stage loader works as shown in the following diagram:
┌──────────────────────────┐
│ GameScript With Exploit │
│ ┌──────────────┐ │
┌────┐ │ │ │ │
│ 1 │ │ │ stage1.bin │◀─ReadFile/VirtualAlloc─┐
└────┘ │ │ │ │ │
│ └──────────────┘ │ │
└──────────────────────────┘ │
│
┌─────────▼─────────────────────┐
│ ┌──────────────┐ ┌──────────┐ │
│ │ │ │ │ │
│ │ stage2.bin │ │ run.exe │ │
│ │ │ │ │ │
│ └──────────────┘ └──────────┘ │
│ File System ▲ │
└────────────────────────┼──────┘
│
Reflective
┌─────────────────────────────────────────────────────┐ PE Loader
│ GameScript With Exploit │ │
│ ┌──────────────┐ ┌──────────────┐ │ │
┌────┐│ │ │ │ │ │ │
│ 2 ││ │ stage1.bin │────invoke()───▶│ stage2.bin │◀───┼────────┘
└────┘│ │ │ │ │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ GameScript With Exploit │
┌────┐│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
│ 3 ││ │ │ │ │ │ │ │
└────┘│ │ stage1.bin │────invoke()───▶│ stage2.bin │───invoke()───▶│ run.exe │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
shellcode_stage1/
is the stage1 shellcode that is embedded directly in the GameScript exploit. This is intended to be as small as possible so that less typing is required from a Rubber Ducky/Flipper if that approach is used. Additionally, GameScript tends to hit parsing errors if the shellcode array has too many elements so we must keep this stage small.
stage1 loads stage2 from disk, maps it into memory, and executes it.
This works basically the same as shellcode_stage
, but works by opening a socket to a remote server and loading stage2 and stage3 over the network.
stage2 is executed directly in-memory, but the run.exe
(stage3) is written to disk. Since the same stage2 is shared across non-network and network-based payloads, it makes things easier to load things in the same manner after stage1.
shellcode_stage2/
is read by stage1 from disk, made executable, and executed. stage2 then reads a PE file from disk, specified at AppData\Local\Packages\27878ConstantineTarasenko.458004FD2C47C_c8b3w9r5va522\LocalState\run.exe
, and manually loads the PE using our solstice_loader
. This shellcode can be arbitrarily large since it's not limited by typing or GameScript sizes.
The main reflective PE loader. This is a fork of Thoxy67/rspe
that's been heavily modified.
The server that communicates with shellcode_stage1_network
over port 8080. Its purpose is to send 4 main TCP packets:
stage2
length (4 bytes)stage2.bin
run.exe
length (4 bytes)run.exe
On port 8081
it can listen for text-based content and print out to the terminal.
shellcode_utils/
contains common functionality shared between the shellcode stages including function definitions and helpers to retrieve functions at runtime.
Reads the resulting exe files from shellcode_stage*/
, applies some patches to make the first few bytes jump to the entrypoint, and generates a flattened .bin file containing only the .text
section.
Changes firewall settings on the Xbox to allow traffic on port 22 and runs an SSH/SFTP server.
shellcode_stage1/
, shellcode_stage1_network
, and shellcode_stage2/
have a special .cargo/config.toml
that merges almost all PE sections into just a single .text
section, and ensures there are no external dependencies (i.e. no runtime linkage required). They are #![no_std]
, #![no_main]
binaries that resolve every platform function at runtime itself.
shellcode_gen/
's main job is to read the .text
section and do some patches to make it position-independent. This idea
was from hasherezade's project masm_shc. It has also been modified to output a new GameScript exploit file with the latest shellcode_stage1/
automatically embedded in it, placed in outputs/
.
This repo is a heavily modified version of b1tg/rust-windows-shellcode
. Thank you to b1tg for their work.
Unfortunately this project is not a proper cargo workspace because Cargo does not allow you to specify a different profile per-crate in a workspace. See: rust-lang/cargo#8264.
Because I could. And I know how to build/debug/deploy my own code better than an off-the-shelf project. The PE loader is certainly not as mature as others, but it works for our purposes.
- @carrot_c4k3 for the GameScript and ntkernel exploits.
- This repo is a heavily modified version of
b1tg/rust-windows-shellcode
. Thank you to b1tg for their work. - Thoxy67 for their original rspe lib which was modified.
- monoxgas/sRDI polycone/pe-loader for their PE loaders which served as a reference to double-check I was doing things right
- horsicq/XPEViewer which was useful for viewing data from PEs I was having trouble loading.
- 0xC0ffee1/sessio which has a ready-to-use PTY Shell impl, used in the daemon-crate.