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.engine = 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, "[Step 1] 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                         "Found [{}] using raw fingerprinting",
189                         () -> engineTest
190                     );
191                     break;
192                 }
193             }
194             if (engineFound == null) {
195                 engineFound = this.injectionModel.getMediatorEngine().getMysql();
196                 LOGGER.log(
197                     LogLevelUtil.CONSOLE_INFORM,
198                     MediatorEngine.LOG_ENGINE,
199                     () -> I18nUtil.valueByKey("LOG_DATABASE_TYPE_NOT_FOUND"),
200                     () -> this.injectionModel.getMediatorEngine().getMysql()
201                 );
202             }
203         }
204 
205         var urlGitHub = this.injectionModel.getMediatorUtils().propertiesUtil().getProperty("github.url");
206         this.injectionModel.appendAnalysisReport(
207             String.join(
208                 StringUtils.EMPTY,
209                 "# Date: ", LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE),
210                 "<br>&#10;# Tested on: ", SystemUtils.OS_NAME, " (", SystemUtils.OS_VERSION, ")",
211                 "<br>&#10;# Tool: ", StringUtil.APP_NAME, " v", this.injectionModel.getPropertiesUtil().getVersionJsql(),
212                 " (<a href=", urlGitHub, ">", urlGitHub, "</a>)",
213                 "<br>&#10;# Database: ", engineFound.toString(),
214                 "<br>&#10;<br>&#10;## Vulnerability summary</span>"
215             ),
216             true
217         );
218 
219         this.injectionModel.sendToViews(new Seal.ActivateEngine(engineFound));
220         return engineFound;
221     }
222     
223     
224     // Getter and setter
225 
226     public Engine getEngine() {
227         return this.engine;
228     }
229 
230     public void setEngine(Engine engine) {
231         this.engine = engine;
232     }
233 
234     public Engine getEngineByUser() {
235         return this.engineByUser;
236     }
237 
238     public void setEngineByUser(Engine engineByUser) {
239         this.engineByUser = engineByUser;
240     }
241 
242     public List<Engine> getEngines() {
243         return this.engines;
244     }
245 
246     public List<Engine> getEnginesForFingerprint() {
247         return this.enginesForFingerprint;
248     }
249 
250 
251     // engines
252 
253     public Engine getAuto() {
254         return this.auto;
255     }
256 
257     public Engine getAccess() {
258         return this.access;
259     }
260 
261     public Engine getAltibase() {
262         return this.altibase;
263     }
264 
265     public Engine getClickhouse() {
266         return this.clickhouse;
267     }
268 
269     public Engine getCubrid() {
270         return this.cubrid;
271     }
272 
273     public Engine getDb2() {
274         return this.db2;
275     }
276 
277     public Engine getDerby() {
278         return this.derby;
279     }
280 
281     public Engine getExasol() {
282         return this.exasol;
283     }
284 
285     public Engine getFirebird() {
286         return this.firebird;
287     }
288 
289     public Engine getH2() {
290         return this.h2;
291     }
292 
293     public Engine getHana() {
294         return this.hana;
295     }
296 
297     public Engine getHsqldb() {
298         return this.hsqldb;
299     }
300 
301     public Engine getInformix() {
302         return this.informix;
303     }
304 
305     public Engine getMckoi() {
306         return this.mckoi;
307     }
308 
309     public Engine getMimer() {
310         return this.mimer;
311     }
312 
313     public Engine getMonetdb() {
314         return this.monetdb;
315     }
316 
317     public Engine getMysql() {
318         return this.mysql;
319     }
320 
321     public Engine getNeo4j() {
322         return this.neo4j;
323     }
324 
325     public Engine getOracle() {
326         return this.oracle;
327     }
328 
329     public Engine getPostgres() {
330         return this.postgres;
331     }
332 
333     public Engine getPresto() {
334         return this.presto;
335     }
336 
337     public Engine getSqlite() {
338         return this.sqlite;
339     }
340 
341     public Engine getSqlserver() {
342         return this.sqlserver;
343     }
344 
345     public Engine getSybase() {
346         return this.sybase;
347     }
348 
349     public Engine getVertica() {
350         return this.vertica;
351     }
352 
353     public Engine getVirtuoso() {
354         return this.virtuoso;
355     }
356 }