View Javadoc
1   package com.jsql.model.accessible;
2   
3   import com.jsql.model.InjectionModel;
4   import com.jsql.model.bean.database.MockElement;
5   import com.jsql.model.exception.InjectionFailureException;
6   import com.jsql.model.exception.LoopDetectedSlidingException;
7   import com.jsql.model.exception.StoppedByUserSlidingException;
8   import com.jsql.model.injection.vendor.model.VendorYaml;
9   import com.jsql.model.suspendable.SuspendableGetRows;
10  import com.jsql.util.LogLevelUtil;
11  import org.apache.commons.codec.binary.Hex;
12  import org.apache.commons.lang3.RandomStringUtils;
13  import org.apache.commons.lang3.StringUtils;
14  import org.apache.logging.log4j.LogManager;
15  import org.apache.logging.log4j.Logger;
16  
17  import java.nio.charset.StandardCharsets;
18  import java.util.concurrent.Callable;
19  
20  /**
21   * Thread unit to read source of a file by SQL injection.
22   * User can interrupt the process and get a partial result of the file content.
23   */
24  public class CallableFile implements Callable<CallableFile> {
25      
26      /**
27       * Log4j logger sent to view.
28       */
29      private static final Logger LOGGER = LogManager.getRootLogger();
30      private static final String REQUIRE_STACK = "Read file requirement : stack query";
31  
32      /**
33       * Path to the file to read.
34       */
35      private final String pathFile;
36  
37      /**
38       * Source of file.
39       */
40      private String sourceFile = StringUtils.EMPTY;
41      
42      /**
43       * Suspendable task that reads lines of the file by injection.
44       */
45      private final SuspendableGetRows suspendableReadFile;
46  
47      private final InjectionModel injectionModel;
48      
49      /**
50       * Create Callable to read a file.
51       */
52      public CallableFile(String pathFile, InjectionModel injectionModel) {
53          this.pathFile = pathFile;
54          this.injectionModel= injectionModel;
55          this.suspendableReadFile = new SuspendableGetRows(injectionModel);
56      }
57      
58      /**
59       * Read a file on the server using SQL injection.
60       * Get partial result if user interrupts the process.
61       */
62      @Override
63      public CallableFile call() throws Exception {
64          var sourcePage = new String[]{ StringUtils.EMPTY };
65  
66          String resultToParse = StringUtils.EMPTY;
67          try {
68              if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getMysql()) {
69                  LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Read file requirement : user FILE privilege");
70                  resultToParse = this.suspendableReadFile.run(
71                      this.injectionModel.getResourceAccess().getExploitMysql().getModelYaml().getFile().getRead().replace(
72                          VendorYaml.FILEPATH_HEX,
73                          Hex.encodeHexString(this.pathFile.getBytes(StandardCharsets.UTF_8))
74                      ),
75                      sourcePage,
76                      false,
77                      1,
78                      MockElement.MOCK,
79                      ResourceAccess.FILE_READ
80                  );
81              } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getH2()) {
82                  resultToParse = this.suspendableReadFile.run(
83                      String.format(
84                          this.injectionModel.getResourceAccess().getExploitH2().getModelYaml().getFile().getReadFromPath(),
85                          this.pathFile
86                      ),
87                      sourcePage,
88                      false,
89                      1,
90                      MockElement.MOCK,
91                      ResourceAccess.FILE_READ
92                  );
93              } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getSqlite()) {
94                  LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Read file requirement : extension fileio loaded");
95                  resultToParse = this.suspendableReadFile.run(
96                      String.format(
97                          this.injectionModel.getResourceAccess().getExploitSqlite().getModelYaml().getExtension().getFileioRead(),
98                          this.pathFile
99                      ),
100                     sourcePage,
101                     false,
102                     1,
103                     MockElement.MOCK,
104                     ResourceAccess.FILE_READ
105                 );
106             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getDerby()) {
107                 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CallableFile.REQUIRE_STACK);
108                 var nameTable = RandomStringUtils.secure().nextAlphabetic(8);
109                 this.injectionModel.injectWithoutIndex(String.format(
110                     this.injectionModel.getResourceAccess().getExploitDerby().getModelYaml().getFile().getCreateTable(),
111                     nameTable,
112                     nameTable, this.pathFile
113                 ), ResourceAccess.TBL_FILL);
114                 resultToParse = this.suspendableReadFile.run(
115                     String.format(
116                         this.injectionModel.getResourceAccess().getExploitDerby().getModelYaml().getFile().getRead(),
117                         nameTable
118                     ),
119                     sourcePage,
120                     true,
121                     0,
122                     MockElement.MOCK,
123                     ResourceAccess.FILE_READ
124                 );
125             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getHsqldb()) {
126                 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CallableFile.REQUIRE_STACK);
127                 var nameTable = RandomStringUtils.secure().nextAlphabetic(8);
128                 this.injectionModel.injectWithoutIndex(String.format(
129                     this.injectionModel.getResourceAccess().getExploitHsqldb().getModelYaml().getFile().getRead().getCreateTable(),
130                     nameTable,
131                     nameTable, this.pathFile
132                 ), ResourceAccess.TBL_FILL);
133                 resultToParse = this.suspendableReadFile.run(
134                     String.format(
135                         this.injectionModel.getResourceAccess().getExploitHsqldb().getModelYaml().getFile().getRead().getResult(),
136                         VendorYaml.TRAIL_SQL,
137                         nameTable
138                     ),
139                     sourcePage,
140                     false,
141                     1,
142                     MockElement.MOCK,
143                     ResourceAccess.TBL_READ
144                 );
145             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getPostgres()) {
146                 try {
147                     resultToParse = this.suspendableReadFile.run(
148                         String.format(
149                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getFromDataFolder(),
150                             this.pathFile
151                         ),
152                         sourcePage,
153                         false,
154                         1,
155                         MockElement.MOCK,
156                         ResourceAccess.FILE_READ
157                     );
158                 } catch (InjectionFailureException e) {
159                     LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Read data folder failure, trying with large object");
160                     var loid = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
161                         this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getLargeObject().getFromPath(),
162                         this.pathFile
163                     ), ResourceAccess.ADD_LOID);
164                     if (StringUtils.isNotEmpty(loid)) {
165                         resultToParse = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
166                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getLargeObject().getToText(),
167                             loid
168                         ), ResourceAccess.READ_LOID);
169                     }
170                     if (StringUtils.isEmpty(resultToParse)) {
171                         LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Read large object failure, trying with stack read");
172                         var nameLibraryRandom = "tmp_" + RandomStringUtils.secure().nextAlphabetic(8);  // no dash in table name
173                         this.injectionModel.injectWithoutIndex(String.format(
174                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getWrite().getTempTable().getDrop(),
175                             nameLibraryRandom
176                         ), ResourceAccess.TBL_DROP);
177                         this.injectionModel.injectWithoutIndex(String.format(
178                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getWrite().getTempTable().getAdd(),
179                             nameLibraryRandom
180                         ), ResourceAccess.TBL_CREATE);
181                         this.injectionModel.injectWithoutIndex(String.format(
182                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getWrite().getTempTable().getFill(),
183                             nameLibraryRandom,
184                             this.pathFile
185                         ), ResourceAccess.TBL_FILL);
186                         resultToParse = this.suspendableReadFile.run(
187                             String.format(
188                                 this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getFromTempTable(),
189                                 nameLibraryRandom
190                             ),
191                             sourcePage,
192                             false,
193                             1,
194                             MockElement.MOCK,
195                             ResourceAccess.TBL_READ
196                         );
197                     }
198                 }
199             } else {
200                 LOGGER.log(
201                     LogLevelUtil.CONSOLE_DEFAULT,
202                     "Read file not implemented for [{}], share a working example to GitHub to speed up release",
203                     this.injectionModel.getMediatorVendor().getVendor()
204                 );
205             }
206         } catch (InjectionFailureException e) {
207             // Usually thrown if File does not exist
208             LOGGER.log(LogLevelUtil.IGNORE, e);
209         } catch (LoopDetectedSlidingException | StoppedByUserSlidingException e) {
210             // Get partial source
211             if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
212                 resultToParse = e.getSlidingWindowAllRows();
213             } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
214                 resultToParse = e.getSlidingWindowCurrentRows();
215             }
216             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e);
217         }
218         
219         this.sourceFile = resultToParse;
220         return this;
221     }
222 
223 
224     // Getters
225     
226     public String getPathFile() {
227         return this.pathFile;
228     }
229 
230     public String getSourceFile() {
231         return this.sourceFile;
232     }
233 
234     public SuspendableGetRows getSuspendableReadFile() {
235         return this.suspendableReadFile;
236     }
237 }