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 |
1
1. call : negated conditional → NO_COVERAGE |
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 |
1
1. call : negated conditional → NO_COVERAGE |
} 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 |
1
1. call : negated conditional → NO_COVERAGE |
} 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 |
1
1. call : negated conditional → NO_COVERAGE |
} 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 |
1
1. call : negated conditional → NO_COVERAGE |
} 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 |
1
1. call : negated conditional → NO_COVERAGE |
} 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 |
1
1. call : negated conditional → NO_COVERAGE |
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 |
1
1. call : negated conditional → NO_COVERAGE |
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 |
1
1. call : negated conditional → NO_COVERAGE |
if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) { |
212 | resultToParse = e.getSlidingWindowAllRows(); | |
213 |
1
1. call : negated conditional → NO_COVERAGE |
} 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 |
1
1. call : replaced return value with null for com/jsql/model/accessible/CallableFile::call → NO_COVERAGE |
return this; |
221 | } | |
222 | ||
223 | ||
224 | // Getters | |
225 | | |
226 | public String getPathFile() { | |
227 |
1
1. getPathFile : replaced return value with "" for com/jsql/model/accessible/CallableFile::getPathFile → NO_COVERAGE |
return this.pathFile; |
228 | } | |
229 | ||
230 | public String getSourceFile() { | |
231 |
1
1. getSourceFile : replaced return value with "" for com/jsql/model/accessible/CallableFile::getSourceFile → NO_COVERAGE |
return this.sourceFile; |
232 | } | |
233 | ||
234 | public SuspendableGetRows getSuspendableReadFile() { | |
235 |
1
1. getSuspendableReadFile : replaced return value with null for com/jsql/model/accessible/CallableFile::getSuspendableReadFile → NO_COVERAGE |
return this.suspendableReadFile; |
236 | } | |
237 | } | |
Mutations | ||
68 |
1.1 |
|
81 |
1.1 |
|
93 |
1.1 |
|
106 |
1.1 |
|
125 |
1.1 |
|
145 |
1.1 |
|
164 |
1.1 |
|
170 |
1.1 |
|
211 |
1.1 |
|
213 |
1.1 |
|
220 |
1.1 |
|
227 |
1.1 |
|
231 |
1.1 |
|
235 |
1.1 |