유닉스 시스템에서 시그널이 발생할 경우

시그널을 받은 프로세스가 Kernel Mode로  handle_signal( ) 함수를 실행합니다.

 

출처 : https://www.lazenca.net/display/TEC/01.SROP%28Sigreturn-oriented+programming%29+-+x86?preview=/16810278/16810404/1527519944764.jpg

 

이후 다시 User Mode의 특정 시점으로 복귀하기 위해 사용중이던 context 를 복구해서 사용하게 됩니다.

이러한 과정은 스택 영역에 정보를 저장하였다가, signal_handler 함수가 종료되면서 값을 복원한다고 볼 수 있습니다.

이러한 기능을 수행하는 함수가 setup_frame( ) , sigreturn( ) 입니다.

  • setup_frame( ) : 사용중인 context 정보를 stack에 저장
  • sigreturn( ) : Kernel Mode stack 에서 context frame 정보를 읽어 User context를 세팅

중요한 점은 Signal Frame이 해당 프로세의 스택 영역에 저장된다는 점입니다.

BOF를 이용하여 Signal Frame의 값을 덮어쓰거나 임의로 값을 생성할 수 있다면,

이후에 레지스터를 조작하여 코드의 실행흐름을 원하는 방향으로 바꿀 수 있습니다.

이러한 공격기법이 SROP라 합니다.

 

SROP 조건


 

  1. 입력을 통해 Instruction Pointer의 조작이 가능해야 합니다.
    대표적인 방법으로는 BOF를 사용한 RET 변조가 있습니다.

  2. sigreturn이나 syscall을 호출하는 가젯이 필요합니다.

 

SROP Gadget


대표적으로 sigreturn gadget 사용해 볼 수 있습니다.

만약 rax값을 조작가능할 경우 syscall ret 가젯을 활용해보는 방법도 있습니다.

rax값을 조작하는 경우로는 pop rax 가젯을 활용해보거나 read( ) 함수 호출 시 반환 값은 입력된 문자열의 길이라는

점을 이용해 rax를 조작하는 방법 등을 사용합니다. 이는 ROP에 비해 상대적으로 적은 가젯 수를 사용하므로

다양한 상황에서 사용할 수 있을 것으로 보입니다.

 

sigcontext

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];
};

해당 구조체는 context frame에 사용되는 구조체입니다.

스택 상에서 순서에 맞게 값을 입력해주면 원하는 레지스터의 값을 변경하여 호출할 수 있게 됩니다.

추가로 다른 함수의 syscall 호출을 위해

rax : 호출하고자 하는 syscall

rdi : agr[0] , rsi : argv[1] , rdx : argv[2] ..

rip : syscall gadget , rsp : 변경하고자 하는 스택의 주소

 

SROP 보안

이러한 SROP를 막기 위해 sigreturn 프레임에 커널 단에서 사용하는 특정 비트를 추가해

해당 값을 검증하는 방식을 검토해 볼 수 있습니다. ( Stack Canary 와 유사. ) 

다음으로 signal hanlder의 호출상태를 추적하여 시그널 발생과 종료를 카운트해 차단하는 방법 등이 있겠습니다.

 

CTF SROP

편의를 위해 pwntools에서 제공하는 SigreturnFrame( ) 을 익스에 사용할 수 있다.

from pwn import *

#x64_86
frame = SigreturnFrame()
frame.rax = 0


#x32의 경우 kernel 매개변수로 전달해야 함
frame2 = SigreturnFrame(kernel='amd64')
frame.rax = 0

'CTF' 카테고리의 다른 글

Memo. RTL (Return to libc)  (0) 2022.04.09
서브루틴 내에서 연속된 함수호출 시 EBP ( RBP )  (0) 2021.07.21
CTF 문제 풀이  (0) 2021.06.19

+ Recent posts