EYEN
Dreamhack-stack canary/ r2s 본문
카나리 정적 분석
카나리를 활성화하여 컴파일한 바이너리와 비활성화하여 컴파일한 바이너리를 비교하여 스택 카나리의 원리를 살펴보겠다.
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 |