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      private static final Logger LOGGER = LogManager.getRootLogger();
26  
27      /**
28       *  Time based works by default, many tests will change it to false if it isn't confirmed.
29       */
30      private boolean isTimeInjectable = true;
31  
32      /**
33       * Create time attack initialization.
34       * If every false requests are under 5 seconds and every true are below 5 seconds,
35       * then time attack is confirmed.
36       */
37      public InjectionTime(InjectionModel injectionModel, BlindOperator blindOperator) {
38          super(injectionModel, blindOperator);
39  
40          List<String> falsys = this.injectionModel.getMediatorVendor().getVendor().instance().getFalsyBit();
41          if (falsys.isEmpty() || this.injectionModel.isStoppedByUser()) {
42              return;
43          }
44  
45          // Concurrent calls to the FALSE statements,
46          // it will use inject() from the model
47          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetTimeTagFalse");
48          Collection<CallableTime> callablesFalsys = new ArrayList<>();
49          for (String falsy: falsys) {
50              callablesFalsys.add(new CallableTime(
51                  falsy,
52                  injectionModel,
53                  this,
54                  blindOperator,
55                  "time#falsy"
56              ));
57          }
58          
59          // If one FALSE query makes less than X seconds,
60          // then the test is a failure => exit
61          // Allow the user to stop the loop
62          try {
63              List<Future<CallableTime>> futuresFalsys = taskExecutor.invokeAll(callablesFalsys);
64              this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
65              for (Future<CallableTime> futureFalsy: futuresFalsys) {
66                  if (this.injectionModel.isStoppedByUser()) {
67                      return;
68                  }
69                  if (futureFalsy.get().isTrue()) {
70                      this.isTimeInjectable = false;
71                      return;
72                  }
73              }
74          } catch (ExecutionException e) {
75              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
76          } catch (InterruptedException e) {
77              LOGGER.log(LogLevelUtil.IGNORE, e, e);
78              Thread.currentThread().interrupt();
79          }
80          
81          this.checkTruthys(blindOperator);
82      }
83  
84      private void checkTruthys(BlindOperator blindOperator) {
85          ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetTimeTagTrue");
86          Collection<CallableTime> callablesTruthys = new ArrayList<>();
87          List<String> truthys = this.injectionModel.getMediatorVendor().getVendor().instance().getTruthyBit();
88          for (String truthy: truthys) {
89              callablesTruthys.add(new CallableTime(
90                  truthy,
91                  this.injectionModel,
92                  this,
93                  blindOperator,
94                  "time#truthy"
95              ));
96          }
97  
98          // If one TRUE query makes more than X seconds then the test is a failure => exit
99          try {
100             List<Future<CallableTime>> futuresTruthys = taskExecutor.invokeAll(callablesTruthys);
101             this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
102             for (Future<CallableTime> futureTruthy: futuresTruthys) {
103                 if (this.injectionModel.isStoppedByUser()) {
104                     return;
105                 }
106                 if (!futureTruthy.get().isTrue()) {
107                     this.isTimeInjectable = false;
108                     return;
109                 }
110             }
111         } catch (ExecutionException e) {
112             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
113         } catch (InterruptedException e) {
114             LOGGER.log(LogLevelUtil.IGNORE, e, e);
115             Thread.currentThread().interrupt();
116         }
117     }
118 
119     @Override
120     public CallableTime getCallableBitTest(String sqlQuery, int indexChar, int bit) {
121         return new CallableTime(
122             sqlQuery,
123             indexChar,
124             bit,
125             this.injectionModel,
126             this,
127             this.blindOperator,
128             "bit#" + indexChar + "~" + bit
129         );
130     }
131 
132     @Override
133     public boolean isInjectable() throws StoppedByUserSlidingException {
134         if (this.injectionModel.isStoppedByUser()) {
135             throw new StoppedByUserSlidingException();
136         }
137         var callable = new CallableTime(
138             this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm(),
139             this.injectionModel,
140             this,
141             this.blindOperator,
142             "time#confirm"
143         );
144         try {
145             callable.call();
146         } catch (Exception e) {
147             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
148         }
149         return this.isTimeInjectable && callable.isTrue();
150     }
151 
152     public int getSleepTime() {
153         return this.injectionModel.getMediatorUtils().getPreferencesUtil().isLimitingSleepTimeStrategy()
154             ? this.injectionModel.getMediatorUtils().getPreferencesUtil().countSleepTimeStrategy()
155             : 5;
156     }
157 
158     @Override
159     public String getInfoMessage() {
160         return "- Strategy Time: query True when "+ this.getSleepTime() +"s delay\n\n";
161     }
162 }