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