유닉스 시스템에서 시그널이 발생할 경우
시그널을 받은 프로세스가 Kernel Mode로 handle_signal( ) 함수를 실행합니다.
이후 다시 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 조건
- 입력을 통해 Instruction Pointer의 조작이 가능해야 합니다.
대표적인 방법으로는 BOF를 사용한 RET 변조가 있습니다. - 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 |