AbstractInjectionBit.java

1
package com.jsql.model.injection.strategy.blind;
2
3
import com.jsql.model.InjectionModel;
4
import com.jsql.model.accessible.DataAccess;
5
import com.jsql.view.subscriber.Seal;
6
import com.jsql.model.exception.InjectionFailureException;
7
import com.jsql.model.exception.StoppedByUserSlidingException;
8
import com.jsql.model.injection.strategy.blind.callable.AbstractCallableBit;
9
import com.jsql.model.suspendable.AbstractSuspendable;
10
import com.jsql.util.LogLevelUtil;
11
import org.apache.logging.log4j.LogManager;
12
import org.apache.logging.log4j.Logger;
13
14
import java.util.ArrayList;
15
import java.util.List;
16
import java.util.concurrent.CompletionService;
17
import java.util.concurrent.ExecutionException;
18
import java.util.concurrent.ExecutorCompletionService;
19
import java.util.concurrent.ExecutorService;
20
import java.util.concurrent.atomic.AtomicInteger;
21
22
public abstract class AbstractInjectionBit<T extends AbstractCallableBit<T>> {
23
    
24
    private static final Logger LOGGER = LogManager.getRootLogger();
25
26
    public enum BlindOperator {
27
        AND, OR, STACK, NO_MODE
28
    }
29
30
    protected final InjectionModel injectionModel;
31
    protected final BlindOperator blindOperator;
32
    
33
    protected AbstractInjectionBit(InjectionModel injectionModel, BlindOperator blindOperator) {
34
        this.injectionModel = injectionModel;
35
        this.blindOperator = blindOperator;
36
    }
37
38
    /**
39
     * Start one test to verify if boolean works.
40
     * @return true if boolean method is confirmed
41
     */
42
    public abstract boolean isInjectable() throws StoppedByUserSlidingException;
43
44
    public abstract void initNextChar(
45
        String sqlQuery,
46
        List<char[]> bytes,
47
        AtomicInteger indexChar,
48
        CompletionService<T> taskCompletionService,
49
        AtomicInteger countTasksSubmitted,
50
        AtomicInteger countBadAsciiCode,
51
        T currentCallable  // required by sequential calls like binary search
52
    );
53
54
    public abstract char[] initMaskAsciiChar(List<char[]> bytes, T currentCallable);
55
56
    /**
57
     * Display a message to explain how is blind/time working.
58
     */
59
    public abstract String getInfoMessage();
60
61
    /**
62
     * Process the whole boolean injection, character by character, bit by bit.
63
     * @param sqlQuery SQL query
64
     * @param suspendable Action a user can stop
65
     * @return Final string: SQLiABCDEF...
66
     */
67
    public String inject(String sqlQuery, AbstractSuspendable suspendable) throws StoppedByUserSlidingException {
68
        // List of the characters, each one represented by an array of 8 bits
69
        // e.g. SQLi: bytes[0] => 01010011:S, bytes[1] => 01010001:Q ...
70
        List<char[]> bytes = new ArrayList<>();
71
        var indexChar = new AtomicInteger(0);  // current char position
72
73
        // Concurrent URL requests
74
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().threadUtil().getExecutor("CallableAbstractBoolean");
75
        CompletionService<T> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
76
77
        var countTasksSubmitted = new AtomicInteger(0);
78
        var countBadAsciiCode = new AtomicInteger(0);
79
80 1 1. inject : removed call to com/jsql/model/injection/strategy/blind/AbstractInjectionBit::initNextChar → NO_COVERAGE
        this.initNextChar(sqlQuery, bytes, indexChar, taskCompletionService, countTasksSubmitted, countBadAsciiCode, null);
81
82
        // Process the job until there is no more active task,
83
        // in other word until all HTTP requests are done
84 2 1. inject : negated conditional → NO_COVERAGE
2. inject : changed conditional boundary → NO_COVERAGE
        while (countTasksSubmitted.get() > 0) {
85 1 1. inject : negated conditional → NO_COVERAGE
            if (suspendable.isSuspended()) {
86
                String result = this.stop(bytes, taskExecutor);
87
                throw new StoppedByUserSlidingException(result);
88
            }
89
            
90
            try {
91
                var currentCallable = taskCompletionService.take().get();  // URL call done
92
                countTasksSubmitted.decrementAndGet();  // one task just ended
93
                
94
                // If SQL result is not empty, then add a new unknown character and define a new array of 7 undefined bit.
95
                // Then add 7 bits requests for that new character.
96
                var isComplete = this.isCharCompleteWithCorruptCheck(bytes, countBadAsciiCode, currentCallable);
97 2 1. inject : negated conditional → NO_COVERAGE
2. inject : negated conditional → NO_COVERAGE
                if (isComplete || currentCallable.isBinary()) {  // prevents bitwise overload new char init on each bit
98 1 1. inject : removed call to com/jsql/model/injection/strategy/blind/AbstractInjectionBit::initNextChar → NO_COVERAGE
                    this.initNextChar(sqlQuery, bytes, indexChar, taskCompletionService, countTasksSubmitted, countBadAsciiCode, currentCallable);
99
                }
100
101
                String result = AbstractInjectionBit.convert(bytes);
102 1 1. inject : negated conditional → NO_COVERAGE
                if (result.matches("(?s).*"+ DataAccess.TRAIL_RGX +".*")) {
103 1 1. inject : removed call to java/util/concurrent/atomic/AtomicInteger::set → NO_COVERAGE
                    countTasksSubmitted.set(0);
104
                    break;
105
                }
106
            } catch (InterruptedException e) {
107
                LOGGER.log(LogLevelUtil.IGNORE, e, e);
108 1 1. inject : removed call to java/lang/Thread::interrupt → NO_COVERAGE
                Thread.currentThread().interrupt();
109
            } catch (ExecutionException e) {
110
                LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
111
            } catch (InjectionFailureException e) {
112
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
113
                break;
114
            }
115
        }
116 1 1. inject : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::inject → NO_COVERAGE
        return this.stop(bytes, taskExecutor);
117
    }
118
119
    private static String convert(List<char[]> bytes) {
120
        var result = new StringBuilder();
121
        for (char[] c: bytes) {
122
            try {
123
                var charCode = Integer.parseInt(new String(c), 2);
124
                var str = Character.toString((char) charCode);
125
                result.append(str);
126
            } catch (NumberFormatException err) {
127
                // Ignore, byte string not fully constructed (0x1x010x)
128
            }
129
        }
130 1 1. convert : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::convert → NO_COVERAGE
        return result.toString();
131
    }
132
133
    protected boolean isCharCompleteWithCorruptCheck(
134
        List<char[]> bytes,
135
        AtomicInteger countBadAsciiCode,
136
        T currentCallable
137
    ) throws InjectionFailureException {
138
        // Process url that has just checked one bit, convert bits to chars,
139
        // and change current bit from undefined to 0 or 1
140
        char[] maskAsciiChar = this.initMaskAsciiChar(bytes, currentCallable);
141
        var asciiCodeBit = new String(maskAsciiChar);
142
        var isComplete = false;
143
144
        // Inform the View if bits array is complete, else nothing #Need fix
145 1 1. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
        if (asciiCodeBit.matches("^[01]{8}$")) {
146
            var asciiCode = Integer.parseInt(asciiCodeBit, 2);
147 2 1. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
2. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
            if (asciiCode == 127 || asciiCode == 0) {  // Stop if many 11111111, 01111111 or 00000000
148 2 1. isCharCompleteWithCorruptCheck : changed conditional boundary → NO_COVERAGE
2. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
                if (countBadAsciiCode.get() > 15) {
149
                    throw new InjectionFailureException("Boolean false positive, stopping...");
150
                }
151
                countBadAsciiCode.incrementAndGet();
152
            }
153
154 1 1. isCharCompleteWithCorruptCheck : removed call to com/jsql/model/injection/strategy/blind/callable/AbstractCallableBit::setCharText → NO_COVERAGE
            currentCallable.setCharText(Character.toString((char) asciiCode));
155
            
156 1 1. isCharCompleteWithCorruptCheck : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(new Seal.MessageBinary(
157
                asciiCodeBit
158
                + "="
159
                + currentCallable.getCharText()
160
                .replace("\n", "\\n")
161
                .replace("\r", "\\r")
162
                .replace("\t", "\\t")
163
            ));
164
            isComplete = true;
165
        }
166 2 1. isCharCompleteWithCorruptCheck : replaced boolean return with false for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::isCharCompleteWithCorruptCheck → NO_COVERAGE
2. isCharCompleteWithCorruptCheck : replaced boolean return with true for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::isCharCompleteWithCorruptCheck → NO_COVERAGE
        return isComplete;
167
    }
168
169
    private String stop(List<char[]> bytes, ExecutorService taskExecutor) {
170 1 1. stop : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
        this.injectionModel.getMediatorUtils().threadUtil().shutdown(taskExecutor);
171
172
        // Get current progress and display
173
        var result = new StringBuilder();
174
        
175
        for (char[] c: bytes) {
176
            try {
177
                var charCode = Integer.parseInt(new String(c), 2);
178
                var str = Character.toString((char) charCode);
179
                result.append(str);
180
            } catch (NumberFormatException err) {
181
                // Byte string not fully constructed : 0x1x010x
182
                // Ignore
183
            }
184
        }
185 1 1. stop : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::stop → NO_COVERAGE
        return result.toString();
186
    }
187
188
    /**
189
     * Run an HTTP call via the model.
190
     * @param urlString URL to inject
191
     * @return Source code
192
     */
193
    public String callUrl(String urlString, String metadataInjectionProcess) {
194 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess);
195
    }
