CVE-2020-8416: Allocation of Resources Without Limits or Throttling (CWE-770)
value: IKTeam BearFTP before 0.2.0 allows remote attackers to achieve denial of service via a large volume of connections to the PASV mode port.
CWE description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
CVSS v3 base score: 7.5
Published: 2020-01-29T17:15Z
Repository: https://github.com/kolya5544/BearFTP
Language: C#
Files changed: BearFTP/Active.cs, BearFTP/Program.cs, CHANGELOG.txt, README.md
Commit date: 2020-01-29 18:41:40 +0300
Commit message: CVE-2020-8416 DoS fix and antispam system improvement. Official v0.2.0

Diff: diff --git a/BearFTP/Active.cs b/BearFTP/Active.cs
new file mode 100644
index 0000000..572d064
--- /dev/null
+++ b/BearFTP/Active.cs
@@ -0,0 +1,53 @@
+﻿using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BearFTP
+{
+    class Active
+    {
+        public string hostname = "";
+        public int connected = 0;
+
+        public Active(string hostname, int connected)
+        {
+            this.hostname = hostname;
+            this.connected = connected;
+        }
+
+        public static bool CheckExists(string hostname, List<Active> list)
+        {
+            foreach (Active act in list)
+            {
+                if (act.hostname == hostname)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public static int GetConnections(string hostname, List<Active> list)
+        {
+            foreach (Active act in list)
+            {
+                if (act.hostname == hostname)
+                {
+                    return act.connected;
+                }
+            }
+            return -1;
+        }
+
+        public static void SetConnections(string hostname, List<Active> list, int connections)
+        {
+            foreach (Active act in list)
+            {
+                if (act.hostname == hostname)
+                {
+                    act.connected = connections;
+                }
+            }
+        }
+    }
+}
diff --git a/BearFTP/Program.cs b/BearFTP/Program.cs
index e391af9..d81670c 100644
--- a/BearFTP/Program.cs
+++ b/BearFTP/Program.cs
@@ -40,18 +40,26 @@ namespace BearFTP
 
         //Default directory. TODO: Implement directories
         public static Directory root = new Directory();
 
         //Current version
-        public static string _VERSION = "v0.1.0 BETA";
+        public static string _VERSION = "v0.2.0 BETA";
 
         //Default log.
         public static StreamWriter logfile = new StreamWriter("log.txt", true);
 
         //Dictionary of passvie clients (clients with PASV mode. Used to communicate directly later.)
         public static Dictionary<Client, Connectivity> passives = new Dictionary<Client, Connectivity>();
 
+        //List of connections per second from hostname
+        public static List<Active> per_second = new List<Active>();
+        //List of overall connections from hostname
+        public static List<Active> actives = new List<Active>();
+
+        //List of overall connections to PASV
+        public static List<Active> pasv_actives = new List<Active>();
+
         /// <summary>
         /// Reports an IP
         /// </summary>
         /// <param name="hostname">IP to report</param>
         /// <param name="comment">Logs or comments regarding report</param>
@@ -294,10 +302,27 @@ namespace BearFTP
                             i--;
                         }
                     }
                 }
             })).Start();
