...
The outer parentheses of the malicious search string defeat the grouping protection. Using the OR operator allows injection of any arbitrary regex. Now this use will reveal all times and IPs the keyword 'C' was searched.
Code Block | ||
---|---|---|
| ||
package edu.cmu.cs392.p3.log; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ExploitableLog {    //*A Say this logfile contains:    * CSV style: search string, time (unix), ip (integer)    *    * Alice,1267773881,2147651708    * Bono,1267774881,2147651708    * Charles,1267775881,1175563058    * Cecilia,1267773222,291232332    *    * and the CSVLog class has a readLine() method which retrieves a single    * line from the CSVLog and returns null when at EOF    */    private CSVLog logfilebuffer containing the log.    private StringBuffer logBuffer;       /* an application repeatedly calls this function that searches through the    * search log for search suggestions for autocompletion    */    public Set<String> suggestSearches(String search)    {       Set<String> searches = new HashSet<String>();             /* Construct regex from user string */       //Regex matches full valid log lines. The grouping characters will limit       //the returned string to only the keyword.       String regex = "^(" + search + ".*),[0-9]+?,[0-9]+?$";       int flags = Pattern.MULTILINE; //needed since matching on a String                               //containing newlines       Pattern p = Pattern.compile(regex, flags);             /* Read from log and match regex */       int  String s;len = logBuffer.length(); //the SB can only be >= to this number       while String ((s = logfilelogBuffer.readLinesubstring()) != null) { //gets a single line from the logfile      0, len);       logBuffer.delete(0, len);             /* Match regex */       Matcher m = p.matcher(s);          while  if (m.find()) {             String found = m.group(1);             searches.add(found);          }       }             return searches;    }       public ExploitableLog()    {       logfile = new CSVLog();    }       public class CSVLog    {       //this is supposed to come from a file, but its here as a string for       //illustrative purposes       public logBuffer static= final String logString =new StringBuffer();           logBuffer.append("Alice,1267773881,2147651408\n" +);           logBuffer.append("Bono,1267774881,2147351708\n" +);           logBuffer.append("Charles,1267775881,1175523058\n" +);           logBuffer.append("Cecilia,1267773222,291232332\n";             private String[] log;             private int index;             public CSVLog()       {          index = 0;          log = logString.split("\n");       }             public String readLine()       {          if (index < log.length)             return log[index++];          return null;       }    } } |
Compliant Solution
...
This solution filters out non-alphanumeric characters from the search string using Java's Character.isLetterOrDigit(). This removes the grouping parentheses and the OR operator which triggers the injection.
Code Block | ||
---|---|---|
| ||
package edu.cmu.cs392.p3.log; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public class FilteredLog {    /*/A Say this logfile contains:    * CSV style: search string, time (unix), ip (integer)    *    * Alice,1267773881,2147651708    * Bono,1267774881,2147651708    * Charles,1267775881,1175563058    * Cecilia,1267773222,291232332    *    * and the CSVLog class has a readLine() method which retrieves a single    * line from the CSVLog and returns null when at EOF    */buffer containing the log.    private CSVLogStringBuffer logfilelogBuffer;       /* an application repeatedly calls this function that searches through the    * search log for search suggestions for autocompletion    */    public Set<String> suggestSearches(String search)    {       Set<String> searches = new HashSet<String>();             /* Filter user input */       StringBuilder sb = new StringBuilder(search.length());       for (int i = 0; i < search.length(); ++i) {          char ch = search.charAt(i);          if (Character.isLetterOrDigit(ch))             sb.append(ch);       }       search = sb.toString();             /* Construct regex from user string */       //Regex matches full valid log lines. The grouping characters will limit       //the returned string to only the keyword.       String regex = "^(" + search + ".*),[0-9]+?,[0-9]+?$";       int flags = Pattern.MULTILINE; //needed since matching on a String                               //containing newlines       Pattern p = Pattern.compile(regex, flags);             /* Read from log and match regex */       String s; int len = logBuffer.length(); //the SB can only be >= to this number       while String ((s = logfilelogBuffer.readLinesubstring()) != null) {0, len);       logBuffer.delete(0, len);             /* Match regex */       Matcher m = p.matcher(s);          if while (m.find()) {             String found = m.group(1);             searches.add(found);          }       }             return searches;    }       public FilteredLog()    {       logfile = new CSVLog();    }       public class CSVLog    {       //this is supposed to come from a file, but its here as a string for       //illustrative purposes       public logBuffer static= final String logString =new StringBuffer();           logBuffer.append("Alice,1267773881,2147651408\n" +);           logBuffer.append("Bono,1267774881,2147351708\n" +);           logBuffer.append("Charles,1267775881,1175523058\n" +);           logBuffer.append("Cecilia,1267773222,291232332\n";             private String[] log;             private int index;             public CSVLog()       {          index = 0;          log = logString.split("\n");       }             public String readLine()       {          if (index < log.length)             return log[index++];          return null;       }    } } |
Risk Assessment
...