View Javadoc
1   package com.jsql.util;
2   
3   import com.jsql.util.tampering.TamperingType;
4   import org.apache.commons.lang3.StringUtils;
5   import org.apache.logging.log4j.LogManager;
6   import org.apache.logging.log4j.Logger;
7   
8   import javax.script.Invocable;
9   import javax.script.ScriptEngine;
10  import javax.script.ScriptEngineManager;
11  import javax.script.ScriptException;
12  import java.util.regex.Pattern;
13  
14  public class TamperingUtil {
15  
16      private static final Logger LOGGER = LogManager.getRootLogger();
17  
18      public static final String TAG_OPENED = "<tampering>";
19      public static final String TAG_CLOSED = "</tampering>";
20  
21      private boolean isBase64 = false;
22      private boolean isVersionComment = false;
23      private boolean isFunctionComment = false;
24      private boolean isEqualToLike = false;
25      private boolean isRandomCase = false;
26      private boolean isHexToChar = false;
27      private boolean isStringToChar = false;
28      private boolean isQuoteToUtf8 = false;
29      private boolean isCharToEncoding = false;
30      private boolean isEval = false;
31      private boolean isSpaceToMultilineComment = false;
32      private boolean isSpaceToDashComment = false;
33      private boolean isSpaceToSharpComment = false;
34  
35      private String customTamper = null;
36  
37      private static final ScriptEngineManager SCRIPT_ENGINE_MANAGER = new ScriptEngineManager();
38  
39      private static String eval(String sqlQuery, String jsTampering) {
40          Object resultSqlTampered;
41          try {
42              if (StringUtils.isEmpty(jsTampering)) {
43                  throw new ScriptException("Tampering context is empty");
44              }
45  
46              ScriptEngine nashornEngine = TamperingUtil.SCRIPT_ENGINE_MANAGER.getEngineByName("nashorn");
47              nashornEngine.eval(jsTampering);
48  
49              var nashornInvocable = (Invocable) nashornEngine;
50              resultSqlTampered = nashornInvocable.invokeFunction("tampering", sqlQuery);
51  
52          } catch (ScriptException e) {
53              LOGGER.log(
54                  LogLevelUtil.CONSOLE_ERROR,
55                  String.format("Tampering context contains errors: %s", e.getMessage()),
56                  e
57              );
58              resultSqlTampered = sqlQuery;
59          } catch (NoSuchMethodException e) {
60              LOGGER.log(
61                  LogLevelUtil.CONSOLE_ERROR,
62                  String.format("Tampering context is not properly defined: %s", e.getMessage()),
63                  e
64              );
65              LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Minimal tampering context is: var tampering = function(sql) {return sql}");
66              resultSqlTampered = sqlQuery;
67          }
68          return resultSqlTampered.toString();
69      }
70  
71      public String tamper(String sqlQueryDefault) {
72          String lead;
73          String sqlQuery;
74          String trail;
75  
76          // Transform only SQL query without HTTP parameters and syntax changed, like p=1'+[sql]
77          String regexToMatchTamperTags = String.format("(?s)(.*%s)(.*)(%s.*)", TamperingUtil.TAG_OPENED, TamperingUtil.TAG_CLOSED);
78          var matcherSql = Pattern.compile(regexToMatchTamperTags).matcher(sqlQueryDefault);
79  
80          if (matcherSql.find()) {
81              lead = matcherSql.group(1);
82              sqlQuery = matcherSql.group(2);
83              trail = matcherSql.group(3);
84          } else {
85              return sqlQueryDefault;
86          }
87  
88          if (this.isEval) {
89              sqlQuery = TamperingUtil.eval(sqlQuery, this.customTamper);
90          }
91  
92          sqlQuery = this.transform(sqlQuery, this.isHexToChar, TamperingType.HEX_TO_CHAR);
93          sqlQuery = this.transform(sqlQuery, this.isStringToChar, TamperingType.STRING_TO_CHAR);
94          sqlQuery = this.transform(sqlQuery, this.isFunctionComment, TamperingType.COMMENT_TO_METHOD_SIGNATURE);
95          sqlQuery = this.transform(sqlQuery, this.isVersionComment, TamperingType.VERSIONED_COMMENT_TO_METHOD_SIGNATURE);
96          sqlQuery = this.transform(sqlQuery, this.isRandomCase, TamperingType.RANDOM_CASE);
97          sqlQuery = this.transform(sqlQuery, this.isEqualToLike, TamperingType.EQUAL_TO_LIKE);
98  
99          sqlQuery = lead + sqlQuery + trail;
100 
101         String regexToRemoveTamperTags = String.format("(?i)%s|%s", TamperingUtil.TAG_OPENED, TamperingUtil.TAG_CLOSED);
102         sqlQuery = sqlQuery.replaceAll(regexToRemoveTamperTags, StringUtils.EMPTY);
103 
104         // Empty when checking character insertion
105         if (StringUtils.isEmpty(sqlQuery)) {
106             return StringUtils.EMPTY;
107         }
108 
109         // Transform all query, SQL and HTTP
110 
111         // Dependency to: EQUAL_TO_LIKE
112         if (this.isSpaceToDashComment) {
113             sqlQuery = TamperingUtil.eval(sqlQuery, TamperingType.SPACE_TO_DASH_COMMENT.instance().getJavascript());
114         } else if (this.isSpaceToMultilineComment) {
115             sqlQuery = TamperingUtil.eval(sqlQuery, TamperingType.SPACE_TO_MULTILINE_COMMENT.instance().getJavascript());
116         } else if (this.isSpaceToSharpComment) {
117             sqlQuery = TamperingUtil.eval(sqlQuery, TamperingType.SPACE_TO_SHARP_COMMENT.instance().getJavascript());
118         }
119 
120         sqlQuery = this.transform(sqlQuery, this.isBase64, TamperingType.BASE64);
121         sqlQuery = this.transform(sqlQuery, this.isCharToEncoding, TamperingType.CHAR_TO_ENCODING);
122         sqlQuery = this.transform(sqlQuery, this.isQuoteToUtf8, TamperingType.QUOTE_TO_UTF8);  // char insertion included
123         return sqlQuery;
124     }
125 
126     private String transform(String sqlQuery, boolean shouldApply, TamperingType tamperingType) {
127         if (shouldApply) {
128             return TamperingUtil.eval(sqlQuery, tamperingType.instance().getJavascript());
129         }
130         return sqlQuery;
131     }
132 
133 
134     // Builder
135 
136     public TamperingUtil withBase64() {
137         this.isBase64 = true;
138         return this;
139     }
140 
141     public TamperingUtil withVersionComment() {
142         this.isVersionComment = true;
143         return this;
144     }
145 
146     public TamperingUtil withFunctionComment() {
147         this.isFunctionComment = true;
148         return this;
149     }
150 
151     public TamperingUtil withEqualToLike() {
152         this.isEqualToLike = true;
153         return this;
154     }
155 
156     public TamperingUtil withRandomCase() {
157         this.isRandomCase = true;
158         return this;
159     }
160 
161     public TamperingUtil withHexToChar() {
162         this.isHexToChar = true;
163         return this;
164     }
165 
166     public TamperingUtil withStringToChar() {
167         this.isStringToChar = true;
168         return this;
169     }
170 
171     public TamperingUtil withQuoteToUtf8() {
172         this.isQuoteToUtf8 = true;
173         return this;
174     }
175 
176     public TamperingUtil withCharToEncoding() {
177         this.isCharToEncoding = true;
178         return this;
179     }
180 
181     public TamperingUtil withEval() {
182         this.isEval = true;
183         return this;
184     }
185 
186     public TamperingUtil withSpaceToMultilineComment() {
187         this.isSpaceToMultilineComment = true;
188         return this;
189     }
190 
191     public TamperingUtil withSpaceToDashComment() {
192         this.isSpaceToDashComment = true;
193         return this;
194     }
195 
196     public TamperingUtil withSpaceToSharpComment() {
197         this.isSpaceToSharpComment = true;
198         return this;
199     }
200 
201     
202     // Getter and setter
203 
204     public String getCustomTamper() {
205         return this.customTamper;
206     }
207 
208     public void setCustomTamper(String customTamper) {
209         this.customTamper = customTamper;
210     }
211 
212     public TamperingUtil withBase64(boolean selected) {
213         this.isBase64 = selected;
214         return this;
215     }
216 
217     public TamperingUtil withEqualToLike(boolean selected) {
218         this.isEqualToLike = selected;
219         return this;
220     }
221 
222     public TamperingUtil withEval(boolean selected) {
223         this.isEval = selected;
224         return this;
225     }
226 
227     public TamperingUtil withFunctionComment(boolean selected) {
228         this.isFunctionComment = selected;
229         return this;
230     }
231 
232     public TamperingUtil withHexToChar(boolean selected) {
233         this.isHexToChar = selected;
234         return this;
235     }
236 
237     public TamperingUtil withQuoteToUtf8(boolean selected) {
238         this.isQuoteToUtf8 = selected;
239         return this;
240     }
241 
242     public TamperingUtil withCharToEncoding(boolean selected) {
243         this.isCharToEncoding = selected;
244         return this;
245     }
246 
247     public TamperingUtil withRandomCase(boolean selected) {
248         this.isRandomCase = selected;
249         return this;
250     }
251 
252     public TamperingUtil withSpaceToDashComment(boolean selected) {
253         this.isSpaceToDashComment = selected;
254         return this;
255     }
256 
257     public TamperingUtil withSpaceToMultilineComment(boolean selected) {
258         this.isSpaceToMultilineComment = selected;
259         return this;
260     }
261 
262     public TamperingUtil withSpaceToSharpComment(boolean selected) {
263         this.isSpaceToSharpComment = selected;
264         return this;
265     }
266 
267     public TamperingUtil withStringToChar(boolean selected) {
268         this.isStringToChar = selected;
269         return this;
270     }
271 
272     public TamperingUtil withVersionComment(boolean selected) {
273         this.isVersionComment = selected;
274         return this;
275     }
276 }