Sunday, July 28, 2013

Windows RT ARMv7-based Shellcode Development

Recently, I've taken an interest in gaining code execution on my Surface RT tablet. I have found Windows RT to be rather enticing since Microsoft has made a concerted effort to prevent the execution of unsigned code. A couple weeks ago I discovered a way to gain arbitrary shellcode execution via PowerShell. I will have a separate blog post on that topic once I get a thumbs up from Microsoft. And for the record, I am aware of the awesome public Windows RT jailbreak.

Anyway, seeing as I'm already fairly comfortable writing x86 and x86_64 shellcode, I wanted to take on the challenge of writing ARMv7-based shellcode for Windows since no one else seems to be doing it publicly. Knowing that writing shellcode from scratch would have been rather painful and prone to error, I decided to write my payload in C and then modify the resulting assembly listing slightly in order to achieve a position independent payload. Here is the result (noting that this is merely a working proof-of-concept):


You may have noticed that this shellcode is written in MASM format, therefore, it can only be assembled using armasm.exe (the ARM equivalent of ml.exe). Unfortunately, armasm doesn't provide the option of outputting to a raw bin file. It will only emit an object (.obj) file. I wrote Get-ObjDump with the intent of pulling out raw payload bytes in mind but armasm doesn't apply relocations. This means that any calls to functions present in the payload won't be fixed up and it will crash upon executing.

So rather than writing my own linker, the natural choice was to leverage Microsoft's linker, link.exe. In theory, all I would need to do is call `link bindshell.obj` and pull out the raw bytes from the '.foo' section of the resulting binary. However, I ran into a couple issues in practice:

1. link.exe requires that you specify an entry point.

Solution: Easy. Provide the '/ENTRY:"main"' switch

2. Depending on the subsystem you choose, link.exe requires certain functions in the CRT to be present. For example, the following subsystems require the following entry point functions:

/SUBSYSTEM:CONSOLE - mainCRTStartup
/DLL - _DllMainCRTStartup
/SUBSYSTEM:WINDOWS - WinMainCRTStartup
/SUBSYSTEM:NATIVE - NtProcessStartup

Solution: Obviously, I don't care about the C runtime library in my shellcode. The solution I came up with was to specify EFI_APPLICATION as the subsystem since it doesn't require the CRT. In the end, I don't care about the type of PE file I output. I just need the linker to fix up relocations for me. I can take care of pulling out the bytes from the .foo section of the resulting executable. Fortunately, Get-PEHeader can rip raw bytes from a PE file.
 
Wrapping things up, here is the process of obtaining fully-functioning ARM-based Windows RT shellcode from end to end:

