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