View Javadoc
1   package com.jsql.view.swing.terminal;
2   
3   import com.jsql.util.LogLevelUtil;
4   import org.apache.commons.lang3.StringUtils;
5   import org.apache.logging.log4j.LogManager;
6   import org.apache.logging.log4j.Logger;
7   
8   import java.io.BufferedReader;
9   import java.io.DataOutputStream;
10  import java.io.IOException;
11  import java.io.InputStreamReader;
12  import java.net.Socket;
13  
14  public class ServerInputConnection {
15  
16      /**
17       * Log4j logger sent to view.
18       */
19      private static final Logger LOGGER = LogManager.getRootLogger();
20  
21      private final BufferedReader bufferedReader;
22      private final Socket clientSocket;
23      private final ServerInput serverInput;
24      private final ExploitReverseShell exploitReverseShell;
25      private boolean running = true;
26      private String command;
27  
28      public ServerInputConnection(ExploitReverseShell exploitReverseShell, Socket clientSocket, ServerInput serverInput) throws IOException {
29          this.clientSocket = clientSocket;
30          this.exploitReverseShell = exploitReverseShell;
31          this.serverInput = serverInput;
32          LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, "Reverse established by {}", clientSocket);
33          LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Type 'exit' in reverse shell to close the connection");
34          this.bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
35      }
36  
37      public void run() throws IOException {
38          try (DataOutputStream dataOutputStream = new DataOutputStream(this.clientSocket.getOutputStream())) {
39              Thread readerThread = new Thread(() -> {
40                  try {
41                      this.handleSocketReading();
42                  } catch (IOException e) {
43                      LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Error reading from socket: {}", e.getMessage());
44                  } finally {
45                      this.closeResources();
46                  }
47              });
48              readerThread.start();
49  
50              while (this.running) {
51                  this.processAndSendCommand(dataOutputStream);
52              }
53  
54              try {
55                  readerThread.join(2000);
56              } catch (InterruptedException e) {
57                  Thread.currentThread().interrupt();
58                  LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Reader thread interrupted");
59              }
60          }
61      }
62  
63      private void processAndSendCommand(DataOutputStream dataOutputStream) throws IOException {
64          if (StringUtils.isNotEmpty(this.command)) {
65              var commandWithoutPrompt = this.command.replaceAll("[^$]*\\$\\s*", "");
66              this.command = null;
67              dataOutputStream.writeBytes(commandWithoutPrompt + "\n");
68          }
69      }
70  
71      private void handleSocketReading() throws IOException {
72          int length = 1024;
73          char[] buffer = new char[length];
74          int charsRead;
75          while (this.running) {
76              charsRead = this.bufferedReader.read(buffer, 0, length);
77              if (charsRead != -1) {
78                  String result = new String(buffer, 0, charsRead);  // discard unused chars from buffer
79                  this.exploitReverseShell.append(result.matches("\\$$") ? result +" " : result);  // space after internal prompt
80                  this.exploitReverseShell.reset(false);
81              } else {
82                  break;
83              }
84          }
85      }
86  
87      private void closeResources() {
88          try {
89              LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Reverse connection closed");
90              this.running = false;
91              this.serverInput.close();
92          } catch (IOException e) {
93              LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, "Error closing resources: {}", e.getMessage());
94          }
95      }
96  
97      public void setCommand(String command) {
98          this.command = command;
99      }
100 }