An Autopsy of Artifice

Summary: A custom tool designed to autonomously achieve privilege escalation for Xbox One Developer Mode

In this post, we'll go over the following:

  • How the tool operates in chronological order
  • Why certain methods were created in a particular fashion
  • What you can accomplish with elevated privileges
Step One:
  • First order of business is deciding how we want to connect to the console. Initially, the plan was to implement some form of subnet scan, looking for an address with port 11443 available. Ultimately, given how networks will vastly differ, this idea was quickly scrapped.
  • Next order was looking to see if there was any tooling that would accomplish the same task, and there happened to be winappdeploycmd available from the UWP tooling of the SDK.
  • The tooling's device discovery service located in Microsoft.Tools.Connectivity.dll currently supports the following protocols:
  • While the TestSirep service is shipped with SystemOS via the Microsoft-OneCore-Sirep-TSP package, in this scenario, mDNS is used.
  • If Sirep rings a bell, maybe you've heard about it from here.
  • Checking the firewall, and performing a quick glance in Wireshark, we can run the devices command to confirm this:
XboxSRA-mDNS-Out-UDP|Action=Allow|Active=TRUE,,MDNS,679,Standard query response 0x0000 PTR XBOXONE._sshsvc._tcp.local
Given that mDNS would ultimately require an exemption in the Windows Firewall, the decision was made to fallback to manual choice.

As we're attempting to make this process as autonomous as possible, if we're going to prompt the user to perform an action, it might as well be an innocuous dialog window.

Otherwise, we would have UAC + inevitably dormant firewall rules.
  • If the toggle is switched on, a simple dialog is shown for the user to enter credentials they wish to set for the admin account.
  • Once a destination is chosen, we do a quick check to see if a hostname was entered, and if so, we resolve the local IP for use throughout.
  • In my testing, hostname resolution could result in abnormal overhead, so we simply just resolve the IP early on.
Connecting to WDP
  • Once resolved we'll attempt to connect to WDP, with credentials if set, to fetch the necessary SMB credentials at the following uri: /ext/smb/developerfolder
  • We'll then establish a connection to the network share to ensure we can perform any necessary actions to/from, and finally move onto the next step.
