https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83520

            Bug ID: 83520
           Summary: format string bug in libvtv
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: other
          Assignee: unassigned at gcc dot gnu.org
          Reporter: charo.ctf at gmail dot com
  Target Milestone: ---

Description:
  On startup of a program compiled with "-fvtable-verify=std" or
  "-fvtable-verify=preinit", argv[0] is directly passed as the
  third argument of snprintf.
  This would cause memory corruption if argv[0] contains format
  specifiers like %n.

Affected version:
  gcc>=4.9 configured with --enable-vtable-verify

Technical description:
  The code below is taken from gcc-5-20171003/libvtv/vtv_rts.cc

  ======vtv_rts.cc (line 832-856)======
  static int
  dl_iterate_phdr_callback (struct dl_phdr_info *info, size_t, void *data)
  {
    int * mprotect_flags = (int *) data;
    off_t map_sect_offset = 0;
    ElfW (Word) map_sect_len = 0;
    char buffer[1024];
    char program_name[1024];
    const char *map_sect_name = VTV_PROTECTED_VARS_SECTION;

    /* Check to see if this is the record for the Linux Virtual Dynamic
       Shared Object (linux-vdso.so.1), which exists only in memory (and
       therefore cannot be read from disk).  */

    if (strcmp (info->dlpi_name, "linux-vdso.so.1") == 0)
      return 0;

    if (strlen (info->dlpi_name) == 0
        && info->dlpi_addr != 0)
      return 0;

    /* Get the name of the main executable.  This may or may not include
       arguments passed to the program.  Find the first space, assume it
       is the start of the argument list, and change it to a '\0'. */
    snprintf (program_name, sizeof (program_name), program_invocation_name);
  =====================================

  As we can see, there is a format string bug at the last line of
  the code above.

  According to the manpage of program_invocation_name,
  program_invocation_name is the same as the value of argv[0] of
  main(), which means that this value is user supplied.

  There is another format string bug with the same situation as
  above in read_section_offset_and_length function (line 576).

===PoC===
$ g++-vtv -v
Using built-in specs.
COLLECT_GCC=g++-vtv
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-linux-gnu/5.4.1/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../gcc-5-20171003/configure -v --enable-languages=c,c++
--program-suffix=-vtv --disable-multilib --enable-libstdcxx-threads
--enable-vtable-verify --build=x86_64-linux-gnu --host=x86_64-linux-gnu
--target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.1 20171003 (GCC)
$ cat victim.cpp
#include <stdio.h>
int main(){
    puts("Hello world");
}
$ g++-vtv -o victim -fvtable-verify=std victim.cpp
$ ./victim
Hello world
$ ln -s ./victim ./%n
$ ./%n
Segmentation fault (core dumped)
=========

Reply via email to