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.CallableTime;
6   import com.jsql.util.LogLevelUtil;
7   import org.apache.logging.log4j.LogManager;
8   import org.apache.logging.log4j.Logger;
9   
10  import java.util.ArrayList;
11  import java.util.Collection;
12  import java.util.List;
13  import java.util.concurrent.ExecutionException;
14  import java.util.concurrent.ExecutorService;
15  import java.util.concurrent.Future;
16  
17  /**
18   * Time attack using parallel threads.
19   * Waiting time in seconds, response time exceeded means query is false.
20   * Noting that sleep() functions will add up for each line from request.
21   * A sleep time of 5 will be executed only if the SELECT returns exactly one line.
22   */
23  public class InjectionTime extends AbstractInjectionMonobit<CallableTime> {
24  
25      /**
26       * Log4j logger sent to view.
27       */
28      private static final Logger LOGGER = LogManager.getRootLogger();
29  
30      /**
31       *  Time based works by default, many tests will change it to false if it isn't confirmed.
32       */
33      private boolean isTimeInjectable = true;
34  
35      /**
36       * Create time attack initialization.
37       * If every false requests are under 5 seconds and every true are below 5 seconds,
38       * then time attack is confirmed.
39       */
40      public InjectionTime(InjectionModel injectionModel, BlindOperator blindOperator) {
41          super(injectionModel, blindOperator);
42          
43          // No blind
44          if (this.falsyBit.isEmpty() || this.injectionModel.isStoppedByUser()) {
45              return;
46          }
47  
48          // Concurrent calls to the FALSE statements,
49          // it will use inject() from the model
50          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetTimeTagFalse");
51          Collection<CallableTime> callablesFalseTest = new ArrayList<>();
52          for (String falseTest: this.falsyBit) {
53              callablesFalseTest.add(new CallableTime(
54                  falseTest,
55                  injectionModel,
56                  this,
57                      blindOperator,
58                  "time#falsy"
59              ));
60          }
61          
62          // If one FALSE query makes less than X seconds,
63          // then the test is a failure => exit
64          // Allow the user to stop the loop
65          try {
66              List<Future<CallableTime>> futuresFalseTest = taskExecutor.invokeAll(callablesFalseTest);
67              this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
68              for (Future<CallableTime> futureFalseTest: futuresFalseTest) {
69                  if (this.injectionModel.isStoppedByUser()) {
70                      return;
71                  }
72                  if (futureFalseTest.get().isTrue()) {
73                      this.isTimeInjectable = false;
74                      return;
75                  }
76              }
77          } catch (ExecutionException e) {
78              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
79          } catch (InterruptedException e) {
80              LOGGER.log(LogLevelUtil.IGNORE, e, e);
81              Thread.currentThread().interrupt();
82          }
83          
84          this.checkTrueTests(blindOperator);
85      }
86  
87      private void checkTrueTests(BlindOperator blindOperator) {
88          // Concurrent calls to the TRUE statements,
89          // it will use inject() from the model
90          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetTimeTagTrue");
91          Collection<CallableTime> callablesTrueTest = new ArrayList<>();
92          for (String trueTest: this.truthyBit) {
93              callablesTrueTest.add(new CallableTime(
94                  trueTest,
95                  this.injectionModel,
96                  this,
97                      blindOperator,
98                  "time#truthy"
99              ));
100         }
101 
102         // If one TRUE query makes more than X seconds,
103         // then the test is a failure => exit.
104         // Allow the user to stop the loop
105         try {
106             List<Future<CallableTime>> futuresTrueTest = taskExecutor.invokeAll(callablesTrueTest);
107             this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
108             for (Future<CallableTime> futureTrueTest: futuresTrueTest) {
109                 if (this.injectionModel.isStoppedByUser()) {
110                     return;
111                 }
112                 if (!futureTrueTest.get().isTrue()) {
113                     this.isTimeInjectable = false;
114                     return;
115                 }
116             }
117         } catch (ExecutionException e) {
118             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
119         } catch (InterruptedException e) {
120             LOGGER.log(LogLevelUtil.IGNORE, e, e);
121             Thread.currentThread().interrupt();
122         }
123     }
124 
125     @Override
126     public CallableTime getCallableBitTest(String sqlQuery, int indexChar, int bit) {
127         return new CallableTime(
128             sqlQuery,
129             indexChar,
130             bit,
131             this.injectionModel,
132             this,
133             this.blindOperator,
134             "bit#" + indexChar + "~" + bit
135         );
136     }
137 
138     @Override
139     public boolean isInjectable() throws StoppedByUserSlidingException {
140         if (this.injectionModel.isStoppedByUser()) {
141             throw new StoppedByUserSlidingException();
142         }
143         var timeTest = new CallableTime(
144             this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm(),
145             this.injectionModel,
146             this,
147             this.blindOperator,
148             "time#confirm"
149         );
150         try {
151             timeTest.call();
152         } catch (Exception e) {
153             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
154         }
155         return this.isTimeInjectable && timeTest.isTrue();
156     }
157 
158     public int getSleepTime() {
159         return this.injectionModel.getMediatorUtils().getPreferencesUtil().isLimitingSleepTimeStrategy()
160             ? this.injectionModel.getMediatorUtils().getPreferencesUtil().countSleepTimeStrategy()
161             : 5;
162     }
163 
164     @Override
165     public String getInfoMessage() {
166         return "- Strategy Time: query True when delays for "+ this.getSleepTime() +"s\n\n";
167     }
168 }