분명 돼야 하는데 안된다면 `` i r``로 레지스터 모두가 정상인 상태로 설정되어 있는지 확인해본다.


Sigreturn

  1. `` int`` instruction을 실행하면, kernel mode로 진입하면서 user mode context를 kernel stack에 push해놓는다.
  2. signal을 감지하는 것은 kernel mode에서 수행된다.
    kernel은 수신된 signal이 있는지 확인하고 nonblocked pending signal이 있으면 ``c do_signal()``를 호출한다.
  3. 여기서 signal을 처리하게 되는데, 이 때 signal handler가 등록되어 있는 경우, signal handler를 실행하기 위해 user mode로 나가야한다.
    일단 kernel mode에서 벗어나면 kernel stack이 초기화되기 때문에, 아까 push해 둔 user mode context가 유실되어 어느 user mode로 돌아가야할지 알 수 없게된다.
  4. 따라서, signal handler를 실행하기 위해 user mode로 나가기 전에 kernel stack에 존재하는 user mode context를 user stack에 복사해두고, 나중에 이를 복원하기 위해 ret addr을 ``c sigreturn()`` syscall 주소로 설정한다.
  5. signal handler를 실행한다.
  6. signal handler 실행을 마치고 리턴하면서 ``c sigreturn()``이 호출되며 다시 kernel mode로 진입하게 되며 user stack에 있는 context가 kernel stack에 복원된다.
    Note ) `` sigcontext``에 지정된 `` eip``는 최종적으로 kernel mode에서 user mode로 나가게 될 때 실행되는 지점이다. ``c sigreturn()``은 syscall 이기 때문에 이미 호출되면서 kernel mode로 넘어간다.
* 따라서 ``c sigreturn()``은 `` iret``과는 다르다.

32bit

/usr/include/x86_64-linux-gnu/asm/unistd_32.h    link

```c
#define __NR_read 3
#define __NR_write 4
#define __NR_sigreturn 119
#define __NR_execve 11    0xb
```

/usr/include/x86_64-linux-gnu/bits/sigcontext.h

```c

struct sigcontext

{

  unsigned short gs, __gsh;

  unsigned short fs, __fsh;

  unsigned short es, __esh;

  unsigned short ds, __dsh;

  unsigned long edi;

  unsigned long esi;

  unsigned long ebp;

  unsigned long esp;

  unsigned long ebx;

  unsigned long edx;

  unsigned long ecx;

  unsigned long eax;

  unsigned long trapno;

  unsigned long err;

  unsigned long eip;

  unsigned short cs, __csh;

  unsigned long eflags;

  unsigned long esp_at_signal;

  unsigned short ss, __ssh;

  struct _fpstate * fpstate;

  unsigned long oldmask;

  unsigned long cr2;

};

```

* sigreturn으로 리턴하는 위치 바로 아래부터 `` gs, __gsh, fs, ...`` 순으로 위치시키면 된다.



```bash

$ objdump -d /lib/i386-linux-gnu/libc.so.6  | grep \<execve\>: -A 20

000b5f40 <execve>:

   b5f40:       57                      push   %edi

   b5f41:       53                      push   %ebx

   b5f42:       8b 54 24 14             mov    0x14(%esp),%edx

   b5f46:       e8 90 08 07 00          call   1267db <__frame_state_for+0x35b>

   b5f4b:       81 c3 b5 50 0f 00       add    $0xf50b5,%ebx

   b5f51:       8b 4c 24 10             mov    0x10(%esp),%ecx

   b5f55:       8b 7c 24 0c             mov    0xc(%esp),%edi

   b5f59:       87 df                   xchg   %ebx,%edi

   b5f5b:       b8 0b 00 00 00          mov    $0xb,%eax

   b5f60:       65 ff 15 10 00 00 00    call   *%gs:0x10

```


따라서 레지스터를 다음과 같이 설정한다.

```bash

eax : 0xb

ebx : argument string

ecx : 0x00000000

edx : 0x00000000

eip : syscall OR ret

```

Note) 이 밖의 다른 레지스터들도 적절히 설정해주어야 한다.


cs, ss

반드시 정상일 때의 값으로 설정해주어야 한다! 
커널모드인지 유저모드인지는 segment register를 보고 판단하기 때문에.
정상일 때의 값은 보통 다음과 같다.
```
cs : 0x33
ss : 0x2b
```

fs, gs

`` fs, gs`` 레지스터를 모두 ``c 0x0``으로 설정했는데도 sigreturn이 실행되고 나면 ``c 0x3``이 된다.
``c 0x3``이어도 실행에는 문제가 없으나, ``c 0xffff``같은 큰 값으로 설정하면 ``c 0x0``으로 만들 수 있다.

*fpstate

반드시 ``c 0x0``으로 설정한다.
`` eax``가 설정해놓은 값 ``c 0xb``가 되는 것이 아니라 ``c 0x0``이 되어버리는 경우 이게 이상한 값으로 설정되어 있을 가능성이 크다.

EOF

쉘이 바로 종료되는 경우 EOF가 넘어가기 때문이다.
```bash
( cat exploit ; cat ) | ./srop_taget

```


64bit

/usr/include/x86_64-linux-gnu/asm/unistd_64.h    link

```c
#define __NR_syncfs 306    // used to set RAX = 0 → __NR_read
#define __NR_read 0
#define __NR_write 1
#define __NR_rt_sigreturn 15    0xf
#define __NR_execve 59          0x3b
```

/usr/include/x86_64-linux-gnu/bits/sigcontext.h

* `` rsp`` 기준으로 parameter를 전달하기 때문에, `` syscall``하기 직전 상태에서 ``c rsp+0x28 == r8``이 되도록 위치시키면 된다.
```c
#include <signal.h>

struct sigcontext    // sizeof(st) == 256 (0x100)
{
  __uint64_t r8;     rsp+0x28
  __uint64_t r9;
  __uint64_t r10;
  __uint64_t r11;
  __uint64_t r12;
  __uint64_t r13;
  __uint64_t r14;
  __uint64_t r15;
  __uint64_t rdi;    rsp+0x68
  __uint64_t rsi;
  __uint64_t rbp;
  __uint64_t rbx;
  __uint64_t rdx;
  __uint64_t rax;
  __uint64_t rcx;
  __uint64_t rsp;    rsp+0xa0
  __uint64_t rip;
  __uint64_t eflags;
  unsigned short cs;
  unsigned short gs;
  unsigned short fs;
  unsigned short __pad0;  // == ss;
  __uint64_t err;
  __uint64_t trapno;
  __uint64_t oldmask;
  __uint64_t cr2;
  __extension__ union        // UNION
    {
      struct _fpstate * fpstate;
      __uint64_t __fpstate_word;
    };
  __uint64_t __reserved1 [8];
};
```

따라서  레지스터를 다음과 같이 설정한다.

```bash

rax : 0x3b

rdx : envp ( 0x0 )

rsi : argv ( 0x0 )

rdi : argument string

rip : syscall OR ret

```

vsyscall's syscall; ret gadget

```c

0xffffffffff600000 0xffffffffff601000 r-xp      [vsyscall]

gdb-peda$ x/3i 0xffffffffff600007

   0xffffffffff600007:  syscall 

   0xffffffffff600009:  ret    

```


'Security > System Exploit' 카테고리의 다른 글

realloc fake size  (0) 2017.11.04
Return to VDSO using ELF Auxiliary Vectors leck  (0) 2017.09.02
The House of Einherjar  (0) 2017.08.16
The House of Force  (0) 2017.08.15
[UNDEAD] The House of Mind  (0) 2017.08.15