View Javadoc
1   package com.jsql.model.injection.strategy.blind;
2   
3   import com.jsql.model.InjectionModel;
4   import com.jsql.model.exception.StoppedByUserSlidingException;
5   import com.jsql.model.injection.strategy.blind.callable.AbstractCallableBit;
6   import com.jsql.model.injection.strategy.blind.callable.CallableCharInsertion;
7   import com.jsql.model.injection.strategy.blind.patch.Diff;
8   import com.jsql.util.LogLevelUtil;
9   import org.apache.commons.lang3.RandomStringUtils;
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.ArrayList;
15  import java.util.Collection;
16  import java.util.List;
17  import java.util.concurrent.ExecutionException;
18  import java.util.concurrent.ExecutorService;
19  import java.util.concurrent.Future;
20  
21  /**
22   * A blind attack class using concurrent threads.
23   */
24  public class InjectionCharInsertion {
25      
26      private static final Logger LOGGER = LogManager.getRootLogger();
27  
28      // Source code of the FALSE web page (e.g. ?id=-123456789)
29      private String blankFalseMark;
30  
31      /**
32       * List of string differences found in all the FALSE queries, compared
33       * to the reference page (aka opcodes). Each FALSE pages should contain
34       * at least one same string, which shouldn't be present in all
35       * the TRUE queries.
36       */
37      private List<Diff> constantTrueMark = new ArrayList<>();
38      
39      protected final InjectionModel injectionModel;
40  
41      private final String prefixSuffix;
42      
43      private static final String PREFIX = "prefix";
44  
45      private final List<String> falsy;
46      
47      /**
48       * Create blind attack initialization.
49       * If every false test are not in true mark and every true test are in
50       * true test, then blind attack is confirmed.
51       */
52      public InjectionCharInsertion(InjectionModel injectionModel, String falseCharInsertion, String prefixSuffix) {
53          this.injectionModel = injectionModel;
54          this.prefixSuffix = prefixSuffix;
55          
56          List<String> truthy = this.injectionModel.getMediatorVendor().getVendor().instance().getTruthyBit();
57          this.falsy = this.injectionModel.getMediatorVendor().getVendor().instance().getFalsyBit();
58          
59          // No blind
60          if (truthy.isEmpty() || this.injectionModel.isStoppedByUser()) {
61              return;
62          }
63          
64          // Call the SQL request which must be FALSE (usually ?id=-123456879)
65          this.blankFalseMark = this.callUrl(
66              falseCharInsertion,
67              "prefix:" + prefixSuffix.replace(InjectionCharInsertion.PREFIX, StringUtils.EMPTY)
68          );
69  
70          // Concurrent calls to the FALSE statements,
71          // it will use inject() from the model
72          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableCharInsertionTagTrue");
73          Collection<CallableCharInsertion> listCallableTagTrue = new ArrayList<>();
74          
75          for (String urlTest: truthy) {
76              listCallableTagTrue.add(
77                  new CallableCharInsertion(
78                      String.join(
79                          StringUtils.SPACE,
80                          prefixSuffix.replace(InjectionCharInsertion.PREFIX, RandomStringUtils.secure().next(10, "345")),
81                          this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy().getBinary().getModeOr(),
82                          urlTest
83                      ),
84                      this,
85                      "prefix#true"
86                  )
87              );
88          }
89          
90          // Delete junk from the results of FALSE statements,
91          // keep only opcodes found in each and every FALSE pages.
92          // Allow the user to stop the loop
93          try {
94              List<Future<CallableCharInsertion>> listTagTrue = taskExecutor.invokeAll(listCallableTagTrue);
95              this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
96              for (var i = 1 ; i < listTagTrue.size() ; i++) {
97                  if (this.injectionModel.isStoppedByUser()) {
98                      return;
99                  }
100 
101                 if (this.constantTrueMark.isEmpty()) {
102                     this.constantTrueMark = listTagTrue.get(i).get().getOpcodes();
103                 } else {
104                     this.constantTrueMark.retainAll(listTagTrue.get(i).get().getOpcodes());
105                 }
106             }
107         } catch (ExecutionException e) {
108             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
109         } catch (InterruptedException e) {
110             LOGGER.log(LogLevelUtil.IGNORE, e, e);
111             Thread.currentThread().interrupt();
112         }
113         
114         this.initFalseMarks();
115     }
116     
117     private void initFalseMarks() {
118         // Concurrent calls to the TRUE statements,
119         // it will use inject() from the model.
120         ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetCharInsertionTagFalse");
121         Collection<CallableCharInsertion> listCallableTagFalse = new ArrayList<>();
122         
123         for (String urlTest: this.falsy) {
124             listCallableTagFalse.add(
125                 new CallableCharInsertion(
126                     String.join(
127                         StringUtils.SPACE,
128                         this.prefixSuffix.replace(InjectionCharInsertion.PREFIX, RandomStringUtils.secure().next(10, "345")),
129                         this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy().getBinary().getModeOr(),
130                         urlTest
131                     ),
132                     this,
133                     "prefix#false"
134                 )
135             );
136         }
137         
138         // Remove TRUE opcodes in the FALSE opcodes, because
139         // a significant FALSE statement shouldn't contain any TRUE opcode.
140         // Allow the user to stop the loop.
141         try {
142             List<Future<CallableCharInsertion>> listTagFalse = taskExecutor.invokeAll(listCallableTagFalse);
143             this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
144         
145             for (Future<CallableCharInsertion> falseTag: listTagFalse) {
146                 if (this.injectionModel.isStoppedByUser()) {
147                     return;
148                 }
149                 this.constantTrueMark.removeAll(falseTag.get().getOpcodes());
150             }
151         } catch (ExecutionException e) {
152             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
153         } catch (InterruptedException e) {
154             LOGGER.log(LogLevelUtil.IGNORE, e, e);
155             Thread.currentThread().interrupt();
156         }
157     }
158 
159     public boolean isInjectable() throws StoppedByUserSlidingException {
160         if (this.injectionModel.isStoppedByUser()) {
161             throw new StoppedByUserSlidingException();
162         }
163         var blindTest = new CallableCharInsertion(
164             String.join(
165                 StringUtils.SPACE,
166                 this.prefixSuffix.replace(InjectionCharInsertion.PREFIX, RandomStringUtils.secure().next(10, "678")),
167                 this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy().getBinary().getModeOr(),
168                 this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm()
169             ),
170             this,
171             "prefix#confirm"
172         );
173         try {
174             blindTest.call();
175         } catch (Exception e) {
176             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
177         }
178         return blindTest.isTrue() && !this.constantTrueMark.isEmpty();
179     }
180     
181     public String callUrl(String urlString, String metadataInjectionProcess) {
182         return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess);
183     }
184 
185     public String callUrl(String urlString, String metadataInjectionProcess, AbstractCallableBit<?> callableBoolean) {
186         return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess, callableBoolean);
187     }
188 
189 
190     // Getter
191 
192     public String getBlankFalseMark() {
193         return this.blankFalseMark;
194     }
195     
196     public List<Diff> getConstantTrueMark() {
197         return this.constantTrueMark;
198     }
199 }