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