1

I was experimenting with how Linux allocates and protects memory.

For some of my experimentations I have created a small program in c:

#include <stdio.h>

int gv=10;

int main(){

  char *v=(char*)0x601000;//0x601030
  printf("gv=%p\n", &gv);
  scanf("%s", v);
  printf("You gave=%s\n", v);

}

After compilation (partial relro), the readelf -t a.out returns:

There are 30 section headers, starting at offset 0x1a18:

Section Headers:
 [Nr] Name
      Type              Address          Offset            Link
      Size              EntSize          Info              Align
      Flags
 [ 0]
      NULL                   NULL             0000000000000000  0000000000000000  0
      0000000000000000 0000000000000000  0                 0
      [0000000000000000]:
 [ 1] .interp
      PROGBITS               PROGBITS         0000000000400238  0000000000000238  0
      000000000000001c 0000000000000000  0                 1
      [0000000000000002]: ALLOC
 [ 2] .note.ABI-tag
      NOTE                   NOTE             0000000000400254  0000000000000254  0
      0000000000000020 0000000000000000  0                 4
      [0000000000000002]: ALLOC
 [ 3] .note.gnu.build-id
      NOTE                   NOTE             0000000000400274  0000000000000274  0
      0000000000000024 0000000000000000  0                 4
      [0000000000000002]: ALLOC
 [ 4] .gnu.hash
      GNU_HASH               GNU_HASH         0000000000400298  0000000000000298  5
      000000000000001c 0000000000000000  0                 8
      [0000000000000002]: ALLOC
 [ 5] .dynsym
      DYNSYM                 DYNSYM           00000000004002b8  00000000000002b8  6
      0000000000000078 0000000000000018  1                 8
      [0000000000000002]: ALLOC
 [ 6] .dynstr
      STRTAB                 STRTAB           0000000000400330  0000000000000330  0
      0000000000000058 0000000000000000  0                 1
      [0000000000000002]: ALLOC
 [ 7] .gnu.version
      VERSYM                 VERSYM           0000000000400388  0000000000000388  5
      000000000000000a 0000000000000002  0                 2
      [0000000000000002]: ALLOC
 [ 8] .gnu.version_r
      VERNEED                VERNEED          0000000000400398  0000000000000398  6
      0000000000000030 0000000000000000  1                 8
      [0000000000000002]: ALLOC
 [ 9] .rela.dyn
      RELA                   RELA             00000000004003c8  00000000000003c8  5
      0000000000000018 0000000000000018  0                 8
      [0000000000000002]: ALLOC
 [10] .rela.plt
      RELA                   RELA             00000000004003e0  00000000000003e0  5
      0000000000000060 0000000000000018  12                8
      [0000000000000042]: ALLOC, INFO LINK
 [11] .init
      PROGBITS               PROGBITS         0000000000400440  0000000000000440  0
      000000000000001a 0000000000000000  0                 4
      [0000000000000006]: ALLOC, EXEC
 [12] .plt
      PROGBITS               PROGBITS         0000000000400460  0000000000000460  0
      0000000000000050 0000000000000010  0                 16
      [0000000000000006]: ALLOC, EXEC
 [13] .text
      PROGBITS               PROGBITS         00000000004004b0  00000000000004b0  0
      00000000000001b2 0000000000000000  0                 16
      [0000000000000006]: ALLOC, EXEC
 [14] .fini
      PROGBITS               PROGBITS         0000000000400664  0000000000000664  0
      0000000000000009 0000000000000000  0                 4
      [0000000000000006]: ALLOC, EXEC
 [15] .rodata
      PROGBITS               PROGBITS         0000000000400670  0000000000000670  0
      0000000000000029 0000000000000000  0                 8
      [0000000000000002]: ALLOC
 [16] .eh_frame_hdr
      PROGBITS               PROGBITS         000000000040069c  000000000000069c  0
      0000000000000034 0000000000000000  0                 4
      [0000000000000002]: ALLOC
 [17] .eh_frame
      PROGBITS               PROGBITS         00000000004006d0  00000000000006d0  0
      00000000000000f4 0000000000000000  0                 8
      [0000000000000002]: ALLOC
 [18] .init_array
      INIT_ARRAY             INIT_ARRAY       0000000000600e10  0000000000000e10  0
      0000000000000008 0000000000000000  0                 8
      [0000000000000003]: WRITE, ALLOC
 [19] .fini_array
      FINI_ARRAY             FINI_ARRAY       0000000000600e18  0000000000000e18  0
      0000000000000008 0000000000000000  0                 8
      [0000000000000003]: WRITE, ALLOC
 [20] .jcr
      PROGBITS               PROGBITS         0000000000600e20  0000000000000e20  0
      0000000000000008 0000000000000000  0                 8
      [0000000000000003]: WRITE, ALLOC
 [21] .dynamic
      DYNAMIC                DYNAMIC          0000000000600e28  0000000000000e28  6
      00000000000001d0 0000000000000010  0                 8
      [0000000000000003]: WRITE, ALLOC
 [22] .got
      PROGBITS               PROGBITS         0000000000600ff8  0000000000000ff8  0
      0000000000000008 0000000000000008  0                 8
      [0000000000000003]: WRITE, ALLOC
 [23] .got.plt
      PROGBITS               PROGBITS         0000000000601000  0000000000001000  0
      0000000000000038 0000000000000008  0                 8
      [0000000000000003]: WRITE, ALLOC
 [24] .data
      PROGBITS               PROGBITS         0000000000601038  0000000000001038  0
      0000000000000008 0000000000000000  0                 4
      [0000000000000003]: WRITE, ALLOC
 [25] .bss
      NOBITS                 NOBITS           0000000000601040  0000000000001040  0
      0000000000000008 0000000000000000  0                 1
      [0000000000000003]: WRITE, ALLOC
 [26] .comment
      PROGBITS               PROGBITS         0000000000000000  0000000000001040  0
      000000000000002d 0000000000000001  0                 1
      [0000000000000030]: MERGE, STRINGS
 [27] .shstrtab
      STRTAB                 STRTAB           0000000000000000  000000000000106d  0
      0000000000000108 0000000000000000  0                 1
      [0000000000000000]:
 [28] .symtab
      SYMTAB                 SYMTAB           0000000000000000  0000000000001178  29
      0000000000000648 0000000000000018  45                8
      [0000000000000000]:
 [29] .strtab
      STRTAB                 STRTAB           0000000000000000  00000000000017c0  0
      0000000000000255 0000000000000000  0                 1
      [0000000000000000]:

