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