+            //Connections per seconds (antibot) handling
+            new Thread(new ThreadStart(() => {
+                Thread.CurrentThread.IsBackground = true;
+
+                while (true)
+                {
+                    Thread.Sleep(1000);
+                    for (int i = 0; i < per_second.Count; i++)
+                    {
+                        if (per_second[i].connected > 0)
+                        {
+                            per_second[i].connected -= 1;
+                        }
+                    }
+                 //   Console.WriteLine("[DBG] Iterated per_second!");
+                }
+            })).Start();
             ftp.Start();
             pasv.Start();
             new Thread(() =>
             {
                 Thread.CurrentThread.IsBackground = true;
@@ -310,10 +335,57 @@ namespace BearFTP
                     ns.WriteTimeout = 3000;
                     StreamReader sr = new StreamReader(ns);
                     StreamWriter sw = new StreamWriter(ns);
 
                     sw.AutoFlush = true;
+                    string hostname = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
+                    if (Active.CheckExists(hostname, actives))
+                    {
+                        if (Active.GetConnections(hostname, actives) >= 5)
+                        {
+                            client.Close();
+                            if (Ban)
+                            {
+                                var aaa = new Ban();
+                                aaa.hostname = hostname;
+                                aaa.time = 3600;
+                                bans.Add(aaa);
+                            }
+                        }
+                        else
+                        {
+                            Active.SetConnections(hostname, actives, Active.GetConnections(hostname, actives) + 1);
+                        }
+                    }
+                    else
+                    {
+                        actives.Add(new Active(hostname, 1));
+                    }
+
+                    if (Active.CheckExists(hostname, per_second))
+                    {
+                        if (Active.GetConnections(hostname, per_second) >= 5)
+                        {
+                            client.Close();
+                            if (Ban)
+                            {
+                                var aaa = new Ban();
+                                aaa.hostname = hostname;
+                                aaa.time = 3600;
+                                bans.Add(aaa);
+                            }
+                        }
+                        else
+                        {
+                            Active.SetConnections(hostname, per_second, Active.GetConnections(hostname, per_second) + 1);
+                        }
+                    }
+                    else
+                    {
+                        per_second.Add(new Active(hostname, 1));
+                       
+                    }
 
                     new Thread(new ThreadStart(() =>
                     {
                         Thread.CurrentThread.IsBackground = true;
 
@@ -323,13 +395,13 @@ namespace BearFTP
                         string username = "";
                         string password = "";
                         string directory = "/";
                         bool Authed = false;
                         bool passive = false;
-                        int error = 10;
-                        string hostname = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
+                        int error = 5;
                         
+
                         //AbuseDBIP.com API
                         bool hacking = false;
                         bool bruteforce = false;
                         bool webapp = false;
                         string comment = "";
@@ -341,29 +413,30 @@ namespace BearFTP
                             if (bans.Any(ban => ban.hostname == hostname))
                             {
                                 client.Close();
                             }
                         }
-                        catch { 
+                        catch
+                        {
 
                         }
 
-
+                        
 
                         try
                         {
                             Thread.Sleep(100);
                             Log("Connected - " + hostname, "in", true, hostname);
-                            LogWrite("220 "+config.Banner.Replace("%host%", Hostname)+"\r\n", sw, hostname);
-                            
+                            LogWrite("220 " + config.Banner.Replace("%host%", Hostname) + "\r\n", sw, hostname);
+
                             while (client.Connected)
                             {
                                 Thread.Sleep(100);
                                 //Receiving handler START
                                 string answ = "";
                                 bool flag = true;
-                                
+
                                 while (flag)
                                 {
                                     int a = sr.Read();
                                     answ += (char)a;
                                     if (a == 10 || a == 13)
@@ -377,11 +450,11 @@ namespace BearFTP
                                 }
                                 answ = answ.Trim();
                                 //Receiving handler END
 
                                 //Command processing.
-                                if (answ.Length > 3) //We dont want dummies to spam/DDoS.
+                                if (answ.Length >= 3) //We dont want dummies to spam/DDoS.
                                 {
                                     Log(answ, "in", true, hostname);
                                 }
                                 if (answ.Contains("CONNECT") || answ.StartsWith("GET http"))
                                 {
@@ -391,16 +464,16 @@ namespace BearFTP
                                         aaa.hostname = hostname;
                                         aaa.time = 3600;
                                         bans.Add(aaa);
                                         client.Close();
                                     }
-                                    var a = ReportAsync(hostname, "["+DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "System scanning (Proxy judging) using CONNECT or GET requests", false, false, true, true, false);
+                                    var a = ReportAsync(hostname, "[" + DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "System scanning (Proxy judging) using CONNECT or GET requests", false, false, true, true, false);
                                     a.Start();
-                                    
-                                    
+
+
                                 }
-                                if (answ.Length > 64)
+                                if (answ.Length > 128)
                                 {
                                     client.Close();
                                 }
                                 if (answ.StartsWith("OPTS"))
                                 {
@@ -547,11 +620,11 @@ namespace BearFTP
                                         {
                                             Thread.Sleep(1000);
                                             LogWrite("150 Ok to send data.\r\n", sw, hostname);
                                             Thread.Sleep(100);
                                             //       byte[] file = aaaa.content;
-                                           //Encoding.ASCII.GetChars(file);
+                                            //Encoding.ASCII.GetChars(file);
                                             //      connn.sw.Write(chars, 0, file.Length);
                                             //      connn.tcp.Close();
                                             SendFile(aaaa, connn.sw);
                                             connn.tcp.Close();
                                             Thread.Sleep(200);
@@ -676,14 +749,18 @@ namespace BearFTP
                                 {
                                     LogWrite("200 OK!\r\n", sw, hostname);
                                 }
                                 else
                                 {
-                                    error--;
-                                    if (error <= 0)
+                                    if (answ.Length >= 3)
                                     {
-                                        client.Close();
+                                        error--;
+                                        if (error <= 0)
+                                        {
+                                            client.Close();
+                                        }
+
                                     }
                                 }
                                 if (answ.Contains("php") && triggered)
                                 {
                                     trigger2 = true; //Second level trigger
@@ -698,24 +775,26 @@ namespace BearFTP
                                         aaa.hostname = hostname;
                                         aaa.time = 3600;
                                         bans.Add(aaa);
                                         client.Close();
                                     }
-                                    var a = ReportAsync(hostname, "["+DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "RCE Attempt at 21 port using ProFTPd exploit", true, false, false, false, false);
+                                    var a = ReportAsync(hostname, "[" + DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "RCE Attempt at 21 port using ProFTPd exploit", true, false, false, false, false);
                                     a.Start();
-                                    
-                                    
+
+
 
                                 }
 
                             }
+
                         }
                         catch (Exception e)
                         {
                             client.Close();
                             c.Connected = false;
                         }
+                        Active.SetConnections(hostname, actives, Active.GetConnections(hostname, actives) - 1);
                     }
                     )).Start();
                 }
             }).Start();
             new Thread(() =>
@@ -740,47 +819,105 @@ namespace BearFTP
                     ns.WriteTimeout = 3000;
                     StreamReader sr = new StreamReader(ns);
                     StreamWriter sw = new StreamWriter(ns);
 
                     sw.AutoFlush = true;
-                    string hostname = "";
+                    string hostname = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
+
+                    try
+                    {
+                        if (bans.Any(ban => ban.hostname == hostname))
+                        {
+                            client.Close();
+                        }
+                    }
+                    catch
+                    {
+
+                    }
+
+                    if (Active.CheckExists(hostname, pasv_actives))
+                    {
+                        if (Active.GetConnections(hostname, pasv_actives) >= 3)
+                        {
+                            client.Close();
+                            if (Ban)
+                            {
+                                var aaa = new Ban();
+                                aaa.hostname = hostname;
+                                aaa.time = 3600;
+                                bans.Add(aaa);
+                            }
+                        }
+                        else
+                        {
+                            Active.SetConnections(hostname, pasv_actives, Active.GetConnections(hostname, pasv_actives) + 1);
+                        }
+                    }
+                    else
+                    {
+                        pasv_actives.Add(new Active(hostname, 1));
+                    }
                     Thread user = new Thread(new ThreadStart(() =>
                     {
                         Thread.CurrentThread.IsBackground = true;
 
                         Client c = new Client("1", "2", "3");
 
+                        
+
                         try
                         {
+                            bool ispresent = false;
                             foreach (Client cl in connected)
                             {
-                                if (cl.hostname == ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString() && cl.Connected)
+                                if (cl.hostname == hostname && cl.Connected)
                                 {
                                     c = cl;
+                                    ispresent = true;
                                 }
                             }
-                            Connectivity ca = new Connectivity();
-                            ca.sr = sr;
-                            ca.sw = sw;
-                            ca.tcp = client;
-                            passives.Add(c, ca);
-                            while (client.Connected)
+                            if (!ispresent)
                             {
-                                Thread.Sleep(3000);
+                                client.Close();
+
+                            }
+                            else
+                            {
+                                Connectivity ca = new Connectivity();
+                                ca.sr = sr;
+                                ca.sw = sw;
+                                ca.tcp = client;
+                                passives.Add(c, ca);
+                                /*  while (client.Connected)
+                                  {
+                                      Thread.Sleep(3000);
+                                  }*/
+                                for (int i = 0; client.Connected; i++)
+                                {
+                                    Thread.Sleep(1000);
+                                    if (i >= 120)
+                                    {
+                                        client.Close();
+                                        passives.Remove(c);
+                                    }
+                                }
+                                client.Close();
+                                passives.Remove(c);
                             }
-                            client.Close();
-                            passives.Remove(c);
                         }
                         catch (Exception e)
                         {
                             if (!e.Message.StartsWith("An item"))
                             {
                                 client.Close();
                                 passives.Remove(c);
                             }
                         }
+                        Active.SetConnections(hostname, pasv_actives, Active.GetConnections(hostname, pasv_actives) - 1);
                     }
+
                     ));
                     user.Start();
 
 
                 }
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index e240726..dc45c1c 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,8 +1,10 @@
 --> IT ONLY CONTAINS THE MOST RECENT CHANGES!
 
