본문 바로가기
Security/System Hacking

[Lazenca] Protection Tech > PIE, RELRO

by 단월໒꒱ 2022. 2. 10.

[PIE]

1. PIE(Position Independent Executable)

 1) PIE

   - 위치 독립 코드로 이루어진 실행 가능한 바이너리

 

 2. 예시

 

#include <stdio.h>
  
char *gBuf = "Lazenca.0x0";
  
void lazenca() {
    printf("Lazenca.0x1\n");
}
  
void main(){
    printf("[.data]    : %p\n",gBuf);
    printf("[Function] : %p\n",lazenca);
}

 

   - build PIE file

 

lazenca0x0@ubuntu:~/Documents/Definition/protection/PIE$ gcc -o NoPIE PIE.c
lazenca0x0@ubuntu:~/Documents/Definition/protection/PIE$ gcc -fPIE -pie -o PIE PIE.c

 

3. 바이너리 파일의 보호 기법 확인

 1) checksec

    - checksec.sh에서 아래와 같은 결과 출력

     ① NoPIE : "No PIE"

     ② PIE : "PIE enabled"

 

 
 
 
4. PIE와 NonPIE 비교
 1) 주소
   - NoPIE PIE가 적용되지 않아 프로그램을 실행할 때마다 전역 변수와 사용자 정의 함수의 주소 변경 x
 
 

 

 

   - PIE : PIE가 적용되어 프로그램을 실행할 때마다 전역 변수와 사용자 정의 함수의 주소가 매번 달라짐

 

 

 

 

 
 2) 코드
   - 아래와 같이 NoPIE와 PIE가 적용된 바이너리의 차이점 확인 가능
      ① PIE가 적용되지 않은 바이너리 : 코드 영역의 값이 고정된 주소값
      ② PIE가 적용된 바이너리 : 코드 영역의 값이 고정된 주소값이 아닌 offset 값
        - 해당 offset 값을 이용해 할당된 메모리 영역에 동적으로 위치할 수 있음
      ③ 다음과 같이 동적으로 사용됨
          : 할당받은 메모리 영역(0x555555554000) + .text 영역의 main함수 코드의 offset 값(0x7c3)
              = main 함수의 시작 주소(0x00005555555547c3)
 
 
 
 
 
5. "Checksec.sh" 파일에서 PIE 여부 확인 방법
 1) Binary
   - 아래와 같은 방법으로 바이너리의 카나리 설정여부 확인
     ① 'readelf' 명령어를 이용해 해당 파일의 ELF Header 정보를 가져와 PIE 설졍여부 확인
     ② "Type:"의 값이 "EXEC"일 경우 PIE가 적용되지 않았다고 판단
     ③ "Type:"의 값이 "DYN"일 경우 PIE가 적용되었을 가능성이 있다고 판단
 

 

 
   
 
 2) Process
   - 아래와 같은 방법으로 프로세서의 카나리 설정여부 확인
     ① Binary의 확인 방식과 비슷하며, 전달되는 파일의 경로가 다음과 같이 다름.  ex. /proc/<PID>/exe
 

 

 

 

 

 

 

[RELRO]

1. RELRO

 1) RELRO (RELocation Read-Only)

   - ELF 바이너리 / 프로세스의 데이터 섹션의 보안을 강화하는 기술

   - Partial RELRO / Full RELRO 두 가지 모드 존재

   - RELRO, Partial RELRO, Full RELRO 차이점

 

 

 

2. 예제

 - 소스 코드

 

#include <stdio.h>
#include <string.h>
 
void main(){
 
        char address[16];
        size_t *pointer;
        int count = 1;
 
        while(count != 100)
        {
                printf("----- %d -----\n",count);
                memset(address,0,16);
                printf("Input Pointer address : ");
                fgets(address,16,stdin);
 
                pointer = strtol(address,0,16);
                printf("Pointer address : %p\n",pointer);
 
                printf("Input Pointer text : ");
                fgets(pointer,16,stdin);
                printf("Pointer text : %s\n",pointer);
                count++;
        }
        scanf("%s",address);
}

 

 

3. 바이너리 파일의 보호 기법 파악

 1) checksec.sh

 

 

 

2) 프로그램 헤더 & 동적 섹션

   - 아래와 같이 RELRO 적용시 "Program Header"와 "Dynamic Section"의 변화 확인 가능

      ① No RELRO

 

 

 

      ② Partial RELRO를 적용하게되면 다음과 같은 변화 발생

          - 'Program Header'에 'RELRO' 영역 생성

               - 해당 영역의 권한은 Read only 

          - 해당 영역에 포함되는 Section : INIT_ARRAY, FINI_ARRAY

          - 즉, GOT영역을 덮어쓸수 있음

 

 

 

 

      ③ Full RELRO를 적용하게되면 다음과 같은 변화 발생

          - 'Program Header'에 'RELRO' 영역 생성

               -해당 영역의 권한은 Read only

          - 해당 영역에 포함되는 Section : INIT_ARRAY, FINI_ARRAY, PLTGOT

          - Section영역에서 PLTRELSZ, PLTREL, JMPREL가 제거되고, 'BIND_NOW', 'FLAGS_1' Section이 추가됨

          - 즉, GOT영역을 덮어쓸수 없음

 

 

 

 

