EYEN

Dreamhack-stack canary/ r2s 본문

Wargame/pwn

Dreamhack-stack canary/ r2s

EYEN 2023. 1. 29. 22:50

카나리 정적 분석
카나리를 활성화하여 컴파일한 바이너리와 비활성화하여 컴파일한 바이너리를 비교하여 스택 카나리의 원리를 살펴보겠다. 
 
main 함수의 프롤로그

   0x00000000000006b2 <+8>:     mov    rax,QWORD PTR fs:0x28     
   0x00000000000006bb <+17>:    mov    QWORD PTR [rbp-0x8],rax   
   0x00000000000006bf <+21>:    xor    eax,eax

fs:0x28의 데이터를 읽어서 rax에 저장한다.
fs는 세그먼트 레지스터의 일종으로, 리눅스는 프로세스가 시작될 때 fs:0x28에 랜덤 값을 저장한다. 따라서 rax에는 리눅스가 생성한 랜덤 값이 저장된다.
 
생성한 랜덤값은 rbp-0x8에 저장된다
 
*리눅스는 fs를 thread local storage(TLS)를 가리키는 포인터로 사용한다. fs에는 카나리를 비롯한 프로세스 실행에 필요한 여러 데이터가 저장된다.
 
main 함수의 에필로그

   0x00000000000006dc <+50>:    mov    rcx,QWORD PTR [rbp-0x8]    
   0x00000000000006e0 <+54>:    xor    rcx,QWORD PTR fs:0x28      
   0x00000000000006e9 <+63>:    je     0x6f0 <main+70>                   
   0x00000000000006eb <+65>:    call   0x570 <__stack_chk_fail@plt>

rbp-8에 저장한 카나리를 rcx로 옮긴다.
rcx와 fs:0x28에 저장된 카나리와 xor한다.
두 값이 동일하면 연산결과가 0이 되면서 je의 조건을 만족하게 되고, main 함수는 정상적으로 반환된다.
동일하지 않으면 __stack_chk_fail이 호출되면서 프로그램이 강제로 종료된다.
 
rax에 첫 바이트가 널바이트인 8바이트 데이터가 저장되어 있다.

pwndbg> ni
   0x5555555546b2 <main+8>     mov    rax, qword ptr fs:[0x28] <0x5555555546aa>
 ► 0x5555555546bb <main+17>    mov    qword ptr [rbp - 8], rax
   0x5555555546bf <main+21>    xor    eax, eax
pwndbg> print /a $rax
$1 = 0xf80f605895da3c00

rbp-0x8에 랜덤한 값이 저장된 모습

pwndbg> ni
   0x5555555546b2 <main+8>     mov    rax, qword ptr fs:[0x28] <0x5555555546aa>
   0x5555555546bb <main+17>    mov    qword ptr [rbp - 8], rax
 ► 0x5555555546bf <main+21>    xor    eax, eax
pwndbg> x/gx $rbp-0x8
0x7fffffffe238:	0x2619d41073c14900

HHH를 계속해서 입력했을 때 rcx에 0x484848가 있는 모습

pwndbg> ni
   0x5555555546dc <main+50>    mov    rcx, qword ptr [rbp - 8] <0x7ffff7af4191>
 ► 0x5555555546e0 <main+54>    xor    rcx, qword ptr fs:[0x28]
   0x5555555546e9 <main+63>    je     main+70 <main+70>
pwndbg> print /a $rcx 
$2 = 0x4848484848484848

 
카나리 생성 과정
카나리 값은 프로세스가 시작될 때, TLS에 전역 변수로 저장되고, 각 함수마다 프롤로그와 에필로그에서 이 값을 참조한다. 
 
TLS의 주소
fs는 TLS를 가리키므로 fs값을 알면 TLS의 주소를 알 수 있다. 그러나 리눅스에서 fs의 값은 특정 시스템 콜을 사용해야만 조회하거나 설정할 수 있다. gdb에서 다른 레지스터의 값을 출력하듯 info register fs나, print $fs 와같은 방식으로는 값을 알수 없다
 
그래서 arch_prtl(int code, unsigned log addr)라는 fs 값을 설정할 때 호출되는 시스템 콜로부터 gs가 어떤 값으로 설정되는지 보겠다.
이 시스템 콜을 arch_prctl(ARCH_SET_FS, addr)의 형태로 호출하면 fs값은 addr로 설정된다. gdb에는 특정 이벤트가 발생했을 때, 프로세스를 중지시키는 catch라는 명령어가 있다. 이 명령어로 arch_prctl에 적용해보면
rdi의 값이 0x1002인데, 이는 ARCH_SET_FS의 상숫값이다. rsi의 값이 0x7ffff7fdb4c0이므로 이 프로세스는 TLS를 여기에 저장할 것이고, fs는 이를 가리키게 될 것이다. 카나리가 저장될 fs+0x28(0x7ffff7fdb4c0+0x28 의 값을 보면

Catchpoint 1 (call to syscall arch_prctl), 0x00007ffff7dd6024 in init_tls () at rtld.c:740
740	rtld.c: No such file or directory.
 ► 0x7ffff7dd4024 <init_tls+276>    test   eax, eax
   0x7ffff7dd4026 <init_tls+278>    je     init_tls+321 <init_tls+321>
   0x7ffff7dd4028 <init_tls+280>    lea    rbx, qword ptr [rip + 0x22721]
pwndbg> info register $rdi
rdi            0x1002   4098          // ARCH_SET_FS = 0x1002
pwndbg> info register $rsi
rsi            0x7ffff7fdb4c0   140737354032320 
pwndbg> x/gx 0x7ffff7fdb4c0+0x28
0x7ffff7fdb4e8:	0x0000000000000000

아무 것도 없다. 
 
gdb의 watch 명령어( 특정 주소에 저장된 값이 변경되면 프로세스를 중단시키는 명령어)이다.
진행시키면 security_init 함수에서 프로세스가 멈춘다. TLS+0x28의 값을 조회하면 0x2f35207b8c368d00 가 카나리로 설정되어있다.
 

 오버플로우를 일으켜 널바이트를 덮어 카나리를 출력한다. 카나리는 0tTc(xU8 일 것이다.
 
 
r2s

5번줄이랑 8번줄 받는 값 조정하는 걸 이해하지 못했다. 코딩실력 부족;;;
 

 
 


 

 

'Wargame > pwn' 카테고리의 다른 글

canary가 할당되는 경로 확실히 알기  (0) 2023.02.25
Dreamhack-basic_rop_x64  (0) 2023.02.17
Dreamhack-basic_exploitation_000  (0) 2023.01.29
Dreamhack-shell_basic  (0) 2023.01.26
dreamhack shellbasic- 쉘코드에 대해  (0) 2023.01.26