InjectionBlindBin.java

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.model.injection.strategy.blind.patch.Diff;
8
import com.jsql.model.injection.strategy.blind.patch.DiffMatchPatch;
9
import com.jsql.util.LogLevelUtil;
10
import org.apache.commons.lang3.StringUtils;
11
import org.apache.logging.log4j.LogManager;
12
import org.apache.logging.log4j.Logger;
13
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.List;
17
import java.util.concurrent.CompletionService;
18
import java.util.concurrent.ExecutionException;
19
import java.util.concurrent.ExecutorService;
20
import java.util.concurrent.Future;
21
import java.util.concurrent.atomic.AtomicInteger;
22
23
/**
24
 * A blind attack class using concurrent threads.
25
 */
26
public class InjectionBlindBin extends AbstractInjectionMonobit<CallableBlindBin> {
27
28
    private static final Logger LOGGER = LogManager.getRootLogger();
29
    private static final int LOW = 0;
30
    private static final int HIGH = 127;
31
32
    private String sourceReferencePage;  // Source code of the TRUE web page (usually ?id=1)
33
34
    /**
35
     * List of string differences found in all the FALSE queries, compared
36
     * to the reference page. Each FALSE pages should contain
37
     * at least one same string, which shouldn't be present in all
38
     * the TRUE queries.
39
     */
40
    private List<Diff> falseDiffs = new ArrayList<>();
41
    private List<Diff> trueDiffs = new ArrayList<>();
42
43
    /**
44
     * Create blind attack initialization.
45
     * If every false diffs are not in true diffs and every true diffs are in
46
     * true diffs, then Blind attack is confirmed.
47
     */
48
    public InjectionBlindBin(InjectionModel injectionModel, BlindOperator blindOperator) {
49
        super(injectionModel, blindOperator);
50
51
        List<String> falsys = this.injectionModel.getMediatorVendor().getVendor().instance().getFalsyBin();
52 2 1. <init> : negated conditional → NO_COVERAGE
2. <init> : negated conditional → NO_COVERAGE
        if (falsys.isEmpty() || this.injectionModel.isStoppedByUser()) {
53
            return;
54
        }
55
        
56
        // Call the SQL request which must be TRUE (usually ?id=1)
57
        this.sourceReferencePage = this.callUrl(StringUtils.EMPTY, "bin#ref:"+ blindOperator.toString().toLowerCase());
58
59
        // Concurrent calls to the FALSE statements,
60
        // it will use inject() from the model
61
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBinTagFalse");
62
        Collection<CallableBlindBin> callablesFalsys = new ArrayList<>();
63
        for (String falsy: falsys) {
64
            callablesFalsys.add(new CallableBlindBin(
65
                falsy,
66
                injectionModel,
67
                this,
68
                blindOperator,
69
                -1, -1, -1,
70
                "bin#falsy"
71
            ));
72
        }
73
        
74
        // Delete junk from the results of FALSE statements,
75
        // keep only diffs found in each and every FALSE pages.
76
        // Allow the user to stop the loop
77
        try {
78
            List<Future<CallableBlindBin>> futuresFalsys = taskExecutor.invokeAll(callablesFalsys);
79 1 1. <init> : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
            this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
80
            for (Future<CallableBlindBin> futureFalsy: futuresFalsys) {
81 1 1. <init> : negated conditional → NO_COVERAGE
                if (this.injectionModel.isStoppedByUser()) {
82
                    return;
83
                }
84 1 1. <init> : negated conditional → NO_COVERAGE
                if (this.falseDiffs.isEmpty()) {
85
                    this.falseDiffs = futureFalsy.get().getDiffsWithReference();  // Init diffs
86
                } else {
87
                    this.falseDiffs.retainAll(futureFalsy.get().getDiffsWithReference());  // Clean un-matching diffs
88
                }
89
            }
90
        } catch (ExecutionException e) {
91
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
92
        } catch (InterruptedException e) {
93
            LOGGER.log(LogLevelUtil.IGNORE, e, e);
94 1 1. <init> : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
95
        }
96
97 1 1. <init> : negated conditional → NO_COVERAGE
        if (this.injectionModel.isStoppedByUser()) {
98
            return;
99
        }
100
        
101 1 1. <init> : removed call to com/jsql/model/injection/strategy/blind/InjectionBlindBin::cleanTrueDiffs → NO_COVERAGE
        this.cleanTrueDiffs(injectionModel, blindOperator);
102
    }
103
104
    private void cleanTrueDiffs(InjectionModel injectionModel, BlindOperator blindOperator) {
105
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindBinTagTrue");
106
        Collection<CallableBlindBin> callablesTruthys = new ArrayList<>();
107
        List<String> truthys = this.injectionModel.getMediatorVendor().getVendor().instance().getTruthyBin();
108
        for (String truthy: truthys) {
109
            callablesTruthys.add(new CallableBlindBin(
110
                truthy,
111
                injectionModel,
112
                this,
113
                blindOperator,
114
                -1, -1, -1,
115
                "bin#truthy"
116
            ));
117
        }
118
        
119
        // Remove TRUE diffs in the FALSE diffs as FALSE statement shouldn't contain any TRUE diff.
120
        try {
121
            List<Future<CallableBlindBin>> futuresTruthys = taskExecutor.invokeAll(callablesTruthys);
122 1 1. cleanTrueDiffs : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
            this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
123
            for (Future<CallableBlindBin> futureTruthy: futuresTruthys) {
124 1 1. cleanTrueDiffs : negated conditional → NO_COVERAGE
                if (this.injectionModel.isStoppedByUser()) {
125
                    return;
126
                }
127 1 1. cleanTrueDiffs : negated conditional → NO_COVERAGE
                if (this.trueDiffs.isEmpty()) {
128
                    this.trueDiffs = futureTruthy.get().getDiffsWithReference();  // Init diffs
129
                } else {
130
                    this.trueDiffs.retainAll(futureTruthy.get().getDiffsWithReference());  // Clean un-matching diffs
131
                }
132
                this.falseDiffs.removeAll(futureTruthy.get().getDiffsWithReference());
133
            }
134
        } catch (ExecutionException e) {
135
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
136
        } catch (InterruptedException e) {
137
            LOGGER.log(LogLevelUtil.IGNORE, e, e);
138 1 1. cleanTrueDiffs : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
139
        }
140
    }
141
142
    @Override
143
    public CallableBlindBin getCallableBitTest(String sqlQuery, int indexChar, int bit) {
144
        return null;  // unused
145
    }
146
147
    @Override
148
    public boolean isInjectable() throws StoppedByUserSlidingException {
149 1 1. isInjectable : negated conditional → NO_COVERAGE
        if (this.injectionModel.isStoppedByUser()) {
150
            throw new StoppedByUserSlidingException();
151
        }
152
        var blindTest = new CallableBlindBin(
153
            this.injectionModel.getMediatorVendor().getVendor().instance().sqlBlindConfirm(),
154
            this.injectionModel,
155
            this,
156
            this.blindOperator,
157
            -1, -1, -1,
158
            "bin#confirm"
159
        );
160
        try {
161
            blindTest.call();
162
        } catch (Exception e) {
163
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
164
        }
165 2 1. isInjectable : negated conditional → NO_COVERAGE
2. isInjectable : replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isInjectable → NO_COVERAGE
        return blindTest.isTrue()
166
            // when insertionChar = true then pages ref == truthy == falsy == confirm => falsy cleaned empty, truthy with opcode EQUAL not reliable
167 3 1. lambda$isInjectable$0 : replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::lambda$isInjectable$0 → NO_COVERAGE
2. lambda$isInjectable$0 : negated conditional → NO_COVERAGE
3. isInjectable : negated conditional → NO_COVERAGE
            && this.trueDiffs.stream().anyMatch(diff -> !DiffMatchPatch.Operation.EQUAL.equals(diff.getOperation()))
168 3 1. lambda$isInjectable$1 : negated conditional → NO_COVERAGE
2. lambda$isInjectable$1 : replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::lambda$isInjectable$1 → NO_COVERAGE
3. isInjectable : negated conditional → NO_COVERAGE
            || this.falseDiffs.stream().anyMatch(diff -> !DiffMatchPatch.Operation.EQUAL.equals(diff.getOperation()));
169
    }
170
171
    @Override
172
    public void initNextChar(
173
        String sqlQuery,
174
        List<char[]> bytes,
175
        AtomicInteger indexChar,
176
        CompletionService<CallableBlindBin> taskCompletionService,
177
        AtomicInteger countTasksSubmitted,
178
        AtomicInteger countBadAsciiCode,
179
        CallableBlindBin currentCallable
180
    ) {
181
        int low;
182
        int mid;
183
        int high;
184
185 1 1. initNextChar : negated conditional → NO_COVERAGE
        if (currentCallable != null) {
186
            low = currentCallable.getLow();
187
            mid = currentCallable.getMid();
188
            high = currentCallable.getHigh();
189
190 2 1. initNextChar : negated conditional → NO_COVERAGE
2. initNextChar : changed conditional boundary → NO_COVERAGE
            if (low >= high) {  // char found
191 1 1. initNextChar : negated conditional → NO_COVERAGE
                if (this.isCorruptOrElseNextChar(bytes, indexChar, countBadAsciiCode, currentCallable, low)) {
192
                    return;  // too many errors
193
                }
194
                low = InjectionBlindBin.LOW;
195
                high = InjectionBlindBin.HIGH;
196 1 1. initNextChar : negated conditional → NO_COVERAGE
            } else if (currentCallable.isTrue()) {  // current >= mid
197 1 1. initNextChar : Replaced integer addition with subtraction → NO_COVERAGE
                low = mid + 1;
198
            } else {  // current < mid
199 1 1. initNextChar : Replaced integer subtraction with addition → NO_COVERAGE
                high = mid - 1;
200
            }
201
        } else {
202
            low = InjectionBlindBin.LOW;
203
            high = InjectionBlindBin.HIGH;
204
            bytes.add(AbstractInjectionBit.getBitsUnset());
205
            indexChar.incrementAndGet();
206
        }
207
208 3 1. initNextChar : Replaced integer division with multiplication → NO_COVERAGE
2. initNextChar : Replaced integer addition with subtraction → NO_COVERAGE
3. initNextChar : Replaced integer subtraction with addition → NO_COVERAGE
        mid = low + (high - low) / 2;
209
        taskCompletionService.submit(
210
            new CallableBlindBin(
211
                sqlQuery,
212
                indexChar.get(),
213
                this.injectionModel,
214
                this,
215
                this.blindOperator,
216
                low, mid, high,
217
                String.format("bin#%s~%s<%s<%s", indexChar, low, mid, high)
218
            )
219
        );
220
        countTasksSubmitted.addAndGet(1);
221
    }
222
223
    private boolean isCorruptOrElseNextChar(List<char[]> bytes, AtomicInteger indexChar, AtomicInteger countBadAsciiCode, CallableBlindBin currentCallable, int low) {
224
        int currentLow = low;
225 2 1. isCorruptOrElseNextChar : negated conditional → NO_COVERAGE
2. isCorruptOrElseNextChar : negated conditional → NO_COVERAGE
        if (currentLow == 0 || currentLow == 127) {
226
            countBadAsciiCode.incrementAndGet();
227
        } else {
228 2 1. isCorruptOrElseNextChar : Replaced integer subtraction with addition → NO_COVERAGE
2. isCorruptOrElseNextChar : negated conditional → NO_COVERAGE
            currentLow = currentCallable.isTrue() ? currentLow : currentLow - 1;
229
        }
230 1 1. isCorruptOrElseNextChar : Replaced integer subtraction with addition → NO_COVERAGE
        char[] asciiCodeMask = bytes.get(currentCallable.getCurrentIndex() - 1);  // bits for current url
231 1 1. isCorruptOrElseNextChar : removed call to com/jsql/model/injection/strategy/blind/InjectionBlindBin::setAsciiCodeMask → NO_COVERAGE
        this.setAsciiCodeMask(asciiCodeMask, currentLow);
232
233
        try {
234
            this.isCharCompleteWithCorruptCheck(bytes, countBadAsciiCode, currentCallable);
235
        } catch (InjectionFailureException e) {
236 1 1. isCorruptOrElseNextChar : replaced boolean return with false for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isCorruptOrElseNextChar → NO_COVERAGE
            return true;
237
        }
238
239
        bytes.add(AbstractInjectionBit.getBitsUnset());
240
        indexChar.incrementAndGet();
241 1 1. isCorruptOrElseNextChar : replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isCorruptOrElseNextChar → NO_COVERAGE
        return false;
242
    }
243
244
    private void setAsciiCodeMask(char[] asciiCodeMask, int value) {
245
        String binary = StringUtils.leftPad(Integer.toBinaryString((char) value), 8, "0");
246 2 1. setAsciiCodeMask : negated conditional → NO_COVERAGE
2. setAsciiCodeMask : changed conditional boundary → NO_COVERAGE
        for (int i = 0; i <= 7; i++) {
247
            asciiCodeMask[i] = binary.charAt(i);
248
        }
249
    }
250
251
    @Override
252
    public char[] initMaskAsciiChar(List<char[]> bytes, CallableBlindBin currentCallable) {
253 2 1. initMaskAsciiChar : Replaced integer subtraction with addition → NO_COVERAGE
2. initMaskAsciiChar : replaced return value with null for com/jsql/model/injection/strategy/blind/InjectionBlindBin::initMaskAsciiChar → NO_COVERAGE
        return bytes.get(currentCallable.getCurrentIndex() - 1);
254
    }
255
256
    @Override
257
    public String getInfoMessage() {
258 1 1. getInfoMessage : replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getInfoMessage → NO_COVERAGE
        return "- Strategy Blind bin: query True when Diffs are matching " + this.falseDiffs + "\n\n";
259
    }
260
    
261
    
262
    // Getter and setter
263
264
    public String getSourceReferencePage() {
265 1 1. getSourceReferencePage : replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getSourceReferencePage → NO_COVERAGE
        return this.sourceReferencePage;
266
    }
267
    
268
    public List<Diff> getFalseDiffs() {
269 1 1. getFalseDiffs : replaced return value with Collections.emptyList for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getFalseDiffs → NO_COVERAGE
        return this.falseDiffs;
270
    }
271
272
    public List<Diff> getTrueDiffs() {
273 1 1. getTrueDiffs : replaced return value with Collections.emptyList for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getTrueDiffs → NO_COVERAGE
        return this.trueDiffs;
274
    }
275
}