4. Overwrite test

 1) No RELRO

   - "__isoc99_scanf"의 GOT Address는 0x600c68이며, 아래와 같이 해당 영역에 값을 변경할 수 있음

 

 

lazenca0x0@ubuntu:~/Documents/Definition/protection/RELRO$ gdb -q ./RELRO-NoRelro
Reading symbols from ./RELRO-NoRelro...(no debugging symbols found)...done.
gdb-peda$ elfsymbol __isoc99_scanf
Detail symbol info
__isoc99_scanf@reloc = 0x6
__isoc99_scanf@plt = 0x4005d0
__isoc99_scanf@got = 0x600c68
gdb-peda$ x/gx 0x600c68
0x600c68:   0x00000000004005d6
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-NoRelro
----- 1 -----
Input Pointer address : 600c68 
Pointer address : 0x600c68
Input Pointer text : AAAA
Pointer text : AAAA
 
----- 2 -----
Input Pointer address : ^C
Program received signal SIGINT, Interrupt.
 
gdb-peda$ x/gx 0x600c68
0x600c68:   0x0000000a41414141
gdb-peda$

 

 

   - 아래와 같이 프로그램 헤더 정보와 메모리 맵을 통해 조금더 자세한 내용을 확인할 수 있음

       - "__isoc99_scanf"의 주소값은 '.got.plt'영역에 저장되어 있음

             - '.got.plt' 영역의 시작 주소는 0x600c20

       - 메모리 맵을 통해 해당 영역(0x00600000 ~ 0x00601000)에 'W' 쓰기 권한이 설정되어 있음

 

 

