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