Windows CE SuperH3 Exploit Development Interlude: Usable Null-Free RISC Shellcode and ASCII Parameter Translation

Elias Augusto
7 min readApr 3, 2019

Author’s Note: When I say that shellcode is ASCII formatted, I’m referring to functions that accept ASCII strings. Two of the characters used in this shellcode are not valid ASCII characters, but they’ll get past strcpy and most other ASCII string functions that are just looking for a null terminator.

I’ve decided to make this part of the series an interlude because it wanders a bit outside of the scope of the main series. While I may not be able to apply the shellcode I’ve developed in this article to Windows CE 2.11 Unicode filtered buffers, I figured it would still be useful to release it. This is because this shellcode can be adapted for use in vulnerable text buffers on SH3-based embedded machines running Windows CE 3.0 .NET to 4.5. This covers a variety of equipment including older models of popular voting machines (I don’t want to mention brand names but some are still in use, check for surplus on eBay), warehouse barcode scanners (eBay), oscilloscopes (wherever oscilloscopes are sold), and PDA’s (try a thrift store).

Here it is!

In my case, I developed this shellcode as an exercise to ensure that I would be able to write shellcode using the venitian method. I’m removing null bytes instead of adding them, but the mathematical concepts I’m employing can be used to do either.

It wouldn’t be fair of me to just post the shellcode example without adapting my guide to parameter translation from the Part 4 of this series for ASCII string buffers. I’ll get to that in a second, but first I want to address the last update I put on Part 4.

I wrote this before attempting to write the shellcode, so I ended up being partially inaccurate here. Not to say that Azeria’s shellcoding article wasn’t helpful, it’s a must read for anyone writing RISC shellcode. The Azeria Labs guide to ARM shellcoding put me on the right track, but there were differences between ARM assembly and SH3 assembly that I didn’t anticipate.

In her guide, Azeria uses the ARM instruction “STRB” to directly modify data stored in her shellcode while it was loaded into memory and replace placeholder characters in her character array with null bytes. I didn’t even know a Harvard architecture based processor could allow this. The SuperH has no similar instructions. I tried to replicate what she was doing using “MOV”, but I kept getting illegal addressing errors. Eventually I realized that I was overthinking the problem. I didn’t need to edit the data section of the code, as Azeria did. I just needed to edit the character I was using somewhere in memory that I could point to. Like say…the stack.

This meant that I couldn’t use MOVA as an LEA substitute anymore, because it can only load offsets from the program counter into r0. Luckily I realized that if you want to push a stack address to a register, you can just use math! I set the register to H’0, added the offset, and added the stack pointer’s position. When I tested it, everything worked.

I’ve illustrated all of the changes I made to the previous shellcode in the above image. This example utilizes the MessageBoxW function and creates the same dialogue box as the example from Part 4. My ASCII parameter translation guidelines are as follows:

  • Character arrays and strings must be padded with characters to replace null terminators and null padding; that padding must later be replaced and the array rewritten to the stack or another location in memory that can be modified
  • Instead of using “MOV H’0, rn”, a technique that I deliberately used in Part 4 to add null bytes to the shellcode, you should use “XOR rn,rn” to eliminate null bytes
  • Make sure to sit your stack parameters far away from each-other so one doesn’t accidentally overwrite another, I’ve dealt with that issue
  • 8 byte alignment of the data occurs when you reuse a register in data transfers too often or normalize it more than two times; I cannot tell you why this is, but 8 byte alignment will create null bytes in the data section and must be avoided
  • Shift instructions, swap instructions, and math instructions can be used to avoid null bytes; dig into the math section of the Renesas SuperH instruction set summary (linked in “Sources”) to see what works best in your situation

After you’ve assembled the shellcode, you need to replace the “09 00” nop and the “0[X] A0” branch instruction with “MOV r1,r1” nops in a hex editor. This is illustrated below.

If you’ve done everything right, you should be able to observe the desired result.

If not, check the memory window in disassembly to see where you parameters may not be loading properly or if your math was off.

That’s all for this article. I’ve uploaded the files used here to my GitHub repository (linked below article). If you would like more information about the SH3 assembler or general Windows CE API parameter analysis and translation, see Part 4 of this series.

The shellcode is getting bigger. I’m at 40 bytes now. Hopefully I won’t go over 255 when I write the Unicode shellcode. I can’t wait to show you all what I’ve come up with when I’m finished!

Update: Hmmm. Decided to paste the null-free shellcode into a database backup file to see what would happen. You know what are less strictly Unicode filtered than I initially thought? CE databases. Shellcode just needs sufficient padding to load properly.

Heap dump of Data.exe after updating the database

I guess I’ll be pushing the Unicode article back to right after I wrap up “Data.exe” exploit development. It’ll be before I do anything with heap overflows, because at that point I’ll definitely need Unicode filtered shellcode. See you all tomorrow!

Update: College events and troubleshooting made the concept of “tomorrow” relative.

Update #2: Very confused, keep getting a datatype mismatch exception when I try to run the shellcode, even though I can point the pc at the shellcode. Tried reversing every two bytes because of how data vs instructions are loaded, didn’t work. Even more curious, the instructions are not recognized by the debugger in either form. I know there’s no DEP, but it seems like the code is never running to begin with. Maybe it’s because the frame pointer gets zeroed?

Not sure, will update if I figure it out. I was trying to finish this up in time for next week so that I could start the Art of Memory Forensics. Hopefully I still do, but no promises.

Update #3: Even weirder, I don’t know for certain that it’s something to do with the shellcode, but I know that there’s no reason shellcode in the heap wouldn’t be recognized by the debugger. The nop sleds I have in the heap of my shellcode tester are always recognized by the debugger. I need to check whether their bytes are reversed. Something is going on, and I’m still trying to figure out what.

Also working on finding a similar vulnerability in an proprietor HTTP server application. I may just switch over to that if I succeed, because a remote exploit would be easier to automate.

Update #4: The HTTP server was incompatible with my device, so I went back to Data.exe and found out that for some reason the shellcode wasn’t even recognized as code, even though I was able to establish that that was not normal behavior for the heap. Embedding shellcode in the heap is a valid strategy, here’s some shellcode in the heap:

I’m not even sure what’s happening with the Data.exe heap. It might be a HeapAlloc/LocalAlloc vs malloc thing? That seems likely, because Data.exe uses LocalAlloc and it’s clearly putting the data into memory a different way.

For now I might try to get the shellcode onto the stack and exploit it from there. That might work. The debugger really isn’t making this any easier. I figured out how to go to a line in the disassembly window, but the random scrolling bug in the memory window is making it difficult to find anything.

I might also just try to move on to another program that lets me use ASCII shellcode, because I really need to do some memory forensics before I spend time on Unicode shellcode. I have one in mind, the one with the heap overflow, but I need to figure out how to exploit that without VEH before I can do anything.

Sorry for the delay folks.

GitHub

Sources

--

--

Elias Augusto

Enjoys edev, cyber forensics, hardware hacking, and RE, former CACI BIT Systems intern, GREM, Security+