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