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