what is master canary & fs_base

우리가 평소에 자주 봐았던

C
Dump of assembler code for function main:
   0x0000000000400b05 <+0>:	push   %rbp
   0x0000000000400b06 <+1>:	mov    %rsp,%rbp
   0x0000000000400b09 <+4>:	sub    $0x60,%rsp
   0x0000000000400b0d <+8>:	mov    %edi,-0x54(%rbp)
   0x0000000000400b10 <+11>:	mov    %rsi,-0x60(%rbp)
   0x0000000000400b14 <+15>:	mov    %fs:0x28,%rax
   0x0000000000400b1d <+24>:	mov    %rax,-0x8(%rbp)

함수의 프롤로그 부분이다. 여기서

0x0000000000400b14 <+15>: mov %fs:0x28,%rax

이 부분이 canary를 담는 부분인 것은 다 알것이다. 그럼 이제 이 부분이 어떻게 작동되는지 fs가 뭔지에 대해 알아보도록 하자

%fs:0x28 이 말은 fs 레지스터의 값에 0x28을 더한 주소의 값을 의미한다.

fs 레지스터 값은 fs_base의 값이 들어간다.

즉, fs_base + 0x28의 위치한 8바이트를 가져온다고 보면 된다.

일반적인 우분투 버전의 Pwngdb 에서는

C
pwndbg> p/x $fs_base
$3 = 0x7ffff7fad740

다음과 같이 fs_base 를 확인 할 있고

다음이나 일반적인 우분투 버전의 일반 gdb에서도

