Questions about this topic? Sign up to ask in the talk tab.

Difference between revisions of "Secure Programming"

From NetSec
Jump to: navigation, search
(Created page with " Throughout the history of application development, programmers have repeatedly made the same mistakes, their educators have repeatedly inadvertantly missed important nuance...")
(No difference)

Revision as of 09:00, 20 April 2013

   Throughout the history of application development, programmers have  repeatedly made the same mistakes, their educators have repeatedly  inadvertantly missed important nuances, and the industry has  globally repeated these mistakes in the corporate world.  This has  yielded many developers who write vulnerable applications simply without  knowing better; in some cases, developers even think that they are  securing the application when in fact they are creating the vulnerabilities  themselves.  Only once developers are properly educated from the ground  up does it become possible to foster the proper development methodology  and good practices for security-centric and quality-assurance driven  development.

    Many of the common programming pitfalls responsible for modern  vulnerabilities are less than obvious and completely language agnostic,  or they are entirely small mistakes that are easy to miss or glance  over.  These pitfalls are mostly based around concepts, so proper and  improper implementations exist in nearly each language.  This article  focuses on interpreted languages, but this does not mean that the  vulnerabilities exist only in interpreted languages.


Concepts:
    Its as simple as realizing that certain things may cause a bug.  Bugs  are what hamper stability, performance/efficiency, and security.  By  solving any of these problems in entirety at least some of the others  will be solved in the process.  So, talking about bugs - bugs include  everything from the program displaying funny characters it shouldn't on  the screen to reproducable application crashes.  The more accidental  impact (complete crash vs. small hiccup) a bug has, typically its  impacts in all these areas are to scale with the impact on a user  experience - while risk applies to and defines the broader scale (the  purpose for the existence of the application or system).
    Most bugs are the result of a program receiving unexpected data and  using it in a way that results in the problem.  Because the ways that an  application is traditionally programmed to receive input are finite,  solutions to several types of  problems with user input can be found.  Data may contain illegal  characters, exceed an allowed length or maximum value, require a type or  sign translation, ad naseum.  When an application is able to compensate  for this, it is because the developer makes one of two choices: to  convert the data somehow into acceptable data, or to reject the data  entirely with an error message and demand re-submission of the input in  proper form.  Sometimes the input's "illegal characters" may even be a  part of the application's desired output - making the challenge more  difficult.


Sanitizing:
  There are two primary techniques used for input sanitization in  interpreted languages: whitelisting and modification.  Modification is a  process which modifies data submitted or inputted by a user, whereas  input whitelisting simply checks to see if the input matches a  particular criterium.  In the case of input modification, the  functionality exposed is usually executed regardless of the resulting  data; whereas in the case of input whitelisting, the functionality is  only exposed if the input meets the whitelist's criterium.  If the  functionality provided by a whitelisted input is still exposed, usually  the whitelisted input is reset to a particular default value of some  sort.  Some more specific examples of each technique are below:

   Input Whitelisting:
    A. Cancel process if malformatted - Only input matching a particular format will trigger the processing of the inputted data
    B. Cancel process if incorrect type - Only input of a particular type (integer, string, negative number, etc) will trigger the processing of the inputted data
    C. Cancel process if out of bounds - Only  input of a particular length, between two particular lengths, or  between two particular values will trigger the processing of the  inputted data
    D. Cancel process if not whitelisted - If the inputted data is not in a list of allowed choices, it is not processed.

   Input Modification:
    A. Sanitize-by-reformat - Characters or strings which may be malicious in user input are escaped or encoded to make them safe for evaluation
    B. Sanitize-by-typecast - User input is forced into the correct data type.  (Casted from string to integer, or vice versa).
    C. Sanitize-by-boundary - When  a user input reaches a particular size, all additional input is  ignored.  In some cases (e.g. decimals) this could be a string  truncation, an integer whole number cast, or rounding.
    D. Sanitize-by-delete - Characters or strings which may be malicious are completely removed from a user input.

  You may notice that whitelisting technique "A" corresponds to  modification technique "A", so on and so forth.  In many cases, a  combination of both modification and whitelisting is used for  sanitizing.  Performing these steps in the wrong order, or even  inadvertantly casting a value to the wrong data type may prove costly.   We will outline some of the more common mistakes involved with  implementations of each technique throughout this paper.  Additionally,  some more generic accidental programming pitfalls (even simple typos)  will be identified - along with solutions to them - so that they may be  avoided by future programmers.  Each language-agnostic pitfall contains  examples in each of the traditional four interpreted languages, mitigation techniques in each of them, and code audit suggestions.




