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.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
 * A blind attack class using concurrent threads.
25
 */
26
public class InjectionBlindBin extends AbstractInjectionMonobit<CallableBlindBin> {
27
28
    /**
29
     * Log4j logger sent to view.
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
    // Source code of the TRUE web page (usually ?id=1)
36
    private String sourceReferencePage;
37
38
    /**
39
     * List of string differences found in all the FALSE queries, compared
40
     * to the reference page. Each FALSE pages should contain
41
     * at least one same string, which shouldn't be present in all
42
     * the TRUE queries.
43
     */
44
    private List<Diff> falseDiffs = new ArrayList<>();
45
46
    /**
47
     * Create blind attack initialization.
48
     * If every false diffs are not in true diffs and every true diffs are in
49
     * true diffs, then Blind attack is confirmed.
50
     */
51
    public InjectionBlindBin(InjectionModel injectionModel, BlindOperator blindMode) {
52
        super(injectionModel, blindMode);
53
        
54
        // No blind
55 2 1. <init> : negated conditional → NO_COVERAGE
2. <init> : negated conditional → NO_COVERAGE
        if (this.falsyBin.isEmpty() || this.injectionModel.isStoppedByUser()) {
56
            return;
57
        }
58
        
59
        // Call the SQL request which must be TRUE (usually ?id=1)
60
        this.sourceReferencePage = this.callUrl(StringUtils.EMPTY, "bin#ref");
61
62
        // Concurrent calls to the FALSE statements,
63
        // it will use inject() from the model
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
        // Delete junk from the results of FALSE statements,
78
        // keep only diffs found in each and every FALSE pages.
79
        // Allow the user to stop the loop
80
        try {
81
            List<Future<CallableBlindBin>> futuresFalseTest = taskExecutor.invokeAll(callablesFalseTest);
82 1 1. <init> : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
            this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
83
            for (Future<CallableBlindBin> futureFalseTest: futuresFalseTest) {
84 1 1. <init> : negated conditional → NO_COVERAGE
                if (this.injectionModel.isStoppedByUser()) {
85
                    return;
86
                }
87 1 1. <init> : negated conditional → NO_COVERAGE
                if (this.falseDiffs.isEmpty()) {
88
                    this.falseDiffs = futureFalseTest.get().getDiffsWithReference();  // Init diffs
89
                } else {
90
                    this.falseDiffs.retainAll(futureFalseTest.get().getDiffsWithReference());  // Clean un-matching diffs
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 1 1. <init> : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
98
        }
99
100 1 1. <init> : negated conditional → NO_COVERAGE
        if (this.injectionModel.isStoppedByUser()) {
101
            return;
102
        }
103
        
104 1 1. <init> : removed call to com/jsql/model/injection/strategy/blind/InjectionBlindBin::cleanTrueDiffs → NO_COVERAGE
        this.cleanTrueDiffs(injectionModel, blindMode);
105
    }
106
107
    private void cleanTrueDiffs(InjectionModel injectionModel, BlindOperator blindMode) {
108
        // Concurrent calls to the TRUE statements,
109
        // it will use inject() from the model.
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
        // Remove TRUE diffs in the FALSE diffs, because
124
        // a significant FALSE statement shouldn't contain any TRUE diff.
125
        // Allow the user to stop the loop.
126
        try {
127
            List<Future<CallableBlindBin>> futuresTrueTest = taskExecutor.invokeAll(callablesTrueTest);
128 1 1. cleanTrueDiffs : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
            this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
129
            for (Future<CallableBlindBin> futureTrueTest: futuresTrueTest) {
130 1 1. cleanTrueDiffs : negated conditional → NO_COVERAGE
                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 1 1. cleanTrueDiffs : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
140
        }
141
    }
142
143
    @Override
144
    public CallableBlindBin getCallableBitTest(String sqlQuery, int indexChar, int bit) {
145
        return null;  // unused
146
    }
147
148
    @Override
149
    public boolean isInjectable() throws StoppedByUserSlidingException {
150 1 1. isInjectable : negated conditional → NO_COVERAGE
        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 3 1. isInjectable : negated conditional → NO_COVERAGE
2. isInjectable : negated conditional → NO_COVERAGE
3. isInjectable : replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionBlindBin::isInjectable → NO_COVERAGE
        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 1 1. initNextChar : negated conditional → NO_COVERAGE
        if (currentCallable != null) {
183
            low = currentCallable.getLow();
184
            mid = currentCallable.getMid();
185
            high = currentCallable.getHigh();
186
187 2 1. initNextChar : changed conditional boundary → NO_COVERAGE
2. initNextChar : negated conditional → NO_COVERAGE
            if (low >= high) {
188 1 1. initNextChar : negated conditional → NO_COVERAGE
                if (low == high) {  // n-1 >= n
189 2 1. initNextChar : negated conditional → NO_COVERAGE
2. initNextChar : Replaced integer subtraction with addition → NO_COVERAGE
                    low = currentCallable.isTrue() ? low : low - 1;
190
                } else {
191 1 1. initNextChar : negated conditional → NO_COVERAGE
                    low = currentCallable.isTrue() ? low : high;
192
                }
193 1 1. initNextChar : Replaced integer subtraction with addition → NO_COVERAGE
                char[] asciiCodeMask = bytes.get(currentCallable.getCurrentIndex() - 1);  // bits for current url
194 1 1. initNextChar : removed call to com/jsql/model/injection/strategy/blind/InjectionBlindBin::setAsciiCodeMask → NO_COVERAGE
                this.setAsciiCodeMask(asciiCodeMask, low);
195
196
                try {
197
                    AtomicInteger countBadAsciiCode = new AtomicInteger();  // todo unused
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 1 1. initNextChar : negated conditional → NO_COVERAGE
            } else if (currentCallable.isTrue()) {  // key < mid
208 1 1. initNextChar : Replaced integer addition with subtraction → NO_COVERAGE
                low = mid + 1;
209
            } else {  // key > mid
210 1 1. initNextChar : Replaced integer subtraction with addition → NO_COVERAGE
                high = mid - 1;
211
            }
212 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;
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 2 1. setAsciiCodeMask : negated conditional → NO_COVERAGE
2. setAsciiCodeMask : changed conditional boundary → NO_COVERAGE
        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 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);
245
    }
246
247
    @Override
248
    public String getInfoMessage() {
249 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";
250
    }
251
    
252
    
253
    // Getter and setter
254
255
    public String getSourceReferencePage() {
256 1 1. getSourceReferencePage : replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getSourceReferencePage → NO_COVERAGE
        return this.sourceReferencePage;
257
    }
258
    
259
    public List<Diff> getFalseDiffs() {
260 1 1. getFalseDiffs : replaced return value with Collections.emptyList for com/jsql/model/injection/strategy/blind/InjectionBlindBin::getFalseDiffs → NO_COVERAGE
        return this.falseDiffs;
261
    }
262
}

Mutations

55

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

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

82

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

84

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

87

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

97

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

100

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

104

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

128

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

130

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

139

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

150

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

166

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

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

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

182

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

187

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

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

188

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

189

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

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

191

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

193

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

194

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

207

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

208

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

210

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

212

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

237

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

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

244

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

249

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

256

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

260

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

Active mutators

Tests examined


Report generated by PIT 1.19.1