1

I tried to make a authentication with C#. It sends a HTTP GET request on login attempt, it looks like:

https://example.com/clogin.php?name=abc&password=abc

And this is only a few characters. But I get a "Request-URI too long" error.

Error

It seems like the simulating the request works, but not when sending it over TcpClient.

image

ClientManager.cs:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;


public class ClientManager
{
    private static TcpListener listener;

    public static void Main()
    {

        ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidationCallback;

        listener = new TcpListener(IPAddress.Any, 10250);
        listener.Start();
        Console.WriteLine("*** ClientManager started ***");
        Console.WriteLine("Listening to port 10250, make sure not used.");
        StartAccept();

        while (true)
        {
            System.Threading.Thread.Sleep(1000);
            string cmd = Console.ReadLine();

            if (cmd.Equals("exit"))
            {
                Console.WriteLine("*** Stopping cman... ***");
                Environment.Exit(0);
            }

            if (cmd.StartsWith("auth"))
            {

                Console.WriteLine("[" + "simulate" + "] Using auth cred to authenticate.");
                char c = '|';
                string[] args = cmd.Split(c);

                if (!(args.Length > 2))
                {
                    Console.WriteLine("INVALID_DATA_GIVEN");
                }
                string username = args[1];
                string password = args[2];

                Console.WriteLine("[" + "simulate" + "] Using credentials: " + username + " and " + password);

                string response = GetAsync(("https://example.com/clogin.php?name=" + username + "&password=" + password + "&test=1"));
                Console.WriteLine("https://example.com/clogin.php?name=" + username + "&password=" + password);
                Console.WriteLine(response);

                if (response.Equals("ERROR_FAILED_CONNECTION"))
                {
                    Console.WriteLine("ERROR_SERVERERROR");
                }
                else if (response.Equals("INVALID_USERNAME"))
                {
                    Console.WriteLine("INVALID_DATA_GIVEN");
                }
                else if (response.Equals("INVALID_PASSWORD"))
                {
                    Console.WriteLine("INVALID_DATA_GIVEN");
                }
                else if (response.Equals("INVALID_CRED"))
                {
                    Console.WriteLine("ERROR_AUTH_INVALID_CRED");
                }
                else if (response.Equals("IS_BANNED"))
                {
                    Console.WriteLine("ERROR_AUTH_BANNED");
                }
                else
                {
                    Console.WriteLine("[" + "simulate" + "] LOGIN OK | RESPONSE: " + response);
                }
            }
        }

    }
    private static void StartAccept()
    {
        listener.BeginAcceptSocket(HandleAsyncConnection, listener);
    }

