SROP
System/System Hacking

SROP

728x90

SROP (SigReturn-Oriented Programming)

: sigreturn 시스템 콜을 이용해 레지스터에 원하는 값을 저장하는 방식 >> 원하는 시스템 함수를 호출할 수 있다.


Signal

: 프로세스에게 이벤트가 발생했음을 알리는 역할

 

- 다른 프로세스에게 시그널을 전송할 수 있다.

   = 시그널이 발생하면 그 시그널에 대한 결과로 다른 프로세스에게 시그널이 전송되는 것이 가능

   원시적인 형태의 IPC (interprocess communication; 프로세스 간 통신)로 사용

    자기 자신에게 시그널을 전송하는 것도 가능

 

- 일반적으로 커널이 시그널을 송신

- 이벤트 종류

    하드웨어 예외가 발생한 경우

    사용자가 터미널 특수 문자 중 하나(Ctrl+C; interrupt character)(Ctrl+Z; suspend character) 입력한 경우

    소프트웨어 이벤트 발생한 경우: 파일 디스크립터에 입력 발생 / 타이머 만료 / 해당 프로세스의 자식 프로세스 종료

 

- 시그널 생성 → 프로세스에 전달 → 종류에 따라 동작 실행    

: 시그널 무시 / 프로세스 종료 / 코어 덤프 파일 생성 후 프로세스 종료 / 프로세스 중지 / 프로세스 실행 재개

   


Signal handler

= 프로그램이 특정 시그널의 기본 동작을 수행하는 대신 프로그래머가 원하는 동작을 수행할 수 있도록 변경

 

- User Mode 프로세스에 정의, User Mode code segment에 포함

 

- Signal handler가 User Mode에서 실행 중  : Kernel Mode에서 handle_signal()함수 실행

handle_signal()

:

User Mode → Kernel Mode 진입: User Mode에서 사용중이던 context를 Kernel Stack에 저장

Kernel Mode User Mode 진입: Kernel Stack 모두 초기화 

 

초기화 문제를 해결하기 위해 setup_frame(), sigreturn() 함수 사용

setup_frame(): User Mode의 스택을 설정

sigreturn(): Kernel Mode 스택에 하드웨어 context 복사 + User Mode 스택의 원래 content 저장

 

- Signal handler 처리 방식

인터럽트/예외 발생: 프로세스 Kernel Mode로 전환

 

User Mode로 돌아가기 전: 커널은 do_signal()함수 실행

do_signal(): handle_signal() 호출해 signal 처리

handle_signal(): setup_frame() 호출해 User Mode 스택에 context 저장

 

User Mode로 전환: signal_handler 실행

 

signal handler 종료: setup_frame()함수에 의해 User Mode 스택에 저장된 리턴 코드 실행 → 리턴 코드에 의해 sigreturn() 시스템 함수 실행

sigreturn() 시스템 함수: Kernel Mode 스택에서 일반 프로그램의 하드웨어 context를 User Mode의 스택에 복사

                                       restore_sigcontext() 호출해 User Mode 스택을 원래 상태로 복원

(restore_sigcontext(): User Mode 스택을 원래 상태로 복원)

 

시스템 호출 종료: 일반 프로그램 재개


sigreturn()

sigreturn() 시스템 함수; signal을 처리하는 프로세스가 Kernel Mode에서 User Mode로 돌아올 때 스택을 복원하기 위해 사용하는 함수

스택 복원을 위해 restore_sigcontext() 함수를 호출

 

- x86 sigreturn()

#ifdef CONFIG_X86_32
asmlinkage unsigned long sys_sigreturn(void){
    struct pt_regs *regs = current_pt_regs();
    struct sigframe __user *frame;
...
    if (restore_sigcontext(regs, &frame->sc, 0))
        goto badframe;
...
}
#endif /* CONFIG_X86_32 */

https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L604

 

- x64 sigreturn()

asmlinkage long sys_rt_sigreturn(void){
    struct pt_regs *regs = current_pt_regs();
    struct rt_sigframe __user *frame;
...
    if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
        goto badframe;
...
}

https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L636

 

restore_sigcontext()

COPY_SEG(), COPY() 등을 이용해 스택에 저장된 값을 각 레지스터에 복사

ROP에서는 값을 레지스터에 저장하는 gadget이 필요했지만

gadget없어도 sigreturn()함수를 이용해 각 레지스터에 원하는 값을 저장 가능

 

- x86 restore_sigcontext()

