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

Mutations

81

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

85

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

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

86

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

98

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

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

99

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

103

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

104

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

109

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

117

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

131

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

146

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

148

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

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

149

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

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

155

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

158

1.1
Location : isCharCompleteWithCorruptCheck
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

159

1.1
Location : isCharCompleteWithCorruptCheck
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

167

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

170

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

174

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

189

1.1
Location : stop
Killed by : none
replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::stop → 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 : callUrl
Killed by : none
replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBit::callUrl → NO_COVERAGE

206

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

210

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