< Explanation >

  • RELRO는 RELocation Read-Only의 줄임말이며, ELF 바이너리 / 프로세스의 데이터 섹션의 보안을 강화하는 일반적인 기술이다
  • RELRO에는 Partial RELRO와 Full RELRO 두 가지 모드가 있다.
  • 아래와 같이 RELRO, Partial RELRO, Full RELRO의 차이점이 있습니다.

< Example program >

▶ Source code

#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);
}

 

< Check the protection techniques of binary files >

▶ checksec.sh

 

▶ Program header & Dynamic Section

- 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영역을 덮어쓸수 없다.

 

< Overwrite test >

▶ No RELRO

  • "__isoc99_scanf"의 GOT Address는 0x600c68이며, 위와 같이 해당 영역에 값을 변경할 수 있다.

 

  • "__isoc99_scanf"의 주소값은 '.got.plt'영역에 저장되어 있다. ('.got.plt' 영역의 시작 주소 : 0x600c20)
  • 메모리 맵을 통해 해당 영역(0x00600000 ~ 0x00601000)에 'W' 쓰기 권한이 설정되어 있다.

 

▶ Partial RELRO

  • "__isoc99_scanf"의 GOT Address는 0x601048이며, 위와 같이 해당 영역에 값을 변경할 수 있다.

 

  • '.got.plt' 영역의 시작 주소는 0x601000 이다.
  • 메모리 맵에서 RELRO가 적용되지 않은 프로그램과 다른 부분을 확인할 수 있다.
    • 0x600000 ~ 0x601000 영역의 권한은 r--p 이다. (해당 영역에는 .init_array, .fini_array, .jcr, .dynamic, .got 헤더가 포함된다.)
    • 0x601000 ~ 0x602000 영역의 권한은 rw-p 이다. (해당 영역에는 .got.plt,등의 헤더가 포함되어 이로 인해 .got.plt 영역에 값을 변경할 수 있다.)

▶ Full RELRO

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
  • 앞에서 테스트한 프로그램과 달리 GOT 영역에 값을 변경할 수 없다. (디버거에서 '__isoc99_scanf'의 심볼 정보를 찾을 수 없다.)
  • 디스어셈블 코드에서 호출되는 함수를 분석해보자
    • 0x4007fd 영역의 코드에서 0x4005f8 영역을 호출한다.
    • 0x4005f8 영역의 코드에서 "rip+0x2009fa" 영역에 저장된 주소로 이동한다.
    • "rip+0x2009fa" 영역은 0x600ff8 이며, 해당 영역에 저장된 값은 0x00007ffff7a784d0 이다.
    • 0x00007ffff7a784d0 영역은 __isoc99_scanf 함수의 시작 주소 이다.

 

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]

해당 프로그램의 헤더 구성이 No RELRO, Partial RELRO와 다르다. 해당 프로그램의 헤더 정보에 '.rela.plt', '.got.plt' 헤더가 존재하지 않는다.

 

< Comparison of function calls >

▶ Partial RELRO

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]

다음과 같이 동적 라이브러리의 주소가 호출된다.

  • main 함수에서 printf 함수를 사용하기 위해 메모리 주소 0x4005b0을 호출한다.
    • 메모리 주소 0x4005b0는 ".plt" 영역이다.
    • ".plt" 영역은 0x400590 ~ 0x400610 이다.
  • 0x4005b0 영역의 코드는 "jmp QWORD PTR [rip+0x200a6a]" 입니다.
    • 메모리 주소 0x601020은 저장된 주소로 JUMP하고, 이는 ".got.plt" 영역이다.  (".got.plt" 영역은 0x601000 ~ 0x601050 이다.)
    • 메모리 주소 0x601020에 저장된 값은 동적 라이브러리의 주소가 아닌 '.plt' 영역이다.
    • 프로그램을 실행하고 printf 함수가 호출되기 시작하면 메모리 주소 0x601020(".got.plt" 영역) 영역에 동적라이브러리의 printf 함수의 시작 주소 값이 저장된다.
  • 즉, Partial RELRO가 적용된 바이너리는 ".got.plt"영역이 Write가 가능하도록 설정되어 있기 때문에 ".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$
  • 다음과 같이 아직 호출되지 않은 함수들의 GOT 값은 어떤지 확인해보자
    • main 함수에서 scanf 함수를 사용하기 위해 메모리 주소 0x400600(".plt")을 호출한다.
    • 0x400600 영역의 코드는 "jmp QWORD PTR [rip+0x200a42]" 이며, 0x601048 영역에 저장된 주소로 이동한다.
    • 0x601048 영역에 저장된 값은 0x400606 이며, 해당 영역은 Stub 코드가 저장되어 있다.
    • scanf 함수가 아직 호출된 적이 없기 때문에 0x601048(".got.plt") 영역에 동적라이브러리의 scanf 함수의 시작 주소 값이 저장되어 있지 않다.
    • Partial RELRO에 Lazy binding을 사용하기 때문에 함수를 호출하지 않으면 동적라이브러리의 주소 값을 ".got.plt" 영역에 저장되지 않다.

 