and the readelf -l a.out returns:

Elf file type is EXEC (Executable file)
Entry point 0x4004b0
There are 9 program headers, starting at offset 64

Program Headers:
 Type           Offset             VirtAddr           PhysAddr
                FileSiz            MemSiz              Flags  Align
 PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                0x00000000000001f8 0x00000000000001f8  R E    8
 INTERP         0x0000000000000238 0x0000000000400238 0x0000000000400238
                0x000000000000001c 0x000000000000001c  R      1
     [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
 LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                0x00000000000007c4 0x00000000000007c4  R E    200000
 LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                0x0000000000000230 0x0000000000000238  RW     200000
 DYNAMIC        0x0000000000000e28 0x0000000000600e28 0x0000000000600e28
                0x00000000000001d0 0x00000000000001d0  RW     8
 NOTE           0x0000000000000254 0x0000000000400254 0x0000000000400254
                0x0000000000000044 0x0000000000000044  R      4
 GNU_EH_FRAME   0x000000000000069c 0x000000000040069c 0x000000000040069c
                0x0000000000000034 0x0000000000000034  R      4
 GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                0x0000000000000000 0x0000000000000000  RW     10
 GNU_RELRO      0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
                0x00000000000001f0 0x00000000000001f0  R      1

Section to Segment mapping:
 Segment Sections...
  00
  01     .interp
  02     .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
  03     .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
  04     .dynamic
  05     .note.ABI-tag .note.gnu.build-id
  06     .eh_frame_hdr
  07
  08     .init_array .fini_array .jcr .dynamic .got

In one instance of the program when run through gdb, /proc/self/maps returned:

00400000-00401000 r-xp 00000000 00:30f 2262070116                        .../a.out
00600000-00601000 r--p 00000000 00:30f 2262070116                        .../a.out
00601000-00602000 rw-p 00001000 00:30f 2262070116                        .../a.out
7ffff7a18000-7ffff7bd0000 r-xp 00000000 fd:00 137613                     /usr/lib64/libc-2.17.so
7ffff7bd0000-7ffff7dd0000 ---p 001b8000 fd:00 137613                     /usr/lib64/libc-2.17.so
7ffff7dd0000-7ffff7dd4000 r--p 001b8000 fd:00 137613                     /usr/lib64/libc-2.17.so
7ffff7dd4000-7ffff7dd6000 rw-p 001bc000 fd:00 137613                     /usr/lib64/libc-2.17.so
7ffff7dd6000-7ffff7ddb000 rw-p 00000000 00:00 0
7ffff7ddb000-7ffff7dfc000 r-xp 00000000 fd:00 137605                     /usr/lib64/ld-2.17.so
7ffff7fbd000-7ffff7fc0000 rw-p 00000000 00:00 0
7ffff7ff7000-7ffff7ffa000 rw-p 00000000 00:00 0
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0                          [vdso]
7ffff7ffc000-7ffff7ffd000 r--p 00021000 fd:00 137605                     /usr/lib64/ld-2.17.so
7ffff7ffd000-7ffff7ffe000 rw-p 00022000 fd:00 137605                     /usr/lib64/ld-2.17.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Having that in mind, what I would expect is that the memory region between 0x601000-0x602000 to be writable. But when I run the program with 24 characters or more, with v=0x601000 it crashed.

When I changed the v pointer to 0x601020 then the program crashed after I entered 0x1000-0x20 characters.

How is that behavior explained. Isn't the memory protection enforced on page granularity? (Here it looks like the memory region between 0x601018-0x601020 is somehow read-only)

I guess the problem is inside the .got.plt section (loaded at 0x601000 with size 0x38), but what is it exactly?


Edit:

The output of ld --verbose | fgrep -A 3 -B 3 -i relro is:

 .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
 .dynamic        : { *(.dynamic) }
 .got            : { *(.got) *(.igot) }
 . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
 .got.plt        : { *(.got.plt)  *(.igot.plt) }
 .data           :

This probably tells us that RELRO has something to do with that, but still, shouldn't the memory be protected in page-size granularity?

2

.got.plt is essential for making library calls (including but not limited to to calls to libc). Once you're done corrupting that section, making any libc calls is off-limits.

If you run the binary through gdb, you shouldn't be getting a segfault in scanf — you should be getting a segfault in printf@plt, as the dynamic call can't be made with a corrupted .got.plt section.

You should still be able to make non-dynamic calls and syscalls:

#include <stdio.h>
  __asm(
    "finish:"
        "mov $60, %rax\n"
        "mov $42, %rdi\n"
        "syscall\n"
     );

int gv=10;

int main(){

  char *v=(char*)0x601000;//0x601030
  printf("gv=%p\n", &gv);
  scanf("%s", v);
  _Noreturn void finish(void);
  finish();
  //should exit with 42

  printf("You gave=%s\n", v);

}
|improve this answer|||||
1

I have found the problem. By filling that region with junk I had overwritten the printf's got entry, which with its final invocation caused the segmentation.

So if we modify the code to:

#include <stdio.h>

int gv=10;

int main(){

  char *v=(char*)0x601000;
  printf("gv=%p\n", &gv);
  scanf("%s", v);
  //printf("You gave=%s\n", v);

}

there should be no problem with filling v with 0x1000 characters (watch out \0)

The RELRO reference of the first post is unrelated.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.