Step Two:
Copy SSH Keys
  • We'll begin by copying our helper files to the dev scratch that we'll use later on, and move onto our first exploit.
  • The first exploit involves the FileCopyPlugin that xbdiagcap utilizes in normal operation.
  • For reference, you can think of xbdiagcap (or its service) as a specialized version of WER for Xbox. This is what gets invoked when you use the "Report a Problem" button in the Guide.
  • It should be noted that standard WER is very much alive and well on Xbox, along with a few other unique services such as XLens.
  • The exploit itself is super simple, and is setup as such:
  1. Pick any of the wildcard locations specified in: C:\xbdiag\FileCopyPlugin\Settings.txt (we'll use S:\Settings\*)
  2. Create a junction to your target you wish to dump the contents from, for example: mklink /J S:\Settings T:\ProgramData\ssh
  3. Start/Stop xbdiagcap with the plugin specified, and an optional output directory:
xbdiagcap start -p FileCopyPlugin
xbdiagcap stop -d D:\DevelopmentFiles\SSHDump
  • Lastly, we'll copy the files from our xbdiagcap output directory back to our local machine, plant our elevation.cmd and sshd_config files on the dev scratch, and move onto the next step.
Step Three:
Convert/Create Keys
  • Circling back to the SSH.NET library for a brief moment, we'll simply perform the following:
  1. Choose any of the available private keys, load it
  2. Derive the public key from said private key
  3. Encode the public key (base64) and write it to a file (authorized_keys)
Step Four:
UWP Deployment
  • The great thing about the wrapper is that it does a lot of the heavy lifting, especially for things like fetching/applying the required CSRF token header.
  • The only thing missing from my requirements, was the process termination capability. Given the modular structure of the wrapper itself, adding this capability in was a trivial task.
  • Moving forwards, we'll construct our command for XboxWDPDriver that will install our app and any dependencies it may require. In our case, the initial value of our string will look something like this:
$"{xboxWdpDriverPath} /x:{hostnameOrIp} /op:install /appx:{MsixPath}";
  • Dependency wise, a simple loop is created so we don't need to hardcode any package names. Instead, we just exhaust the items available in the path specified and append them to the constructed command above.
  • Finally, if the user has authorization enabled for WDP, we'll append the credentials provided from earlier.
  • If all goes well, the app is fully installed and we may proceed.
Step Five:
Create SSH Folder
  • Starting us off, we'll launch our app by using XboxWDPDriver from before. Supplying the PFN and AUMID, once launched we'll hit our first check. If no "success.txt" file is present on the root of the dev scratch, we'll call into COM in order to invoke the recursiveCopyDirectory function from the Microsoft.Xbox.Development namespace.
  • Usage of this namespace has a very powerful benefit that's worth mentioning.
The recursiveCopyDirectory method will create any destination directory if it doesn't already exist.
  • After our destination directory is created, we can move onto the next step.
Step Six:
AuthKeys Drop
  • This brings us to our second exploit. Functionally, it accomplishes the same thing that recursiveCopyDirectory would, but I figured I might as well include it anyways, given that it was found whilst attempting to avoid the need for COM.
  • Continuing on from what our UWP app previously did with creating the .ssh directory for the systemprofile, we can now target said directory with another junction as such:
mklink /J D:\DevelopmentFiles\LooseApps\AuthKeys S:\Windows\System32\config\systemprofile\.ssh
  • The name "AuthKeys" is chosen because WDP will set the destination folder as whatever is picked for loose-app deployment. In our case, "AuthKeys" is located within the "Scratch" folder. 
  • Just as before, we'll invoke XboxWDPDriver again, this time specifying our loose-folder path, and appending an optional parameter of /transfer:HTTP so that we route the deployment through WDP.
  • A few notes on how this exploit operates:
Our "AuthKeys" folder contains a barebones AppxManifest.xml in order for our deployment to be seen as valid. Otherwise, no other extraneous files are required.

In the context of packaged app deployment, our attempt will be properly caught, and the untrusted mount point that we created is completely removed.

Conversely, this functionality is not carried over for loose-folder deployment. The same error message is provided as its packaged counterpart, however it lacks the same fail-safe that forces it to rescind the attempt and remove the offending mount point.

The end result, is that our junction will be traversed, the mount point is not removed, and the contents are successfully written to the specified destination.

The plus side is that the write operation abides by the inheritance ACL for the destination folder, which is why we're able to use it for our authorized_keys file.

The down side is that unlike recursiveCopyDirectory, the destination directory will not be created if it doesn't exist. However, if it does exist, the directory is seemingly completely recreated. This is why it's kept to this step only, as Sshd will reject this.
  • After a quick rmdir cleanup operation on our junction, we can finally move onto the next step.
Step Seven:
SSH Drop
  • Our first task is to pop back into SSH, and run our "acl.bat" file.
  • Ultimately, its core functionality are the following lines:

for /R "D:\DevelopmentFiles\SSH" %%f in (*) do

    D:\DevelopmentFiles\icacls.exe "%%f" /grant:r Administrators:F
    D:\DevelopmentFiles\icacls.exe "%%f" /grant:r SYSTEM:F
    D:\DevelopmentFiles\icacls.exe "%%f" /grant:r "Authenticated Users":RX

  • Grants Administrators/SYSTEM Full Control, while setting Authenticated Users to Read & Execute only. This ensures that when when we drop these into the ssh folder and cycle the service, Sshd will accept them.
  • Given the downside previously mentioned in Step Six, we'll be returning to our UWP app to finish the second copy operation.
  • Just as before, we'll pop into XboxWDPDriver, and launch our app. This time, given that we now have our success.txt "marker", we'll copy our elevate.cmd and modified sshd_config we planted on the dev scratch back in Step Two to the original ssh directory. 
  • Lastly, we'll cleanup any miscellaneous files and uninstall the app before proceeding as its functionality serves no purpose beyond this point.
Step Eight:
SSH Cycle
  • Starting things off, we'll pop back into a shell. This time however, we'll be using the Telnet session that was prepared back in Step Two when we ran our xbdiagcap exploit, within the "art.bat" file:
devtoolslauncher launchforprofiling telnetd "cmd.exe 24"
  • This will pop a telnet shell as VSProfilingAccount on port 24, which we'll connect to in order to retrieve detailed process information using tlist with the -s parameter.
  • After we retrieve the process list, we can parse out the id for each, and send them to WDP to be terminated.
  • All it takes is a DELETE request to the following WDP uri:
  • Once the processes are terminated, we can move onto the final step of the process.
Step Nine:
Invoke AKC
  • We start off the final step with a waiting period for the service to come back online, or more specifically, the broker. 
  • Same as before, we'll pop into the same Telnet shell, and begin a cycle to check the process list every three seconds. There's a few additional checks implemented for issues I've ran into.
  • The first check is to see if the service comes back in less than 5 seconds after we start the process cycle. On every occurrence of having had this issue, it has almost always been after a factory reset. The current step is never fully finished, our custom user is never added, and a restart is required.
The average timeframe, as mentioned in the log, is around 60 seconds.
  • The second check is to see if more than 3 minutes have elapsed. It's an extreme timespan for the typical restart cycle, and if you reach it, it's highly likely you've had the Sshd service abruptly terminated before on that boot cycle. 
  • The third check involves an attempt to login with the credentials parsed from the elevate.cmd file. If we're unable to login, our user most likely wasn't correctly added, so we'll start over in order to attempt the process again.
  • Otherwise, we can perform the final task, which is just a simple connect with the private key. The sequence will occur as such:
1. Specify our username to connect with as "systemprofile"
2. Select a private key copied over from the dev scratch
3. Upon connection, the service will parse our modified sshd_config file
4. AuthorizedKeysCommand routes to the elevate.cmd file we placed
5. AuthorizedKeysCommandUser specifies the user it should run as
6. The elevate.cmd script calls net1.exe we placed on DevelopmentFiles
 • Adds a custom user
 • Adds said user to every available localgroup
 • Sets the password expiry property to unlimited
  • Regarding changes to the sshd_config file, the following is performed:
Uncomment #PubkeyAuthentication, keep its value set to 'yes'
Comment out the Match Group administrators block
Add AuthorizedKeysCommand --> T:\ProgramData\ssh\elevate.cmd
Add AuthorizedKeysCommandUser --> NT AUTHORITY\SYSTEM
  • To give credit where credit is due: this step wasn't possible before, and wouldn't be possible now, without the following two PR's:
Step Ten:
  • You have an active administrator user, now what? Here's a few examples for things you can poke at:
Disable the numerous services for telemetry (highly recommend)
Properly poke around with PWSH (highly recommend NtObjectManager)
Running your own elevated software
Explore and modify the registry remotely
Dump your flash
Manually load kernel drivers such as KsDumper (Durango)
Create your own "overlay" xvd for SystemOS (with some caveats)
Debug usermode with dbgsrv + windbg (avoid firewall issues with clicon)