Mutations

52

1.1
Location : <init>
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : <init>
Killed by : none
negated conditional → NO_COVERAGE

79

1.1
Location : <init>
Killed by : none
removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE

81

1.1
Location : <init>
Killed by : none
negated conditional → NO_COVERAGE

84

1.1
Location : <init>
Killed by : none
negated conditional → NO_COVERAGE

94

1.1
Location : <init>
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

97

1.1
Location : <init>
Killed by : none
negated conditional → NO_COVERAGE

101

1.1
Location : <init>
Killed by : none
removed call to com/jsql/model/injection/strategy/blind/InjectionBlindBin::cleanTrueDiffs → NO_COVERAGE

122

1.1
Location : cleanTrueDiffs
Killed by : none
removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE

124

1.1
Location : cleanTrueDiffs
Killed by : none
negated conditional → NO_COVERAGE

127

1.1
Location : cleanTrueDiffs
Killed by : none
negated conditional → NO_COVERAGE

138

1.1
Location : cleanTrueDiffs
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

149

1.1
Location : isInjectable
Killed by : none
negated conditional → NO_COVERAGE

165

1.1
Location : isInjectable
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : isInjectable
Killed by : none
replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isInjectable → NO_COVERAGE

167

1.1
Location : lambda$isInjectable$0
Killed by : none
replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::lambda$isInjectable$0 → NO_COVERAGE