static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long uc_flags){
    unsigned long buf_val;
    void __user *buf;
    unsigned int tmpflags;
    unsigned int err = 0;
 
    /* Always make any pending restarted system calls return -EINTR */
    current->restart_block.fn = do_no_restart_syscall;
 
    get_user_try {
 
#ifdef CONFIG_X86_32
        set_user_gs(regs, GET_SEG(gs));
        COPY_SEG(fs);
        COPY_SEG(es);
        COPY_SEG(ds);
#endif /* CONFIG_X86_32 */
 
        COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
        COPY(dx); COPY(cx); COPY(ip); COPY(ax);
...
}

https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L96

#define sigframe_ia32       sigframe
 
...
 
#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
struct sigframe_ia32 {
    u32 pretcode;
    int sig;
    struct sigcontext_32 sc;
 
    struct _fpstate_32 fpstate_unused;
#ifdef CONFIG_IA32_EMULATION
    unsigned int extramask[_COMPAT_NSIG_WORDS-1];
#else /* !CONFIG_IA32_EMULATION */
    unsigned long extramask[_NSIG_WORDS-1];
#endif /* CONFIG_IA32_EMULATION */
    char retcode[8];
    /* fp state follows here */
};

 

스택에 저장된 레지스터 값들은 restore_sigcontext()함수의 인자값 &frame->sc에 의해 전달

&frame->sc : sigcontext 구조체; SROP를 이용할 때 스택에 아래와 같은 형태로 값을 저장해야 함

# ifdef __i386__
struct sigcontext {
    __u16               gs, __gsh;
    __u16               fs, __fsh;
    __u16               es, __esh;
    __u16               ds, __dsh;
    __u32               edi;
    __u32               esi;
    __u32               ebp;
    __u32               esp;
    __u32               ebx;
    __u32               edx;
    __u32               ecx;
    __u32               eax;
    __u32               trapno;
    __u32               err;
    __u32               eip;
    __u16               cs, __csh;
    __u32               eflags;
    __u32               esp_at_signal;
    __u16               ss, __ssh;
    struct _fpstate __user      *fpstate;
    __u32               oldmask;
    __u32               cr2;
};

 

- x64 restore_sigcontext()

static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long uc_flags){
...
#ifdef CONFIG_X86_64
        COPY(r8);
        COPY(r9);
        COPY(r10);
        COPY(r11);
        COPY(r12);
        COPY(r13);
        COPY(r14);
        COPY(r15);
#endif /* CONFIG_X86_64 */
 
        COPY_SEG_CPL3(cs);
        COPY_SEG_CPL3(ss);
 
#ifdef CONFIG_X86_64
        /*
         * Fix up SS if needed for the benefit of old DOSEMU and
         * CRIU.
         */
        if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) &&
                 user_64bit_mode(regs)))
            force_valid_ss(regs);
#endif
...
}

https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L96

x64의 경우

스택에 저장된 레지스터 값들은 restore_sigcontext()함수의 인자값 &frame->uc.uc_mcontext에 의해 전달

struct rt_sigframe_x32 {
    u64 pretcode;
    struct ucontext_x32 uc;
    compat_siginfo_t info;
    /* fp state follows here */
};



struct ucontext_x32 {
    unsigned int      uc_flags;
    unsigned int      uc_link;
    compat_stack_t    uc_stack;
    unsigned int      uc__pad0;     /* needed for alignment */
    struct sigcontext uc_mcontext;  /* the 64-bit sigcontext type */
    compat_sigset_t   uc_sigmask;   /* mask last for extensibility */
};

x64이기 때문에 사용되는 레지스터가 다름 >> sigcontext 구조체의 형태도 조금 다름

# else /* __x86_64__: */
struct sigcontext {
    __u64               r8;
    __u64               r9;
    __u64               r10;
    __u64               r11;
    __u64               r12;
    __u64               r13;
    __u64               r14;
    __u64               r15;
    __u64               rdi;
    __u64               rsi;
    __u64               rbp;
    __u64               rbx;
    __u64               rdx;
    __u64               rax;
    __u64               rcx;
    __u64               rsp;
    __u64               rip;
    __u64               eflags;     /* RFLAGS */
    __u16               cs;
    __u16               gs;
    __u16               fs;
    union {
        __u16           ss; /* If UC_SIGCONTEXT_SS */
        __u16           __pad0; /* Alias name for old (!UC_SIGCONTEXT_SS) user-space */
    };
    __u64               err;
    __u64               trapno;
    __u64               oldmask;
    __u64               cr2;
    struct _fpstate __user      *fpstate;   /* Zero when no FPU context */
#  ifdef __ILP32__
    __u32               __fpstate_pad;
#  endif
    __u64               reserved1[8];
};

 

https://www.lazenca.net/display/TEC/01.SROP%28Sigreturn-oriented+programming%29+-+x86

https://www.lazenca.net/display/TEC/02.SROP%28Sigreturn-oriented+programming%29+-+x64

SMALL

'System > System Hacking' 카테고리의 다른 글

SROP x64  (0) 2020.07.24
SROP x86  (0) 2020.07.23
64bit ROP  (0) 2020.06.14
32bit ROP  (0) 2020.06.14
RTL (Return to libc)  (0) 2020.05.24