1 package com.jsql.model.injection.strategy.blind;
2
3 import com.jsql.model.InjectionModel;
4 import com.jsql.model.exception.InjectionFailureException;
5 import com.jsql.model.exception.StoppedByUserSlidingException;
6 import com.jsql.model.injection.strategy.blind.callable.CallableBlindBin;
7 import com.jsql.util.LogLevelUtil;
8 import org.apache.commons.lang3.StringUtils;
9 import org.apache.logging.log4j.LogManager;
10 import org.apache.logging.log4j.Logger;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.List;
15 import java.util.concurrent.CompletionService;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Future;
19 import java.util.concurrent.atomic.AtomicInteger;
20
21 import static name.fraser.neil.plaintext.diff_match_patch.Diff;
22
23
24
25
26 public class InjectionBlindBin extends AbstractInjectionMonobit<CallableBlindBin> {
27
28
29
30
31 private static final Logger LOGGER = LogManager.getRootLogger();
32 private static final int LOW = 0;
33 private static final int HIGH = 127;
34
35
36 private String sourceReferencePage;
37
38
39
40
41
42
43
44 private List<Diff> falseDiffs = new ArrayList<>();
45
46
47
48
49
50
51 public InjectionBlindBin(InjectionModel injectionModel, BlindOperator blindMode) {
52 super(injectionModel, blindMode);
53
54
55 if (this.falsyBin.isEmpty() || this.injectionModel.isStoppedByUser()) {
56 return;
57 }
58
59
60 this.sourceReferencePage = this.callUrl(StringUtils.EMPTY, "bin#ref");
61
62
63
64 ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBinTagFalse");
65 Collection<CallableBlindBin> callablesFalseTest = new ArrayList<>();
66 for (String falseTest: this.falsyBin) {
67 callablesFalseTest.add(new CallableBlindBin(
68 falseTest,
69 injectionModel,
70 this,
71 blindMode,
72 -1, -1, -1,
73 "bin#falsy"
74 ));
75 }
76
77
78
79
80 try {
81 List<Future<CallableBlindBin>> futuresFalseTest = taskExecutor.invokeAll(callablesFalseTest);
82 this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
83 for (Future<CallableBlindBin> futureFalseTest: futuresFalseTest) {
84 if (this.injectionModel.isStoppedByUser()) {
85 return;
86 }
87 if (this.falseDiffs.isEmpty()) {
88 this.falseDiffs = futureFalseTest.get().getDiffsWithReference();
89 } else {
90 this.falseDiffs.retainAll(futureFalseTest.get().getDiffsWithReference());
91 }
92 }
93 } catch (ExecutionException e) {
94 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
95 } catch (InterruptedException e) {
96 LOGGER.log(LogLevelUtil.IGNORE, e, e);
97 Thread.currentThread().interrupt();
98 }
99
100 if (this.injectionModel.isStoppedByUser()) {
101 return;
102 }
103
104 this.cleanTrueDiffs(injectionModel, blindMode);
105 }
106
107 private void cleanTrueDiffs(InjectionModel injectionModel, BlindOperator blindMode) {
108
109
110 ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBinTagTrue");
111 Collection<CallableBlindBin> callablesTrueTest = new ArrayList<>();
112 for (String trueTest: this.truthyBin) {
113 callablesTrueTest.add(new CallableBlindBin(
114 trueTest,
115 injectionModel,
116 this,
117 blindMode,
118 -1, -1, -1,
119 "bin#truthy"
120 ));
121 }
122
123
124
125
126 try {
127 List<Future<CallableBlindBin>> futuresTrueTest = taskExecutor.invokeAll(callablesTrueTest);
128 this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
129 for (Future<CallableBlindBin> futureTrueTest: futuresTrueTest) {
130 if (this.injectionModel.isStoppedByUser()) {
131 return;
132 }
133 this.falseDiffs.removeAll(futureTrueTest.get().getDiffsWithReference());
134 }
135 } catch (ExecutionException e) {
136 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
137 } catch (InterruptedException e) {
138 LOGGER.log(LogLevelUtil.IGNORE, e, e);
139 Thread.currentThread().interrupt();
140 }
141 }
142
143 @Override
144 public CallableBlindBin getCallableBitTest(String sqlQuery, int indexChar, int bit) {
145 return null;
146 }
147
148 @Override
149 public boolean isInjectable() throws StoppedByUserSlidingException {
150 if (this.injectionModel.isStoppedByUser()) {
151 throw new StoppedByUserSlidingException();
152 }
153 var blindTest = new CallableBlindBin(
154 this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm(),
155 this.injectionModel,
156 this,
157 this.blindOperator,
158 -1, -1, -1,
159 "bin#confirm"
160 );
161 try {
162 blindTest.call();
163 } catch (Exception e) {
164 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
165 }
166 return blindTest.isTrue() && !this.falseDiffs.isEmpty();
167 }
168
169 @Override
170 public void initNextChar(
171 String sqlQuery,
172 List<char[]> bytes,
173 AtomicInteger indexChar,
174 CompletionService<CallableBlindBin> taskCompletionService,
175 AtomicInteger countTasksSubmitted,
176 CallableBlindBin currentCallable
177 ) {
178 int low;
179 int mid;
180 int high;
181
182 if (currentCallable != null) {
183 low = currentCallable.getLow();
184 mid = currentCallable.getMid();
185 high = currentCallable.getHigh();
186
187 if (low >= high) {
188 if (low == high) {
189 low = currentCallable.isTrue() ? low : low - 1;
190 } else {
191 low = currentCallable.isTrue() ? low : high;
192 }
193 char[] asciiCodeMask = bytes.get(currentCallable.getCurrentIndex() - 1);
194 this.setAsciiCodeMask(asciiCodeMask, low);
195
196 try {
197 AtomicInteger countBadAsciiCode = new AtomicInteger();
198 this.injectCharacter(bytes, countTasksSubmitted, countBadAsciiCode, currentCallable);
199 } catch (InjectionFailureException e) {
200 return;
201 }
202
203 bytes.add(AbstractInjectionBit.getBitsUnset());
204 indexChar.incrementAndGet();
205 low = InjectionBlindBin.LOW;
206 high = InjectionBlindBin.HIGH;
207 } else if (currentCallable.isTrue()) {
208 low = mid + 1;
209 } else {
210 high = mid - 1;
211 }
212 mid = low + (high - low) / 2;
213 } else {
214 low = InjectionBlindBin.LOW;
215 mid = InjectionBlindBin.LOW + (InjectionBlindBin.HIGH - InjectionBlindBin.LOW) / 2;
216 high = InjectionBlindBin.HIGH;
217 bytes.add(AbstractInjectionBit.getBitsUnset());
218 indexChar.incrementAndGet();
219 }
220
221 taskCompletionService.submit(
222 new CallableBlindBin(
223 sqlQuery,
224 indexChar.get(),
225 this.injectionModel,
226 this,
227 this.blindOperator,
228 low, mid, high,
229 "bin#" + indexChar + "~" + mid
230 )
231 );
232 countTasksSubmitted.addAndGet(1);
233 }
234
235 private void setAsciiCodeMask(char[] asciiCodeMask, int value) {
236 String binary = StringUtils.leftPad(Integer.toBinaryString((char) value), 8, "0");
237 for (int i = 0; i <= 7; i++) {
238 asciiCodeMask[i] = binary.charAt(i);
239 }
240 }
241
242 @Override
243 public char[] initMaskAsciiChar(List<char[]> bytes, CallableBlindBin currentCallable) {
244 return bytes.get(currentCallable.getCurrentIndex() - 1);
245 }
246
247 @Override
248 public String getInfoMessage() {
249 return "- Strategy Blind bin: query True when Diffs are matching " + this.falseDiffs + "\n\n";
250 }
251
252
253
254
255 public String getSourceReferencePage() {
256 return this.sourceReferencePage;
257 }
258
259 public List<Diff> getFalseDiffs() {
260 return this.falseDiffs;
261 }
262 }