    private static void HandleAsyncConnection(IAsyncResult res)
    {
        StartAccept();
        TcpClient client = listener.EndAcceptTcpClient(res);
        string clientSession = "NULL";
        string ip = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();

        Console.WriteLine("[" + ip + "] Incoming connection.");

        while (true)
        {
            try
            {
                System.Threading.Thread.Sleep(250);
                Console.WriteLine("Trying to read data from " + ip);
                NetworkStream stream = null;
                Byte[] data = new Byte[8192];
                String responseData = String.Empty;
                Int32 bytes = 0;

                stream = client.GetStream();
                bytes = stream.Read(data, 0, data.Length);
                Console.WriteLine("Bytes: " + bytes + " Data: " + System.Text.Encoding.ASCII.GetString(data));
                responseData = System.Text.Encoding.ASCII.GetString(data);


                Console.WriteLine("[" + ip + "] " + responseData);

                if (responseData.StartsWith("close"))
                {
                    Console.WriteLine("[" + ip + "] Connection closed.");
                    break;
                }else if (responseData.StartsWith("useauthtoken"))
                {
                    Console.WriteLine("[" + ip + "] Using auth token to authenticate.");
                    char c = '|';
                    string[] args = responseData.Split(c);

                    if (!(args.Length > 1))
                    {
                        SendMessage(stream, "INVALID_DATA_GIVEN");
                    }

                    string response = GetAsync("http://example.com/cauthtokencheck.php?auth=" + args[1]);

                    if (response.Equals("yes"))
                    {
                        clientSession = args[1];
                        SendMessage(stream, "OK");
                    }
                    else
                    {
                        SendMessage(stream, "ERROR_AUTH_INVALID");
                    }
                }
                else if (responseData.StartsWith("auth"))
                {

                    Console.WriteLine("[" + ip + "] Using auth cred to authenticate.");
                    char c = '|';
                    string[] args = responseData.Split(c);
                    Console.WriteLine("Data splitted");

                    if (!(args.Length > 2))
                    {
                        SendMessage(stream, "INVALID_DATA_GIVEN");
                    }
                    string username = args[1];
                    string password = args[2];

                    Console.WriteLine("[" + ip + "] Using credentials: " + username + " and " + password);

                    Console.WriteLine("Logging in...");
                    string response = GetAsync(("https://example.com/clogin.php?name=" + username + "&password=" + password + ""));
                    Console.WriteLine("Login attempt completed, with " + response);

                    if (response.Equals("ERROR_FAILED_CONNECTION"))
                    {
                        SendMessage(stream, "ERROR_SERVERERROR");
                    }
                    else if (response.Equals("INVALID_USERNAME"))
                    {
                        SendMessage(stream, "INVALID_DATA_GIVEN");
                    }
                    else if (response.Equals("INVALID_PASSWORD"))
                    {
                        SendMessage(stream, "INVALID_DATA_GIVEN");
                    }
                    else if (response.Equals("INVALID_CRED"))
                    {
                        SendMessage(stream, "ERROR_AUTH_INVALID_CRED");
                    }
                    else if (response.Equals("IS_BANNED"))
                    {
                        SendMessage(stream, "ERROR_AUTH_BANNED");
                    }
                    else
                    {
                        Console.WriteLine("[" + ip + "] LOGIN OK | RESPONSE: " + response);
                        if (response == null) response = "Response was null?";
                        SendMessage(stream, response);
                        Console.WriteLine("Sended message...");
                        clientSession = response;
                    }
                }

                if ((!responseData.StartsWith("auth") || !responseData.StartsWith("useauthtoken") || !responseData.StartsWith("close")) && clientSession.Equals("NULL"))
                {
                    SendMessage(stream, "ERROR_AUTH_MISSING");
                }

                if (responseData.Equals("endGame"))
                {
                    char c = '|';
                    string[] args = responseData.Split(c);

                    if (!(args.Length > 3))
                    {
                        SendMessage(stream, "INVALID_DATA_GIVEN");
                    }

                    string won = args[0];
                    string kills = args[1];
                    string singleplayer = args[2];

                    string response = GetAsync("https://example.com/cgameend.php?session=" + client + "&won=" + won + "&kills=" + kills + "&singleplayer=" + singleplayer);

                    if (response.Equals("ERROR_FAILED_CONNECTION"))
                    {
                        SendMessage(stream, "ERROR_SERVERERROR");
                    }
                    else if (response.Equals("SESSION_INVALID"))
                    {
                        SendMessage(stream, "ERROR_AUTH_MISSING");
                    }
                    else if (response.Equals("SUCCESS"))
                    {
                        SendMessage(stream, "SUCCESS");
                    }
                }


            }
            catch (Exception e)
            {
                Console.WriteLine("[" + ip + "] Connection closed: " + e.Message);
                break;
            }
        }
    }

    public static string GetAsync(string uri, Action<WebHeaderCollection> headers = null)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        //request.Headers.Set(HttpRequestHeader.ContentLocation, uri);
        //request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

        Console.WriteLine(request.RequestUri);
        using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
        using (Stream stream = response.GetResponseStream())
        using (StreamReader reader = new StreamReader(stream))
        {
            return reader.ReadToEnd();
        }
    }

    public static void SendMessage(NetworkStream stream, string msg)
    {
        Byte[] sendBytes = Encoding.ASCII.GetBytes(msg);
        stream.Write(sendBytes, 0, sendBytes.Length);
    }

    public static bool RemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        bool isOk = true;

        return isOk;
    }
}

clogin.php:

    <?php
$conn = new mysqli("localhost", "root", "not-the-real-password", "topdown");

if ($conn->connect_error) {
    die("ERROR_FAILED_CONNECTION");
} 
$name = $_GET["name"];
$password = $_GET["password"];

