Building shellcode, egghunters and decoders.

Creating shellcode on System Z (Mainframe)  Unix System Services (USS) employs the same disciplines required for the same activities on Intel platforms.   The difference lies in the syntax, assembler mnemonics, tools available, and debugging utilities.  There are certainly other ways to achieve this, and I’m still refining my favorites.  The below is one of my early successful attempts at doing so.

If you have never created shellcode from scratch, including a hand-hewn encoder for zapping bad characters, I recommend you do so on a well-documented platform.   Become familiar with using basic (but powerful tools for the task) such as dd, od, gdb and basic python scripting.

The process I followed developing shellcode for mainframe USS uses pretty common tried and true processes, using only basic tools available on the platform.   At a high level, it looks like this:

1)  Develop a working payload.   Here, for example is a simple C program that launches a shell.  Note, this program uses hard-coded strings, lengths, and pointers.  This is done to make the resulting assembler as simple as possible.  See previous posts on how to find the callable services, making this possible.

2) Use a C compiler that generates assembler (if you aren’t writing in HLASM to begin with) to help decide which bytes are relevant.  IBM’s metal compiler xlc used with the following options works nicely.  Note, the assembly generated here is IBM’s HLASM (High Level assembly), It is still 1 degree removed from the actual machine-language mnemonics they use to generate the 1’s and 0’s.   To get this, you have to use the debugger dbx.

3)  The primary debugger I’m using, dbx, can easily save memory dumps to a file.  These can be formatted to create simple shellcode.  Example:

4)  Format the shellcode, I use a python script for this which takes the binary name, output file name, beginning offset and ending offset.   The output can be C buffer style or assembler an assembler constant declaration, for use in another assembly program (such as the decoder used to de-obfuscate it).  This one is built with each byte XOR’d with 0x01 to remove nulls.

5)  Test with a stub program, the following is a tried and true method for jumping to your buffer.

Once you have working shellcode, then it’s time to remove bad characters (such as nulls 0x00) so the string can be copied across the network, piped on a command line, or read via environment variable (or whichever deployment method you choose).

Since there are no virus protection programs on the mainframe systems (yet?) simple encoding should suffice.   Here’s a stub I wrote in HLASM that finds the buffer via egg hunter (for ease of separation later).  Then, simply XORs a block of code with a static byte to yield the predetermined shellcode (which was XOR d with the same byte, prior to implementation to prevent bad characters).

The offsets will depend on buffer size and location.   The plan is to refine, then combine, this along with the scripts that build the above into a tool set that can easily generate workable shellcode.  The obtuse coding is a combo of this being an early draft and the need to not have any 00’s in the resulting object code.

More to come on the details and inner-workings of these steps, as well as ideas about fuzzing / finding vulnerable USS apps and what to do if you find one.  Have I piqued your interest? If so make sure you see my talk at DEFCON23 on Saturday, August 8 at 5pm in Las Vegas.

Some great resources:

zArchitecture Principles of Operations

DBX debugger for UNIX

Note – the above examples are not meant to be followed as a step-by-step how-to.   Rather, they are meant to get the reader started, with ideas and examples that can easily be modified or expanded upon to create working models.