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