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) {  // ending either when no more task or force break
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);  // force break
104
                }
105
            } catch (InterruptedException e) {
106
                LOGGER.log(LogLevelUtil.IGNORE, e, e);
107 1 1. inject : removed call to java/lang/Thread::interrupt → NO_COVERAGE
                Thread.currentThread().interrupt();
108
            } catch (ExecutionException e) {
109
                LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
110
            } catch (InjectionFailureException e) {
111
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
112 1 1. inject : removed call to java/util/concurrent/atomic/AtomicInteger::set → NO_COVERAGE
                countTasksSubmitted.set(0);  // force break
113
            }
114
        }
115 1 1. inject : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::inject → NO_COVERAGE
        return this.stop(bytes, taskExecutor);
116
    }
117
118
    private static String convert(List<char[]> bytes) {
119
        var result = new StringBuilder();
120
        for (char[] c: bytes) {
121
            try {
122
                var charCode = Integer.parseInt(new String(c), 2);
123
                var str = Character.toString((char) charCode);
124
                result.append(str);
125
            } catch (NumberFormatException err) {
126
                // Ignore, byte string not fully constructed (0x1x010x)
127
            }
128
        }
129 1 1. convert : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::convert → NO_COVERAGE
        return result.toString();
130
    }
131
132
    protected boolean isCharCompleteWithCorruptCheck(
133
        List<char[]> bytes,
134
        AtomicInteger countBadAsciiCode,
135
        T currentCallable
136
    ) throws InjectionFailureException {
137
        // Process url that has just checked one bit, convert bits to chars,
138
        // and change current bit from undefined to 0 or 1
139
        char[] maskAsciiChar = this.initMaskAsciiChar(bytes, currentCallable);
140
        var asciiCodeBit = new String(maskAsciiChar);
141
        var isComplete = false;
142
143
        // Inform the View if bits array is complete, else nothing #Need fix
144 1 1. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
        if (asciiCodeBit.matches("^[01]{8}$")) {
145
            var asciiCode = Integer.parseInt(asciiCodeBit, 2);
146 3 1. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
2. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
3. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
            if (asciiCode == 255 || asciiCode == 127 || asciiCode == 0) {  // Stop if many 11111111, 01111111 or 00000000
147 2 1. isCharCompleteWithCorruptCheck : negated conditional → NO_COVERAGE
2. isCharCompleteWithCorruptCheck : changed conditional boundary → NO_COVERAGE
                if (countBadAsciiCode.get() > 15) {
148
                    throw new InjectionFailureException("Boolean false positive, stopping...");
149
                }
150
                countBadAsciiCode.incrementAndGet();
151
            }
152
153 1 1. isCharCompleteWithCorruptCheck : removed call to com/jsql/model/injection/strategy/blind/callable/AbstractCallableBit::setCharText → NO_COVERAGE
            currentCallable.setCharText(Character.toString((char) asciiCode));
154
            
155 1 1. isCharCompleteWithCorruptCheck : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(new Seal.MessageBinary(
156
                asciiCodeBit
157
                + "="
158
                + currentCallable.getCharText()
159
                .replace("\n", "\\n")
160
                .replace("\r", "\\r")
161
                .replace("\t", "\\t")
162
            ));
163
            isComplete = true;
164
        }
165 2 1. isCharCompleteWithCorruptCheck : replaced boolean return with true for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::isCharCompleteWithCorruptCheck → NO_COVERAGE
2. isCharCompleteWithCorruptCheck : replaced boolean return with false for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::isCharCompleteWithCorruptCheck → NO_COVERAGE
        return isComplete;
166
    }
167
168
    private String stop(List<char[]> bytes, ExecutorService taskExecutor) {
169 1 1. stop : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
        this.injectionModel.getMediatorUtils().threadUtil().shutdown(taskExecutor);
170
171
        // Get current progress and display
172
        var result = new StringBuilder();
173
        
174
        for (char[] c: bytes) {
175
            try {
176
                var charCode = Integer.parseInt(new String(c), 2);
177
                var str = Character.toString((char) charCode);
178
                result.append(str);
179
            } catch (NumberFormatException err) {
180
                // Byte string not fully constructed : 0x1x010x
181
                // Ignore
182
            }
183
        }
184 1 1. stop : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::stop → NO_COVERAGE
        return result.toString();
185
    }
186
187
    /**
188
     * Run an HTTP call via the model.
189
     * @param urlString URL to inject
190
     * @return Source code
191
     */
192
    public String callUrl(String urlString, String metadataInjectionProcess) {
193 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess);
194
    }
195
196
    public String callUrl(String urlString, String metadataInjectionProcess, AbstractCallableBit<?> callableBoolean) {
197 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);
198
    }
199
200
    public BlindOperator getBlindOperator() {
201 1 1. getBlindOperator : replaced return value with null for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::getBlindOperator → NO_COVERAGE
        return this.blindOperator;
202
    }
203
204
    protected static char[] getBitsUnset() {
205 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' };
206
    }
207
}

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

107

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

112

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

115

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

129

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

144

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

146

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

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

3.3
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
changed conditional boundary → NO_COVERAGE

153

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

155

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

165

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

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

169

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

184

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

193

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

197

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

201

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

205

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.23.0