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                  LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CallableFile.REQUIRE_STACK);
83                  var nameTable = RandomStringUtils.secure().nextAlphabetic(8);
84                  this.injectionModel.injectWithoutIndex(String.format(
85                      this.injectionModel.getResourceAccess().getExploitH2().getModelYaml().getFile().getCreateTable(),
86                      nameTable,
87                      this.pathFile
88                  ), ResourceAccess.TBL_FILL);
89                  resultToParse = this.suspendableReadFile.run(
90                      String.format(
91                          this.injectionModel.getResourceAccess().getExploitH2().getModelYaml().getFile().getRead(),
92                          VendorYaml.TRAIL_SQL,
93                          nameTable
94                      ),
95                      sourcePage,
96                      false,
97                      1,
98                      MockElement.MOCK,
99                      ResourceAccess.FILE_READ
100                 );
101             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getSqlite()) {
102                 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Read file requirement : extension fileio loaded");
103                 resultToParse = this.suspendableReadFile.run(
104                     String.format(
105                         this.injectionModel.getResourceAccess().getExploitSqlite().getModelYaml().getExtension().getFileioRead(),
106                         this.pathFile
107                     ),
108                     sourcePage,
109                     false,
110                     1,
111                     MockElement.MOCK,
112                     ResourceAccess.FILE_READ
113                 );
114             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getDerby()) {
115                 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CallableFile.REQUIRE_STACK);
116                 var nameTable = RandomStringUtils.secure().nextAlphabetic(8);
117                 this.injectionModel.injectWithoutIndex(String.format(
118                     this.injectionModel.getResourceAccess().getExploitDerby().getModelYaml().getFile().getCreateTable(),
119                     nameTable,
120                     nameTable, this.pathFile
121                 ), ResourceAccess.TBL_FILL);
122                 resultToParse = this.suspendableReadFile.run(
123                     String.format(
124                         this.injectionModel.getResourceAccess().getExploitDerby().getModelYaml().getFile().getRead(),
125                         nameTable
126                     ),
127                     sourcePage,
128                     true,
129                     0,
130                     MockElement.MOCK,
131                     ResourceAccess.FILE_READ
132                 );
133             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getHsqldb()) {
134                 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, CallableFile.REQUIRE_STACK);
135                 var nameTable = RandomStringUtils.secure().nextAlphabetic(8);
136                 this.injectionModel.injectWithoutIndex(String.format(
137                     this.injectionModel.getResourceAccess().getExploitHsqldb().getModelYaml().getFile().getRead().getCreateTable(),
138                     nameTable,
139                     nameTable, this.pathFile
140                 ), ResourceAccess.TBL_FILL);
141                 resultToParse = this.suspendableReadFile.run(
142                     String.format(
143                         this.injectionModel.getResourceAccess().getExploitHsqldb().getModelYaml().getFile().getRead().getResult(),
144                         VendorYaml.TRAIL_SQL,
145                         nameTable
146                     ),
147                     sourcePage,
148                     false,
149                     1,
150                     MockElement.MOCK,
151                     ResourceAccess.TBL_READ
152                 );
153             } else if (this.injectionModel.getMediatorVendor().getVendor() == this.injectionModel.getMediatorVendor().getPostgres()) {
154                 try {
155                     resultToParse = this.suspendableReadFile.run(
156                         String.format(
157                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getFromDataFolder(),
158                             this.pathFile
159                         ),
160                         sourcePage,
161                         false,
162                         1,
163                         MockElement.MOCK,
164                         ResourceAccess.FILE_READ
165                     );
166                 } catch (InjectionFailureException e) {
167                     LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Read data folder failure, trying with large object");
168                     var loid = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
169                         this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getLargeObject().getFromPath(),
170                         this.pathFile
171                     ), ResourceAccess.ADD_LOID);
172                     if (StringUtils.isNotEmpty(loid)) {
173                         resultToParse = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
174                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getLargeObject().getToText(),
175                             loid
176                         ), ResourceAccess.READ_LOID);
177                     }
178                     if (StringUtils.isEmpty(resultToParse)) {
179                         LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Read large object failure, trying with stack read");
180                         var nameLibraryRandom = "tmp_" + RandomStringUtils.secure().nextAlphabetic(8);  // no dash in table name
181                         this.injectionModel.injectWithoutIndex(String.format(
182                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getWrite().getTempTable().getDrop(),
183                             nameLibraryRandom
184                         ), ResourceAccess.TBL_DROP);
185                         this.injectionModel.injectWithoutIndex(String.format(
186                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getWrite().getTempTable().getAdd(),
187                             nameLibraryRandom
188                         ), ResourceAccess.TBL_CREATE);
189                         this.injectionModel.injectWithoutIndex(String.format(
190                             this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getWrite().getTempTable().getFill(),
191                             nameLibraryRandom,
192                             this.pathFile
193                         ), ResourceAccess.TBL_FILL);
194                         resultToParse = this.suspendableReadFile.run(
195                             String.format(
196                                 this.injectionModel.getResourceAccess().getExploitPostgres().getModelYaml().getFile().getRead().getFromTempTable(),
197                                 nameLibraryRandom
198                             ),
199                             sourcePage,
200                             false,
201                             1,
202                             MockElement.MOCK,
203                             ResourceAccess.TBL_READ
204                         );
205                     }
206                 }
207             } else {
208                 LOGGER.log(
209                     LogLevelUtil.CONSOLE_DEFAULT,
210                     "Read file not implemented for [{}], share a working example to GitHub to speed up release",
211                     this.injectionModel.getMediatorVendor().getVendor()
212                 );
213             }
214         } catch (InjectionFailureException e) {
215             // Usually thrown if File does not exist
216             LOGGER.log(LogLevelUtil.IGNORE, e);
217         } catch (LoopDetectedSlidingException | StoppedByUserSlidingException e) {
218             // Get partial source
219             if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
220                 resultToParse = e.getSlidingWindowAllRows();
221             } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
222                 resultToParse = e.getSlidingWindowCurrentRows();
223             }
224             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e);
225         }
226         
227         this.sourceFile = resultToParse;
228         return this;
229     }
230 
231 
232     // Getters
233     
234     public String getPathFile() {
235         return this.pathFile;
236     }
237 
238     public String getSourceFile() {
239         return this.sourceFile;
240     }
241 
242     public SuspendableGetRows getSuspendableReadFile() {
243         return this.suspendableReadFile;
244     }
245 }