AbstractManagerShell.java

/*******************************************************************************
 * Copyhacked (H) 2012-2020.
 * This program and the accompanying materials
 * are made available under no term at all, use it like
 * you want, but share and discuss about it
 * every time possible with every body.
 * 
 * Contributors:
 *      ron190 at ymail dot com - initial implementation
 ******************************************************************************/
package com.jsql.view.swing.manager;

import com.jsql.model.exception.JSqlException;
import com.jsql.util.I18nUtil;
import com.jsql.util.LogLevelUtil;
import com.jsql.view.swing.list.DnDList;
import com.jsql.view.swing.list.ItemList;
import com.jsql.view.swing.manager.util.JButtonStateful;
import com.jsql.view.swing.scrollpane.LightScrollPane;
import com.jsql.view.swing.text.JPopupTextField;
import com.jsql.view.swing.ui.FlatButtonMouseAdapter;
import com.jsql.view.swing.util.I18nViewUtil;
import com.jsql.view.swing.util.UiUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Manager for uploading PHP webshell to the host and send system commands.
 */
public abstract class AbstractManagerShell extends AbstractManagerList {
    
    /**
     * Log4j logger sent to view.
     */
    private static final Logger LOGGER = LogManager.getRootLogger();

    private final JTextField textfieldUrlShell = new JPopupTextField(I18nUtil.valueByKey("SHELL_URL_LABEL")).getProxy();
    
    /**
     * Build the manager panel.
     */
    protected AbstractManagerShell() {
        
        this.setLayout(new BorderLayout());
        
        List<ItemList> itemsList = new ArrayList<>();
        
        try (
            var inputStream = UiUtil.class.getClassLoader().getResourceAsStream(UiUtil.PATH_WEB_FOLDERS);
            var inputStreamReader = new InputStreamReader(Objects.requireNonNull(inputStream), StandardCharsets.UTF_8);
            var reader = new BufferedReader(inputStreamReader)
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                itemsList.add(new ItemList(line));
            }
        } catch (IOException e) {
            LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
        }

        this.listPaths = new DnDList(itemsList);
        
        this.getListPaths().setBorder(BorderFactory.createEmptyBorder(0, 0, LightScrollPane.THUMB_SIZE, 0));
        
        this.add(new LightScrollPane(0, 0, 0, 0, this.getListPaths()), BorderLayout.CENTER);
        
        var southPanel = new JPanel();
        southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));

        String urlTooltip = I18nUtil.valueByKey("SHELL_URL_TOOLTIP");
        
        this.textfieldUrlShell.setToolTipText(urlTooltip);
        this.textfieldUrlShell.setBorder(
            BorderFactory.createCompoundBorder(
                BorderFactory.createCompoundBorder(
                    BorderFactory.createMatteBorder(0, 0, 0, 0, UiUtil.COLOR_COMPONENT_BORDER),
                    BorderFactory.createMatteBorder(1, 1, 0, 1, UiUtil.COLOR_DEFAULT_BACKGROUND)
                ),
                UiUtil.BORDER_BLU
            )
        );

        JPanel lastLine = this.initializeRunButtonPanel();

        southPanel.add(this.textfieldUrlShell);
        southPanel.add(lastLine);
        
        this.add(southPanel, BorderLayout.SOUTH);
    }
    
    protected abstract void createPayload(String pathShell, String urlShell) throws JSqlException;

    private JPanel initializeRunButtonPanel() {

        this.defaultText = "SHELL_RUN_BUTTON_LABEL";
        
        var lastLine = new JPanel();
        lastLine.setLayout(new BoxLayout(lastLine, BoxLayout.X_AXIS));
        lastLine.setBorder(
            BorderFactory.createCompoundBorder(
                BorderFactory.createMatteBorder(0, 0, 0, 0, UiUtil.COLOR_COMPONENT_BORDER),
                BorderFactory.createEmptyBorder(1, 0, 1, 1)
            )
        );
        
        this.run = new JButtonStateful(this.defaultText);
        I18nViewUtil.addComponentForKey(this.defaultText, this.run);
        this.run.setToolTipText(I18nUtil.valueByKey("SHELL_RUN_BUTTON_TOOLTIP"));
        this.run.setEnabled(false);

        this.run.setContentAreaFilled(false);
        this.run.setBorder(BorderFactory.createEmptyBorder(4, 8, 4, 8));
        this.run.setBackground(UiUtil.COLOR_FOCUS_GAINED);
        
        this.run.addMouseListener(new FlatButtonMouseAdapter(this.run));

        this.run.addActionListener(new ActionCreationShell());

        this.privilege = new JLabel(I18nUtil.valueByKey("PRIVILEGE_LABEL"), UiUtil.ICON_SQUARE_GREY, SwingConstants.LEFT);
        I18nViewUtil.addComponentForKey("PRIVILEGE_LABEL", this.privilege);
        this.privilege.setBorder(BorderFactory.createMatteBorder(2, 0, 0, 0, UiUtil.COLOR_DEFAULT_BACKGROUND));
        this.privilege.setToolTipText(I18nUtil.valueByKey("PRIVILEGE_TOOLTIP"));

        lastLine.add(this.privilege);
        lastLine.add(Box.createHorizontalStrut(5));
        lastLine.add(Box.createHorizontalGlue());
        lastLine.add(this.run);
        
        return lastLine;
    }
    
    private class ActionCreationShell implements ActionListener {
        
        @Override
        public void actionPerformed(ActionEvent evt) {
            
            if (AbstractManagerShell.this.getListPaths().getSelectedValuesList().isEmpty()) {
                
                LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Select at least one directory in the list");
                return;
            }

            String refUrlShell = AbstractManagerShell.this.textfieldUrlShell.getText();
            
            if (!refUrlShell.isEmpty() && !refUrlShell.matches("(?i)^https?://.*")) {
                if (!refUrlShell.matches("(?i)^\\w+://.*")) {
                    
                    LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Undefined shell URL protocol, forcing to [http://]");
                    refUrlShell = "http://"+ refUrlShell;
                    
                } else {
                    
                    LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Unknown URL protocol");
                    return;
                }
            }
            
            if (StringUtils.isNotEmpty(refUrlShell)) {
                try {
                    new URI(refUrlShell);
                } catch (URISyntaxException e) {
                    
                    LOGGER.log(
                        LogLevelUtil.CONSOLE_ERROR,
                        String.format("Incorrect URL: %s", e.getMessage())
                    );
                    return;
                }
            }
            
            String urlShellFinal = refUrlShell;

            AbstractManagerShell.this.getListPaths()
                .getSelectedValuesList()
                .forEach(pathShell -> new Thread(
                    () -> {
                        try {
                            AbstractManagerShell.this.createPayload(pathShell.toString(), urlShellFinal);
                        } catch (JSqlException e) {
                            LOGGER.log(
                                LogLevelUtil.CONSOLE_ERROR,
                                String.format("Payload creation error: %s", e.getMessage())
                            );
                        }
                    },
                    "ThreadGetShell"
                ).start());
        }
    }
}