2.2
Location : lambda$isInjectable$0
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : isInjectable
Killed by : none
negated conditional → NO_COVERAGE

168

1.1
Location : lambda$isInjectable$1
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : lambda$isInjectable$1
Killed by : none
replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::lambda$isInjectable$1 → NO_COVERAGE

3.3
Location : isInjectable
Killed by : none
negated conditional → NO_COVERAGE

185

1.1
Location : initNextChar
Killed by : none
negated conditional → NO_COVERAGE

190

1.1
Location : initNextChar
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : initNextChar
Killed by : none
changed conditional boundary → NO_COVERAGE

191

1.1
Location : initNextChar
Killed by : none
negated conditional → NO_COVERAGE

196

1.1
Location : initNextChar
Killed by : none
negated conditional → NO_COVERAGE

197

1.1
Location : initNextChar
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

199

1.1
Location : initNextChar
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

208

1.1
Location : initNextChar
Killed by : none
Replaced integer division with multiplication → NO_COVERAGE

2.2
Location : initNextChar
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

3.3
Location : initNextChar
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

225

1.1
Location : isCorruptOrElseNextChar
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : isCorruptOrElseNextChar
Killed by : none
negated conditional → NO_COVERAGE

228

1.1
Location : isCorruptOrElseNextChar
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : isCorruptOrElseNextChar
Killed by : none
negated conditional → NO_COVERAGE