13 comments:

  1. Unfortunately, the RT jailbreak tool doesn't work anymore for Windows 8.1. Is there any way to get the exe's (compiled for ARM) running via powershell? What you did here looks promising, but I don't understand what your c payload was... Can you could write the payload so that it launches an arbitrary exe (compiled for ARM)?

    ReplyDelete
    Replies
    1. Unfortunately, the technique I came up with doesn't work in 8.1. Also, like I said, I can't discuss that technique right now.

      All I did in my post was offer up a PoC ARM shellcode payload. A means to execute the payload is up to you. It was not my goal in the post to describe a Windows 8.1 jailbreak.

      If you're wondering how to compile exes for ARM though, that's fairly easy. I use Visual Studio 2012, fire up the VS2012 ARM Cross Tools Command Prompt and then call cl.exe. It will throw the following error the first time:

      C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\INCLUDE\crtdefs.h(338) : fatal error C1189: #error : Compiling Desktop applications for the AR
      M platform is not supported.

      Just remove that line from crtdefs.h and then you can compile your code.

      Delete
    2. Why can't you discuss it? Other people talk about exploits against Microsoft code all the time. Do you have some sort of contractual relationship with them? I'm just curious because I'm not discussing mine, either.

      By the way, I've gotten into 8.1:
      https://twitter.com/Myriachan/status/365350790803619840

      I'm not discussing how I did it as a strategic decision, not because I can't.

      Delete
    3. I can't discuss it right now because I'm going through the coordinated disclosure process with Microsoft. As soon as Microsoft closes the ticket, trust me, I'll be sure to blog about the technique.

      You probably wouldn't find it too impressive though considering it's only a UMCI bypass (i.e. not a full-fledged jailbreak). I like it though because it unlocks PowerShell and as a PS fanboy, that's mostly all I need.

      I've been impressed with your contributions to XDA forums, btw. Keep up the good work. :D

      P.S. As of an hour ago, according to Microsoft, "the UMCI feature is not a security boundary." Then why not allow unsigned code in WinRT?!? :P

      Delete
    4. It annoys me the effort they went to just to lock down WindowsRT and PS specifically for something they then don't consider a security issue. Then again if it isn't a security issue why does it get fixed in 8.1?

      Delete
    5. I understand Microsoft's attempts to lock down PowerShell as it is installed by default on all Windows machines and poses such a powerful post-exploitation environment.

      I really don't understand the logic to lock down WinRT though. Considering it cannot be domain joined, no enterprise in their right mind would want to purchase them. It was designed to be a consumer device and what do consumers want? Control! Perhaps MS would get more sales of WinRT tablets if they just unlocked the damn OS.

      Either way, I'm OK with the state we're in. I enjoy finding UMCI bypasses.

      Really clever bypass, btw!

      Delete
    6. Well from MS's point of view I am sure they would argue that once you are on the machine it is game over anyway. So having something as useful as PS for post-exploitation isn't something on their radar, might as well just run your own code :)

      So is mine different from your technique? I do have another one around, but it didn't fit into a tweet ;)

      Delete
    7. Nope. Mine is different. Mine involves flicking a few bit to get around Constrained Language mode.

      Delete
    8. Care to share your other bypass? You have any many characters as you need here. ;D

      Delete
    9. Well, the other issue was in the Import-PSWorkflow command which is implemented in Microsoft.PowerShell.Workflow.ServiceCore assembly. Most of the workflow stuff doesn't work and loading the workflow module errors with missing assemblies. But if you copy that assembly to your documents folder as a private module you can load it.

      From there you can execute non-compiled XAML workflow (there are two forms, one which gets compiled, bad, and one which is interpreted, good), which basically allows you to execute arbitrary .NET functions and capture the results. From that you can bootstrap the assembly loading trick.

      So do something like:

      $dir = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\Microsoft.PowerShell.Workflow.ServiceCore"
      new-item -type directory -path $dir -force
      copy-item -path "c:\windows\Microsoft.net\assembly\gac_msil\Microsoft.PowerShell.Workflow.ServiceCore\*\Microsoft.PowerShell.Workflow.ServiceCore.dll" -destination $dir -force
      import-psworkflow messagebox.xaml
      messagebox

      to setup the module, then use the following XAML (pops a messagebox)

      <Activity x:Class="WorkflowConsoleApplication.Workflow1"
      xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
      xmlns:s="clr-namespace:System;assembly=mscorlib"
      xmlns:fm="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      <Sequence>
      <InvokeMethod MethodName="Show" TargetType="fm:MessageBox">
      <InArgument x:TypeArguments="s:String">Hello World!</InArgument>
      </InvokeMethod>
      </Sequence>
      </Activity>

      Simple ;) Hopefully the PoC wasn't mangled by posting it.

      Delete
  2. I got a similar reply years ago when I reported a memory overwrite exploit that csrss.exe could use to escalate to kernel mode in Vista, allowing bypassing driver signing. They didn't see it as a problem. And yet, in Windows 8.1, they made csrss.exe a protected process just so that you couldn't do attacks against win32k's lax protection of csrss parameters. Microsoft security people can be quite inconsistent sometimes.

    ReplyDelete
  3. A related, but not exactly on-topic question... I'm trying to build a VS2012 WinRT project with an ARM asm file as part of the project's source. The compile part of the build shows no errors, but ARMASM is never run on the file. When it tries to link, it complains that the obj file for my asm code is not found. My x86/x64 builds of the project both work fine with external asm files. Is there some trick to get VS2012 to properly compile and link ARM asm modules?

    Thanks,
    Larry B.

    ReplyDelete
    Replies
    1. In order to assemble and link an ASM file in VS2012, you need to do two things:

      1) Assemble your ARM asm file as a pre-build or pre-link step. Go to - Project Properties->Configuration Properties->Build Events->Pre-Build Event. Add the armasm command-line options there. That will output the object file.

      2) Add the object file as a dependency for the linker: Project Properties->Configuration Properties->Linker->Input->Additional Dependencies. Add the path to your object file there.

      You'll find a decent example of this configuration in the x64 configuration of my PIC_Bindshell project - https://github.com/mattifestation/PIC_Bindshell. Obviously, just replace the ml64 options with your armasm options.

      Cheers,
      Matt

      Delete