▶ Full RELRO

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]

다음과 같이 동적 라이브러리의 주소를 호출하게 된다.

  • main 함수에서 printf 함수를 사용하기 위해 메모리 주소 0x4005c8을 호출한다.
    • 메모리 주소 0x4005c8는 ".plt.got" 영역이다.
    • ".plt.got" 영역은 0x4005c0 ~ 0x400600 이다.
  • 0x4005c8 영역의 코드는 "jmp QWORD PTR [rip+0x2009fa]" 이다.
    • 메모리 주소 0x600fc8에 저장된 주소로 JUMP 하며 이는 ".got"영역이다. (".got.plt" 영역 : 0x600fa8 ~ 0x601000)
    • 메모리 주소 0x600fc8에 아무런 값도 저장되어 있지 않는다.
    • 프로그램을 실행하고 printf 함수가 호출되기 시작하면 메모리 주소 0x600fc8(".got" 영역) 영역에 동적라이브러리의 printf 함수의 시작 주소 값이 저장된다.
  •  Full RELRO가 적용된 바이너리는 ".got"영역이 Read-only로 설정되지 때문에 ".got" 영역에 저장된 값을 변경할 수 없다.

 

gdb-peda$ x/i 0x4005f8
   0x4005f8:    jmp    QWORD PTR [rip+0x2009fa]        # 0x600ff8
gdb-peda$ x/gx 0x600ff8
0x600ff8:   0x00007ffff7a784d0
  • 다음과 같이 아직 호출되지 않은 함수들의 GOT 값은 어떤지 확인해보자
    • main 함수에서 scanf 함수를 사용하기 위해 메모리 주소 0x4005f8(".plt.got")을 호출한다.
    • 0x4005f8 영역의 코드는 "jmp QWORD PTR [rip+0x2009fa]" 이며, 0x600ff8 영역에 저장된 주소로 이동한다.
    • 0x600ff8 영역에 저장된 값은 0x00007ffff7a784d0 이며, 해당 영역은 동적라이브러리의 scanf 함수의 시작 주소 값이다.
    • Full RELRO에서는 Now binding을 사용하기 때문에 프로그래임 메모리에 로드 될때 해당 프로그램에서 사용되는 모든 동적 함수의 주소를 ".got" 영역에 저장된다.

 

< How to detect NX in the "Checksec.sh" file >

▶ Binary

다음과 같은 방법으로 바이너리의 RELRO 설정여부를 확인하자

  • 'readelf' 명령어를 이용해 해당 파일의 프로그래 헤더와 Dynamic section 정보를 가져와 RELRO를 설졍여부를 확인한다.
  • 파일의 프로그래 헤더에 'GNU_RELRO'가 있으면 RELRO가 적용되었다고 판단한다.
    • 그리고 Dynamic section에 BIND_NOW가 있으면 Full RELRO가 적용되었다고 판단한다.
    • Dynamic section에 BIND_NOW가 없으면 Partial RELRO가 적용되었다고 판단한다.

 

▶ Process

위와 같은 방법으로 프로세서의 RELRO 설정여부를 확인한다

+ Recent posts