View Javadoc
1   package com.jsql.model.injection.engine;
2   
3   import com.jsql.model.InjectionModel;
4   import com.jsql.view.subscriber.Seal;
5   import com.jsql.model.injection.engine.model.Engine;
6   import com.jsql.model.injection.engine.model.EngineYaml;
7   import com.jsql.util.I18nUtil;
8   import com.jsql.util.LogLevelUtil;
9   import com.jsql.util.StringUtil;
10  import org.apache.commons.lang3.StringUtils;
11  import org.apache.commons.lang3.SystemUtils;
12  import org.apache.logging.log4j.LogManager;
13  import org.apache.logging.log4j.Logger;
14  
15  import java.net.URLEncoder;
16  import java.nio.charset.StandardCharsets;
17  import java.time.Clock;
18  import java.time.LocalDate;
19  import java.time.format.DateTimeFormatter;
20  import java.util.Arrays;
21  import java.util.List;
22  
23  public class MediatorEngine {
24      
25      private static final Logger LOGGER = LogManager.getRootLogger();
26      
27      private static final String LOG_ENGINE = "{} [{}]";
28  
29      /**
30       * Database engine currently used.
31       * It can be switched to another engine by automatic detection or manual selection.
32       */
33      private Engine engine;
34  
35      /**
36       * Database engine selected by user (default UNDEFINED).
37       * If not UNDEFINED then the next injection will be forced to use the selected engine.
38       */
39      private Engine engineByUser;
40  
41      // TODO Replace with enum
42      private final Engine auto;
43      private final Engine access;
44      private final Engine altibase;
45      private final Engine clickhouse;
46      private final Engine cockroachdb;
47      private final Engine cubrid;
48      private final Engine dameng;
49      private final Engine db2;
50      private final Engine derby;
51      private final Engine duckdb;
52      private final Engine exasol;
53      private final Engine firebird;
54      private final Engine greenplum;
55      private final Engine h2;
56      private final Engine hana;
57      private final Engine hsqldb;
58      private final Engine informix;
59      private final Engine mariadb;
60      private final Engine mckoi;
61      private final Engine mimer;
62      private final Engine monetdb;
63      private final Engine mysql;
64      private final Engine neo4j;
65      private final Engine oracle;
66      private final Engine percona;
67      private final Engine postgres;
68      private final Engine presto;
69      private final Engine spanner;
70      private final Engine sqlite;
71      private final Engine sqlserver;
72      private final Engine sybase;
73      private final Engine tidb;
74      private final Engine vertica;
75      private final Engine virtuoso;
76  
77      private final List<Engine> engines;
78      private final List<Engine> enginesForFingerprint;
79  
80      private final InjectionModel injectionModel;
81  
82      public MediatorEngine(InjectionModel injectionModel) {
83          this.injectionModel = injectionModel;
84          
85          Engine ctreeace = new Engine(new EngineYaml("ctreeace.yml", injectionModel));
86          Engine frontbase = new Engine(new EngineYaml("frontbase.yml", injectionModel));
87          Engine ingres = new Engine(new EngineYaml("ingres.yml", injectionModel));
88          Engine iris = new Engine(new EngineYaml("iris.yml", injectionModel));
89          Engine maxdb = new Engine(new EngineYaml("maxdb.yml", injectionModel));
90          Engine netezza = new Engine(new EngineYaml("netezza.yml", injectionModel));
91          Engine nuodb = new Engine(new EngineYaml("nuodb.yml", injectionModel));
92          Engine teradata = new Engine(new EngineYaml("teradata.yml", injectionModel));
93  
94          this.auto = new Engine();
95          this.access = new Engine(new EngineYaml("access.yml", injectionModel));
96          this.altibase = new Engine(new EngineYaml("altibase.yml", injectionModel));
97          this.cubrid = new Engine(new EngineYaml("cubrid.yml", injectionModel));
98          this.clickhouse = new Engine(new EngineYaml("clickhouse.yml", injectionModel));
99          this.cockroachdb = new Engine(new EngineYaml("cockroachdb.yml", injectionModel));
100         this.dameng = new Engine(new EngineYaml("dameng.yml", injectionModel));
101         this.db2 = new Engine(new EngineYaml("db2.yml", injectionModel));
102         this.derby = new Engine(new EngineYaml("derby.yml", injectionModel));
103         this.duckdb = new Engine(new EngineYaml("duckdb.yml", injectionModel));
104         this.exasol = new Engine(new EngineYaml("exasol.yml", injectionModel));
105         this.firebird = new Engine(new EngineYaml("firebird.yml", injectionModel));
106         this.greenplum = new Engine(new EngineYaml("greenplum.yml", injectionModel));
107         this.h2 = new Engine(new EngineYaml("h2.yml", injectionModel));
108         this.hana = new Engine(new EngineYaml("hana.yml", injectionModel));
109         this.hsqldb = new Engine(new EngineYaml("hsqldb.yml", injectionModel));
110         this.informix = new Engine(new EngineYaml("informix.yml", injectionModel));
111         this.mariadb = new Engine(new EngineYaml("mariadb.yml", injectionModel));
112         this.mckoi = new Engine(new EngineYaml("mckoi.yml", injectionModel));
113         this.mimer = new Engine(new EngineYaml("mimersql.yml", injectionModel));
114         this.monetdb = new Engine(new EngineYaml("monetdb.yml", injectionModel));
115         this.mysql = new Engine(new EngineYaml("mysql.yml", injectionModel));
116         this.neo4j = new Engine(new EngineYaml("neo4j.yml", injectionModel));
117         this.oracle = new Engine(new EngineYaml("oracle.yml", injectionModel));
118         this.percona = new Engine(new EngineYaml("percona.yml", injectionModel));
119         this.postgres = new Engine(new EngineYaml("postgres.yml", injectionModel));
120         this.presto = new Engine(new EngineYaml("presto.yml", injectionModel));
121         this.spanner = new Engine(new EngineYaml("spanner.yml", injectionModel));
122         this.sqlite = new Engine(new EngineYaml("sqlite.yml", injectionModel)) {
123             @Override
124             public String transform(String resultToParse) {
125                 var resultSqlite = new StringBuilder();
126 
127                 String resultTmp = resultToParse
128                     .replaceFirst("[^(]+\\(", StringUtils.EMPTY)
129                     .trim()
130                     .replaceAll("\\)$", StringUtils.EMPTY)
131                     .replaceAll("\\([^)]+\\)", StringUtils.EMPTY);
132 
133                 for (String columnNameAndType: resultTmp.split(",")) {
134                     if (columnNameAndType.trim().startsWith("primary key")) {
135                         continue;
136                     }
137                     // Some recent SQLite use tabulation character as a separator => split() by any white space \s
138                     String columnName = columnNameAndType.trim().split("\\s")[0];
139                     // Some recent SQLite enclose names with ` => strip those `
140                     columnName = StringUtils.strip(columnName, "`");
141                     if (
142                         !"CONSTRAINT".equals(columnName)
143                         && !"UNIQUE".equals(columnName)
144                     ) {
145                         // Generate pattern \4\5\4\6 for injection parsing
146                         resultSqlite.append((char) 4).append(columnName).append((char) 5).append("0").append((char) 4).append((char) 6);
147                     }
148                 }
149                 return resultSqlite.toString();
150             }
151         };
152         this.sqlserver = new Engine(new EngineYaml("sqlserver.yml", injectionModel));
153         this.sybase = new Engine(new EngineYaml("sybase.yml", injectionModel));
154         this.tidb = new Engine(new EngineYaml("tidb.yml", injectionModel));
155         this.vertica = new Engine(new EngineYaml("vertica.yml", injectionModel));
156         this.virtuoso = new Engine(new EngineYaml("virtuoso.yml", injectionModel));
157 
158         this.engines = Arrays.asList(
159             this.auto, this.access, this.altibase, ctreeace, this.clickhouse, this.cockroachdb, this.cubrid, this.dameng, this.db2, this.derby, this.duckdb,
160             this.exasol, this.firebird, frontbase, this.greenplum, this.h2, this.hana, this.hsqldb, this.informix, ingres, iris, maxdb, this.mariadb, this.mckoi,
161             this.mimer, this.monetdb, this.mysql, this.neo4j, netezza, nuodb, this.oracle, this.percona, this.postgres, this.presto, this.spanner, this.sqlite,
162             this.sqlserver, this.sybase, this.tidb, teradata, this.vertica, this.virtuoso
163         );
164         this.enginesForFingerprint = Arrays.asList(  // Add sortIndex
165             this.mysql, this.postgres, this.sqlite, this.h2, this.hsqldb, this.oracle, this.sqlserver, this.mariadb, this.spanner, this.duckdb,
166             this.altibase, ctreeace, this.cubrid, this.db2, this.derby, this.exasol, this.firebird, frontbase, this.hana, this.informix, ingres,
167             iris, maxdb, this.mckoi, this.mimer, this.monetdb, this.neo4j, netezza, nuodb, this.presto, this.sybase, teradata, this.vertica,
168             this.virtuoso, this.clickhouse, this.access, this.dameng, this.cockroachdb, this.greenplum, this.percona, this.tidb
169         );
170 
171         this.engine = this.mysql;
172         this.engineByUser = this.auto;
173     }
174     
175     public Engine fingerprintEngine() {
176         Engine engineFound = null;
177         if (this.injectionModel.getMediatorEngine().getEngineByUser() != this.injectionModel.getMediatorEngine().getAuto()) {
178             engineFound = this.injectionModel.getMediatorEngine().getEngineByUser();
179             LOGGER.log(
180                 LogLevelUtil.CONSOLE_INFORM,
181                 MediatorEngine.LOG_ENGINE,
182                 () -> I18nUtil.valueByKey("LOG_DATABASE_TYPE_FORCED_BY_USER"),
183                 () -> this.injectionModel.getMediatorEngine().getEngineByUser()
184             );
185         } else {
186             LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "[Step 1] Fingerprinting database using raw fingerprinting...");
187             var insertionCharacter = URLEncoder.encode("'\"#-)'\"*", StandardCharsets.UTF_8);
188             String pageSource = this.injectionModel.injectWithoutIndex(insertionCharacter, "test#engine");
189                 
190             var mediatorEngine = this.injectionModel.getMediatorEngine();
191             Engine[] enginesWithoutAuto = mediatorEngine.getEngines()
192                 .stream()
193                 .filter(v -> v != mediatorEngine.getAuto())
194                 .toArray(Engine[]::new);
195             
196             // Test each engine
197             for (Engine engineTest : enginesWithoutAuto) {
198                 if (pageSource.matches(engineTest.instance().fingerprintErrorsAsRegex())) {
199                     engineFound = engineTest;
200                     LOGGER.log(
201                         LogLevelUtil.CONSOLE_SUCCESS,
202                         "Found database [{}] using raw fingerprinting",
203                         () -> engineTest
204                     );
205                     break;
206                 }
207             }
208             if (engineFound == null) {
209                 engineFound = this.injectionModel.getMediatorEngine().getMysql();
210                 LOGGER.log(
211                     LogLevelUtil.CONSOLE_INFORM,
212                     MediatorEngine.LOG_ENGINE,
213                     () -> I18nUtil.valueByKey("LOG_DATABASE_TYPE_NOT_FOUND"),
214                     () -> this.injectionModel.getMediatorEngine().getMysql()
215                 );
216             }
217         }
218 
219         var urlGitHub = this.injectionModel.getMediatorUtils().propertiesUtil().getProperty("github.url");
220         this.injectionModel.appendAnalysisReport(
221             String.join(
222                 StringUtils.EMPTY,
223                 "# Date: ", LocalDate.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_LOCAL_DATE),
224                 "<br>&#10;# Tested on: ", SystemUtils.OS_NAME, " (", SystemUtils.OS_VERSION, ")",
225                 "<br>&#10;# Tool: ", StringUtil.APP_NAME, " v", this.injectionModel.getPropertiesUtil().getVersionJsql(),
226                 " (<a href=", urlGitHub, ">", urlGitHub, "</a>)",
227                 "<br>&#10;# Database: ", engineFound.toString(),
228                 "<br>&#10;<br>&#10;## Vulnerability summary</span>"
229             ),
230             true
231         );
232 
233         this.injectionModel.sendToViews(new Seal.ActivateEngine(engineFound));
234         return engineFound;
235     }
236     
237     
238     // Getter and setter
239 
240     public Engine getEngine() {
241         return this.engine;
242     }
243 
244     public void setEngine(Engine engine) {
245         this.engine = engine;
246     }
247 
248     public Engine getEngineByUser() {
249         return this.engineByUser;
250     }
251 
252     public void setEngineByUser(Engine engineByUser) {
253         this.engineByUser = engineByUser;
254     }
255 
256     public List<Engine> getEngines() {
257         return this.engines;
258     }
259 
260     public List<Engine> getEnginesForFingerprint() {
261         return this.enginesForFingerprint;
262     }
263 
264 
265     // engines
266 
267     public Engine getAuto() {
268         return this.auto;
269     }
270 
271     public Engine getAccess() {
272         return this.access;
273     }
274 
275     public Engine getAltibase() {
276         return this.altibase;
277     }
278 
279     public Engine getClickhouse() {
280         return this.clickhouse;
281     }
282 
283     public Engine getCockroachdb() {
284         return this.cockroachdb;
285     }
286 
287     public Engine getCubrid() {
288         return this.cubrid;
289     }
290 
291     public Engine getDameng() {
292         return this.dameng;
293     }
294 
295     public Engine getDb2() {
296         return this.db2;
297     }
298 
299     public Engine getDerby() {
300         return this.derby;
301     }
302 
303     public Engine getDuckdb() {
304         return this.duckdb;
305     }
306 
307     public Engine getExasol() {
308         return this.exasol;
309     }
310 
311     public Engine getFirebird() {
312         return this.firebird;
313     }
314 
315     public Engine getGreenplum() {
316         return this.greenplum;
317     }
318 
319     public Engine getH2() {
320         return this.h2;
321     }
322 
323     public Engine getHana() {
324         return this.hana;
325     }
326 
327     public Engine getHsqldb() {
328         return this.hsqldb;
329     }
330 
331     public Engine getInformix() {
332         return this.informix;
333     }
334 
335     public Engine getMariadb() {
336         return this.mariadb;
337     }
338 
339     public Engine getMckoi() {
340         return this.mckoi;
341     }
342 
343     public Engine getMimer() {
344         return this.mimer;
345     }
346 
347     public Engine getMonetdb() {
348         return this.monetdb;
349     }
350 
351     public Engine getMysql() {
352         return this.mysql;
353     }
354 
355     public Engine getNeo4j() {
356         return this.neo4j;
357     }
358 
359     public Engine getOracle() {
360         return this.oracle;
361     }
362 
363     public Engine getPercona() {
364         return this.percona;
365     }
366 
367     public Engine getPostgres() {
368         return this.postgres;
369     }
370 
371     public Engine getPresto() {
372         return this.presto;
373     }
374 
375     public Engine getSpanner() {
376         return this.spanner;
377     }
378 
379     public Engine getSqlite() {
380         return this.sqlite;
381     }
382 
383     public Engine getSqlserver() {
384         return this.sqlserver;
385     }
386 
387     public Engine getSybase() {
388         return this.sybase;
389     }
390 
391     public Engine getTidb() {
392         return this.tidb;
393     }
394 
395     public Engine getVertica() {
396         return this.vertica;
397     }
398 
399     public Engine getVirtuoso() {
400         return this.virtuoso;
401     }
402 }