C
pwndbg> i r
rax            0x400b05            4197125
rbx            0x7fffffffe208      140737488347656
rcx            0x400c90            4197520
rdx            0x7fffffffe218      140737488347672
rsi            0x7fffffffe208      140737488347656
rdi            0x1                 1
rbp            0x7fffffffe0e0      0x7fffffffe0e0
rsp            0x7fffffffe0e0      0x7fffffffe0e0
r8             0x400d00            4197632
r9             0x7ffff7fca380      140737353917312
r10            0x7ffff7c140c8      140737350025416
r11            0x7ffff7c2a200      140737350115840
r12            0x1                 1
r13            0x0                 0
r14            0x0                 0
r15            0x7ffff7ffd000      140737354125312
rip            0x400b09            0x400b09 <main+4>
eflags         0x246               [ PF ZF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
k0             0x100010            1048592
k1             0x402010            4202512
k2             0x0                 0
k3             0x0                 0
k4             0x0                 0
k5             0x0                 0
k6             0x0                 0
k7             0x0                 0
fs_base        0x7ffff7fad740      140737353799488
gs_base        0x0                 0

info reg 명령어로 쉽게 알아낼 수 있다.

하지만 16.04 우분투에서는 안된다. 따라서

C
pwndbg> p/x $fs_base
$1 = Value can't be converted to integer.
pwndbg> fsbase      
0x7ffff7fed700
pwndbg> 

fsbase 명령어로 해야 한다.

fs_base를 알았으면, 다음과 같이 바로 master canary 값을 볼 수가 있다.

C
pwndbg> p/x $fs_base
$1 = 0x7ffff7fad740
pwndbg> x/gx 0x7ffff7fad740 + 0x28
0x7ffff7fad768:	0xd1d621876d961200
pwndbg> 

참고로,

C
pwndbg> vmmap 0x7ffff7fad740
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x7ffff7e05000     0x7ffff7e12000 rw-p     d000      0 [anon_7ffff7e05]
►   0x7ffff7fad000     0x7ffff7fb0000 rw-p     3000      0 [anon_7ffff7fad] +0x740
    0x7ffff7fb0000     0x7ffff7fb1000 r--p     1000      0 /usr/lib/x86_64-linux-gnu/libpthread.so.0
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
          0x400000           0x401000 r-xp     1000      0 /home/amemoyoi/pwn_venv/master_canary
          0x601000           0x602000 r--p     1000   1000 /home/amemoyoi/pwn_venv/master_canary
          0x602000           0x603000 rw-p     1000   2000 /home/amemoyoi/pwn_venv/master_canary
    0x7ffff7c00000     0x7ffff7c28000 r--p    28000      0 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7c28000     0x7ffff7db0000 r-xp   188000  28000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7db0000     0x7ffff7dff000 r--p    4f000 1b0000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7dff000     0x7ffff7e03000 r--p     4000 1fe000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e03000     0x7ffff7e05000 rw-p     2000 202000 /usr/lib/x86_64-linux-gnu/libc.so.6
    0x7ffff7e05000     0x7ffff7e12000 rw-p     d000      0 [anon_7ffff7e05]
    0x7ffff7fad000     0x7ffff7fb0000 rw-p     3000      0 [anon_7ffff7fad]
    0x7ffff7fb0000     0x7ffff7fb1000 r--p     1000      0 /usr/lib/x86_64-linux-gnu/libpthread.so.0
    0x7ffff7fb1000     0x7ffff7fb2000 r-xp     1000   1000 /usr/lib/x86_64-linux-gnu/libpthread.so.0
    0x7ffff7fb2000     0x7ffff7fb3000 r--p     1000   2000 /usr/lib/x86_64-linux-gnu/libpthread.so.0
    0x7ffff7fb3000     0x7ffff7fb4000 r--p     1000   2000 /usr/lib/x86_64-linux-gnu/libpthread.so.0
    0x7ffff7fb4000     0x7ffff7fb5000 rw-p     1000   3000 /usr/lib/x86_64-linux-gnu/libpthread.so.0
    0x7ffff7fbd000     0x7ffff7fbf000 rw-p     2000      0 [anon_7ffff7fbd]
    0x7ffff7fbf000     0x7ffff7fc3000 r--p     4000      0 [vvar]
    0x7ffff7fc3000     0x7ffff7fc5000 r-xp     2000      0 [vdso]
    0x7ffff7fc5000     0x7ffff7fc6000 r--p     1000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fc6000     0x7ffff7ff1000 r-xp    2b000   1000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ff1000     0x7ffff7ffb000 r--p     a000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000  36000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000  38000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp     1000      0 [vsyscall]

이 fs_base 주소는 TLS( Thread Local Storage ) 영역을 가리키므로 므로 커널상에서 mmap으로 매핑되어 있는 anon_7ffff7fad → 익명 영역임을 알 수 있다.

TLS 영역은 스레드 마다 존재하며 메인스레드에서 TLS 영역은 상당히 떨어져 있지만,

이후에 사용자에 의해 생긴 스레드의 TLS 영역은 스레드의 스택 영역의 근처의 위치하게 된다. 따라서 스레드 스택에서 TLS 영역을 침범하여 master canary를 leak을 하거나 스레드 상에서만 canary overwrite이 가능해진다.

링크

thread 마다 fs_base의 위치가 다르다.

fs_base는 TLS ( Thread Local Storage ) 영역을 가리키고 있기에 당연하게도

thread 마다 fs_base값이 다르다.

C
pwndbg> threads
  global_num  name           status    pc        symbol
------------  -------------  --------  --------  --------------
           1  master_canary  stopped   0x400bb4  main+175
           3  master_canary  stopped   0x400a5b  thread_routine

Showing 2 of 2 threads.
pwndbg> thread 3
[Switching to thread 3 (Thread 0x7ffff70006c0 (LWP 1741248))]
#0  0x0000000000400a5b in thread_routine ()
pwndbg> p/x $fs_base
$2 = 0x7ffff70006c0
pwndbg> thread 1
[Switching to thread 1 (Thread 0x7ffff7fad740 (LWP 1740526))]
#0  0x0000000000400bb4 in main ()
pwndbg> p/x $fs_base
$3 = 0x7ffff7fad740