230

1.1
Location : isCorruptOrElseNextChar
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

231

1.1
Location : isCorruptOrElseNextChar
Killed by : none
removed call to com/jsql/model/injection/strategy/blind/InjectionBlindBin::setAsciiCodeMask → NO_COVERAGE

236

1.1
Location : isCorruptOrElseNextChar
Killed by : none
replaced boolean return with false for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isCorruptOrElseNextChar → NO_COVERAGE

241

1.1
Location : isCorruptOrElseNextChar
Killed by : none
replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isCorruptOrElseNextChar → NO_COVERAGE

246

1.1
Location : setAsciiCodeMask
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : setAsciiCodeMask
Killed by : none
changed conditional boundary → NO_COVERAGE

253

1.1
Location : initMaskAsciiChar
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : initMaskAsciiChar
Killed by : none
replaced return value with null for com/jsql/model/injection/strategy/blind/InjectionBlindBin::initMaskAsciiChar → NO_COVERAGE

258

1.1
Location : getInfoMessage
Killed by : none
replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getInfoMessage → NO_COVERAGE

265

1.1
Location : getSourceReferencePage
Killed by : none
replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getSourceReferencePage → NO_COVERAGE

269

1.1
Location : getFalseDiffs
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getFalseDiffs → NO_COVERAGE

273

1.1
Location : getTrueDiffs
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getTrueDiffs → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.0