196
197
    public String callUrl(String urlString, String metadataInjectionProcess, AbstractCallableBit<?> callableBoolean) {
198 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess, callableBoolean);
199
    }
200
201
    public BlindOperator getBlindOperator() {
202 1 1. getBlindOperator : replaced return value with null for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::getBlindOperator → NO_COVERAGE
        return this.blindOperator;
203
    }
204
205
    protected static char[] getBitsUnset() {
206 1 1. getBitsUnset : replaced return value with null for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::getBitsUnset → NO_COVERAGE
        return new char[]{ '0', 'x', 'x', 'x', 'x', 'x', 'x', 'x' };
207
    }
208
}

Mutations

80

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

84

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

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

85

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

97

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

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

98

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

102

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

103

1.1
Location : inject
Killed by : none
removed call to java/util/concurrent/atomic/AtomicInteger::set → NO_COVERAGE

108

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

116

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

130

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

145

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

147

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

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

148

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

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

154

1.1
Location : isCharCompleteWithCorruptCheck
Killed by : none
removed call to com/jsql/model/injection/strategy/blind/callable/AbstractCallableBit::setCharText → NO_COVERAGE

156

1.1
Location : isCharCompleteWithCorruptCheck
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

166

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

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

170

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

185

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

194

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

198

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

202

1.1
Location : getBlindOperator
Killed by : none
replaced return value with null for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::getBlindOperator → NO_COVERAGE

206

1.1
Location : getBitsUnset
Killed by : none
replaced return value with null for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::getBitsUnset → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.1