View Javadoc
1   package com.jsql.model.injection.strategy;
2   
3   import com.jsql.model.InjectionModel;
4   import com.jsql.model.exception.JSqlException;
5   import com.jsql.model.injection.engine.model.EngineYaml;
6   import com.jsql.model.suspendable.Input;
7   import com.jsql.model.suspendable.SuspendableGetCharInsertion;
8   import com.jsql.model.suspendable.SuspendableGetEngine;
9   import com.jsql.util.LogLevelUtil;
10  import com.jsql.util.StringUtil;
11  import org.apache.commons.lang3.StringUtils;
12  import org.apache.logging.log4j.LogManager;
13  import org.apache.logging.log4j.Logger;
14  
15  import java.util.AbstractMap.SimpleEntry;
16  import java.util.Arrays;
17  import java.util.List;
18  import java.util.regex.Matcher;
19  
20  public class MediatorStrategy {
21  
22      private static final Logger LOGGER = LogManager.getRootLogger();
23      
24      private final AbstractStrategy time;
25      private final AbstractStrategy blindBit;
26      private final AbstractStrategy blindBin;
27      private final AbstractStrategy multibit;
28      private final AbstractStrategy dns;
29      private final StrategyError error;
30      private final AbstractStrategy union;
31      private final AbstractStrategy stack;
32  
33      private final List<AbstractStrategy> strategies;
34      
35      /**
36       * Current injection strategy.
37       */
38      private AbstractStrategy strategy;
39  
40      private final InjectionModel injectionModel;
41      
42      public MediatorStrategy(InjectionModel injectionModel) {
43          this.injectionModel = injectionModel;
44          
45          this.time = new StrategyTime(this.injectionModel);
46          this.blindBit = new StrategyBlindBit(this.injectionModel);
47          this.blindBin = new StrategyBlindBin(this.injectionModel);
48          this.multibit = new StrategyMultibit(this.injectionModel);
49          this.dns = new StrategyDns(this.injectionModel);
50          this.error = new StrategyError(this.injectionModel);
51          this.union = new StrategyUnion(this.injectionModel);
52          this.stack = new StrategyStack(this.injectionModel);
53  
54          this.strategies = Arrays.asList(this.time, this.blindBin, this.blindBit, this.multibit, this.error, this.dns, this.stack, this.union);
55      }
56      
57      public String getMeta() {
58          String strategyName = this.strategy == null ? StringUtils.EMPTY : this.strategy.toString().toLowerCase();
59          var strategyMode = "default";
60          if (this.injectionModel.getMediatorUtils().preferencesUtil().isDiosStrategy()) {
61              strategyMode = "dios";
62          } else if (this.injectionModel.getMediatorUtils().preferencesUtil().isZipStrategy()) {
63              strategyMode = "zip";
64          }
65          return String.format("%s#%s", strategyName.replace(" ", "-"), strategyMode);
66      }
67      
68      /**
69       * Build correct data for GET, POST, HEADER.
70       * Each can be either raw data (no injection), SQL query without index requirement,
71       * or SQL query with index requirement.
72       * @param urlBase Beginning of the request data
73       * @param isUsingIndex False if request doesn't use indexes
74       * @param sqlTrail SQL statement
75       * @return Final data
76       */
77      public String buildPath(String urlBase, boolean isUsingIndex, String sqlTrail) {
78          String result = urlBase;
79          if (urlBase.contains(InjectionModel.STAR)) {
80              if (!isUsingIndex) {
81                  result = urlBase.replace(InjectionModel.STAR, this.encodePath(sqlTrail));
82              } else {
83                  result = urlBase.replace(
84                      InjectionModel.STAR,
85                      this.encodePath(
86                          this.getSpecificUnion().getIndexesInUrl().replaceAll(
87                              String.format(EngineYaml.FORMAT_INDEX, this.getSpecificUnion().getVisibleIndex()),
88                              Matcher.quoteReplacement(sqlTrail)  // Oracle column can contain regex char $ => quoteReplacement()
89                          )
90                      )
91                  );
92              }
93          }
94          return result;
95      }
96  
97      private String encodePath(String sqlTrail) {
98          String sqlTrailEncoded = StringUtil.cleanSql(sqlTrail);
99  
100         if (!this.injectionModel.getMediatorUtils().preferencesUtil().isUrlEncodingDisabled()) {
101             sqlTrailEncoded = sqlTrailEncoded
102                 .replace("'", "%27")
103                 .replace("(", "%28")
104                 .replace(")", "%29")
105                 .replace("{", "%7b")
106                 .replace("[", "%5b")
107                 .replace("]", "%5d")
108                 .replace("}", "%7d")
109                 .replace(">", "%3e")
110                 .replace("<", "%3c")
111                 .replace("?", "%3f")
112                 .replace("_", "%5f")
113                 .replace("\\", "%5c")
114                 .replace(",", "%2c");
115         }
116 
117         // URL forbidden characters
118         return (sqlTrailEncoded + this.injectionModel.getMediatorEngine().getEngine().instance().endingComment())
119             .replace("\"", "%22")
120             .replace("|", "%7c")
121             .replace("`", "%60")
122             .replace(StringUtils.SPACE, "%20")
123             .replace("+", "%20");
124     }
125     
126     /**
127      * Find the insertion character, test each strategy, inject metadata and list databases.
128      * @param parameterToInject to be tested, null when injection point
129      * @return true when successful injection
130      * @throws JSqlException when no params integrity, process stopped by user, or injection failure
131      */
132     public boolean testStrategies(SimpleEntry<String, String> parameterToInject) throws JSqlException {
133         // Define insertionCharacter, i.e, -1 in "[..].php?id=-1 union select[..]",
134         
135         String parameterOriginalValue = null;
136         
137         // Fingerprint database
138         this.injectionModel.getMediatorEngine().setEngine(this.injectionModel.getMediatorEngine().fingerprintEngine());
139         
140         // If not an injection point then find insertion character.
141         // Force to 1 if no insertion char works and empty value from user,
142         // Force to user's value if no insertion char works,
143         // Force to insertion char otherwise.
144         // parameterToInject null on true STAR injection
145         // TODO Use also on Json injection where parameter == null
146         if (parameterToInject != null) {
147             parameterOriginalValue = parameterToInject.getValue();
148                      
149             // Test for params integrity
150             String characterInsertionByUser = this.injectionModel.getMediatorUtils().parameterUtil().initStar(parameterToInject);
151             
152             String characterInsertion = this.injectionModel.getMediatorUtils().preferencesUtil().isNotSearchingCharInsertion()
153                 ? characterInsertionByUser
154                 : new SuspendableGetCharInsertion(this.injectionModel).run(
155                     new Input(characterInsertionByUser)
156                 );
157             if (characterInsertion.contains(InjectionModel.STAR)) {  // When injecting all parameters or JSON
158                 parameterToInject.setValue(characterInsertion);
159             } else {  // When injecting last parameter
160                 parameterToInject.setValue(characterInsertion.replaceAll("(\\w)$", "$1+") + InjectionModel.STAR);
161             }
162         } else if (this.injectionModel.getMediatorUtils().connectionUtil().getUrlBase().contains(InjectionModel.STAR)) {
163             String characterInsertion = new SuspendableGetCharInsertion(this.injectionModel).run(
164                 new Input(StringUtils.EMPTY)
165             );
166             String urlBase = this.injectionModel.getMediatorUtils().connectionUtil().getUrlBase();
167             this.injectionModel.getMediatorUtils().connectionUtil().setUrlBase(
168                 // Space %20 for URL, do not use +
169                 urlBase.replace(InjectionModel.STAR, characterInsertion.replaceAll("(\\w)$", "$1%20") + InjectionModel.STAR)
170             );
171         }
172 
173         if (this.injectionModel.getMediatorEngine().getEngineByUser() == this.injectionModel.getMediatorEngine().getAuto()) {
174             new SuspendableGetEngine(this.injectionModel).run();
175         }
176 
177         // Test each injection strategies: time < blind binary < blind bitwise < multibit < error < stack < union
178         this.time.checkApplicability();
179         this.blindBin.checkApplicability();
180         this.blindBit.checkApplicability();
181 
182         if (parameterToInject != null) {
183             // Multibit requires '0'
184             // TODO char insertion 0' should also work on "where x='$param'"
185             var backupCharacterInsertion = parameterToInject.getValue();
186             parameterToInject.setValue(InjectionModel.STAR);
187             this.multibit.checkApplicability();
188             parameterToInject.setValue(backupCharacterInsertion);
189         } else {
190             this.multibit.checkApplicability();
191         }
192 
193         this.dns.checkApplicability();
194         this.error.checkApplicability();
195         this.stack.checkApplicability();
196         this.union.checkApplicability();
197 
198         // Set most efficient strategy first
199         this.union.activateWhenApplicable();
200         this.stack.activateWhenApplicable();
201         this.error.activateWhenApplicable();
202         this.dns.activateWhenApplicable();
203         this.multibit.activateWhenApplicable();
204         this.blindBit.activateWhenApplicable();
205         this.blindBin.activateWhenApplicable();
206         this.time.activateWhenApplicable();
207 
208         if (this.injectionModel.getMediatorStrategy().getStrategy() == null) {  // no strategy found
209             // Restore initial parameter value on injection failure
210             // Only when not true STAR injection
211             if (parameterOriginalValue != null) {
212                 parameterToInject.setValue(parameterOriginalValue.replace(InjectionModel.STAR, StringUtils.EMPTY));
213             }
214 
215             LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "No injection found");
216             return false;
217         }
218         
219         return true;
220     }
221     
222     
223     // Getter and setter
224 
225     public AbstractStrategy getUnion() {
226         return this.union;
227     }
228 
229     public StrategyUnion getSpecificUnion() {
230         return (StrategyUnion) this.union;
231     }
232 
233     public StrategyError getError() {
234         return this.error;
235     }
236 
237     public AbstractStrategy getBlindBit() {
238         return this.blindBit;
239     }
240 
241     public AbstractStrategy getBlindBin() {
242         return this.blindBin;
243     }
244 
245     public AbstractStrategy getMultibit() {
246         return this.multibit;
247     }
248 
249     public AbstractStrategy getTime() {
250         return this.time;
251     }
252 
253     public AbstractStrategy getStack() {
254         return this.stack;
255     }
256 
257     public AbstractStrategy getDns() {
258         return this.dns;
259     }
260 
261     public List<AbstractStrategy> getStrategies() {
262         return this.strategies;
263     }
264 
265     public AbstractStrategy getStrategy() {
266         return this.strategy;
267     }
268 
269     public void setStrategy(AbstractStrategy strategy) {
270         this.strategy = strategy;
271     }
272 }