function generateRandomString($length = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

function endsWith($haystack, $needle)
{
    $length = strlen($needle);
    if ($length == 0) {
        return true;
    }

    return (substr($haystack, -$length) === $needle);
}
    
    $sql = "SELECT `password`,`banned` FROM `accounts` WHERE `name`='".$name."';";
    $result = $conn->query($sql);

    if ($result->num_rows < 1) { 
        die("INVALID_CRED");
        die();
    }
    
    $found = "";
    $banned = false;
    
    while($row = $result->fetch_assoc()) {
        $found = $row["password"];
        $banned = $row["banned"];
    }
    
    if($found == $password){
        if($banned==1){
            die("IS_BANNED");
        }
        
        $session = generateRandomString(16);
        $sql = "UPDATE `accounts` SET `clientsession`='".$session."' WHERE `name`='".$name."'";

        if ($conn->query($sql) === TRUE) {
            die($session);
        }else{
            die("ERROR_FAILED_CONNECTION");
        }
    }else{
        die("INVALID_CRED");
    }
?>
GoldenretriverYT
  • 3,043
  • 1
  • 9
  • 22
  • Are there any redirects in place? Those could easily turn this huge. It could also be that the Login Form and the Login Form target are two different files, and the Login Form simply throws an exception on being given any paramters (it is too long by a few HTTP Paramters). – Christopher Aug 03 '19 at 19:00
  • Also could it be that the server is plain not ready for requests? I tried it and the page looks very "comming soon". – Christopher Aug 03 '19 at 19:01
  • @Christopher nope, its just the api page for the login. Not the real login page. – GoldenretriverYT Aug 03 '19 at 19:09
  • @Christopher and there are no redirects. – GoldenretriverYT Aug 03 '19 at 19:16
  • I don't get such an error with `curl` and this URL. And you provide no code so that it is unclear how your request differs from what I tried. Apart from that: please don't include text (the error) as image - paste the text instead into the question. – Steffen Ullrich Aug 03 '19 at 19:21

2 Answers2

1

Your simulation does not reflect what you send in in the non-simulated case to the server and that's why you cannot reproduce the error in the simulation.

In the simulated case you read a line and the length of the string cmd is the line length. In the non-simulated case you instead read into a buffer data which has a size of 8192 bytes. This means that if you read the response of auth|abc|abc the contents of data will be auth|abc|abc\0\0\0\0...., i.e. the content send by the server and then 8180 (8192-12) characters \0 (i.e. \x00, \000 or however this can be written in C#).

After your responseData.Split the password (i.e. args[2]) will therefore not be abc as you expected but abc\0\0\0\0..... This again means that the URL which should be https://....?user=abc&pass=abc is in reality https://....?user=abc&pass=abc\0\0\0..... The \0 needs to be encoded with URL encoding as %00 which results in https://....?user=abc&pass=abc%00%00%00....

And already all these 8180 %00 will result in 24540 characters in the URL alone, which explains why the server complains about the URL being too large. A look into the servers access log or error log will probably show this kind of problem too.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
0

That message is coming from the server, not the client. See this answer for possible solutions: How do I resolve a HTTP 414 "Request URI too long" error?

jhilgeman
  • 1,543
  • 10
  • 27
  • Nope, can't be the server. Try visiting this site with your web browser - there is no problem. – GoldenretriverYT Aug 03 '19 at 19:16
  • 1
    @GoldenretriverYT: *"Nope, can't be the server."* - sure it is. Look closely at the error you get and there it clearly says *"The __remote server returned an error__: (414)"*. – Steffen Ullrich Aug 03 '19 at 19:20
  • @SteffenUllrich again - you can use your web browser - there will be NO problem - so how in the world should it be the server? – GoldenretriverYT Aug 03 '19 at 19:24
  • @GoldenretriverYT: It is very likely that your request differs from the browsers request and also from the request I've tried with `curl`. The server can send a different response to different requests. Unfortunately you don't provide any kind of code so it is impossible to say what you are doing different and therefore what might result in the different response of the server. – Steffen Ullrich Aug 03 '19 at 19:26
  • @GoldenretriverYT: Welcome to the Internet. You need to learn about **why** we spoof user agent strings. https://en.wikipedia.org/wiki/User_agent#User_agent_spoofing – Christopher Aug 03 '19 at 19:27
  • @GoldenretriverYT - It looks like you got your answer but I just wanted to note that clients do not generate server response codes because they have no way of knowing how the server might respond. So if you get a response code, then that means that the server generated that code and sent it back to you. For anyone else that has this problem, the next thing to do is to install and run Fiddler (with HTTPS capture), then run your program, switch back to Fiddler and inspect the raw requests/responses. In this scenario, you would have seen the unexpectedly long request. – jhilgeman Aug 03 '19 at 21:51