InjectionCharInsertion.java

1
package com.jsql.model.injection.strategy.blind;
2
3
import com.jsql.model.InjectionModel;
4
import com.jsql.model.exception.StoppedByUserSlidingException;
5
import com.jsql.model.injection.strategy.blind.patch.Diff;
6
import com.jsql.util.LogLevelUtil;
7
import org.apache.commons.lang3.RandomStringUtils;
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.ExecutionException;
16
import java.util.concurrent.ExecutorService;
17
import java.util.concurrent.Future;
18
19
/**
20
 * A blind attack class using concurrent threads.
21
 */
22
public class InjectionCharInsertion {
23
    
24
    /**
25
     * Log4j logger sent to view.
26
     */
27
    private static final Logger LOGGER = LogManager.getRootLogger();
28
29
    // Source code of the FALSE web page (eg. ?id=-123456789)
30
    private String blankFalseMark;
31
32
    /**
33
     * List of string differences found in all the FALSE queries, compared
34
     * to the reference page (aka opcodes). Each FALSE pages should contain
35
     * at least one same string, which shouldn't be present in all
36
     * the TRUE queries.
37
     */
38
    private List<Diff> constantTrueMark = new ArrayList<>();
39
    
40
    protected final InjectionModel injectionModel;
41
42
    private final String prefixSuffix;
43
    
44
    private static final String PREFIX = "prefix";
45
46
    private final List<String> falsy;
47
    
48
    /**
49
     * Create blind attack initialization.
50
     * If every false test are not in true mark and every true test are in
51
     * true test, then blind attack is confirmed.
52
     * @param prefixSuffix
53
     */
54
    public InjectionCharInsertion(InjectionModel injectionModel, String falseCharInsertion, String prefixSuffix) {
55
        
56
        this.injectionModel = injectionModel;
57
        this.prefixSuffix = prefixSuffix;
58
        
59
        List<String> truthy = this.injectionModel.getMediatorVendor().getVendor().instance().getTruthy();
60
        this.falsy = this.injectionModel.getMediatorVendor().getVendor().instance().getFalsy();
61
        
62
        // No blind
63 2 1. <init> : negated conditional → NO_COVERAGE
2. <init> : negated conditional → NO_COVERAGE
        if (truthy.isEmpty() || this.injectionModel.isStoppedByUser()) {
64
            return;
65
        }
66
        
67
        // Call the SQL request which must be FALSE (usually ?id=-123456879)
68
        this.blankFalseMark = this.callUrl(
69
            falseCharInsertion,
70
            "prefix:" + prefixSuffix.replace(PREFIX, StringUtils.EMPTY)
71
        );
72
73
        // Concurrent calls to the FALSE statements,
74
        // it will use inject() from the model
75
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableCharInsertionTagTrue");
76
        Collection<CallableCharInsertion> listCallableTagTrue = new ArrayList<>();
77
        
78
        for (String urlTest: truthy) {
79
            listCallableTagTrue.add(
80
                new CallableCharInsertion(
81
                    String.join(
82
                        StringUtils.SPACE,
83
                        prefixSuffix.replace(PREFIX, RandomStringUtils.random(10, "345")),
84
                        this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy().getBoolean().getModeOr(),
85
                        urlTest
86
                    ),
87
                    this,
88
                    "prefix#true"
89
                )
90
            );
91
        }
92
        
93
        // Delete junk from the results of FALSE statements,
94
        // keep only opcodes found in each and every FALSE pages.
95
        // Allow the user to stop the loop
96
        try {
97
            List<Future<CallableCharInsertion>> listTagTrue = taskExecutor.invokeAll(listCallableTagTrue);
98 1 1. <init> : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
            this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
99
            
100 2 1. <init> : negated conditional → NO_COVERAGE
2. <init> : changed conditional boundary → NO_COVERAGE
            for (var i = 1 ; i < listTagTrue.size() ; i++) {
101
                
102 1 1. <init> : negated conditional → NO_COVERAGE
                if (this.injectionModel.isStoppedByUser()) {
103
                    return;
104
                }
105
106 1 1. <init> : negated conditional → NO_COVERAGE
                if (this.constantTrueMark.isEmpty()) {
107
                    this.constantTrueMark = listTagTrue.get(i).get().getOpcodes();
108
                } else {
109
                    this.constantTrueMark.retainAll(listTagTrue.get(i).get().getOpcodes());
110
                }
111
            }
112
        } catch (ExecutionException e) {
113
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
114
        } catch (InterruptedException e) {
115
            
116
            LOGGER.log(LogLevelUtil.IGNORE, e, e);
117 1 1. <init> : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
118
        }
119
        
120 1 1. <init> : removed call to com/jsql/model/injection/strategy/blind/InjectionCharInsertion::initializeFalseMarks → NO_COVERAGE
        this.initializeFalseMarks();
121
    }
122
    
123
    private void initializeFalseMarks() {
124
        
125
        // Concurrent calls to the TRUE statements,
126
        // it will use inject() from the model.
127
        ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().getThreadUtil().getExecutor("CallableGetBlindTagTrue");
128
        Collection<CallableCharInsertion> listCallableTagFalse = new ArrayList<>();
129
        
130
        for (String urlTest: this.falsy) {
131
            listCallableTagFalse.add(
132
                new CallableCharInsertion(
133
                    String.join(
134
                        StringUtils.SPACE,
135
                        this.prefixSuffix.replace(PREFIX, RandomStringUtils.random(10, "345")),
136
                        this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy().getBoolean().getModeOr(),
137
                        urlTest
138
                    ),
139
                    this,
140
                    "prefix#false"
141
                )
142
            );
143
        }
144
        
145
        // Remove TRUE opcodes in the FALSE opcodes, because
146
        // a significant FALSE statement shouldn't contain any TRUE opcode.
147
        // Allow the user to stop the loop.
148
        try {
149
            List<Future<CallableCharInsertion>> listTagFalse = taskExecutor.invokeAll(listCallableTagFalse);
150 1 1. initializeFalseMarks : removed call to com/jsql/util/ThreadUtil::shutdown → NO_COVERAGE
            this.injectionModel.getMediatorUtils().getThreadUtil().shutdown(taskExecutor);
151
        
152
            for (Future<CallableCharInsertion> falseTag: listTagFalse) {
153
                
154 1 1. initializeFalseMarks : negated conditional → NO_COVERAGE
                if (this.injectionModel.isStoppedByUser()) {
155
                    return;
156
                }
157
158
                this.constantTrueMark.removeAll(falseTag.get().getOpcodes());
159
            }
160
        } catch (ExecutionException e) {
161
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
162
        } catch (InterruptedException e) {
163
164
            LOGGER.log(LogLevelUtil.IGNORE, e, e);
165 1 1. initializeFalseMarks : removed call to java/lang/Thread::interrupt → NO_COVERAGE
            Thread.currentThread().interrupt();
166
        }
167
    }
168
169
    public boolean isInjectable() throws StoppedByUserSlidingException {
170
        
171 1 1. isInjectable : negated conditional → NO_COVERAGE
        if (this.injectionModel.isStoppedByUser()) {
172
            throw new StoppedByUserSlidingException();
173
        }
174
        
175
        var blindTest = new CallableCharInsertion(
176
            String.join(
177
                StringUtils.SPACE,
178
                this.prefixSuffix.replace(PREFIX, RandomStringUtils.random(10, "678")),
179
                this.injectionModel.getMediatorVendor().getVendor().instance().getModelYaml().getStrategy().getBoolean().getModeOr(),
180
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlTestBooleanInitialization()
181
            ),
182
            this,
183
            "prefix#confirm"
184
        );
185
        
186
        try {
187
            blindTest.call();
188
        } catch (Exception e) {
189
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
190
        }
191
192 3 1. isInjectable : replaced boolean return with true for com/jsql/model/injection/strategy/blind/InjectionCharInsertion::isInjectable → NO_COVERAGE
2. isInjectable : negated conditional → NO_COVERAGE
3. isInjectable : negated conditional → NO_COVERAGE
        return blindTest.isTrue() && !this.constantTrueMark.isEmpty();
193
    }
194
    
195
    public String callUrl(String urlString, String metadataInjectionProcess) {
196 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionCharInsertion::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess);
197
    }
198
199
    public String callUrl(String urlString, String metadataInjectionProcess, AbstractCallableBoolean<?> callableBoolean) {
200 1 1. callUrl : replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionCharInsertion::callUrl → NO_COVERAGE
        return this.injectionModel.injectWithoutIndex(urlString, metadataInjectionProcess, callableBoolean);
201
    }
202
203
204
    // Getter
205
206
    public String getBlankFalseMark() {
207 1 1. getBlankFalseMark : replaced return value with "" for com/jsql/model/injection/strategy/blind/InjectionCharInsertion::getBlankFalseMark → NO_COVERAGE
        return this.blankFalseMark;
208
    }
209
    
210
    public List<Diff> getConstantTrueMark() {
211 1 1. getConstantTrueMark : replaced return value with Collections.emptyList for com/jsql/model/injection/strategy/blind/InjectionCharInsertion::getConstantTrueMark → NO_COVERAGE
        return this.constantTrueMark;
212
    }
213
}

Mutations

63

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

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

98

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

100

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

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

102

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

106

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

117

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

120

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

150

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

154

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

165

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

171

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

192

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

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

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

196

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

200

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

207

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

211

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

Active mutators

Tests examined


Report generated by PIT 1.16.1