-v0.1.0:
-- Ability to RECV files more than 2 kbs in size
-- Ability to add byte[] contents to files (a.k.a you can now send .exes over FTP)
-- JSON format fixed
-- Possible fix of unique characters breaking encoding of logs.
-- Download speed limited to 40 kbit/sec.
\ No newline at end of file
+v0.2.0:
+- Fixed "errors" handling, designed to prevent users spamming invalid FTP commands
+- Fixed PWN not being logged due to its size
+- Added "connections per second" and "total connections" antispam for default port
+- Added "total connections" antispam for PASV port
+- Fixed PASV mode handler not checking for default port connection, as well as fixed handling of non-active users (CVE-2020-8416 fix)
+- Passive mode now autodisconnects AFK persons after 120 seconds of inactivity (Beware when sending big files!)
+- Minor bugfixes
\ No newline at end of file
diff --git a/README.md b/README.md
index 7d92620..309381b 100644
--- a/README.md
+++ b/README.md
@@ -81,11 +81,10 @@ We use VS2019 and .NET core 3.1 for development.
 
 Here's our CURRENT todo list:
 - Administrator commands (adding files right through FTP!)
 - Local command handler (execute FTP commands in your console, ban spamming IPs and such)
 - Implement directories
-- Implement support of files with size more than 2 kbs
 - Add more features (Active mode, more advanced ban system)
 
 
 License
 ----