Common pitfalls of traditional whitelisting:
Accidental Assignment
    This is a problem common to all programming languages in general.  Due  to the general nature of this pitfall, examples will not be extensively  provided.  Effectively, during a traditional "if" statement comparison,  at least two '=' characters must be used to perform a comparison as the  single '=' character is reserved for value assignment.  An accidental  placement of a single '=' operator without any additional comparison  operators will change the value in the variable, rather than comparing  it.  
  
Unsafe substring indexing:
    String positioning functions are often inadvertantly misused in a wide  variety of languages when determining where a pattern exists within a  string.  

 Examples of unsafe indexing:
      The problem is that ruby and  perl's index(), php's strpos(), or  python's find() may return 0 or  something else that equates to false if  the search character or string  is at position 0 in the string  (starting with the first letter at 0).   Because 0 evaluates to null or  false, the conditions will not be met  even if the needle exists in the  haystack.  Often times these functions are used to determine whether or  not a string exists within a string (rather than regular expressions or  other string comparison operators) due to the performance benefits.  The  downside is that careless implementations result in vulnerabilities.

   PHP:
     if (strpos('/', $var)) ...

   Python:
     if (var.find('/')): ...

   Ruby:
     if (var.index('/')) ...

   Perl:
     if (index($var,'/')) ...

 Mitigation:
      Because of the simplicity of the problem, it is trivial to add  additional boundary checks are added.  While this can be a bit language  specific, they are all easily remembered.  In PHP, multiple '='  operators are used to ensure that strpos() is returning an integer zero  rather than a null value (adding an implicit type-check).

   PHP:
     if (strpos("/",$var) >== 0) ...
  
   Python:
     if (var.find("/") >= 0): ...

   Ruby:
     if (var.index('/') >= 0) ...

   Perl:
     if (index($var,'/') >= 0) ...

   Note:  Some perl, ruby, or python implementations may require 'gte' rather  than '>=' as the "greater-than or equals" comparison operator.
   Tip: Some  lanuages will require you to properly enforce type (scalar vs. array)  and may have unpredictable behaviors if an array is passed (e.g.  var[1]=foo&var[2]=bar in the url.)

 Auditing:
     Using a simple bash command, one can search for all of the occurances (with context) of these functions:
       find -name \*.py -exec grep -HnC2 \\.find\( '{}' \; \
             -o -name \*.php -exec  grep -HnC2 str.\?pos\( '{}' \; \
             -o -regextype posix-awk -regex ".*\.(rb|pl|pm)" -exec grep -HnC2 \\bindex\( '{}' \; &> string_locating.txt

     It is not hard to isolate vulnerable implementations from there:
       grep \\.php:[0-9]\\+: string_locating.txt|grep -v "\!==\|==="


Unsafe string comparison
   Comparing strings, if not done properly, can be dangerous, too. Checking only that a string contains a certain value rather than checking for equality can leave inputs open to exploitation.  Normally this is not  something worth mentioning, but these are often easily and accidentally  confused.  This can happen any time strings are compared, whether with  regular expressions or traditional comparisons.
  
 Examples of unsafe string comparison (traditional) :    
   The below examples are unsafe because 'admin123' is still still valid, as 'admin' still occurs in the string. This would allow "admin123" to access features that should only be available to "admin":  

   >>> username = "admin123"
   >>> if "admin" in username:
   ...   print("User is an admin")
   'User is an admin'
  
   These functions check to see if  the search string occurs at all   within a string, but do not properly check equality between the two.    When on a programming binge (hours and hours of coding), developers   often overlook simple flaws like this.

   PHP:
     if (strstr("admin", $username)) ...

   Python:
     if "admin" in username: ...

   Ruby:
     if username.include? "admin" ...
     # OR
     # if username["admin"] ...

   Perl:
     if (index($username, "admin") != -1) ...

 An example using regular expressions:
   >>> username = "admin"
   >>> if re.match("admin", username): print("admin")
   ...
   'admin'

 Mitigation:
   Mitigation is rather simple, make sure that comparison operators are used instead of find functions.  Be extra careful to avoid accidental assignment during input checks.

   PHP:
     if ($username == "admin") ...

   Python:
     if username == "admin": ...

   Ruby:
     if  username == 'admin': ...

   Perl:
     if ($username eq 'admin') ...

  All regular expressions used for absolute comparison should be all inclusive.  This means that the first character of the pattern should be '^', indicating the beginning of the string, while the last character should be '$', indicating the end of the string.

   >>> pattern = '^admin$'
   >>> username = "admin123"
   >>> if re.match(pattern, username): print("is an admin")
   ...
  
 Unless the developer is explicity searching for the position or index of a substring or character within the target or trying to determine its presence in the target string, find functions should not be used and all regular expressions must be exact (with the exception of case insensitivity).

   In the wild:
     Wordpress timthumb.php domain name handling exploit (2011) leveraged this type of vulnerability.  
     Secunia Advisory 45416 https://secunia.com/advisories/45416/

   Auditing:
     In order to audit this, a developer needs to identify all uses of find functions and verify that the usage is safe. A search can be done using:
    
        find -name \*.py -exec grep -HnC2 \\bin\\b '{}' \; \
               -o -name \*.php\* -exec grep -HnC2 str.\?str\( '{}' \; \
               -o -name \*.rb -exec grep -HnC2 \\.include? '{}' \; \
               -o -regextype posix-awk -regex ".*\.(pl|pm)"  -exec grep -HnC2 \\bindex\( '{}' \; &>  string_comparison.txt
    
     Note: Regular expressions auditing information provided later in this article.







Common pitfalls encountered during input modification:

Non-recursive sanitize by delete
    In this scenario the attacker supplies a string, that only after being  parsed by the sanitization routines becomes capable of causing harm.   Without the sanitizing routines in place, the input would be  unsanitized and vulnerable.  With the sanitizing routines in place, they  have only increased the difficulty level for exploitation by 1. This  scenario exists because all of the existing string replace functions  only replace all occurrences of a given string search within the string target with no check to see if the newly parsed string still contains string search.  As a result, it is possible to embed the string within itself to prevent removal.

    For example, suppose a developer wanted to remove all instances of the  string "delete" from the user-supplied string "delete me".  Because  delete occurs only once, the string replace functionality of any given  language will return the value " me"; however, if the user supplied  value was "deldeleteete me", the string replace function for the given language returns "delete me".
  
   Warning: This pitfall applies to every string replace function, including Perl-Compliant-Regular-Expressions (PCRE).

 Examples of unsafe string replacement:  
  Python:
     >>> string = "....//....//....//"
     >>> string.replace("../","")
     '../../../'

  PHP:
    str_replace("../", "", $string);
  
   Note: Natively, Perl and Ruby use exclusively regular expressions for string replacement.

 Examples of unsafe pattern replacement:  
   PHP:
     $string = preg_replace('@[.]{2}\/@',,$string);
  
   Ruby:
     string =~ s/[.]{2}\///g
     # OR
     # string = string.gsub("../", "")
    
   Perl:
     $string =~ s/[.]{2}\///g;

   Python:
    string = re.sub('[.]{2}\/',,string)

 Mitigation:
   These cases are somewhat trivial in that they only attempt to remove the unix parent directory ("../") string non-recursively. Because  this does not apply exclusively to path strings, but also to xss and  other dynamic-content related vulnerabilities, we have  documented the  proper way to recursively remove unwanted substrings. There are multiple  ways to mitigate this particular pitfall.  The two to that immediately come to mind are loops and recursion.  
  
   We'll test loops out in the python shell:
     >>> string = "....//....//....//....//"    
     >>> while "../" in string: string = string.replace('../',)
     ...
     >>> string
     

     >>> string = "....//....//....//....//"
     >>> while re.search('[.]{2}\/',string): string = re.sub('[.]{2}\/',,string)
     ...
     >>> string
     

   Safe string replace using recursive functions and loops (multiple languages):
     Python:      
       def safereplace(text, replacement, string):   # Traditional Replacement
           if text in string: return safereplace(text, replacement, string.replace(text, replacement))
           return string
  
       def safesub(pattern, replace, string):           # Pattern/regex
           if re.search(pattern,string): return safesub(pattern, replace, re.sub(pattern, replace, string))
           return string
      
       >>> string = "....//....//....//....//"
       >>> safereplace("../","",string)
       
       >>> safesub('[.]{2}\/',,string)
       

     PHP:
       Note: Additional recursion functions should be added for preg_replace and ereg_replace if you plan on using these for sanitizing.
       function safe_str_replace($search, $replace, $string) {
            if (strpos($string, $search) >== 0) return  safe_str_replace($search,$replace,str_replace($search, $replace,  $string));
           return($string);
       }

     Perl:
       sub safe_str_replace {
           my $pattern         = shift;
           my $replacement = shift;
           my $string           = shift;
           $string =~ s/$pattern/$replace/g until ($string !~ /$pattern/);
           return($string);
       }
    
     Ruby:
       def safe_sub(pattern, replacement, string)
         if (string ~ '/#{pattern}/') return safe_sub(pattern, replacement, string.gsub(pattern,replacement))
         return string
       end
      
  Auditing:
     find -regextype posix-awk -regex ".*\.e?(rb|py|php)"  -exec grep -HnC2 \\b\\(.*sub\|replace\\)\( '{}' \; &>  string_replacement.txt

Integer handled as string
  Improper type handling is as common as it is dangerous.  If a programmer makes the mistake of handling an integer as a string (or other type confusions), sanitizing inputs can become useless.  There are a variety of approaches for mitigation of this type of issue.

  Examples of integral handling vulnerabilities:

   PHP:
       <?php
         $id = mysql_real_escape_string($_GET['id']);
         @mysql_query("SELECT * FROM user WHERE user_id = " . $id . " LIMIT 1");
       ?>

   Ruby:
      
   Perl:
   Python:

 Mitigation:
  If user_id is an integer, this statement is still vulnerable, if a user  inputs "id=1%20OR1=1", the statement will not be escaped (since there  are no quotes) and still a valid SQL statement, rendering the database open to injection. While this example uses SQL, other situations may still be vulnerable. In this case, for forward compatibility and "universal solution" purposes, the programmer should use the appropriate native (or generic) method to cast to an integer instead of mysql_real_escape_string():

   PHP:
     <?php
       $id = (int)$_GET['id'];
       @mysql_query("SELECT * FROM user WHERE user_id = " . $id . " LIMIT 1");
     ?>

   Ruby:
       Warning:  (link to ruby paramaterized section)Activerecord supports paramaterized  statements(/link).  It is likely that if you need to use this in  conjunction with active record, you may be (link to unparamaterized  queries) doing something wrong (/link).
      
      
      

  
   Perl:
   Python:

  Auditing:


Improper signedness
   Improper signedness is caused by allowing signed data when expecting  unsigned data. This can cause information disclosure or even code  execution in extreme circumstances. Our previous "fixed" code in the  "Integer Handled as String" section does still have this problem. It is  handled as an integer but the sign is not considered, as "-10" is still a  valid integer, this will slip through the (int) cast. Therefore, we  need to pass this to the abs() function which takes the absolute value  of the data.
    
  Mitigation:

   PHP:
     <?php
       $id = abs((int)$_GET['id']);
       @mysql_query("SELECT * FROM user WHERE user_id = " . $id . " LIMIT 1");
     ?>

  Auditing:


Unparamaterized Statements:

Examples:

Mitigation:
       Rails:
           user = User.Find(:conditions => ['id = ?', params[id]]  
Auditing:



Pitfalls resulting from improper combination of whitelisting and modification:
Delete after length-check (Empty Where Clauses)

 Proof of concept:

 ------------------------
 While the example here is based on SQL injection, it can apply to other scenarios as well.  Suppose the following php code:

  <?php
  # pwreset.php
  if (strlen($_GET['reset']) == 16) {
    $pwreset_code = preg_replace_all('/([^\w]+)/',,$_GET['reset']); #alphanumeric only.
    $user_query   = "SELECT * FROM user WHERE pwreset_code= '" . $pwresetcode ."' LIMIT 1";
    $user_data    = @mysql_query($user_query);
    ...
  }
  -----------------------

  pwreset.php?reset='''''''''''

  The generated sql statement:
    SELECT * FROM user WHERE pwreset_code= LIMIT 1;

   As reset codes are generated on reset request (forgot password form),  this will leave the first user (usually the admin) of the web app  exposed to arbitrary password overwrite.

 Mitigation:

 Instead of checking length before replacement, verify after deletion.

  * PHP:
    <?php
    # pwreset.php
    $pwreset_code = preg_replace_all('/([^\w]+)/',,$_GET['reset']); #alphanumeric only.
    if (strlen($pwreset) == 16) {
      $user_query   = "SELECT * FROM user WHERE pwreset_code= '" . $pwresetcode ."' LIMIT 1";
      $user_data    = @mysql_query($user_query);
      ...
    }

 In the wild:
   A Joomla! framework exploit leveraged this weakness for an administrative password reset (2008).
   CVE 2008-3861 http://www.exploit-db.com/exploits/6234/

 Auditing:
    Follow the same auditing steps as listed for unsafe deletion, with an  eye towards where clauses or other empty type comparisons that could  lead to problematic outcomes.


Deletion or truncation after reformat
   This vulnerability is caused by reformatting a string and then  truncating it to a specific length, this allows an attacker to trigger  an error and possibly execute code. For example, if we have a filter  that runs mysql_real_escape_string() and then truncates the string to 16  characters, we could run into this problem. The attacker inputs  "123456789012345'", when run through mysql_real_escape_string() it  becomes "123456789012345\'", when truncated to 16 characters, this  string finally becomes "123456789012345\", which would escape the quotes  surrounding it. This would at the very least cause an error for  information disclosure, but could also lead to sql injection and xss notwithstanding.

 Examples:

  * PHP:
    <?php
      $username = substring(mysql_real_escape_string($_GET['username']), 0, 16);
      $query   = "SELECT * FROM user WHERE username= '" . $username ."'";
      $user_data    = @mysql_query($query);
    ?>
  
   * Python:
     >>> username = "123456789012345'"
     >>> username = username.replace("'", "\\'")
     >>> print("SELECT * FROM users WHERE username = '%s'" % username[0:16])
     SELECT * FROM users WHERE username = '123456789012345\'
    
   * Perl:
     my $username = "123456789012345'";
     $username =~ s/\'/\\'/g;
     $username = substr($username, 0, 16);
     print "$username\n";

   Ruby:


 Mitigation:
   This attack can be mitigated by truncating the input before reformatting and checking the length (failing if not correct).
  
  * PHP:
    <?php
      $username = mysql_real_escape_string(substring($_GET['username'], 0, 16));
      if(strlen($username) == 16){
         $query   = "SELECT * FROM user WHERE username= '" . $username ."'";
         $user_data    = @mysql_query($query);
         ...
      }
    ?>
  
   * Python:
     >>> username = "123456789012345'"
     >>> username = username[0:16].replace("'", "\\'")
     >>> if len(username) == 16:
     ...        print("SELECT * FROM users WHERE username = '%s'" % username)

   * Perl:    
     my $username = "123456789012345'";
     $username = substr($username, 0, 16);
     $username =~ s/\'/\\'/g;
     if (length $username == 16){
       print "$username\n";
     }
  
 Auditing:

Common language-agnostic non-sanitizing related pitfalls

   Lack of paramaterization for prepared statements
      Examples:
    
      Mitigation:
    
      Auditing:
    
   Unsafe programmatic construct referencing
    Dynamic referencing of functions and classes through user input is  dangerous and should never be done, some examples are listed below.

     Unsafe PHP examples:
        Anonymous function implementation example:
          <?php
            $func = $_GET['func'];
            $func($args);
          ?>          

        Anonymous object implementation example:
          <?php
            $class = $_GET['object_type'];
            $object = new $class();
          ?>

        Anonymous object method implementation example:
          <?php
            $class    = $_GET['object_type'];
            $method = $_GET['method'];
            $object   = new $class();
            $object->$method();
          ?>
      
     Unsafe Perl examples:
        Anonymous function implementation example:
          #!/usr/bin/perl
          $func = <>;
          $func->();

        Anonymous object implementation example:
          #!/usr/bin/perl
          $class = <>;
          $object = $class->new();

        Anonymous object method implementation example:
          #!/usr/bin/perl
          $class = <>;
          $method = <>;
          $object = $class->new()->method();
        
     Unsafe Python examples:
        Anonymous function implementation example:
          var = "function_name"
          locals()[var]() # or globals()

        Anonymous object implementation example:
          class_name = input()
          instance = globals()[class_name]()
          # OR
          # instance = locals()[class_name]()
           # instance = dict(target  for classlist in  [inspect.getmembers(sys.modules[module],inspect.isclass) for module in  sys.modules]  for target in classlist)[class_name]()
           any_scoped_class = lambda x:  dict(target  for classlist in   [inspect.getmembers(sys.modules[module],inspect.isclass) for module in   sys.modules]  for target in classlist)[x]()
          
        Anonymous object method implementation example:
          ClassName = "ObjectName"
          func_name = "object_method"
          instance = globals()[ClassName]()
          func = getattr(instance, func_name)
          instance.func();

     Unsafe ruby examples:
       Anonymous function implementation example:
         send("function_name")
        
       Anonymous object implementation example:
         Object.const_get('ClassName').new()
        
       Anonymous object method implementation example:
         Object.const_get("ClassName").new().send("function_name")

    Mitigation:
       These functions are best left unused unless the data is whitelisted and  not directly based on user input. There is no safe way to allow users  to call arbitrary (non-whitelisted) classes and functions. Do not do this!  Whitelists should always be in place when referencing objects from user input.

      Whitelisting examples:
      PHP:
         <?php
             $class_whitelist['class_name'] = 'real_class_name';
            
             if (isset($_GET['module']) &&                                                # Check that input is defined
                  isset($class_whitelist[$_GET['module']]   &&                       # Be sure the class is in the whitelist
                  class_exists($class_whitelist[$_GET['module']])) {              # even  if its whitelisted, lets make sure its a class... typos exist.

                    $class = new $class_whitelist[$_GET['module']]();            # new  real_class_name() - this way user input isn't trusted directly for  instantiation.
             } else {
                 # Handle Error...
             }
         ?>


    Auditing:
      To audit:
        find -name \*.py -exec grep -EHnC2 "[a-zA-Z0-9\)]\^\'\"\\\+\]\(" '{}' \; \
               -o -name \*.rb -exec grep -EHnC2 "const_get\(\|send\(\|eval\(" '{}' \; &>  string_comparison.txt
              
   Unsafe file i/o:
       Accepting path names as input over the network should be avoided if at all possible, and instead should use a whitelist containing possible file-names.  Accepting file names as input will cause  file disclosure, source disclosure, and file inclusion  vulnerabilities.  This can apply to more than file inclusion.  Some  languages even output entire directory listings when "." is read as a  file.  Filenames provided by user input simply should not be used.  An  SQL database (for constantly changing filenames and updated listings) or  other similar indexing method (static array whitelisting) can provide a  safe way (like an ID) for file access without allowing the user to  directly specify the path to the file on the server.

 Auditing:

   Unsanitized input split
        If input is being split and parsed based on an expected number of  delimeters, it is trivial for an attacker to input an extra delimeter  and depending on the severity of the issue, execute code or other  mayhem.
      
   Proof of concept:

 * PHP:
   <?php
     $username = $argv[1];
     if ($username == "admin") {
       $username = $username . ":1";
     } else {
       $username = $username . ":0";
    }
    $username = split(":", $username);
    if($username[1] == "1") {
      echo "Is an admin\n";
    } else {
      echo "Not an admin\n";
    }
   ?>

   Mitigation:
  
    In order to mitigate this attack, sanitize input before splitting. Be  certain that there are no malicious delimeters. For example:
  
 * PHP:
   <?php
     $username = str_replace(":", "", $argv[1]);
     if ($username == "admin") {
       $username = $username . ":1";
     } else {
       $username = $username . ":0";
    }
    $username = split(":", $username);
    if($username[1] == "1") {
      echo "Is an admin\n";
    } else {
      echo "Not an admin\n";
    }
   ?>
  
   Auditing:
  
   Unsafe evaluation
     Proof of concept:
       It is never safe to eval() user input, plain and simple.  There is always another way to skin the cat; try and consider that!
    
     Mitigation:
       Design around combining the function "eval" with user input.
    
     Auditing:
       IGNORECASE=1 find -regextype posix-awk -regex ".*\.(rb|php|pl|py|pm)" -exec grep -HnC2 \\beval\( '{}' \;

   Unsafe command processing
       Using user input as arguments for a system call should never be done.  The system() and exec() functions are mostly universal between languages along with the backtick (`) characters for command substitution.  In some languages, the $() operator for command substitution is also valid.

     Proof of concept:  
       >>> os.system("echo %s" % input("# "))
      # unsafe; ping -c1 google.com
      unsafe
      PING google.com (74.125.224.168) 56(84) bytes of data.
      64 bytes from lax02s01-in-f8.1e100.net (74.125.224.168): icmp_req=1 ttl=51 time=13.1 ms
    
    Mitigation:
      Instead of using system, backticks, popen, exec or any other command executing function, use the language's native built in library.   If it does not have one, then write one - but do not simply wrap  system() and call this a library; this is bad form.  All interpreted  languages have a way to load shared libraries (*.so files) and interface  with the functions provided by their export tables.  These are what  should be utilized when authoring libraries that seemingly need you to  run a command.  Instead, the C interface (CTypes) can be used.  
    
      Ctypes Examples:
      * Python:
          >>> from ctypes import *;  cdll.LoadLibrary("libc.so.6").printf(c_char_p("abcdefgh\n")); # import  printf() from libc.so.6
         'abcdefgh'
        
      * Perl:
    
      * PHP:
    
      * Ruby:
    

    Auditing:  
       Auditing command processing is simple: check for all uses of system(),  exec(), backticks, popen, and any language specific function.
    
        find -type f -regextype posix-awk -regex ".*\.(rb|php|py|pl|pm)"  -exec grep -EHnC2 "system\(\|[pP]open\(\|\`\|exec.*\(\|passthru\(" '{}' \; &>  command_processing.txt

   Value overwrite by mass assignment
      These are rare scenarios caused by mass object property assignment from  HTTP parameters.  Many web applications do this on one level or another  to simplify form generation.  Advanced ORM's are usually (but not  always) involved in this sort of problem, so be on the lookout for PHP's  "Doctrine", Ruby's "ActiveRecord", or Python's "sqlAlchemy".  
    
     Suppose when a signup form post occurs, an ajax request is sent to:
       /users/signup/?username=newguy&password1=crackme&password2=crackme&[email protected]
    
     And our SQL table creation statement looked something like:
    
      create table user (id int auto_increment primary key, group_id int  foreign key not null default 1, username varchar(24), password  varchar(512), activated timestamp, email varchar(256)) foreign key  (group_id) references group(id);
  
     The target software will automatically put new users in group 1  (non-activated users list) on registration.  Perhaps on update it would  update us to group 2, and additional groups became available for further  permissions.  
  
     Suppose the "administrators group" for the web application is group id  5.  When unchecked mass assignment is in place, the following signup URI  would make user `superhacker' an admin:
       /users/signup/?username=superhacker&password=1337&[email protected]&group_id=5
    
   Examples:
  
     PHP:
         <?php
            foreach ($_GET as $property => $value) {
              $user->$property = $value;
            }
            $user->save();
         ?>

     Python:
        for param in request.GET:
          user.__dict__[param] = request.GET[param]
    
     Ruby on Rails (ActiveRecord):
         user = User.new(params[:user])
        
   Mitigation:
       PHP:
      


    
   Auditing:

Common language-specific pitfalls:
 PHP specific pitfalls:
   File inclusion by remote and local

   Situationally bad sanitizing:
    addslashes()
    htmlspecialchars()
    mysql_real_escape_string()

 Perl specific pitfalls:
   Command injection with open()      

 Python specific pitfalls:
   Urllib opens/follows file:// resource location response headers   (Python)

 Ruby (eruby and rails) specific pitfalls:
   attr_protected
   CGI.EscapeHTML()