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