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