gdb-peda$ elfheader
.interp = 0x400200
.note.ABI-tag = 0x40021c
.note.gnu.build-id = 0x40023c
.gnu.hash = 0x400260
.dynsym = 0x400288
.dynstr = 0x400378
.gnu.version = 0x400406
.gnu.version_r = 0x400420
.rela.dyn = 0x400460
.rela.plt = 0x400490
.init = 0x400538
.plt = 0x400560
.plt.got = 0x4005e0
.text = 0x4005f0
.fini = 0x400884
.rodata = 0x400890
.eh_frame_hdr = 0x400900
.eh_frame = 0x400938
.init_array = 0x600a30
.fini_array = 0x600a38
.jcr = 0x600a40
.dynamic = 0x600a48
.got = 0x600c18
.got.plt = 0x600c20
.data = 0x600c70
.bss = 0x600c80
gdb-peda$ vmmap
Start              End                Perm  Name
0x00400000         0x00401000         r-xp  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-NoRelro
0x00600000         0x00601000         rw-p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-NoRelro
0x00601000         0x00622000         rw-p  [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p  mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd9000 0x00007ffff7fdc000 rw-p  mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p  mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p  [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp  [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p  mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p  [stack]
0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

 

 

 2) Partial RELRO

   - "__isoc99_scanf"의 GOT Address는 0x601048이며, 아래와 같이 해당 영역에 값을 변경할 수 있음

 

lazenca0x0@ubuntu:~/Documents/Definition/protection/RELRO$ gdb -q ./RELRO-Relro
Reading symbols from ./RELRO-Relro...(no debugging symbols found)...done.
gdb-peda$ elfsymbol __isoc99_scanf
Detail symbol info
__isoc99_scanf@reloc = 0x6
__isoc99_scanf@plt = 0x400600
__isoc99_scanf@got = 0x601048
gdb-peda$ x/gx 0x601048
0x601048:   0x0000000000400606
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
----- 1 -----
Input Pointer address : 601048
Pointer address : 0x601048
Input Pointer text : AAAA
Pointer text : AAAA
 
----- 2 -----
Input Pointer address : ^C
Program received signal SIGINT, Interrupt.
 
 
gdb-peda$ x/gx 0x601048
0x601048:   0x0000000a41414141
gdb-peda$

 

 

   - 아래와 같이 프로그램 헤더 정보와 메모리 맵을 통해 조금더 자세한 내용을 확인할 수 있습니다.

       - '.got.plt' 영역의 시작 주소는 0x601000 

       - 메모리 맵에서 RELRO가 적용되지 않은 프로그램과 다른 부분을 확인할 수 있습니다.

           - 0x600000 ~ 0x601000 영역의 권한은 r--p

               - 해당 영역에는 .init_array, .fini_array, .jcr, .dynamic, .got 헤더가 포함됩니다.

           - 0x601000 ~ 0x602000 영역의 권한은 rw-p

               - 해당 영역에는 .got.plt,등의 헤더가 포함됨

               - 이로 인해 .got.plt 영역에 값을 변경할 수 있음

 

 

gdb-peda$ elfheader
.interp = 0x400238
.note.ABI-tag = 0x400254
.note.gnu.build-id = 0x400274
.gnu.hash = 0x400298
.dynsym = 0x4002c0
.dynstr = 0x4003b0
.gnu.version = 0x40043e
.gnu.version_r = 0x400458
.rela.dyn = 0x400498
.rela.plt = 0x4004c8
.init = 0x400570
.plt = 0x400590
.plt.got = 0x400610
.text = 0x400620
.fini = 0x4008b4
.rodata = 0x4008c0
.eh_frame_hdr = 0x400930
.eh_frame = 0x400968
.init_array = 0x600e10
.fini_array = 0x600e18
.jcr = 0x600e20
.dynamic = 0x600e28
.got = 0x600ff8
.got.plt = 0x601000
.data = 0x601050
.bss = 0x601060
gdb-peda$ vmmap
Start              End                Perm  Name
0x00400000         0x00401000         r-xp  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
0x00600000         0x00601000         r--p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
0x00601000         0x00602000         rw-p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
0x00602000         0x00623000         rw-p  [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p  mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd9000 0x00007ffff7fdc000 rw-p  mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p  mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p  [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp  [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p  mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p  [stack]
0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

 

 

 3) Full RELRO

   - GOT 영역에 값을 변경할 수 없음

       - 디버거에서 '__isoc99_scanf'의 심볼 정보를 찾을 수 없음

   - 디스어셈블 코드에서 호출되는 함수 분석

       ① 0x4007fd 영역의 코드에서 0x4005f8 영역 호출

       ② 0x4005f8 영역의 코드에서 "rip+0x2009fa" 영역에 저장된 주소로 이동

       ③ "rip+0x2009fa" 영역은 0x600ff8 이며, 해당 영역에 저장된 값은 0x00007ffff7a784d0

       ④ 0x00007ffff7a784d0 영역은 __isoc99_scanf 함수의 시작 주소

 

 

lazenca0x0@ubuntu:~/Documents/Definition/protection/RELRO$ gdb -q ./RELRO-FullRelro
Reading symbols from ./RELRO-FullRelro...(no debugging symbols found)...done.
gdb-peda$ elfsymbol __isoc99_scanf
'__isoc99_scanf': no match found
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x00000000004006f6 <+0>:   push   rbp
   0x00000000004006f7 <+1>:   mov    rbp,rsp
   0x00000000004006fa <+4>:   sub    rsp,0x30
   0x00000000004006fe <+8>:   mov    rax,QWORD PTR fs:0x28
   0x0000000000400707 <+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x000000000040070b <+21>:  xor    eax,eax
   0x000000000040070d <+23>:  mov    DWORD PTR [rbp-0x2c],0x1
   0x0000000000400714 <+30>:  jmp    0x4007e2 <main+236>
   0x0000000000400719 <+35>:  mov    eax,DWORD PTR [rbp-0x2c]
   0x000000000040071c <+38>:  mov    esi,eax
   0x000000000040071e <+40>:  mov    edi,0x4008a4
   0x0000000000400723 <+45>:  mov    eax,0x0
   0x0000000000400728 <+50>:  call   0x4005c8
   0x000000000040072d <+55>:  lea    rax,[rbp-0x20]
   0x0000000000400731 <+59>:  mov    edx,0x10
   0x0000000000400736 <+64>:  mov    esi,0x0
   0x000000000040073b <+69>:  mov    rdi,rax
   0x000000000040073e <+72>:  call   0x4005d0
   0x0000000000400743 <+77>:  mov    edi,0x4008b4
   0x0000000000400748 <+82>:  mov    eax,0x0
   0x000000000040074d <+87>:  call   0x4005c8
   0x0000000000400752 <+92>:  mov    rdx,QWORD PTR [rip+0x2008b7]        # 0x601010 <stdin@@GLIBC_2.2.5>
   0x0000000000400759 <+99>:  lea    rax,[rbp-0x20]
   0x000000000040075d <+103>: mov    esi,0x10
   0x0000000000400762 <+108>: mov    rdi,rax
   0x0000000000400765 <+111>: call   0x4005e0
   0x000000000040076a <+116>: lea    rax,[rbp-0x20]
   0x000000000040076e <+120>: mov    edx,0x10
   0x0000000000400773 <+125>: mov    esi,0x0
   0x0000000000400778 <+130>: mov    rdi,rax
   0x000000000040077b <+133>: mov    eax,0x0
   0x0000000000400780 <+138>: call   0x4005f0
   0x0000000000400785 <+143>: cdqe  
   0x0000000000400787 <+145>: mov    QWORD PTR [rbp-0x28],rax
   0x000000000040078b <+149>: mov    rax,QWORD PTR [rbp-0x28]
   0x000000000040078f <+153>: mov    rsi,rax
   0x0000000000400792 <+156>: mov    edi,0x4008cd
   0x0000000000400797 <+161>: mov    eax,0x0
   0x000000000040079c <+166>: call   0x4005c8
   0x00000000004007a1 <+171>: mov    edi,0x4008e3
   0x00000000004007a6 <+176>: mov    eax,0x0
   0x00000000004007ab <+181>: call   0x4005c8
   0x00000000004007b0 <+186>: mov    rdx,QWORD PTR [rip+0x200859]        # 0x601010 <stdin@@GLIBC_2.2.5>
   0x00000000004007b7 <+193>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007bb <+197>: mov    esi,0x10
   0x00000000004007c0 <+202>: mov    rdi,rax
   0x00000000004007c3 <+205>: call   0x4005e0
   0x00000000004007c8 <+210>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007cc <+214>: mov    rsi,rax
   0x00000000004007cf <+217>: mov    edi,0x4008f9
   0x00000000004007d4 <+222>: mov    eax,0x0
   0x00000000004007d9 <+227>: call   0x4005c8
   0x00000000004007de <+232>: add    DWORD PTR [rbp-0x2c],0x1
   0x00000000004007e2 <+236>: cmp    DWORD PTR [rbp-0x2c],0x64
   0x00000000004007e6 <+240>: jne    0x400719 <main+35>
   0x00000000004007ec <+246>: lea    rax,[rbp-0x20]
   0x00000000004007f0 <+250>: mov    rsi,rax
   0x00000000004007f3 <+253>: mov    edi,0x40090c
   0x00000000004007f8 <+258>: mov    eax,0x0
   0x00000000004007fd <+263>: call   0x4005f8
   0x0000000000400802 <+268>: nop
   0x0000000000400803 <+269>: mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400807 <+273>: xor    rax,QWORD PTR fs:0x28
   0x0000000000400810 <+282>: je     0x400817 <main+289>
   0x0000000000400812 <+284>: call   0x4005c0
   0x0000000000400817 <+289>: leave 
   0x0000000000400818 <+290>: ret   
End of assembler dump.
 
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
----- 1 -----
Input Pointer address : ^C
Program received signal SIGINT, Interrupt.
 
 
gdb-peda$ x/i 0x4005f8
   0x4005f8:    jmp    QWORD PTR [rip+0x2009fa]        # 0x600ff8
gdb-peda$ x/gx 0x600ff8
0x600ff8:   0x00007ffff7a784d0
gdb-peda$ x/5i 0x00007ffff7a784d0
   0x7ffff7a784d0 <__isoc99_scanf>:   push   rbx
   0x7ffff7a784d1 <__isoc99_scanf+1>: mov    r10,rdi
   0x7ffff7a784d4 <__isoc99_scanf+4>: sub    rsp,0xd0
   0x7ffff7a784db <__isoc99_scanf+11>:    test   al,al
   0x7ffff7a784dd <__isoc99_scanf+13>:    mov    QWORD PTR [rsp+0x28],rsi

 

 

   - 해당 프로그램의 헤더 구성이 No RELRO, Partial RELRO와 다름

       - 해당 프로그램의 헤더 정보에 '.rela.plt', '.got.plt' 헤더가 존재X

 

 

gdb-peda$ elfheader
.interp = 0x400238
.note.ABI-tag = 0x400254
.note.gnu.build-id = 0x400274
.gnu.hash = 0x400298
.dynsym = 0x4002e0
.dynstr = 0x4003d0
.gnu.version = 0x40045e
.gnu.version_r = 0x400478
.rela.dyn = 0x4004b8
.init = 0x400590
.plt = 0x4005b0
.plt.got = 0x4005c0
.text = 0x400600
.fini = 0x400894
.rodata = 0x4008a0
.eh_frame_hdr = 0x400910
.eh_frame = 0x400948
.init_array = 0x600dd0
.fini_array = 0x600dd8
.jcr = 0x600de0
.dynamic = 0x600de8
.got = 0x600fa8
.data = 0x601000
.bss = 0x601010
gdb-peda$ vmmap
Start              End                Perm  Name
0x00400000         0x00401000         r-xp  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
0x00600000         0x00601000         r--p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
0x00601000         0x00602000         rw-p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
0x00602000         0x00623000         rw-p  [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p  mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd9000 0x00007ffff7fdc000 rw-p  mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p  mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p  [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp  [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p  mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p  [stack]
0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

 

 

5. 함수 호출 비교

 1) Partial RELRO

   - 아래와 같이 동적 라이브러리의 주소가 호출됨

       - main 함수에서 printf 함수를 사용하기 위해 메모리 주소 0x4005b0을 호출

           - 메모리 주소 0x4005b0는 ".plt" 영역

           - ".plt" 영역은 0x400590 ~ 0x400610

       - 0x4005b0 영역의 코드는 "jmp QWORD PTR [rip+0x200a6a]"

           - 즉, 메모리 주소 0x601020에 저장된 주소로 JUMP

               - 메모리 주소 0x601020은 ".got.plt" 영역

               - ".got.plt" 영역은 0x601000 ~ 0x601050 

           - 메모리 주소 0x601020에 저장된 값은 동적 라이브러리의 주소가 아닌 '.plt' 영역

               - 이는 해당 프로그램에서 printf 함수가 호출되지 않았기 때문에 Stub 코드("printf@plt+6") 주소 값이 저장되어 있음

           - printf 함수가 호출되기 시작하면 메모리 주소 0x601020(".got.plt" 영역) 영역에 동적라이브러리의 printf 함수의 시작 주소 값이 저장됨

       - Partial RELRO가 적용된 바이너리는 ".got.plt"영역이 Write가 가능하도록 설정되어 있기 때문에 ".got.plt" 영역에 저장된 값을 변경할 수 있음

 

 

lazenca0x0@ubuntu:~/Documents/Definition/protection/RELRO$ gdb -q ./RELRO-Relro
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x0000000000400716 <+0>:   push   rbp
   0x0000000000400717 <+1>:   mov    rbp,rsp
   0x000000000040071a <+4>:   sub    rsp,0x30
   0x000000000040071e <+8>:   mov    rax,QWORD PTR fs:0x28
   0x0000000000400727 <+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x000000000040072b <+21>:  xor    eax,eax
   0x000000000040072d <+23>:  mov    DWORD PTR [rbp-0x2c],0x1
   0x0000000000400734 <+30>:  jmp    0x400802 <main+236>
   0x0000000000400739 <+35>:  mov    eax,DWORD PTR [rbp-0x2c]
   0x000000000040073c <+38>:  mov    esi,eax
   0x000000000040073e <+40>:  mov    edi,0x4008c4
   0x0000000000400743 <+45>:  mov    eax,0x0
   0x0000000000400748 <+50>:  call   0x4005b0 <printf@plt>
   0x000000000040074d <+55>:  lea    rax,[rbp-0x20]
   0x0000000000400751 <+59>:  mov    edx,0x10
   0x0000000000400756 <+64>:  mov    esi,0x0
   0x000000000040075b <+69>:  mov    rdi,rax
   0x000000000040075e <+72>:  call   0x4005c0 <memset@plt>
   0x0000000000400763 <+77>:  mov    edi,0x4008d4
   0x0000000000400768 <+82>:  mov    eax,0x0
   0x000000000040076d <+87>:  call   0x4005b0 <printf@plt>
   0x0000000000400772 <+92>:  mov    rdx,QWORD PTR [rip+0x2008e7]        # 0x601060 <stdin@@GLIBC_2.2.5>
   0x0000000000400779 <+99>:  lea    rax,[rbp-0x20]
   0x000000000040077d <+103>: mov    esi,0x10
   0x0000000000400782 <+108>: mov    rdi,rax
   0x0000000000400785 <+111>: call   0x4005e0 <fgets@plt>
   0x000000000040078a <+116>: lea    rax,[rbp-0x20]
   0x000000000040078e <+120>: mov    edx,0x10
   0x0000000000400793 <+125>: mov    esi,0x0
   0x0000000000400798 <+130>: mov    rdi,rax
   0x000000000040079b <+133>: mov    eax,0x0
   0x00000000004007a0 <+138>: call   0x4005f0 <strtol@plt>
   0x00000000004007a5 <+143>: cdqe  
   0x00000000004007a7 <+145>: mov    QWORD PTR [rbp-0x28],rax
   0x00000000004007ab <+149>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007af <+153>: mov    rsi,rax
   0x00000000004007b2 <+156>: mov    edi,0x4008ed
   0x00000000004007b7 <+161>: mov    eax,0x0
   0x00000000004007bc <+166>: call   0x4005b0 <printf@plt>
   0x00000000004007c1 <+171>: mov    edi,0x400903
   0x00000000004007c6 <+176>: mov    eax,0x0
   0x00000000004007cb <+181>: call   0x4005b0 <printf@plt>
   0x00000000004007d0 <+186>: mov    rdx,QWORD PTR [rip+0x200889]        # 0x601060 <stdin@@GLIBC_2.2.5>
   0x00000000004007d7 <+193>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007db <+197>: mov    esi,0x10
   0x00000000004007e0 <+202>: mov    rdi,rax
   0x00000000004007e3 <+205>: call   0x4005e0 <fgets@plt>
   0x00000000004007e8 <+210>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007ec <+214>: mov    rsi,rax
   0x00000000004007ef <+217>: mov    edi,0x400919
   0x00000000004007f4 <+222>: mov    eax,0x0
   0x00000000004007f9 <+227>: call   0x4005b0 <printf@plt>
   0x00000000004007fe <+232>: add    DWORD PTR [rbp-0x2c],0x1
   0x0000000000400802 <+236>: cmp    DWORD PTR [rbp-0x2c],0x64
   0x0000000000400806 <+240>: jne    0x400739 <main+35>
   0x000000000040080c <+246>: lea    rax,[rbp-0x20]
   0x0000000000400810 <+250>: mov    rsi,rax
   0x0000000000400813 <+253>: mov    edi,0x40092c
   0x0000000000400818 <+258>: mov    eax,0x0
   0x000000000040081d <+263>: call   0x400600 <__isoc99_scanf@plt>
   0x0000000000400822 <+268>: nop
   0x0000000000400823 <+269>: mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400827 <+273>: xor    rax,QWORD PTR fs:0x28
   0x0000000000400830 <+282>: je     0x400837 <main+289>
   0x0000000000400832 <+284>: call   0x4005a0 <__stack_chk_fail@plt>
   0x0000000000400837 <+289>: leave 
   0x0000000000400838 <+290>: ret   
End of assembler dump.
Reading symbols from ./RELRO-Relro...(no debugging symbols found)...done.
gdb-peda$ elfheader
.interp = 0x400238
.note.ABI-tag = 0x400254
.note.gnu.build-id = 0x400274
.gnu.hash = 0x400298
.dynsym = 0x4002c0
.dynstr = 0x4003b0
.gnu.version = 0x40043e
.gnu.version_r = 0x400458
.rela.dyn = 0x400498
.rela.plt = 0x4004c8
.init = 0x400570
.plt = 0x400590
.plt.got = 0x400610
.text = 0x400620
.fini = 0x4008b4
.rodata = 0x4008c0
.eh_frame_hdr = 0x400930
.eh_frame = 0x400968
.init_array = 0x600e10
.fini_array = 0x600e18
.jcr = 0x600e20
.dynamic = 0x600e28
.got = 0x600ff8
.got.plt = 0x601000
.data = 0x601050
.bss = 0x601060
gdb-peda$ x/i 0x4005b0
   0x4005b0 <printf@plt>: jmp    QWORD PTR [rip+0x200a6a]        # 0x601020
gdb-peda$ x/gx 0x601020
0x601020:   0x00000000004005b6
gdb-peda$ x/i 0x00000000004005b6
   0x4005b6 <printf@plt+6>:   push   0x1
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
----- 1 -----
Input Pointer address : ^C
Program received signal SIGINT, Interrupt.
 
 
gdb-peda$ x/gx 0x601020
0x601020:   0x00007ffff7a62800
gdb-peda$ x/5i 0x00007ffff7a62800
   0x7ffff7a62800 <__printf>: sub    rsp,0xd8
   0x7ffff7a62807 <__printf+7>:   test   al,al
   0x7ffff7a62809 <__printf+9>:   mov    QWORD PTR [rsp+0x28],rsi
   0x7ffff7a6280e <__printf+14>:  mov    QWORD PTR [rsp+0x30],rdx
   0x7ffff7a62813 <__printf+19>:  mov    QWORD PTR [rsp+0x38],rcx
gdb-peda$ vmmap
Start              End                Perm  Name
0x00400000         0x00401000         r-xp  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
0x00600000         0x00601000         r--p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
0x00601000         0x00602000         rw-p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-Relro
0x00602000         0x00623000         rw-p  [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p  mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd9000 0x00007ffff7fdc000 rw-p  mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p  mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p  [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp  [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p  mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p  [stack]
0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

 

 

   - 아래와 같이 아직 호출되지 않은 함수들의 GOT 값은 어떤지 확인해보자

       - main 함수에서 scanf 함수를 사용하기 위해 메모리 주소 0x400600(".plt")을 호출

       - 0x400600 영역의 코드는 "jmp QWORD PTR [rip+0x200a42]" 이며, 0x601048 영역에 저장된 주소로 이동

       - 0x601048 영역에 저장된 값은 0x400606 이며, 해당 영역은 Stub 코드가 저장되어 있음

       - scanf 함수가 아직 호출된 적이 없기 때문에 0x601048(".got.plt") 영역에 동적 라이브러리의 scanf 함수의 시작 주소 값이 저장x

       - Partial RELRO에 Lazy binding을 사용하기 때문에 함수를 호출하지 않으면 동적 라이브러리의 주소 값을 ".got.plt" 영역에 저장되지 않음

 

 

gdb-peda$ x/i 0x400600
   0x400600 <__isoc99_scanf@plt>: jmp    QWORD PTR [rip+0x200a42]        # 0x601048
gdb-peda$ x/gx 0x601048
0x601048:   0x0000000000400606
gdb-peda$ x/2i 0x0000000000400606
   0x400606 <__isoc99_scanf@plt+6>:   push   0x6
   0x40060b <__isoc99_scanf@plt+11>:  jmp    0x400590
gdb-peda$

 

 

 2) Full RELRO

   - 아래와 같이 동적 라이브러리의 주소를 호출하게 됨

       - main 함수에서 printf 함수를 사용하기 위해 메모리 주소 0x4005c8을 호출

            - 메모리 주소 0x4005c8는 ".plt.got" 영역

            - ".plt.got" 영역은 0x4005c0 ~ 0x400600

       - 0x4005c8 영역의 코드는 "jmp QWORD PTR [rip+0x2009fa]"

            - 즉, 메모리 주소 0x600fc8에 저장된 주소로 JUMP

                 - 메모리 주소 0x600fc8은 ".got" 영역

                 - ".got.plt" 영역은 0x600fa8 ~ 0x601000

            - 메모리 주소 0x600fc8에 아무런 값도 저장되어 있지 않음 (해당 프로그램에서 printf 함수가 호출되지 않았기 때문)

            - 프로그램을 실행하고 printf 함수가 호출되기 시작하면 메모리 주소 0x600fc8(".got" 영역) 영역에 동적라이브러리의 printf 함수의 시작 주소 값이 저장됨

       - Full RELRO가 적용된 바이너리는 ".got"영역이 Read-only로 설정되지 때문에 ".got" 영역에 저장된 값을 변경할 수 없음

 

 

lazenca0x0@ubuntu:~/Documents/Definition/protection/RELRO$ gdb -q ./RELRO-FullRelro
Reading symbols from ./RELRO-FullRelro...(no debugging symbols found)...done.
gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x00000000004006f6 <+0>:   push   rbp
   0x00000000004006f7 <+1>:   mov    rbp,rsp
   0x00000000004006fa <+4>:   sub    rsp,0x30
   0x00000000004006fe <+8>:   mov    rax,QWORD PTR fs:0x28
   0x0000000000400707 <+17>:  mov    QWORD PTR [rbp-0x8],rax
   0x000000000040070b <+21>:  xor    eax,eax
   0x000000000040070d <+23>:  mov    DWORD PTR [rbp-0x2c],0x1
   0x0000000000400714 <+30>:  jmp    0x4007e2 <main+236>
   0x0000000000400719 <+35>:  mov    eax,DWORD PTR [rbp-0x2c]
   0x000000000040071c <+38>:  mov    esi,eax
   0x000000000040071e <+40>:  mov    edi,0x4008a4
   0x0000000000400723 <+45>:  mov    eax,0x0
   0x0000000000400728 <+50>:  call   0x4005c8
   0x000000000040072d <+55>:  lea    rax,[rbp-0x20]
   0x0000000000400731 <+59>:  mov    edx,0x10
   0x0000000000400736 <+64>:  mov    esi,0x0
   0x000000000040073b <+69>:  mov    rdi,rax
   0x000000000040073e <+72>:  call   0x4005d0
   0x0000000000400743 <+77>:  mov    edi,0x4008b4
   0x0000000000400748 <+82>:  mov    eax,0x0
   0x000000000040074d <+87>:  call   0x4005c8
   0x0000000000400752 <+92>:  mov    rdx,QWORD PTR [rip+0x2008b7]        # 0x601010 <stdin@@GLIBC_2.2.5>
   0x0000000000400759 <+99>:  lea    rax,[rbp-0x20]
   0x000000000040075d <+103>: mov    esi,0x10
   0x0000000000400762 <+108>: mov    rdi,rax
   0x0000000000400765 <+111>: call   0x4005e0
   0x000000000040076a <+116>: lea    rax,[rbp-0x20]
   0x000000000040076e <+120>: mov    edx,0x10
   0x0000000000400773 <+125>: mov    esi,0x0
   0x0000000000400778 <+130>: mov    rdi,rax
   0x000000000040077b <+133>: mov    eax,0x0
   0x0000000000400780 <+138>: call   0x4005f0
   0x0000000000400785 <+143>: cdqe  
   0x0000000000400787 <+145>: mov    QWORD PTR [rbp-0x28],rax
   0x000000000040078b <+149>: mov    rax,QWORD PTR [rbp-0x28]
   0x000000000040078f <+153>: mov    rsi,rax
   0x0000000000400792 <+156>: mov    edi,0x4008cd
   0x0000000000400797 <+161>: mov    eax,0x0
   0x000000000040079c <+166>: call   0x4005c8
   0x00000000004007a1 <+171>: mov    edi,0x4008e3
   0x00000000004007a6 <+176>: mov    eax,0x0
   0x00000000004007ab <+181>: call   0x4005c8
   0x00000000004007b0 <+186>: mov    rdx,QWORD PTR [rip+0x200859]        # 0x601010 <stdin@@GLIBC_2.2.5>
   0x00000000004007b7 <+193>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007bb <+197>: mov    esi,0x10
   0x00000000004007c0 <+202>: mov    rdi,rax
   0x00000000004007c3 <+205>: call   0x4005e0
   0x00000000004007c8 <+210>: mov    rax,QWORD PTR [rbp-0x28]
   0x00000000004007cc <+214>: mov    rsi,rax
   0x00000000004007cf <+217>: mov    edi,0x4008f9
   0x00000000004007d4 <+222>: mov    eax,0x0
   0x00000000004007d9 <+227>: call   0x4005c8
   0x00000000004007de <+232>: add    DWORD PTR [rbp-0x2c],0x1
   0x00000000004007e2 <+236>: cmp    DWORD PTR [rbp-0x2c],0x64
   0x00000000004007e6 <+240>: jne    0x400719 <main+35>
   0x00000000004007ec <+246>: lea    rax,[rbp-0x20]
   0x00000000004007f0 <+250>: mov    rsi,rax
   0x00000000004007f3 <+253>: mov    edi,0x40090c
   0x00000000004007f8 <+258>: mov    eax,0x0
   0x00000000004007fd <+263>: call   0x4005f8
   0x0000000000400802 <+268>: nop
   0x0000000000400803 <+269>: mov    rax,QWORD PTR [rbp-0x8]
   0x0000000000400807 <+273>: xor    rax,QWORD PTR fs:0x28
   0x0000000000400810 <+282>: je     0x400817 <main+289>
   0x0000000000400812 <+284>: call   0x4005c0
   0x0000000000400817 <+289>: leave 
   0x0000000000400818 <+290>: ret   
End of assembler dump.
gdb-peda$ elfheader
.interp = 0x400238
.note.ABI-tag = 0x400254
.note.gnu.build-id = 0x400274
.gnu.hash = 0x400298
.dynsym = 0x4002e0
.dynstr = 0x4003d0
.gnu.version = 0x40045e
.gnu.version_r = 0x400478
.rela.dyn = 0x4004b8
.init = 0x400590
.plt = 0x4005b0
.plt.got = 0x4005c0
.text = 0x400600
.fini = 0x400894
.rodata = 0x4008a0
.eh_frame_hdr = 0x400910
.eh_frame = 0x400948
.init_array = 0x600dd0
.fini_array = 0x600dd8
.jcr = 0x600de0
.dynamic = 0x600de8
.got = 0x600fa8
.data = 0x601000
.bss = 0x601010
gdb-peda$ x/i 0x4005c8
   0x4005c8:    jmp    QWORD PTR [rip+0x2009fa]        # 0x600fc8
gdb-peda$ x/gx 0x600fc8
0x600fc8:   0x0000000000000000
gdb-peda$ r
Starting program: /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
----- 1 -----
Input Pointer address : ^C
Program received signal SIGINT, Interrupt.
 
gdb-peda$ x/gx 0x600fc8
0x600fc8:   0x00007ffff7a62800
gdb-peda$ x/5i 0x00007ffff7a62800
   0x7ffff7a62800 <__printf>: sub    rsp,0xd8
   0x7ffff7a62807 <__printf+7>:   test   al,al
   0x7ffff7a62809 <__printf+9>:   mov    QWORD PTR [rsp+0x28],rsi
   0x7ffff7a6280e <__printf+14>:  mov    QWORD PTR [rsp+0x30],rdx
   0x7ffff7a62813 <__printf+19>:  mov    QWORD PTR [rsp+0x38],rcx
gdb-peda$ vmmap
Start              End                Perm  Name
0x00400000         0x00401000         r-xp  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
0x00600000         0x00601000         r--p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
0x00601000         0x00602000         rw-p  /home/lazenca0x0/Documents/Definition/protection/RELRO/RELRO-FullRelro
0x00602000         0x00623000         rw-p  [heap]
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7bcd000 0x00007ffff7dcd000 ---p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dcd000 0x00007ffff7dd1000 r--p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd1000 0x00007ffff7dd3000 rw-p  /lib/x86_64-linux-gnu/libc-2.23.so
0x00007ffff7dd3000 0x00007ffff7dd7000 rw-p  mapped
0x00007ffff7dd7000 0x00007ffff7dfd000 r-xp  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7fd9000 0x00007ffff7fdc000 rw-p  mapped
0x00007ffff7ff6000 0x00007ffff7ff8000 rw-p  mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r--p  [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 r-xp  [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p  /lib/x86_64-linux-gnu/ld-2.23.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p  mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p  [stack]
0xffffffffff600000 0xffffffffff601000 r-xp  [vsyscall]

 

 

   - 아래와 같이 아직 호출되지 않은 함수들의 GOT 값은 어떤지 확인해보자

       - main 함수에서 scanf 함수를 사용하기 위해 메모리 주소 0x4005f8(".plt.got")을 호출

       - 0x4005f8 영역의 코드는 "jmp QWORD PTR [rip+0x2009fa]" 이며, 0x600ff8 영역에 저장된 주소로 이동

       - 0x600ff8 영역에 저장된 값은 0x00007ffff7a784d0 이며, 해당 영역은 동적라이브러리의 scanf 함수의 시작 주소 값

       - Full RELRO에서는 Now binding을 사용하기 때문에 프로그래임 메모리에 로드 될때 해당 프로그램에서 사용되는 모든 동적 함수의 주소가 ".got" 영역에 저장됨

 

 

gdb-peda$ x/i 0x4005f8
   0x4005f8:    jmp    QWORD PTR [rip+0x2009fa]        # 0x600ff8
gdb-peda$ x/gx 0x600ff8
0x600ff8:   0x00007ffff7a784d0
gdb-peda$

 

 

 cf) printf 함수 호출 차이

 

 

 

 

6. "Checksec.sh" 파일의 NX 여부 확인

 1) 바이너리

   - 아래와 같은 방법으로 바이너리의 RELRO 설정여부 확인

      ① 'readelf' 명령어를 이용해 해당 파일의 프로그래 헤더와 Dynamic section 정보를 가져와 RELRO를 설졍여부 확인

      ② 파일의 프로그래 헤더에 'GNU_RELRO'가 있으면 RELRO가 적용되었다고 판단

          - Dynamic section에 BIND_NOW가 있으면 Full RELRO가 적용되었다고 판단

          - Dynamic section에 BIND_NOW가 없으면 Partial RELRO가 적용되었다고 판단

 

 

 

 

2) 프로세서

   - 아래와 같은 방법으로 프로세서의 RELRO 설정여부 확인

      ① Binary의 확인 방식과 비슷하며, 전달되는 파일의 경로가 다음과 같이 다름  ex. /proc/<PID>/exe

      ② 추가된 동작은 '/proc/<PID>/exe' 파일에 'Program Headers' 정보가 있는지 확인

 

 

 

 

댓글