ASA-0000: GV Execution of Arbitrary Shell Commands

From: Marc Bevand (bevand_mat_private)
Date: Mon Sep 30 2002 - 17:00:02 PDT

  • Next message: Rossen Raykov: "Insecure XML-RPC handling in Zope reveals the distribution physic al location."

    
     ('binary' encoding is not supported, stored as-is)
                          "After" Security Advisory
    
            Title: GV Execution of Arbitrary Shell Commands
          Affects: gv-3.5.8 and probably older versions
      Advisory ID: ASA-0000
     Release Date: 2002-10-01
           Author: Marc Bevand <bevand_m (at) epita.fr>
              URL: http://www.epita.fr/~bevand_m/asa/asa-0000
    
    
    --oOo-- 0. Table of Contents
    
    0. Table of Contents
    1. Introduction
    2. Problem
    3. Solution
    4. Conclusion
    5. References
    6. Attached files
    
    
    --oOo-- 1. Introduction
    
      GV [0] is a PostScript and PDF previewer available on
    many unix
    systems and even on some non-unix systems. Technically,
    it is a user
    interface for Ghostscript [1], which is a PostScript
    and PDF language
    interpreter. GV is also able to automatically
    decompress GZip'ed [2]
    files on-the-fly before reading them.
    
      When GV detects that the document is either a PDF
    file or a
    GZip compressed file, it executes some commands with
    the help of the
    system() function. Unfortunately, these commands
    contain the
    filename, which can be considered as untrusted user
    input. It is then
    possible to distribute a file (with a meticulously
    choosed filename,
    that can even seems innocent) that causes execution of
    arbitrary
    shell commands when it is read with GV.
    
    
    --oOo-- 2. Problem
    
      GV detects PDF files or GZip compressed files by
    reading the first
    bytes of datas:
    
      o when "%PDF-" is read, GV assumes it is a PDF file
    and call
        system() with the following argument (default value
    of the
        GV.gsCmdScanPDF X11 ressource):
        "gs -dNODISPLAY -dQUIET -sPDFname=%s -sDSCname=%s
    pdf2dsc.ps -c quit"
        The 1st "%s" corresponds to the PDF filename, and
    the 2nd to a
        temporary filename.
    
      o when "\037\235" or "\037\213" is read, GV assumes
    it is a GZip
        compressed file and call system() with the
    following argument
        (default value of the GV.uncompressCommand X11
    ressource):
        "gzip -d -c %s > %s"
        The 1st "%s" corresponds to the GZip compressed
    filename, and the
        2nd to a temporary filename.
    
      In these conditions, trying to open, for example, a
    PDF file named
    "xxx & echo hello & xxx" leads to execution of
    "gs -dNODISPLAY -dQUIET -sPDFname=xxx & echo hello &
    xxx ...". Thus,
    "echo hello" (a part of the filename) is executed:
    
          $ file "xxx & echo hello & xxx"
          xxx & echo hello & xxx: PDF document, version 1.2
          $ gv "xxx & echo hello & xxx" 
      --> hello
          sh: xxx: command not found
          GS>hello
          sh: xxx..tmp: command not found
    
    The error messages ("sh: xxx: command not found", etc)
    are just
    results of the garbage introduced in the system()
    argument by the
    unusual "xxx & echo hello & xxx" filename. Moreover, GV
    displays a
    dialog box explaining that execution of Ghostscript failed.
    
      But all these "inconvenients" (from the malicious
    user point-of-
    view) can be easily avoided. Imagine a site where each
    host access a
    file server through the mount point "/sgoinfre", and
    suppose that
    someone (Charly) creates 2 files in this directory:
    
      o a PDF file named 'Huhu_"`source evil`".pdf'
    (doublequotes and
        backquotes are part of the filename)
      o a shell script named 'evil' that contains:
          #!/bin/sh
          echo '"`source evil`"'
          touch _it_works_
    
    Now, here is what happens if someone else (Alice) wants
    to read the
    PDF file:
    
      $ cd /sgoinfre
      $ gv 'Huhu_"`source evil`".pdf'
    
    All works fine for Alice (no error messages, no dialog
    box), except
    that she hasn't realized that 'evil' has been executed
    under her
    identity:
    
      $ ls -l _it_works_
      -rw-------    1 alice   users         0 Jul 30 05:56
    _it_works_
    
    Note: this example works only if the /bin/sh shell
    executed by
    system() supports the 'source' builtin; that's the case
    when /bin/sh
    is a link to bash.
    
    
    --oOo-- 3. Solution
    
      The GV maintainer, Johannes Plass <plass (at)
    thep.physik.uni-mainz.de>, has been e-mailed twice
    about this
    problem. Unfortunately, no response has been received,
    it seems that
    he has stopped his work on GV since 1997.
    
      However, I propose a temporary fix: the attached patch
    ("asa-0000.gv-3.5.8.patch"), done against GV 3.5.8,
    checks if
    the filename contains only allowed characters
    (alphanumeric and
    ``+,-./:=@\^_''). If this is not the case, an error
    message is
    displayed and system() is not called.
    
    
    --oOo-- 4. Conclusion
    
      GV contains a security hole allowing execution of
    arbitrary shell
    commands. Since the author seems to have stopped his
    work on GV, a
    temporary fix has been developped: see the attached
    patch done
    against GV 3.5.8.
    
    
    --oOo-- 5. References
    
    [0] GV
        http://wwwthep.physik.uni-mainz.de/~plass/gv/
    
    [1] Ghostscript
        http://www.cs.wisc.edu/~ghost/index.html
    
    [2] GNU Zip
        http://www.gzip.org
    
    --oOo-- 6. Attached files
    
    The following file is also available at:
    http://www.epita.fr/~bevand_m/asa/asa-0000.gv-3.5.8.patch
    
    ---8<------------------ asa-0000.gv-3.5.8.patch
    -------------------------
    diff -ur gv-3.5.8.orig/source/file.c gv-3.5.8/source/file.c
    --- gv-3.5.8.orig/source/file.c 1997-06-07
    00:00:00.000000000 +0200
    +++ gv-3.5.8/source/file.c      2002-09-26
    23:56:00.000000000 +0200
    @@ -285,6 +285,22 @@
     }
     
     /*############################################################*/
    +/* file_nameIsDangerous */
    +/*############################################################*/
    +
    +char *file_charsAllowedInName = "+,-./:=@\\^_";
    +
    +int
    +file_nameIsDangerous(fn)
    +  char *fn;
    +{
    +  for (; *fn; fn++)
    +    if (!isalnum(*fn) &&
    !strchr(file_charsAllowedInName, *fn))
    +      return(1);
    +  return(0);
    +}
    +
    +/*############################################################*/
     /* file_pdfname2psname */
     /* If the file ends in .pdf, change this to .ps.*/
     /* Return pointer to temp copy if changed, else to
    input string. */
    diff -ur gv-3.5.8.orig/source/file.h gv-3.5.8/source/file.h
    --- gv-3.5.8.orig/source/file.h 1997-04-26
    00:00:00.000000000 +0200
    +++ gv-3.5.8/source/file.h      2002-09-26
    23:28:38.000000000 +0200
    @@ -70,6 +70,14 @@
     #endif
     );
     
    +extern char *file_charsAllowedInName;
    +
    +extern int                     file_nameIsDangerous (
    +#if NeedFunctionPrototypes
    +   char *
    +#endif
    +);
    +
     extern char*                   file_pdfname2psname (
     #if NeedFunctionPrototypes
        char *      /* name */
    diff -ur gv-3.5.8.orig/source/ps.c gv-3.5.8/source/ps.c
    --- gv-3.5.8.orig/source/ps.c   1997-06-07
    00:00:00.000000000 +0200
    +++ gv-3.5.8/source/ps.c        2002-09-27
    00:29:35.000000000 +0200
    @@ -420,6 +420,16 @@
           char cmd[512];
           char s[512];
           filename_unc=file_getTmpFilename(NULL,filename_raw);
    +      if (file_nameIsDangerous(filename))
    +       {
    +         INFMESSAGE(the filename is dangerous)
    +         sprintf(s, "The filename \"%s\" is dangerous:
    only alphanumeric "
    +                 "characters and \"%s\" are allowed.\n",
    +                 filename, file_charsAllowedInName);
    +         NotePopupShowMessage(s);
    +         ENDMESSAGE(psscan)
    +         return(NULL);
    +       }
           sprintf(cmd,cmd_uncompress,filename,filename_unc);
           INFMESSAGE(is compressed)
           INFSMESSAGE(uncompress command,cmd)
    @@ -491,6 +501,16 @@
           char cmd[512];
           char s[512];
           filename_dsc=file_getTmpFilename(NULL,filename_raw);
    +      if (file_nameIsDangerous(filename))
    +       {
    +         INFMESSAGE(the filename is dangerous)
    +         sprintf(s, "The filename \"%s\" is dangerous:
    only alphanumeric "
    +                 "characters and \"%s\" are allowed.\n",
    +                 filename, file_charsAllowedInName);
    +         NotePopupShowMessage(s);
    +         ENDMESSAGE(psscan)
    +         return(NULL);
    +       }
           sprintf(cmd,cmd_scan_pdf,filename,filename_dsc);
           INFMESSAGE(is PDF)
           INFSMESSAGE(scan command,cmd)
    ---8<------------------ asa-0000.gv-3.5.8.patch
    -------------------------
    



    This archive was generated by hypermail 2b30 : Tue Oct 01 2002 - 08:09:45 PDT