Wednesday, August 13, 2014

A simple TCP proxy forwarder in .NET

Here's a way of doing a super simple TCP proxy in C#.

Warning: this should only be used for testing purposes, as it will leak threads.

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class Program
{
    static void ProxyStream(string stream1name, NetworkStream stream1, string stream2name, NetworkStream stream2)
    {
        var buffer = new byte[65536];

        try
        {
            while (true)
            {
                var len = stream1.Read(buffer, 0, 65536);
                stream2.Write(buffer, 0, len);
            }
        }
        catch (Exception)
        {
            Console.WriteLine("Stream from " + stream1name + " to " + stream2name + " closed");
        }
    }

    static void Main(string[] args)
    {
        if (args.Length != 3)
        {
            Console.WriteLine("Usage: program.exe localport remoteServerHost remoteServerPort");
            Console.WriteLine("Example: program.exe 13389 10.1.2.3 3389");
            return;
        }

        var localPort = int.Parse(args[0]);
        var remoteServerHost = args[1];
        var remoteServerPort = int.Parse(args[2]);

        var l = new TcpListener(IPAddress.Any, localPort);
        l.Start();
        while (true)
        {
            var client1 = l.AcceptTcpClient();
            var remoteAddress = (client1.Client.RemoteEndPoint as IPEndPoint).Address.ToString();
            Console.WriteLine("Accepted session from " + remoteAddress);
            var client2 = new TcpClient(remoteServerHost, remoteServerPort);
            Console.WriteLine("Created connection to " + remoteServerHost + ":" + remoteServerPort);

            new Thread(() => { ProxyStream(remoteAddress, client1.GetStream(), remoteServerHost, client2.GetStream()); }).Start();
            new Thread(() => { ProxyStream(remoteServerHost, client2.GetStream(), remoteAddress, client1.GetStream()); }).Start();
        }
    }
}

Or, if you need this from PowerShell, I would suggest wrapping it (as threads in PowerShell are painful):
$source = '
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class Program
{
    static void ProxyStream(string stream1name, NetworkStream stream1, string stream2name, NetworkStream stream2)
    {
        var buffer = new byte[65536];

        try
        {
            while (true)
            {
                var len = stream1.Read(buffer, 0, 65536);
                stream2.Write(buffer, 0, len);
            }
        }
        catch (Exception)
        {
            Console.WriteLine("Stream from " + stream1name + " to " + stream2name + " closed");
        }
    }

    static void Main(string[] args)
    {
        if (args.Length != 3)
        {
            Console.WriteLine("Usage: program.exe localport remoteServerHost remoteServerPort");
            Console.WriteLine("Example: program.exe 13389 10.1.2.3 3389");
            return;
        }

        var localPort = int.Parse(args[0]);
        var remoteServerHost = args[1];
        var remoteServerPort = int.Parse(args[2]);

        var l = new TcpListener(IPAddress.Any, localPort);
        l.Start();
        while (true)
        {
            var client1 = l.AcceptTcpClient();
            var remoteAddress = (client1.Client.RemoteEndPoint as IPEndPoint).Address.ToString();
            Console.WriteLine("Accepted session from " + remoteAddress);
            var client2 = new TcpClient(remoteServerHost, remoteServerPort);
            Console.WriteLine("Created connection to " + remoteServerHost + ":" + remoteServerPort);

            new Thread(() => { ProxyStream(remoteAddress, client1.GetStream(), remoteServerHost, client2.GetStream()); }).Start();
            new Thread(() => { ProxyStream(remoteServerHost, client2.GetStream(), remoteAddress, client1.GetStream()); }).Start();
        }
    }
}

'
Add-Type `
    -TypeDefinition $source `
    -Language CSharp `
    -OutputAssembly 'c:\windows\temp\tcpproxy.exe' `
    -OutputType 'ConsoleApplication'

Tuesday, August 05, 2014

Command prompt running as NT AUTHORITY\Network Service

Here's a convenient way to get a command prompt running as Network Service. This is useful for troubleshooting authentication issues for services running as Network Service.

Get Psexec from http://live.sysinternals.com/psexec.exe.

Run:

psexec -u "NT AUTHORITY\Network Service" -i cmd

