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