0

For academic purposes I want to try to exploit an strcpy function with a buffer overflow attack using C#, by passing some arbitrarily long string, that contains a specific binary data (raw bytes).

I am trying something similar to this C# code, but that obviously doesn't work:

static void Main(string[] args)
{
    string cPath = @"C:\Debug";
    var strCmdText =
        "0xCA" + "0xCA" + + "0xCA" + "0xCA" + "0xCA" + ...;
    string filename = Path.Combine(cPath, "Buffer.exe");
    var proc = System.Diagnostics.Process.Start(filename, strCmdText);
}
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
Jishan
  • 1,654
  • 4
  • 28
  • 62
  • Ah, I must edit it the naming. – Jishan Jun 25 '16 at 17:26
  • 1
    You can't pass completely arbitrary bytes on a command-line, nor a completely arbitrary length. The OS determines the character encoding and maximum length. (Whether the OS enforces the character encoding is a separate issue, which you might want to investigate.) For the Win32 API [CreateProcessW](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396) function, you can pass 32767 UTF-16 code units. – Tom Blodget Jun 26 '16 at 10:14
  • @TomBlodget Thanks for the insight Tom. – Jishan Jun 26 '16 at 13:34

1 Answers1

1

We assume that the target application uses single-byte characters as its argv. So...

1. Test application

Our test application is called Buffer.exe and its source code is:

void print_str(char* str)
{
    size_t length = strlen(str);
    printf("Line: '%s'\n", str);
    printf("Len : '%d'\n", length);
    printf("Hex :", length);

    for (size_t i = 0; i < length; i++)
    {
        printf("\t0x%02x", str[i]);
    }

    printf("\n");
}


int main(int argc, char* argv[])
{
    for (int i = 1; i < argc; i++)
    {
        char* str = argv[i];
        printf("Argument #%d:\n", i);
        print_str(str);
    }

    printf("Press enter to exit\n");
    getchar();
}

It just prints the passed arguments both as strings and as hex values, so for arguments "a1" "b2" it will print

Argument #1:
Line: 'a1'
Len : '2'
Hex :   0x61    0x31
Argument #2:
Line: 'b2'
Len : '2'
Hex :   0x62    0x32
Press enter to exit

2. Main "ingredients"

What we need are:

  1. A way to turn bytes to analogous "single byte characters" (actually they will still be 2-byte UTF-16 chars on the C# side, but [OS should pass them to our receiver]/[Buffer.exe should interpret] as single byte characters with the same binary value we started on the C# side) - for that we need Encoding.Default (as corrected by @Tom Blodget) property and its Encoding.GetString method.
  2. A way to ensure that our argument line is correctly escaped (so that 0x20-Space won't split our big argument into multiple and other particularities won't affect the command line either) - for simplicity we will just escape an entire line with quotes, but for a proper solution(at least on Win32) see https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/.

3. Implementation

Our argument creating application is the following:

public static String BytesToCommandLineArgument(Byte[] array)
{            
    var ascii = Encoding.Default.GetString(array);
    // "Escape" it here. Disclaimer - it is actually a wrong way to escape a command line argument.
    // See https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ 
    // for a way to do it correctly at least on Win32
    return String.Format("\"{0}\"", ascii); 
}

public static  void Main()
{
    try
    {
        var bytes = new Byte[] { 0x10, 0x31, 0x13, 0x61, 0x20 };

        using (var process = Process.Start(
            fileName: "Buffer.exe",
            arguments: BytesToCommandLineArgument(bytes)))
        {
            process.WaitForExit();
        }
    }
    catch (Exception exc)
    {
        Console.WriteLine(exc);
    }
    Console.WriteLine("Press any key...");
    Console.ReadKey(true);
}

4. Testing

As you can see our test byte array contains spaces and newlines, so we can at least be sure that our solution won't strip them (though as I've said something like quote on the end will break it).

Argument #1:
Line: '►1‼a '
Len : '5'
Hex :   0x10    0x31    0x13    0x61    0x20
Press enter to exit

P.S.1: Don't forget that this solution escapes command line incorrectly - https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/! It may not cause any issues on most of the data in the possible dataspace, but it will certainly do on some data.

P.S.2: How do you convert Byte Array to Hexadecimal String, and vice versa? may be of use for you if you have a need to input the data dynamically.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • My goodness! Thanks a ton for writing such a detailed and thorough answer, which actually teaches me rather than merely solving the problem. Thank you! – Jishan Jun 25 '16 at 21:24
  • 1
    I think you mean ANSI instead of ASCII. In which case, since the OS and user's selected ANSI character set probably isn't ASCII, get the bytes with `Encoding.Default` Actually, [Don’t Use Encoding.Default](https://blogs.msdn.microsoft.com/shawnste/2005/03/15/dont-use-encoding-default/) or ANSI, for that matter. – Tom Blodget Jun 26 '16 at 10:19
  • @TomBlodget Thanks for pointing my mistake. But Encoding.Default is the right thing to use here (though there can theoretically be some issues in some setups/environments) - we need to pass bytes as one-byte characters through OS start process call and Buffer.exe startup. In either of those cases default ANSI page would probably be used to transform UTF-16 to ANSI chars, thus reverting our initial byte to UTF-16 char transformation - preserving the original binary byte value. – Eugene Podskal Jun 26 '16 at 10:47
  • You're right. It depends on the startup code of the target program. It could very well be converting the command-line to the ANSI character set it is running under. It could also be splitting it into an `argv`, removing quotes, etc or it could ignore all that an take the whole command-line as ANSI or UTF-16. – Tom Blodget Jun 26 '16 at 15:41