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