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  import static name.fraser.neil.plaintext.diff_match_patch.Operation;
20  
21  /**
22   * A blind attack class using concurrent threads.
23   */
24  public class InjectionBlindBit extends AbstractInjectionMonobit<CallableBlindBit> {
25  
26      private static final Logger LOGGER = LogManager.getRootLogger();
27  
28      private String sourceReferencePage;  // Source code of the TRUE web page (usually ?id=1)
29  
30      /**
31       * List of string differences found in all the FALSE queries, compared
32       * to the reference page. 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> falseDiffs = new ArrayList<>();
37      private List<Diff> trueDiffs = new ArrayList<>();
38  
39      /**
40       * Create blind attack initialization.
41       * If every false diffs are not in true diffs and every true diffs are in
42       * true diffs, then Blind attack is confirmed.
43       */
44      public InjectionBlindBit(InjectionModel injectionModel, BlindOperator blindOperator) {
45          super(injectionModel, blindOperator);
46  
47          List<String> falsys = this.injectionModel.getMediatorVendor().getVendor().instance().getFalsyBit();
48          if (falsys.isEmpty() || this.injectionModel.isStoppedByUser()) {
49              return;
50          }
51          
52          // Call the SQL request which must be TRUE (usually ?id=1)
53          this.sourceReferencePage = this.callUrl(StringUtils.EMPTY, "bit#ref:"+ blindOperator.toString().toLowerCase());
54  
55          // Concurrent calls to the FALSE statements,
56          // it will use inject() from the model
57          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBitTagFalse");
58          Collection<CallableBlindBit> callablesFalsys = new ArrayList<>();
59          for (String falsy: falsys) {
60              callablesFalsys.add(new CallableBlindBit(
61                  falsy,
62                  injectionModel,
63                  this,
64                  blindOperator,
65                  "bit#falsy"
66              ));
67          }
68          
69          // Delete junk from the results of FALSE statements,
70          // keep only diffs found in each and every FALSE pages.
71          // Allow the user to stop the loop
72          try {
73              List<Future<CallableBlindBit>> futuresFalsys = taskExecutor.invokeAll(callablesFalsys);
74              this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
75              for (Future<CallableBlindBit> futureFalsy: futuresFalsys) {
76                  if (this.injectionModel.isStoppedByUser()) {
77                      return;
78                  }
79                  if (this.falseDiffs.isEmpty()) {
80                      this.falseDiffs = futureFalsy.get().getDiffsWithReference();  // Init diffs
81                  } else {
82                      this.falseDiffs.retainAll(futureFalsy.get().getDiffsWithReference());  // Clean un-matching diffs
83                  }
84              }
85          } catch (ExecutionException e) {
86              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
87          } catch (InterruptedException e) {
88              LOGGER.log(LogLevelUtil.IGNORE, e, e);
89              Thread.currentThread().interrupt();
90          }
91  
92          if (this.injectionModel.isStoppedByUser()) {
93              return;
94          }
95          
96          this.cleanTrueDiffs(injectionModel, blindOperator);
97      }
98  
99      private void cleanTrueDiffs(InjectionModel injectionModel, BlindOperator blindOperator) {
100         ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBitTagTrue");
101         Collection<CallableBlindBit> callablesTruthys = new ArrayList<>();
102         List<String> truthys = this.injectionModel.getMediatorVendor().getVendor().instance().getTruthyBit();
103         for (String truthy: truthys) {
104             callablesTruthys.add(new CallableBlindBit(
105                 truthy,
106                 injectionModel,
107                 this,
108                 blindOperator,
109                 "bit#truthy"
110             ));
111         }
112 
113         // Remove TRUE diffs in the FALSE diffs as FALSE statement shouldn't contain any TRUE diff.
114         try {
115             List<Future<CallableBlindBit>> futuresTruthys = taskExecutor.invokeAll(callablesTruthys);
116             this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
117             for (Future<CallableBlindBit> futureTruthy: futuresTruthys) {
118                 if (this.injectionModel.isStoppedByUser()) {
119                     return;
120                 }
121                 if (this.trueDiffs.isEmpty()) {
122                     this.trueDiffs = futureTruthy.get().getDiffsWithReference();  // Init diffs
123                 } else {
124                     this.trueDiffs.retainAll(futureTruthy.get().getDiffsWithReference());  // Clean un-matching diffs
125                 }
126                 this.falseDiffs.removeAll(futureTruthy.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     @Override
137     public CallableBlindBit getCallableBitTest(String sqlQuery, int indexChar, int bit) {
138         return new CallableBlindBit(
139             sqlQuery,
140             indexChar,
141             bit,
142             this.injectionModel,
143             this,
144             this.blindOperator,
145             "bit#" + indexChar + "~" + bit
146         );
147     }
148 
149     @Override
150     public boolean isInjectable() throws StoppedByUserSlidingException {
151         if (this.injectionModel.isStoppedByUser()) {
152             throw new StoppedByUserSlidingException();
153         }
154         var callable = new CallableBlindBit(
155             this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm(),
156             this.injectionModel,
157             this,
158             this.blindOperator,
159             "bit#confirm"
160         );
161         try {
162             callable.call();
163         } catch (Exception e) {
164             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
165         }
166         return callable.isTrue()
167             // when insertionChar = true then pages ref == truthy == falsy == confirm => falsy cleaned empty, truthy with opcode EQUAL not reliable
168             && this.trueDiffs.stream().anyMatch(diff -> !Operation.EQUAL.equals(diff.operation))
169             || this.falseDiffs.stream().anyMatch(diff -> !Operation.EQUAL.equals(diff.operation));
170     }
171 
172     @Override
173     public String getInfoMessage() {
174         return "- Strategy Blind bit: query True when Diffs are matching " + this.falseDiffs + "\n\n";
175     }
176     
177     
178     // Getter and setter
179 
180     public String getSourceReferencePage() {
181         return this.sourceReferencePage;
182     }
183     
184     public List<Diff> getFalseDiffs() {
185         return this.falseDiffs;
186     }
187 
188     public List<Diff> getTrueDiffs() {
189         return this.trueDiffs;
190     }
191 }