Or, if you prefer PowerShell:

psexec -u "NT AUTHORITY\Network Service" -i powershell

Or, if you prefer PowerShell, and a bigger than standard window with some distinguishable color:

psexec -u "NT AUTHORITY\Network Service" -i powershell -noexit "cd c:\ ; mode con:cols=170 lines=60 ; (Get-Host).UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size(170,3000) ; (Get-Host).UI.RawUI.BackgroundColor='DarkCyan' ; clear"



WCF net.tcp test client in PowerShell

Here's a convenient way of creating a WCF net.tcp client for a quick test. It uses PowerShell to compile C# code to an exe on the fly. Note that the interface can be partial (only containing the method you want to call).
$source = '
    using System;
    using System.ServiceModel;

    [ServiceContract(Namespace = "http://example.com/SomeSchema")]
    public interface ISomeService
    {
        [OperationContract]
        bool SomeMethod();
    }

    public class Wcftest
    {
        public static void Main(string[] args)
        {
            var binding = new NetTcpBinding();
            var address = new EndpointAddress("net.tcp://" + args[0] + ":808/SomeService");

            var factory = new ChannelFactory<ISomeService>(binding, address);
            var proxy = factory.CreateChannel();

            Console.WriteLine(proxy.SomeMethod());
        }
    }
