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.patch.Diff;
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  /**
19   * A blind attack class using concurrent threads.
20   */
21  public class InjectionBlind extends AbstractInjectionMonobit<CallableBlind> {
22      
23      /**
24       * Log4j logger sent to view.
25       */
26      private static final Logger LOGGER = LogManager.getRootLogger();
27  
28      // Source code of the TRUE web page (usually ?id=1)
29      private String sourceReferencePage;
30  
31      /**
32       * List of string differences found in all the FALSE queries, compared
33       * to the reference page. Each FALSE pages should contain
34       * at least one same string, which shouldn't be present in all
35       * the TRUE queries.
36       */
37      private List<Diff> falseDiffs = 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 InjectionBlind(InjectionModel injectionModel, BinaryMode blindMode) {
45          super(injectionModel, blindMode);
46          
47          // No blind
48          if (this.falsy.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, "blind#ref");
54  
55          // Concurrent calls to the FALSE statements,
56          // it will use inject() from the model
57          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindTagFalse");
58          Collection<CallableBlind> callablesFalseTest = new ArrayList<>();
59          
60          for (String falseTest: this.falsy) {
61              callablesFalseTest.add(new CallableBlind(
62                  falseTest,
63                  injectionModel,
64                  this,
65                  blindMode,
66                  "blind#falsy"
67              ));
68          }
69          
70          // Delete junk from the results of FALSE statements,
71          // keep only diffs found in each and every FALSE pages.
72          // Allow the user to stop the loop
73          try {
74              List<Future<CallableBlind>> futuresFalseTest = taskExecutor.invokeAll(callablesFalseTest);
75              this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
76              for (Future<CallableBlind> futureFalseTest: futuresFalseTest) {
77                  if (this.injectionModel.isStoppedByUser()) {
78                      return;
79                  }
80                  if (this.falseDiffs.isEmpty()) {
81                      this.falseDiffs = futureFalseTest.get().getDiffsWithReference();  // Init diffs
82                  } else {
83                      this.falseDiffs.retainAll(futureFalseTest.get().getDiffsWithReference());  // Clean un-matching diffs
84                  }
85              }
86          } catch (ExecutionException e) {
87              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
88          } catch (InterruptedException e) {
89              LOGGER.log(LogLevelUtil.IGNORE, e, e);
90              Thread.currentThread().interrupt();
91          }
92  
93          if (this.injectionModel.isStoppedByUser()) {
94              return;
95          }
96          
97          this.cleanTrueDiffs(injectionModel, blindMode);
98      }
99  
100     private void cleanTrueDiffs(InjectionModel injectionModel, BinaryMode blindMode) {
101         // Concurrent calls to the TRUE statements,
102         // it will use inject() from the model.
103         ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindTagTrue");
104 
105         Collection<CallableBlind> callablesTrueTest = new ArrayList<>();
106         
107         for (String trueTest: this.truthy) {
108             callablesTrueTest.add(new CallableBlind(
109                 trueTest,
110                 injectionModel,
111                 this,
112                 blindMode,
113                 "blind#truthy"
114             ));
115         }
116         
117         // Remove TRUE diffs in the FALSE diffs, because
118         // a significant FALSE statement shouldn't contain any TRUE diff.
119         // Allow the user to stop the loop.
120         try {
121             List<Future<CallableBlind>> futuresTrueTest = taskExecutor.invokeAll(callablesTrueTest);
122             this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
123         
124             for (Future<CallableBlind> futureTrueTest: futuresTrueTest) {
125                 
126                 if (this.injectionModel.isStoppedByUser()) {
127                     return;
128                 }
129                 this.falseDiffs.removeAll(futureTrueTest.get().getDiffsWithReference());
130             }
131         } catch (ExecutionException e) {
132             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
133         } catch (InterruptedException e) {
134             LOGGER.log(LogLevelUtil.IGNORE, e, e);
135             Thread.currentThread().interrupt();
136         }
137     }
138 
139     @Override
140     public CallableBlind getCallableBitTest(String sqlQuery, int indexCharacter, int bit) {
141         return new CallableBlind(
142             sqlQuery,
143             indexCharacter,
144             bit,
145             this.injectionModel,
146             this,
147             this.binaryMode,
148             "bit#" + indexCharacter + "~" + bit
149         );
150     }
151 
152     @Override
153     public boolean isInjectable() throws StoppedByUserSlidingException {
154         if (this.injectionModel.isStoppedByUser()) {
155             throw new StoppedByUserSlidingException();
156         }
157         
158         var blindTest = new CallableBlind(
159             this.injectionModel.getMediatorVendor().getVendor().instance().sqlTestBinaryInit(),
160             this.injectionModel,
161             this,
162             this.binaryMode,
163             "blind#confirm"
164         );
165         try {
166             blindTest.call();
167         } catch (Exception e) {
168             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
169         }
170         return blindTest.isTrue() && !this.falseDiffs.isEmpty();
171     }
172 
173     @Override
174     public String getInfoMessage() {
175         return "- Strategy Blind: query True when Diffs are matching " + this.falseDiffs + "\n\n";
176     }
177     
178     
179     // Getter and setter
180 
181     public String getSourceReferencePage() {
182         return this.sourceReferencePage;
183     }
184     
185     public List<Diff> getFalseDiffs() {
186         return this.falseDiffs;
187     }
188 }