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.CallableBlindBit;
6   import com.jsql.util.LogLevelUtil;
7   import org.apache.commons.lang3.StringUtils;
8   import org.apache.logging.log4j.LogManager;
9   import org.apache.logging.log4j.Logger;
10  
11  import java.util.ArrayList;
12  import java.util.Collection;
13  import java.util.List;
14  import java.util.concurrent.ExecutionException;
15  import java.util.concurrent.ExecutorService;
16  import java.util.concurrent.Future;
17  
18  import static name.fraser.neil.plaintext.diff_match_patch.Diff;
19  
20  /**
21   * A blind attack class using concurrent threads.
22   */
23  public class InjectionBlindBit extends AbstractInjectionMonobit<CallableBlindBit> {
24      
25      /**
26       * Log4j logger sent to view.
27       */
28      private static final Logger LOGGER = LogManager.getRootLogger();
29  
30      // Source code of the TRUE web page (usually ?id=1)
31      private String sourceReferencePage;
32  
33      /**
34       * List of string differences found in all the FALSE queries, compared
35       * to the reference page. Each FALSE pages should contain
36       * at least one same string, which shouldn't be present in all
37       * the TRUE queries.
38       */
39      private List<Diff> falseDiffs = new ArrayList<>();
40  
41      /**
42       * Create blind attack initialization.
43       * If every false diffs are not in true diffs and every true diffs are in
44       * true diffs, then Blind attack is confirmed.
45       */
46      public InjectionBlindBit(InjectionModel injectionModel, BlindOperator blindMode) {
47          super(injectionModel, blindMode);
48          
49          // No blind
50          if (this.falsyBit.isEmpty() || this.injectionModel.isStoppedByUser()) {
51              return;
52          }
53          
54          // Call the SQL request which must be TRUE (usually ?id=1)
55          this.sourceReferencePage = this.callUrl(StringUtils.EMPTY, "bit#ref");
56  
57          // Concurrent calls to the FALSE statements,
58          // it will use inject() from the model
59          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBitTagFalse");
60          Collection<CallableBlindBit> callablesFalseTest = new ArrayList<>();
61          for (String falseTest: this.falsyBit) {
62              callablesFalseTest.add(new CallableBlindBit(
63                  falseTest,
64                  injectionModel,
65                  this,
66                  blindMode,
67                  "bit#falsy"
68              ));
69          }
70          
71          // Delete junk from the results of FALSE statements,
72          // keep only diffs found in each and every FALSE pages.
73          // Allow the user to stop the loop
74          try {
75              List<Future<CallableBlindBit>> futuresFalseTest = taskExecutor.invokeAll(callablesFalseTest);
76              this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
77              for (Future<CallableBlindBit> futureFalseTest: futuresFalseTest) {
78                  if (this.injectionModel.isStoppedByUser()) {
79                      return;
80                  }
81                  if (this.falseDiffs.isEmpty()) {
82                      this.falseDiffs = futureFalseTest.get().getDiffsWithReference();  // Init diffs
83                  } else {
84                      this.falseDiffs.retainAll(futureFalseTest.get().getDiffsWithReference());  // Clean un-matching diffs
85                  }
86              }
87          } catch (ExecutionException e) {
88              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
89          } catch (InterruptedException e) {
90              LOGGER.log(LogLevelUtil.IGNORE, e, e);
91              Thread.currentThread().interrupt();
92          }
93  
94          if (this.injectionModel.isStoppedByUser()) {
95              return;
96          }
97          
98          this.cleanTrueDiffs(injectionModel, blindMode);
99      }
100 
101     private void cleanTrueDiffs(InjectionModel injectionModel, BlindOperator blindMode) {
102         // Concurrent calls to the TRUE statements,
103         // it will use inject() from the model.
104         ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBitTagTrue");
105         Collection<CallableBlindBit> callablesTrueTest = new ArrayList<>();
106         for (String trueTest: this.truthyBit) {
107             callablesTrueTest.add(new CallableBlindBit(
108                 trueTest,
109                 injectionModel,
110                 this,
111                 blindMode,
112                 "bit#truthy"
113             ));
114         }
115         
116         // Remove TRUE diffs in the FALSE diffs, because
117         // a significant FALSE statement shouldn't contain any TRUE diff.
118         // Allow the user to stop the loop.
119         try {
120             List<Future<CallableBlindBit>> futuresTrueTest = taskExecutor.invokeAll(callablesTrueTest);
121             this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
122             for (Future<CallableBlindBit> futureTrueTest: futuresTrueTest) {
123                 if (this.injectionModel.isStoppedByUser()) {
124                     return;
125                 }
126                 this.falseDiffs.removeAll(futureTrueTest.get().getDiffsWithReference());
127             }
128         } catch (ExecutionException e) {
129             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
130         } catch (InterruptedException e) {
131             LOGGER.log(LogLevelUtil.IGNORE, e, e);
132             Thread.currentThread().interrupt();
133         }
134     }
135 
136     // todo should not be shared
137     @Override
138     public CallableBlindBit getCallableBitTest(String sqlQuery, int indexChar, int bit) {
139         return new CallableBlindBit(
140             sqlQuery,
141             indexChar,
142             bit,
143             this.injectionModel,
144             this,
145             this.blindOperator,
146             "bit#" + indexChar + "~" + bit
147         );
148     }
149 
150     @Override
151     public boolean isInjectable() throws StoppedByUserSlidingException {
152         if (this.injectionModel.isStoppedByUser()) {
153             throw new StoppedByUserSlidingException();
154         }
155         var blindTest = new CallableBlindBit(
156             this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm(),
157             this.injectionModel,
158             this,
159             this.blindOperator,
160             "bit#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.falseDiffs.isEmpty();
168     }
169 
170     @Override
171     public String getInfoMessage() {
172         return "- Strategy Blind bit: query True when Diffs are matching " + this.falseDiffs + "\n\n";
173     }
174     
175     
176     // Getter and setter
177 
178     public String getSourceReferencePage() {
179         return this.sourceReferencePage;
180     }
181     
182     public List<Diff> getFalseDiffs() {
183         return this.falseDiffs;
184     }
185 }