CVE-2020-8416 (C#, published 2020-01-29T17:15Z)
Description: 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.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-770 \u2014 Allocation of Resources Without Limits or Throttling
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.
Repository: https://github.com/kolya5544/BearFTP
Files changed:
BearFTP/Active.cs
BearFTP/Program.cs
CHANGELOG.txt
README.md
Commit message: CVE-2020-8416 DoS fix and antispam system improvement. Official v0.2.0


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