AbstractInjectionBoolean.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.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 AbstractInjectionBoolean<T extends AbstractCallableBoolean<T>> {
23
    
24
    /**
25
     * Log4j logger sent to view.
26
     */
27
    private static final Logger LOGGER = LogManager.getRootLogger();
28
    
29
    // Every FALSE SQL statements will be checked,
30
    // more statements means a more robust application
31
    protected final List<String> falsy;
32
    
33
    // Every TRUE SQL statements will be checked,
34
    // more statements means a more robust application
35
    protected final List<String> truthy;
36
    
37
    public enum BooleanMode {
38
        AND, OR, STACKED, NO_MODE
39
    }
40
    
41
    protected final InjectionModel injectionModel;
42
    
43
    protected final BooleanMode booleanMode;
44
    
45
    protected AbstractInjectionBoolean(InjectionModel injectionModel, BooleanMode booleanMode) {
46
        
47
        this.injectionModel = injectionModel;
48
        this.booleanMode = booleanMode;
49
        this.falsy = this.injectionModel.getMediatorVendor().getVendor().instance().getFalsy();
50
        this.truthy = this.injectionModel.getMediatorVendor().getVendor().instance().getTruthy();
51
    }
52
53
    /**
54
     * Start one test to verify if boolean works.
55
     * @return true if boolean method is confirmed
56
     */
57
    public abstract boolean isInjectable() throws StoppedByUserSlidingException;
58
59
    public abstract void initializeNextCharacters(
60
        String sqlQuery,
61
        List<char[]> bytes,
62
        AtomicInteger indexCharacter,
63
        CompletionService<T> taskCompletionService,
64
        AtomicInteger countTasksSubmitted
65
    );
66
67
    public abstract char[] initializeBinaryMask(List<char[]> bytes, T currentCallable);
68
69
    /**
70
     * Display a message to explain how is blind/time working.
71
     * @return
72
     */
73
    public abstract String getInfoMessage();
74
75
    /**
76
     * Process the whole boolean injection, character by character, bit by bit.
77
     * @param sqlQuery SQL query
78
     * @param suspendable Action a user can stop
79
     * @return Final string: SQLiABCDEF...
80
     */
81
    public String inject(String sqlQuery, AbstractSuspendable suspendable) throws StoppedByUserSlidingException {
82
83
        // List of the characters, each one represented by an array of 8 bits
84
        // e.g. SQLi: bytes[0] => 01010011:S, bytes[1] => 01010001:Q ...
85
        List<char[]> bytes = new ArrayList<>();
86
        
87
        // Cursor for current character position
88
        var indexCharacter = new AtomicInteger(0);
89
90
        // Concurrent URL requests
91
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableAbstractBoolean");
92
        
93
        CompletionService<T> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
94
95
        var countTasksSubmitted = new AtomicInteger(0);
96
        var countBadAsciiCode = new AtomicInteger(0);
97
98 1 1. inject : removed call to com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::initializeNextCharacters → NO_COVERAGE
        this.initializeNextCharacters(sqlQuery, bytes, indexCharacter, taskCompletionService, countTasksSubmitted);
99
100
        // Process the job until there is no more active task,
101
        // in other word until all HTTP requests are done
102 2 1. inject : changed conditional boundary → NO_COVERAGE
2. inject : negated conditional → NO_COVERAGE
        while (countTasksSubmitted.get() > 0) {
103
            
104 1 1. inject : negated conditional → NO_COVERAGE
            if (suspendable.isSuspended()) {
105
                
106
                String result = this.stop(bytes, taskExecutor);
107
                throw new StoppedByUserSlidingException(result);
108
            }
109
            
110
            try {
111
                // The URL call is done, bring back the finished task
112
                var currentCallable = taskCompletionService.take().get();
113
                
114
                // One task has just ended, decrease active tasks by 1
115
                countTasksSubmitted.decrementAndGet();
116
                
117
                // If SQL result is not empty, then add a new unknown character,
118
                // and define a new array of 8 undefined bit.
119
                // Then add 8 bits requests for that new character.
120 1 1. inject : removed call to com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::injectCharacter → NO_COVERAGE
                this.injectCharacter(bytes, countTasksSubmitted, countBadAsciiCode, currentCallable);
121 1 1. inject : removed call to com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::initializeNextCharacters → NO_COVERAGE
                this.initializeNextCharacters(sqlQuery, bytes, indexCharacter, taskCompletionService, countTasksSubmitted);
122
123
                String result = convert(bytes);
124 1 1. inject : negated conditional → NO_COVERAGE
                if (result.matches("(?s).*"+ DataAccess.TRAIL_RGX +".*")) {
125 1 1. inject : removed call to java/util/concurrent/atomic/AtomicInteger::set → NO_COVERAGE
                    countTasksSubmitted.set(0);
126
                    break;
127
                }
128
            } catch (InterruptedException e) {
129
                
130
                LOGGER.log(LogLevelUtil.IGNORE, e, e);
131 1 1. inject : removed call to java/lang/Thread::interrupt → NO_COVERAGE
                Thread.currentThread().interrupt();
132
                
133
            } catch (ExecutionException e) {
134
                LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
135
            } catch (InjectionFailureException e) {
136
                
137
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
138
                break;
139
            }
140
        }
141
142 1 1. inject : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::inject → NO_COVERAGE
        return this.stop(bytes, taskExecutor);
143
    }
144
145
    private static String convert(List<char[]> bytes) {
146
147
        var result = new StringBuilder();
148
149
        for (char[] c: bytes) {
150
            try {
151
                var charCode = Integer.parseInt(new String(c), 2);
152
                var str = Character.toString((char) charCode);
153
                result.append(str);
154
155
            } catch (NumberFormatException err) {
156
                // Byte string not fully constructed : 0x1x010x
157
                // Ignore
158
            }
159
        }
160 1 1. convert : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::convert → NO_COVERAGE
        return result.toString();
161
    }
162
163
    private void injectCharacter(List<char[]> bytes, AtomicInteger countTasksSubmitted, AtomicInteger countBadAsciiCode, T currentCallable) throws InjectionFailureException {
164
        
165
        // Process url that has just checked one bit, convert bits to chars,
166
        // and change current bit from undefined to 0 or 1
167
        
168
        char[] asciiCodeMask = this.initializeBinaryMask(bytes, currentCallable);
169
        
170
        var asciiCodeBinary = new String(asciiCodeMask);
171
        
172
        // Inform the View if bits array is complete, else nothing #Need fix
173 1 1. injectCharacter : negated conditional → NO_COVERAGE
        if (asciiCodeBinary.matches("^[01]{8}$")) {
174
            
175
            var asciiCode = Integer.parseInt(asciiCodeBinary, 2);
176 2 1. injectCharacter : negated conditional → NO_COVERAGE
2. injectCharacter : negated conditional → NO_COVERAGE
            if (asciiCode == 127 || asciiCode == 0) {  // Stop if many 11111111, 01111111 or 00000000
177
                
178 3 1. injectCharacter : changed conditional boundary → NO_COVERAGE
2. injectCharacter : negated conditional → NO_COVERAGE
3. injectCharacter : negated conditional → NO_COVERAGE
                if (countTasksSubmitted.get() != 0 && countBadAsciiCode.get() > 15) {
179
                    throw new InjectionFailureException("Boolean false positive, stopping...");
180
                }
181
                
182
                countBadAsciiCode.incrementAndGet();
183
            }
184
185
            currentCallable.charText = Character.toString((char) asciiCode);
186
            
187
            var interaction = new Request();
188 1 1. injectCharacter : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            interaction.setMessage(Interaction.MESSAGE_BINARY);
189 1 1. injectCharacter : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
            interaction.setParameters(
190
                asciiCodeBinary
191
                + "="
192
                + currentCallable.charText
193
                .replace("\\n", "\\\\\\n")
194
                .replace("\\r", "\\\\\\r")
195
                .replace("\\t", "\\\\\\t")
196
            );
197 1 1. injectCharacter : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(interaction);
198
        }
199
    }
200
201
    private String stop(List<char[]> bytes, ExecutorService taskExecutor) {
202
        
203 1 1. stop : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
        this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
204
205
        // Get current progress and display
206
        var result = new StringBuilder();
207
        
208
        for (char[] c: bytes) {
209
            try {
210
                var charCode = Integer.parseInt(new String(c), 2);
211
                var str = Character.toString((char) charCode);
212
                result.append(str);
213
                
214
            } catch (NumberFormatException err) {
215
                // Byte string not fully constructed : 0x1x010x
216
                // Ignore
217
            }
218
        }
219
        
220 1 1. stop : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::stop → NO_COVERAGE
        return result.toString();
221
    }
222
223
    /**
224
     * Run a HTTP call via the model.
225
     * @param urlString URL to inject
226
     * @return Source code
227
     */
228
    public String callUrl(String urlString, String metadataInjectionProcess) {
229 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess);
230
    }
231
232
    public String callUrl(String urlString, String metadataInjectionProcess, AbstractCallableBoolean<?> callableBoolean) {
233 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess, callableBoolean);
234
    }
235
236
    public BooleanMode getBooleanMode() {
237 1 1. getBooleanMode : replaced return value with null for com/jsql/model/injection/strategy/blind/AbstractInjectionBoolean::getBooleanMode → NO_COVERAGE
        return this.booleanMode;
238
    }
239
}

Mutations

98

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

102

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

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

104

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

120

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

121

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

124

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

125

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

131

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

142

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

160

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

173

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

176

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

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

178

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

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

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

188

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

189

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

197

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

203

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

220

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

229

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

233

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

237

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

Active mutators

Tests examined


Report generated by PIT 1.16.1