Vulnerable code:         public static string _VERSION = "v0.1.0 BETA";
                        int error = 10;
                        string hostname = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
                        catch { 

                            LogWrite("220 "+config.Banner.Replace("%host%", Hostname)+"\r\n", sw, hostname);
                            
                                
                                if (answ.Length > 3) //We dont want dummies to spam/DDoS.
                                    var a = ReportAsync(hostname, "["+DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "System scanning (Proxy judging) using CONNECT or GET requests", false, false, true, true, false);
                                    
                                    
                                if (answ.Length > 64)
                                           //Encoding.ASCII.GetChars(file);
                                    error--;
                                    if (error <= 0)
                                        client.Close();
                                    var a = ReportAsync(hostname, "["+DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "RCE Attempt at 21 port using ProFTPd exploit", true, false, false, false, false);
                                    
                                    
                    string hostname = "";
                                if (cl.hostname == ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString() && cl.Connected)
                            Connectivity ca = new Connectivity();
                            ca.sr = sr;
                            ca.sw = sw;
                            ca.tcp = client;
                            passives.Add(c, ca);
                            while (client.Connected)
                                Thread.Sleep(3000);
                            client.Close();
                            passives.Remove(c);
v0.1.0:
- Ability to RECV files more than 2 kbs in size
- Ability to add byte[] contents to files (a.k.a you can now send .exes over FTP)
- JSON format fixed
- Possible fix of unique characters breaking encoding of logs.
- Download speed limited to 40 kbit/sec.
- Implement support of files with size more than 2 kbs
Fixed code: ﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BearFTP
{
    class Active
    {
        public string hostname = "";
        public int connected = 0;

        public Active(string hostname, int connected)
        {
            this.hostname = hostname;
            this.connected = connected;
        }

        public static bool CheckExists(string hostname, List<Active> list)
        {
            foreach (Active act in list)
            {
                if (act.hostname == hostname)
                {
                    return true;
                }
            }
            return false;
        }

        public static int GetConnections(string hostname, List<Active> list)
        {
            foreach (Active act in list)
            {
                if (act.hostname == hostname)
                {
                    return act.connected;
                }
            }
            return -1;
        }

        public static void SetConnections(string hostname, List<Active> list, int connections)
        {
            foreach (Active act in list)
            {
                if (act.hostname == hostname)
                {
                    act.connected = connections;
                }
            }
        }
    }
}
        public static string _VERSION = "v0.2.0 BETA";
        //List of connections per second from hostname
        public static List<Active> per_second = new List<Active>();
        //List of overall connections from hostname
        public static List<Active> actives = new List<Active>();

        //List of overall connections to PASV
        public static List<Active> pasv_actives = new List<Active>();

            //Connections per seconds (antibot) handling
            new Thread(new ThreadStart(() => {
                Thread.CurrentThread.IsBackground = true;

                while (true)
                {
                    Thread.Sleep(1000);
                    for (int i = 0; i < per_second.Count; i++)
                    {
                        if (per_second[i].connected > 0)
                        {
                            per_second[i].connected -= 1;
                        }
                    }
                 //   Console.WriteLine("[DBG] Iterated per_second!");
                }
            })).Start();
                    string hostname = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
                    if (Active.CheckExists(hostname, actives))
                    {
                        if (Active.GetConnections(hostname, actives) >= 5)
                        {
                            client.Close();
                            if (Ban)
                            {
                                var aaa = new Ban();
                                aaa.hostname = hostname;
                                aaa.time = 3600;
                                bans.Add(aaa);
                            }
                        }
                        else
                        {
                            Active.SetConnections(hostname, actives, Active.GetConnections(hostname, actives) + 1);
                        }
                    }
                    else
                    {
                        actives.Add(new Active(hostname, 1));
                    }

                    if (Active.CheckExists(hostname, per_second))
                    {
                        if (Active.GetConnections(hostname, per_second) >= 5)
                        {
                            client.Close();
                            if (Ban)
                            {
                                var aaa = new Ban();
                                aaa.hostname = hostname;
                                aaa.time = 3600;
                                bans.Add(aaa);
                            }
                        }
                        else
                        {
                            Active.SetConnections(hostname, per_second, Active.GetConnections(hostname, per_second) + 1);
                        }
                    }
                    else
                    {
                        per_second.Add(new Active(hostname, 1));
                       
                    }
                        int error = 5;

                        catch
                        {
                        
                            LogWrite("220 " + config.Banner.Replace("%host%", Hostname) + "\r\n", sw, hostname);


                                if (answ.Length >= 3) //We dont want dummies to spam/DDoS.
                                    var a = ReportAsync(hostname, "[" + DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "System scanning (Proxy judging) using CONNECT or GET requests", false, false, true, true, false);


                                if (answ.Length > 128)
                                            //Encoding.ASCII.GetChars(file);
                                    if (answ.Length >= 3)
                                        error--;
                                        if (error <= 0)
                                        {
                                            client.Close();
                                        }

                                    var a = ReportAsync(hostname, "[" + DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + "] " + "RCE Attempt at 21 port using ProFTPd exploit", true, false, false, false, false);



                        Active.SetConnections(hostname, actives, Active.GetConnections(hostname, actives) - 1);
                    string hostname = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();

                    try
                    {
                        if (bans.Any(ban => ban.hostname == hostname))
                        {
                            client.Close();
                        }
                    }
                    catch
                    {

                    }

                    if (Active.CheckExists(hostname, pasv_actives))
                    {
                        if (Active.GetConnections(hostname, pasv_actives) >= 3)
                        {
                            client.Close();
                            if (Ban)
                            {
                                var aaa = new Ban();
                                aaa.hostname = hostname;
                                aaa.time = 3600;
                                bans.Add(aaa);
                            }
                        }
                        else
                        {
                            Active.SetConnections(hostname, pasv_actives, Active.GetConnections(hostname, pasv_actives) + 1);
                        }
                    }
                    else
                    {
                        pasv_actives.Add(new Active(hostname, 1));
                    }
                        

                            bool ispresent = false;
                                if (cl.hostname == hostname && cl.Connected)
                                    ispresent = true;
                            if (!ispresent)
                                client.Close();

                            }
                            else
                            {
                                Connectivity ca = new Connectivity();
                                ca.sr = sr;
                                ca.sw = sw;
                                ca.tcp = client;
                                passives.Add(c, ca);
                                /*  while (client.Connected)
                                  {
                                      Thread.Sleep(3000);
                                  }*/
                                for (int i = 0; client.Connected; i++)
                                {
                                    Thread.Sleep(1000);
                                    if (i >= 120)
                                    {
                                        client.Close();
                                        passives.Remove(c);
                                    }
                                }
                                client.Close();
                                passives.Remove(c);
                        Active.SetConnections(hostname, pasv_actives, Active.GetConnections(hostname, pasv_actives) - 1);

v0.2.0:
- Fixed "errors" handling, designed to prevent users spamming invalid FTP commands
- Fixed PWN not being logged due to its size
- Added "connections per second" and "total connections" antispam for default port
- Added "total connections" antispam for PASV port
- Fixed PASV mode handler not checking for default port connection, as well as fixed handling of non-active users (CVE-2020-8416 fix)
- Passive mode now autodisconnects AFK persons after 120 seconds of inactivity (Beware when sending big files!)
- Minor bugfixes