3omh4n
3omh4
3omh4n
  • 분류 전체보기 (65)
    • wargame - system (0)
      • DreamHack (9)
      • FTZ (5)
      • LoB (4)
      • pwnable.kr (1)
    • wargame - reversing (0)
      • DreamHack (5)
    • wargame - web (6)
      • webhacking.kr (3)
      • DreamHack (3)
      • HackCTF (0)
    • CS (21)
      • system (19)
      • reversing (0)
      • Linux (2)
    • python (5)
      • Flask (5)
    • C++ (3)
    • 개인기록 (2)
    • 이거저거 (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

최근 댓글

최근 글

태그

  • LOB
  • C++
  • Dreamhack
  • hackerschool
  • systemhacking
  • pwntools
  • langage
  • ROP
  • System
  • flask
  • BOF
  • Devtools
  • error
  • Programming
  • ftz
  • RELRO
  • python
  • Reversing
  • hacking
  • webhacking

티스토리

전체 방문자
오늘
어제
3omh4n

3omh4

[System][Dreamhack] bypass canary
CS/system

[System][Dreamhack] bypass canary

2022. 8. 10. 18:18

카나리 우회 방법

 

1. 무차별 대입 (Brute Force)

 x64 아키텍처에서는 8바이트의 카나리가 생성되고, x86 아키텍처에서는 4바이트의 카나리가 생성된다. 카나리의 첫 바이트는 NULL 바이트이므로, 실제 7바이트(x64), 3바이트(x86)의 랜덤한 값이 포함된다.

⇒ 카나리를 알아내려면 256^7(x64), 256^3(x86) 번의 연산이 필요하다. (1바이트 = 8비트 = 2^8 = 256)

⇒ 연산량이 많아 무차별 대입은 비현실적, 불가능

 

2. TLS 접근

 카나리는 TLS에 전역변수로 저장(fs:0x28)되고, 매 함수마다 이를 참조해 사용한다. TLS 주소는 매 실행마다 바뀌지만, 실행 중 TLS의 주소를 알 수 있고, 임의 주소에 대한 읽기 또는 쓰기가 가능하다면 TLS에 설정된 카나리 값을 읽거나 조작가능하다.

-1. 알아낸 카나리를 이용해 스택 버퍼 오버플로우를 일으킬 때 알아낸 값으로 카나리 덮기

-2. 조작한 카나리 값을 이용해 스택 버퍼 오버플로우를 일으킬 때 조작한 값으로 카나리 덮기

 

 3. 스택 카나리 릭

// Name: bypass_canary.c
// Compile: gcc -o bypass_canary bypass_canary.c

#include <stdio.h>
#include <unistd.h>

int main() {

  char memo[8];
  char name[8];
  
  printf("name : ");
  read(0, name, 64);
  printf("hello %s\n", name);
  
  printf("memo : ");
  read(0, memo, 64);
  printf("memo %s\n", memo);
  
  return 0;
}

카나리 릭 실습을 위한 실습코드이다.  카나리의 첫 바이트가 NULL 바이트이므로 name을 입력할 때 'a' 8개 (+개행(0x0a) )입력하면 다음과 같이 이상한 값도 같이 출력되는 것을 확인할 수 있다. memo를 입력할 때는 'a'를 16개 (+개행(0x0a) ) 입력하면 name과 똑같은 이상한 값이 출력되는 것을 알 수 있다.

gdb를 이용해 하나씩 확인해보자.

 

fs:0x28 값 확인

pwndbg> disassemble main
Dump of assembler code for function main:
   0x0000000000001189 <+0>:	endbr64 
   0x000000000000118d <+4>:	push   rbp
   0x000000000000118e <+5>:	mov    rbp,rsp
   0x0000000000001191 <+8>:	sub    rsp,0x20
   0x0000000000001195 <+12>:	mov    rax,QWORD PTR fs:0x28
   0x000000000000119e <+21>:	mov    QWORD PTR [rbp-0x8],rax
   0x00000000000011a2 <+25>:	xor    eax,eax
   0x00000000000011a4 <+27>:	lea    rdi,[rip+0xe59]        # 0x2004
   0x00000000000011ab <+34>:	mov    eax,0x0
   0x00000000000011b0 <+39>:	call   0x1080 <printf@plt>
   0x00000000000011b5 <+44>:	lea    rax,[rbp-0x10]		  # name(8) + canary(8)
   0x00000000000011b9 <+48>:	mov    edx,0x40
   0x00000000000011be <+53>:	mov    rsi,rax
   0x00000000000011c1 <+56>:	mov    edi,0x0
   0x00000000000011c6 <+61>:	call   0x1090 <read@plt>
   0x00000000000011cb <+66>:	lea    rax,[rbp-0x10]
   0x00000000000011cf <+70>:	mov    rsi,rax
   0x00000000000011d2 <+73>:	lea    rdi,[rip+0xe33]        # 0x200c
   0x00000000000011d9 <+80>:	mov    eax,0x0
   0x00000000000011de <+85>:	call   0x1080 <printf@plt>
   0x00000000000011e3 <+90>:	lea    rdi,[rip+0xe2c]        # 0x2016
   0x00000000000011ea <+97>:	mov    eax,0x0
   0x00000000000011ef <+102>:	call   0x1080 <printf@plt>
   0x00000000000011f4 <+107>:	lea    rax,[rbp-0x18]		  # memo(8) + name(8) + canary(8)
   0x00000000000011f8 <+111>:	mov    edx,0x40
   0x00000000000011fd <+116>:	mov    rsi,rax
   0x0000000000001200 <+119>:	mov    edi,0x0
   0x0000000000001205 <+124>:	call   0x1090 <read@plt>
   0x000000000000120a <+129>:	lea    rax,[rbp-0x18]
   0x000000000000120e <+133>:	mov    rsi,rax
   0x0000000000001211 <+136>:	lea    rdi,[rip+0xe06]        # 0x201e
   0x0000000000001218 <+143>:	mov    eax,0x0
   0x000000000000121d <+148>:	call   0x1080 <printf@plt>
   0x0000000000001222 <+153>:	mov    eax,0x0
   0x0000000000001227 <+158>:	mov    rcx,QWORD PTR [rbp-0x8]
   0x000000000000122b <+162>:	xor    rcx,QWORD PTR fs:0x28
   0x0000000000001234 <+171>:	je     0x123b <main+178>
   0x0000000000001236 <+173>:	call   0x1070 <__stack_chk_fail@plt>
   0x000000000000123b <+178>:	leave  
   0x000000000000123c <+179>:	ret    
End of assembler dump.

main의 disassemble을 확인하면 위와 같다. main + 12에 브레이크 포인트를 설정하고 이를 실행 후 rax를 확인한다. 

이 다음 명령을 실행한 후 스택을 확인하면 canary에 동일한 값이 들어가있는 것을 확인할 수 있다.

스택의 구조를 보면 다음과 같다.

카나리의 첫번째 NULL바이트로 인해 name에 'a' 8개, 즉 8바이트만 채우게되면 다음 출력시 카나리의 값은 출력되지 않고 'hello aaaaaaaa"만 출력된다. 따라서 카나리의 첫 바이트까지 덮어야하기 때문에 'a'를 9개 입력해야 카나리 값을 화면에 출력할 수 있다.

 

name에 'a' 8개 입력

8개 입력 후 스택값의 변화를 보면 'aaaaaaaa\x0a'로 바뀌는 것을 확인할 수 있다. 'aaaaaaaa'의 입력을 하면 엔터(개행)를 통해 입력을 완료한다. read()함수는 'aaaaaaaa'를 입력하면 실제 입력은 'aaaaaaaa\x0a'가 된다. 따라서 카나리의 첫번째 바이트가 \x0a로 바뀌게 된다. 따라서 문자열의 끝을 뜻하는 NULL(\x00)이 없어 카나리 값까지 출력된다.

 

 

'CS > system' 카테고리의 다른 글

[System][Dreamhack] Exploit tech : Return to Shellcode  (0) 2022.09.06
[System][Dreamhack] wargame - ssp_001 스택 순서  (0) 2022.08.24
[System][Dreamhack] 메모리 보호 기법 memory mitigation - Stack Canary  (0) 2022.08.09
[System][Dreamhack] stack buffer overflow - 스택 버퍼 오버플로우  (0) 2022.05.11
[System] 레이스 컨디션(Race Condition)  (0) 2022.05.09
    'CS/system' 카테고리의 다른 글
    • [System][Dreamhack] Exploit tech : Return to Shellcode
    • [System][Dreamhack] wargame - ssp_001 스택 순서
    • [System][Dreamhack] 메모리 보호 기법 memory mitigation - Stack Canary
    • [System][Dreamhack] stack buffer overflow - 스택 버퍼 오버플로우
    3omh4n
    3omh4n

    티스토리툴바