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