View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.view.swing.manager;
12  
13  import com.formdev.flatlaf.util.SystemFileChooser;
14  import com.jsql.model.accessible.ExploitMode;
15  import com.jsql.model.exception.JSqlException;
16  import com.jsql.model.injection.engine.model.Engine;
17  import com.jsql.util.I18nUtil;
18  import com.jsql.util.LogLevelUtil;
19  import com.jsql.view.swing.manager.util.*;
20  import com.jsql.view.swing.text.JPasswordFieldPlaceholder;
21  import com.jsql.view.swing.text.JPopupTextField;
22  import com.jsql.view.swing.text.JTextFieldPlaceholder;
23  import com.jsql.view.swing.text.JToolTipI18n;
24  import com.jsql.view.swing.util.I18nViewUtil;
25  import com.jsql.view.swing.util.MediatorHelper;
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.logging.log4j.LogManager;
28  import org.apache.logging.log4j.Logger;
29  
30  import javax.swing.*;
31  import java.awt.*;
32  import java.awt.event.ActionEvent;
33  import java.awt.event.ActionListener;
34  import java.awt.event.ItemEvent;
35  import java.awt.event.ItemListener;
36  import java.io.File;
37  import java.net.URI;
38  import java.net.URISyntaxException;
39  import java.util.Arrays;
40  import java.util.Objects;
41  import java.util.concurrent.atomic.AtomicReference;
42  
43  /**
44   * Manager for uploading PHP SQL shell to the host and send queries.
45   */
46  public class ManagerExploit extends AbstractManagerList {
47  
48      private static final Logger LOGGER = LogManager.getRootLogger();
49      public static final String SHELL_URL_TOOLTIP = "SHELL_URL_TOOLTIP";
50  
51      private final AtomicReference<JTextField> username = new AtomicReference<>();
52      private final AtomicReference<JTextField> password = new AtomicReference<>();
53      private final AtomicReference<JTextField> netshare = new AtomicReference<>();
54      protected final JTextField textfieldUrlShell;
55  
56      public static final String EXPLOIT_UDF = "EXPLOIT_UDF";
57      public static final String EXPLOIT_WEB = "EXPLOIT_WEB";
58      public static final String EXPLOIT_SQL = "EXPLOIT_SQL";
59      public static final String EXPLOIT_UPLOAD = "EXPLOIT_UPLOAD";
60  
61      private final JComboBox<Object> comboBoxExploitTypes = new JComboBox<>(new Object[]{
62          new ModelItemType(ManagerExploit.EXPLOIT_UDF, "EXPLOIT_UDF_TOOLTIP"),
63          ComboBoxMethodRenderer.SEPARATOR,
64          new ModelItemType(ManagerExploit.EXPLOIT_WEB, "EXPLOIT_WEB_TOOLTIP"),
65          new ModelItemType(ManagerExploit.EXPLOIT_SQL, "EXPLOIT_SQL_TOOLTIP"),
66          new ModelItemType(ManagerExploit.EXPLOIT_UPLOAD, "EXPLOIT_UPLOAD_TOOLTIP"),
67      });
68  
69      private final JComboBox<Object> comboBoxExploitModes = new JComboBox<>(new Object[]{
70          ExploitMode.AUTO,
71          ComboBoxMethodRenderer.SEPARATOR,
72          ExploitMode.QUERY_BODY,
73          ExploitMode.TEMP_TABLE,
74          ComboBoxMethodRenderer.SEPARATOR,
75          ExploitMode.NETSHARE
76      });
77  
78      public ManagerExploit() {
79          super("swing/list/payload.txt");
80  
81          var tooltipShellUrl = new AtomicReference<>(new JToolTipI18n(I18nUtil.valueByKey(ManagerExploit.SHELL_URL_TOOLTIP)));
82          var placeholderResult = new JTextFieldPlaceholder(I18nUtil.valueByKey("SHELL_URL_LABEL")) {
83              @Override
84              public JToolTip createToolTip() {
85                  return tooltipShellUrl.get();
86              }
87          };
88          this.textfieldUrlShell = new JPopupTextField(placeholderResult).getProxy();
89          I18nViewUtil.addComponentForKey("SHELL_URL_LABEL", this.textfieldUrlShell);
90          I18nViewUtil.addComponentForKey(ManagerExploit.SHELL_URL_TOOLTIP, tooltipShellUrl.get());
91          this.textfieldUrlShell.setToolTipText(I18nUtil.valueByKey(ManagerExploit.SHELL_URL_TOOLTIP));
92  
93          this.buildRunButton("SHELL_RUN_BUTTON_LABEL", "SHELL_RUN_BUTTON_TOOLTIP");
94          this.run.setEnabled(false);
95          this.buildPrivilege();
96  
97          var southPanel = new JPanel();
98          southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));
99          southPanel.add(this.textfieldUrlShell);
100         southPanel.add(this.lastLine);
101         this.add(southPanel, BorderLayout.SOUTH);
102 
103         var userPassPanel = new JPanel();
104         var groupLayout = new GroupLayout(userPassPanel);
105         userPassPanel.setLayout(groupLayout);
106 
107         this.run.addActionListener(new ActionExploit(this.comboBoxExploitTypes));
108 
109         Arrays.asList(
110             new ModelExploit(this.netshare, "EXPLOIT_NETSHARE_LABEL", "EXPLOIT_NETSHARE_TOOLTIP"),
111             new ModelExploit(this.username, "SQL_SHELL_USERNAME_LABEL", "SQL_SHELL_USERNAME_TOOLTIP"),
112             new ModelExploit(this.password, "SQL_SHELL_PASSWORD_LABEL", "SQL_SHELL_PASSWORD_TOOLTIP", true)
113         ).forEach(model -> {
114             var tooltip = new AtomicReference<>(new JToolTipI18n(I18nUtil.valueByKey(model.tooltipI18n)));
115             if (model.isPassword) {
116                 model.textfield.set(new JPopupTextField(new JPasswordFieldPlaceholder(I18nUtil.valueByKey(model.labelI18n)) {
117                     @Override
118                     public JToolTip createToolTip() {
119                         return tooltip.get();
120                     }
121                 }).getProxy());
122             } else {
123                 model.textfield.set(new JPopupTextField(new JTextFieldPlaceholder(I18nUtil.valueByKey(model.labelI18n)) {
124                     @Override
125                     public JToolTip createToolTip() {
126                         return tooltip.get();
127                     }
128                 }).getProxy());
129             }
130             I18nViewUtil.addComponentForKey(model.labelI18n, model.textfield.get());
131             I18nViewUtil.addComponentForKey(model.tooltipI18n, tooltip.get());
132             model.textfield.get().setToolTipText(I18nUtil.valueByKey(model.tooltipI18n));
133         });
134 
135         Arrays.asList(this.username.get(), this.password.get(), this.scrollListPaths, this.textfieldUrlShell, this.netshare.get())
136         .forEach(component -> component.setVisible(false));
137 
138         this.comboBoxExploitTypes.setRenderer(new ComboBoxTypeRenderer());
139         this.comboBoxExploitTypes.addActionListener(new SeparatorListener(this.comboBoxExploitTypes));
140         this.comboBoxExploitTypes.addItemListener(this.getTypesItemListener());
141         this.comboBoxExploitModes.addItemListener(this.getModesItemListener());
142 
143         this.comboBoxExploitModes.setRenderer(new ComboBoxMethodRenderer());
144         this.comboBoxExploitModes.addActionListener(new SeparatorListener(this.comboBoxExploitModes));
145         var labelUsing = new JLabel("via");
146         labelUsing.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
147         groupLayout.setHorizontalGroup(
148             groupLayout
149             .createParallelGroup()
150             .addGroup(
151                 groupLayout
152                 .createSequentialGroup()
153                 .addComponent(this.comboBoxExploitTypes)
154                 .addComponent(labelUsing, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
155                 .addComponent(this.comboBoxExploitModes, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE)
156             )
157             .addGroup(
158                 groupLayout.createParallelGroup()
159                 .addComponent(this.netshare.get())
160                 .addComponent(this.username.get())
161                 .addComponent(this.password.get())
162             )
163         );
164 
165         groupLayout.setVerticalGroup(
166             groupLayout
167             .createSequentialGroup()
168             .addGroup(
169                 groupLayout
170                 .createParallelGroup(GroupLayout.Alignment.BASELINE)
171                 .addComponent(this.comboBoxExploitTypes)
172                 .addComponent(labelUsing)
173                 .addComponent(this.comboBoxExploitModes)
174             )
175             .addGroup(
176                 groupLayout
177                 .createParallelGroup()
178                 .addComponent(this.netshare.get())
179             )
180             .addGroup(
181                 groupLayout
182                 .createParallelGroup()
183                 .addComponent(this.username.get())
184             )
185             .addGroup(
186                 groupLayout
187                 .createParallelGroup()
188                 .addComponent(this.password.get())
189             )
190         );
191         
192         this.add(userPassPanel, BorderLayout.NORTH);
193     }
194 
195     private ItemListener getModesItemListener() {
196         return itemEvent -> {
197             if (itemEvent.getStateChange() == ItemEvent.SELECTED && itemEvent.getItem() instanceof ExploitMode selectedItem) {
198                 this.netshare.get().setVisible(false);
199                 if (selectedItem.equals(ExploitMode.NETSHARE)) {
200                     this.netshare.get().setVisible(true);
201                 }
202                 this.updateUI();  // required to adapt panel
203             }
204         };
205     }
206 
207     private ItemListener getTypesItemListener() {
208         return itemEvent -> {
209             if (itemEvent.getStateChange() != ItemEvent.SELECTED || itemEvent.getItem() == ComboBoxMethodRenderer.SEPARATOR) {
210                 return;
211             }
212             Arrays.asList(
213                 this.username.get(), this.password.get(), this.scrollListPaths, this.textfieldUrlShell
214             ).forEach(component -> component.setVisible(false));
215             ModelItemType selectedItem = (ModelItemType) itemEvent.getItem();
216             if (!ManagerExploit.EXPLOIT_UDF.equals(selectedItem.keyLabel())) {
217                 this.scrollListPaths.setVisible(true);
218                 this.textfieldUrlShell.setVisible(true);
219                 if (ManagerExploit.EXPLOIT_SQL.equals(selectedItem.keyLabel())) {
220                     this.username.get().setVisible(true);
221                     this.password.get().setVisible(true);
222                 }
223             }
224             this.updateUI();  // required to adapt panel
225         };
226     }
227 
228     protected class ActionExploit implements ActionListener {
229         private final JComboBox<Object> comboBoxExploitTypes;
230 
231         public ActionExploit(JComboBox<Object> comboBoxExploitTypes) {
232             this.comboBoxExploitTypes = comboBoxExploitTypes;
233         }
234 
235         @Override
236         public void actionPerformed(ActionEvent evt) {
237             var modelSelectItem = (ModelItemType) this.comboBoxExploitTypes.getSelectedItem();
238             var labelSelectItem = Objects.requireNonNull(modelSelectItem).keyLabel();
239             if (!ManagerExploit.isValid(labelSelectItem)) {
240                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Currently unsupported for [{}], contribute and share exploit method on GitHub to improve the app", MediatorHelper.model().getMediatorEngine().getEngine());
241                 return;
242             }
243             if (ManagerExploit.EXPLOIT_UDF.equals(labelSelectItem)) {
244                 new SwingWorker<>() {
245                     @Override
246                     protected Object doInBackground() { Thread.currentThread().setName("SwingWorkerExploit");
247                         ActionExploit.this.start(null, null, null);
248                         return null;
249                     }
250                 }.execute();
251                 return;
252             }
253             if (
254                 ManagerExploit.EXPLOIT_SQL.equals(labelSelectItem)
255                 && (ManagerExploit.this.password.get().getText().isEmpty() || ManagerExploit.this.username.get().getText().isEmpty())
256             ) {
257                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Missing credentials (tips: search and read file containing hardcoded credentials)");
258                 return;
259             }
260             if (ManagerExploit.this.listPaths.getSelectedValuesList().isEmpty()) {
261                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Select at least one directory in the list");
262                 return;
263             }
264 
265             String urlShell = this.validateAndNormalizeUrlShell();
266             if (urlShell == null) {
267                 return;
268             }
269             AtomicReference<File> fileToUpload = new AtomicReference<>();
270             if (this.validateFileSelection(labelSelectItem, fileToUpload)) {
271                 return;
272             }
273 
274             new SwingWorker<>() {
275                 @Override
276                 protected Object doInBackground() { Thread.currentThread().setName("SwingWorkerExploitNonUdf");
277                     ManagerExploit.this.horizontalGlue.setVisible(false);
278                     ManagerExploit.this.progressBar.setVisible(true);
279                     ManagerExploit.this.listPaths.getSelectedValuesList().forEach(pathExploit -> {
280                         LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, String.format("Checking path [%s]...", pathExploit));
281                         ActionExploit.this.start(pathExploit.toString(), urlShell, fileToUpload.get());
282                     });
283                     ManagerExploit.this.endProcess();
284                     return null;
285                 }
286             }.execute();
287         }
288 
289         private boolean validateFileSelection(String labelSelectItem, AtomicReference<File> fileToUpload) {
290             if (ManagerExploit.EXPLOIT_UPLOAD.equals(labelSelectItem)) {
291                 fileToUpload.set(ManagerExploit.chooseFile());
292                 if (fileToUpload.get() == null) {
293                     LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Missing file, upload cancelled");
294                     return true;
295                 }
296             }
297             return false;
298         }
299 
300         private String validateAndNormalizeUrlShell() {
301             String urlShell = ManagerExploit.this.textfieldUrlShell.getText();
302             if (!urlShell.isEmpty() && !urlShell.matches("(?i)^https?://.*")) {
303                 if (!urlShell.matches("(?i)^\\w+://.*")) {
304                     LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Undefined shell URL protocol, forcing to [https://]");
305                     urlShell = "https://"+ urlShell;
306                 } else {
307                     LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Unknown URL protocol");
308                     return null;
309                 }
310             }
311             if (StringUtils.isNotEmpty(urlShell)) {
312                 try {
313                     new URI(urlShell);
314                 } catch (URISyntaxException e) {
315                     LOGGER.log(LogLevelUtil.CONSOLE_ERROR, String.format("Incorrect URL: %s", e.getMessage()));
316                     return null;
317                 }
318             }
319             return urlShell;
320         }
321 
322         private void start(String pathExploit, String urlShellFinal, File fileToUpload) {
323             try {
324                 ManagerExploit.this.createPayload(pathExploit, urlShellFinal, fileToUpload);
325             } catch (JSqlException | IllegalArgumentException e) {
326                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, String.format("Payload creation failure: %s", e.getMessage()));
327             }
328         }
329     }
330 
331     private static boolean isValid(String labelSelectItem) {
332         return
333             ManagerExploit.EXPLOIT_UDF.equals(labelSelectItem) && Arrays.asList(
334                 MediatorHelper.model().getMediatorEngine().getSqlite(),
335                 MediatorHelper.model().getMediatorEngine().getMysql(),
336                 MediatorHelper.model().getMediatorEngine().getPostgres(),
337                 MediatorHelper.model().getMediatorEngine().getH2()
338             ).contains(MediatorHelper.model().getMediatorEngine().getEngine())
339             || Arrays.asList(ManagerExploit.EXPLOIT_WEB, ManagerExploit.EXPLOIT_UPLOAD).contains(labelSelectItem) && Arrays.asList(
340                 MediatorHelper.model().getMediatorEngine().getDerby(),
341                 MediatorHelper.model().getMediatorEngine().getHsqldb(),
342                 MediatorHelper.model().getMediatorEngine().getH2(),
343                 MediatorHelper.model().getMediatorEngine().getSqlite(),
344                 MediatorHelper.model().getMediatorEngine().getMysql(),
345                 MediatorHelper.model().getMediatorEngine().getPostgres()
346             ).contains(MediatorHelper.model().getMediatorEngine().getEngine())
347             || Objects.equals(ManagerExploit.EXPLOIT_SQL, labelSelectItem) && Arrays.asList(
348                 MediatorHelper.model().getMediatorEngine().getMysql(),
349                 MediatorHelper.model().getMediatorEngine().getPostgres()
350             ).contains(MediatorHelper.model().getMediatorEngine().getEngine());
351     }
352 
353     private static File chooseFile() {
354         var filechooser = new SystemFileChooser(MediatorHelper.model().getMediatorUtils().preferencesUtil().getPathFile());
355         filechooser.setDialogTitle(I18nUtil.valueByKey("UPLOAD_DIALOG_TEXT"));
356         int returnVal = filechooser.showOpenDialog(MediatorHelper.frame());
357         if (returnVal == JFileChooser.APPROVE_OPTION) {
358             return filechooser.getSelectedFile();
359         }
360         return null;
361     }
362 
363     protected void createPayload(String pathExploit, String urlShell, File fileToUpload) throws JSqlException {
364         var exploitMethod = ExploitMode.forName(
365             Objects.requireNonNull(this.comboBoxExploitModes.getSelectedItem()).toString()
366         ).orElse(ExploitMode.AUTO);
367 
368         String pathExploitFixed = pathExploit;
369         if (pathExploitFixed != null && !pathExploitFixed.endsWith("/")) {
370             pathExploitFixed += "/";
371         }
372         String pathNetshare = this.netshare.get().getText();
373         if (exploitMethod == ExploitMode.NETSHARE && !pathNetshare.endsWith("\\")) {
374             pathNetshare += "\\";
375         }
376 
377         var modelItemType = (ModelItemType) Objects.requireNonNull(this.comboBoxExploitTypes.getSelectedItem());
378         var keyLabel = modelItemType.keyLabel();
379         var engine = MediatorHelper.model().getMediatorEngine().getEngine();
380 
381         if (ManagerExploit.EXPLOIT_UDF.equals(keyLabel)) {
382             ManagerExploit.handleUdfExploit(engine, pathNetshare, exploitMethod);
383         } else if (ManagerExploit.EXPLOIT_WEB.equals(keyLabel)) {
384             ManagerExploit.handleWebExploit(pathExploitFixed, urlShell, engine, pathNetshare, exploitMethod);
385         } else if (ManagerExploit.EXPLOIT_SQL.equals(keyLabel)) {
386             this.handleSqlExploit(pathExploitFixed, urlShell, engine, pathNetshare, exploitMethod);
387         } else if (ManagerExploit.EXPLOIT_UPLOAD.equals(keyLabel)) {
388             ManagerExploit.handleUploadExploit(pathExploitFixed, urlShell, fileToUpload, engine, pathNetshare, exploitMethod);
389         }
390     }
391 
392     private static void handleUdfExploit(Engine engine, String pathNetshare, ExploitMode exploitMethod) throws JSqlException {
393         if (engine == MediatorHelper.model().getMediatorEngine().getMysql()) {
394             MediatorHelper.model().getResourceAccess().getExploitMysql().createUdf(pathNetshare, exploitMethod);
395         } else if (engine == MediatorHelper.model().getMediatorEngine().getPostgres()) {
396             MediatorHelper.model().getResourceAccess().getExploitPostgres().createUdf(null);
397         } else if (engine == MediatorHelper.model().getMediatorEngine().getSqlite()) {
398             MediatorHelper.model().getResourceAccess().getExploitSqlite().createUdf();
399         } else if (engine == MediatorHelper.model().getMediatorEngine().getH2()) {
400             MediatorHelper.model().getResourceAccess().getExploitH2().createUdf();
401         }
402     }
403 
404     private static void handleWebExploit(String pathExploit, String urlShell, Engine engine, String pathNetshare, ExploitMode exploitMethod) throws JSqlException {
405         if (engine == MediatorHelper.model().getMediatorEngine().getMysql()) {
406             MediatorHelper.model().getResourceAccess().getExploitMysql().createWeb(pathExploit, urlShell, pathNetshare, exploitMethod);
407         } else if (engine == MediatorHelper.model().getMediatorEngine().getPostgres()) {
408             MediatorHelper.model().getResourceAccess().getExploitPostgres().createWeb(pathExploit, urlShell);
409         } else if (engine == MediatorHelper.model().getMediatorEngine().getSqlite()) {
410             MediatorHelper.model().getResourceAccess().getExploitSqlite().createWeb(pathExploit, urlShell);
411         } else if (engine == MediatorHelper.model().getMediatorEngine().getHsqldb()) {
412             MediatorHelper.model().getResourceAccess().getExploitHsqldb().createWeb(pathExploit, urlShell);
413         } else if (engine == MediatorHelper.model().getMediatorEngine().getH2()) {
414             MediatorHelper.model().getResourceAccess().getExploitH2().createWeb(pathExploit, urlShell);
415         } else if (engine == MediatorHelper.model().getMediatorEngine().getDerby()) {
416             MediatorHelper.model().getResourceAccess().getExploitDerby().createWeb(pathExploit, urlShell);
417         }
418     }
419 
420     private void handleSqlExploit(String pathExploit, String urlShell, Engine engine, String pathNetshare, ExploitMode exploitMethod) throws JSqlException {
421         String login = this.username.get().getText();
422         String pass = this.password.get().getText();
423         if (engine == MediatorHelper.model().getMediatorEngine().getMysql()) {
424             MediatorHelper.model().getResourceAccess().getExploitMysql().createSql(pathExploit, urlShell, pathNetshare, exploitMethod, login, pass);
425         } else if (engine == MediatorHelper.model().getMediatorEngine().getPostgres()) {
426             MediatorHelper.model().getResourceAccess().getExploitPostgres().createSql(pathExploit, urlShell, login, pass);
427         }
428     }
429 
430     private static void handleUploadExploit(String pathExploit, String urlShell, File fileToUpload, Engine engine, String pathNetshare, ExploitMode exploitMethod) throws JSqlException {
431         if (engine == MediatorHelper.model().getMediatorEngine().getMysql()) {
432             MediatorHelper.model().getResourceAccess().getExploitMysql().createUpload(pathExploit, urlShell, pathNetshare, exploitMethod, fileToUpload);
433         } else if (engine == MediatorHelper.model().getMediatorEngine().getPostgres()) {
434             MediatorHelper.model().getResourceAccess().getExploitPostgres().createUpload(pathExploit, urlShell, fileToUpload);
435         } else if (engine == MediatorHelper.model().getMediatorEngine().getSqlite()) {
436             MediatorHelper.model().getResourceAccess().getExploitSqlite().createUpload(pathExploit, urlShell, fileToUpload);
437         } else if (engine == MediatorHelper.model().getMediatorEngine().getHsqldb()) {
438             MediatorHelper.model().getResourceAccess().getExploitHsqldb().createUpload(pathExploit, urlShell, fileToUpload);
439         } else if (engine == MediatorHelper.model().getMediatorEngine().getH2()) {
440             MediatorHelper.model().getResourceAccess().getExploitH2().createUpload(pathExploit, urlShell, fileToUpload);
441         } else if (engine == MediatorHelper.model().getMediatorEngine().getDerby()) {
442             MediatorHelper.model().getResourceAccess().getExploitDerby().createUpload(pathExploit, urlShell, fileToUpload);
443         }
444     }
445 }