'
Add-Type `
    -ReferencedAssemblies 'System.ServiceModel.dll' `
    -TypeDefinition $source `
    -Language CSharp `
    -OutputAssembly 'c:\windows\temp\wcftest.exe' `
    -OutputType 'ConsoleApplication'

c:\windows\temp\wcftest.exe "example.com"
del c:\windows\temp\wcftest.*

Saturday, June 07, 2014

Simple web UI for managing Libvirt / KVM

When looking for a simple web interface to create / start / stop / delete VMs, I was only able to find systems that seemed overkill for my needs. I wanted something very minimal, where I wouldn't have to install and keep too many libraries up to date. I therefore wrote a simple Python CGI script and a few helper scripts to do the actual privileged operations, to be called via sudo. The code is available here on Github.

Wednesday, June 19, 2013

Column selection in Sublimetext using alt + left click

Many editors use alt + left click to do column selection. In Sublimetext the default is middle mouse button or shift + right click. To use alt + left click, create a file Data\Packages\User\Default (Windows).sublime-mousemap (or equivalent if different OS), with the following contents:
[
 {
  "button": "button1", "modifiers": ["alt"],
  "press_command": "drag_select",
  "press_args": {"by": "columns"}
 },
 {
  "button": "button1", "modifiers": ["alt", "ctrl"],
  "press_command": "drag_select",
  "press_args": {"by": "columns", "additive": true}
 }
]

Tuesday, January 29, 2013

Lorem ipsum hotkey

Here's an AutoHotKey script to generate "lorem ipsum" when you press scroll lock. If you press again within a second it also inserts a space and avoids title-casing the word.
SetKeyDelay, -1

lipsum = Lorem ipsum dolor sit amet consectetur adipiscing elit sed et suscipit nunc in egestas velit condimentum nunc egestas feugiat nunc scelerisque tincidunt nisi vitae aliquet in eget tortor mauris sed porttitor velit quisque vehicula pretium rutrum vestibulum nec quam lectus et eleifend nibh morbi placerat facilisis ante quis elementum quisque a lacus velit

StringSplit, lipsum_array, lipsum, %A_Space%

n := 1
t := 0

ScrollLock::
    if n > %lipsum_array0%
    {
        n := 1
    }

    word := lipsum_array%n%

    t2 := DllCall("GetTickCount64")
    if ((t2 - 1000) < t) {
        send {space}
    } else {
        StringUpper, word, word, T
    }

    send %word%

    n := n + 1
    t := DllCall("GetTickCount64")
return

Tuesday, July 24, 2012

Adding domain user as local admin immediatly after domain join

Here's a way to add a domain user as a local admin immediatly after joining the domain, without rebooting first:
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;

class NativeMethods
{
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_LOGON_NETWORK = 3;
    public const int LOGON32_LOGON_BATCH = 4;
    public const int LOGON32_LOGON_SERVICE = 5;
    public const int LOGON32_LOGON_UNLOCK = 7;
    public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
    public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

    public enum SID_NAME_USE
    {
        SidTypeUser = 1,
        SidTypeGroup,
        SidTypeDomain,
        SidTypeAlias,
        SidTypeWellKnownGroup,
        SidTypeDeletedAccount,
        SidTypeInvalid,
        SidTypeUnknown,
        SidTypeComputer,
    }

    public struct LOCALGROUP_MEMBERS_INFO_0
    {
        public IntPtr PSID;
    }

    [DllImport("kernel32.dll")]
    public extern static bool CloseHandle(IntPtr hToken);

    [DllImport("advapi32.DLL", SetLastError = true)]
    public static extern int LogonUser(
        string lpszUsername,
        string lpszDomain,
        string lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        out IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool LookupAccountName(
        string lpSystemName,
        string lpAccountName,
        [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
        ref uint cbSid,
        StringBuilder ReferencedDomainName,
        ref uint cchReferencedDomainName,
        out SID_NAME_USE peUse);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool LookupAccountSid(
        string lpSystemName,
        [MarshalAs(UnmanagedType.LPArray)] byte[] lpSid,
        StringBuilder lpName,
        ref uint cchName,
        StringBuilder lpReferencedDomainName,
        ref uint cchReferencedDomainName,
        out SID_NAME_USE peUse);

    [DllImport("netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int NetLocalGroupAddMembers(
        string servername,
        string groupname,
        uint level,
        ref LOCALGROUP_MEMBERS_INFO_0 buf,
        uint totalentries);
}

public class AddAdminUserHelper
{
    public static void AddAdminUser(string domain, string username, string password)
    {
        // Get built in administrators account name
        StringBuilder adminGroupName = new StringBuilder();
        uint adminGroupNameCapacity = (uint)adminGroupName.Capacity;
        StringBuilder referencedDomainName = new StringBuilder();
        uint referencedDomainNameCapacity = (uint)referencedDomainName.Capacity;
        NativeMethods.SID_NAME_USE eUse;
        byte[] adminGroupSid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2 };
        if (!NativeMethods.LookupAccountSid(
            null,
            adminGroupSid,
            adminGroupName,
            ref adminGroupNameCapacity,
            referencedDomainName,
            ref referencedDomainNameCapacity,
            out eUse))
        {
            Console.WriteLine("LookupAccountSid failed with error " + Marshal.GetLastWin32Error());
            return;
        }

        // Get a security token needed to be able to afterwards query for the user's SID
        IntPtr token = IntPtr.Zero;
        if (NativeMethods.LogonUser(
            username,
            domain,
            password,
            NativeMethods.LOGON32_LOGON_NEW_CREDENTIALS,
            0,
            out token) == 0)
        {
            Console.WriteLine("LogonUser failed with error " + Marshal.GetLastWin32Error());
            return;
        }

        // Get user's SID
        byte[] userSid = new byte[1024];
        uint userSidLength = (uint)userSid.Length;
        referencedDomainName = new StringBuilder();
        referencedDomainNameCapacity = (uint)referencedDomainName.Capacity;
        NativeMethods.SID_NAME_USE peUse;
        using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))
        {
            if (!NativeMethods.LookupAccountName(
                domain,
                username,
                userSid,
                ref userSidLength,
                referencedDomainName,
                ref referencedDomainNameCapacity,
                out peUse))
            {
                Console.WriteLine("LookupAccountName failed with error " + Marshal.GetLastWin32Error());
                return;
            }
        }
        NativeMethods.CloseHandle(token);

        // Add user's SID to local admins group
        IntPtr userSidNative = Marshal.AllocHGlobal(userSid.Length);
        Marshal.Copy(userSid, 0, userSidNative, (int)userSid.Length);
        NativeMethods.LOCALGROUP_MEMBERS_INFO_0 info0;
        info0.PSID = userSidNative;
        int r = NativeMethods.NetLocalGroupAddMembers(
            null,
            adminGroupName.ToString(),
            0,
            ref info0,
            1);
        Marshal.FreeHGlobal(userSidNative);
        if (r != 0)
        {
            Console.WriteLine("NetLocalGroupAddMembers failed by returning " + r);
            return;
        }
    }
}