יום שישי, 22 בפברואר 2013

Proxy Server Guide



הרבה ארגונים אינם מאפשרים כניסה לאתרים מסויימים בשעות העבודה, שלא נדבר על ארגונים מאובטחים שאינם רוצים שמידע חסוי יצא החוצה, אחד הכלים שמאפשרים לנו לנתח תעבורה הם Proxies שמהווים נקודת מעבר למידע שעובר ברשת, לדוגמה ארגון שמנצל את ה Proxy על מנת לעקוב אחרי אנשים בארגון, או Proxy שמזהה פרסומות בדפי אינטרנט ומוריד אותם, מסנן אתרי פורנו וכו'.



קיימים 2 סוגים של Proxies:
  • Forward Proxies - מקבל בקשות ומעביר לאינטרנט.
  • Reverse Proxies - מקבל בקשות מהאינטרנט ומעביר לשרת.
בתחום אבטחת המידע ה Proxy מנצח גם בתחום ההתקפה וגם בתחום ההגנה, בעזרת Proxy ניתן לטשטש את העקבות בכך שנסתיר את הכתובת שלנו בעזרת הכתובת של ה Proxy (ראה Anonymous Proxy) ,  אבל גם מאפשר לנו לעקוב אחרי הבקשות ולסנן בקשות זדוניות כמו Sql Injection / Cross Side Scripting.

יאללה לקוד!

שימו לב! התוכנית עובדת על Sockets בלבד ולא תומכת ב  (Secure Socket Layer (SSL.

נקודת המוצא עבור התוכנית שהיא צריכה קודם כל להאזין לאיזה Port בעזרת אובייקט מסוג TcpListener השלב הבא הוא לתפוס את החבילה לנתח ולהעביר אותה ליעד, אומנם זה נשמע פשוט אבל כמו תמיד העסק נהפך למסובך יותר כאשר מספר חבילות מגיעות ביחד לדוגמה דף אינטרנט יכול להכיל בתוכו מספר קישורים משרתים שונים שזמני התגובה שלהם שונים לכן עלינו לייצר מחסנית שתכיל בתוכה את כל החיבורים ותנהל אותם וכמובן לעטוף כל חיבור עם Thread על מנת שנוכל לעבוד במקביל.

ProxyHandler.cs

המחלקה שמנהלת את החיבור של המשתמש לשרת המבוקש, בתוכה יש Socket שמייצג את המשתמש ו Thread שמריץ את הפונקציה ()handle שמעבירה את הבקשה לשרת ומהשרת למשתמש, זו הפונקציה החשובה ביותר ובתוכה ניתן לערוך את הבקשות והתגובות מהשרתים והמשתמשים ולממש את הסוג הרצוי של ה Proxy, קיימות פונקציות נוספות במחלקה שתומכות בתהליך ועורכות את הבקשות.

במקרה שלנו ה Proxy מעביר בקשות ממשתמשים לשרתי אינטרנט ב Port 80, משנה את הבקשות המקוריות לבקשות של ה Proxy ומחזיר את התשובות למשתמשים.


//**** Proxytype.blogspot.com ****

using System;
using System.Text;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using System.Net;

namespace Proxy
{
    public enum TaskStaus
    {
        STANDBY,
        ACTIVE,
        DONE
    }

    public class ProxyHandler
    {
       
        public TaskStaus _taskStatus = new TaskStaus();
        Thread _thread;
        Socket _socket;

        public ProxyHandler(Socket socket)
        {
            _socket = socket;
        }

        public void run()
        {
           _taskStatus = TaskStaus.ACTIVE;

           _thread = new Thread(new ThreadStart(handle));
           _thread.Start();
        }

        public void handle()
        {

            try
            {

                //set request buffer as the size of the socket buffer
                byte[] _clientRequestBuffer = new byte[_socket.ReceiveBufferSize];
                if (_socket.Receive(_clientRequestBuffer, 0, _clientRequestBuffer.Length, SocketFlags.None) != 0)
                {
                    string request = Encoding.ASCII.GetString(_clientRequestBuffer);

                    //start spliting the request to rows
                    string[] _lineArray = split_packet(request);

                    //checking if splitting success
                    if (_lineArray.Length == 0)
                    {
                        _taskStatus = TaskStaus.DONE;
                        _socket.Close();
                        return;
                    }

                    //geting host from first request line
                    string hosturl = get_hostname(_lineArray[0]);
                    //geting the requsted page
                    string page = get_page(_lineArray[0], hosturl);

                    //drop ssl
                    if (hosturl.Contains("443"))
                    {
                        _taskStatus = TaskStaus.DONE;
                        _socket.Close();
                        return;
                    }

                    //replace original page with edit page by proxy
                    _lineArray[0] = page;
                    request = rebuild_pkt(_lineArray);

                    //using DNS to get the ip of the host
                    IPHostEntry IPHost = Dns.GetHostEntry(hosturl);

                    //create the remote socket for the proxy connect with.
                    Socket remote_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                    //make the connection on port 80
                    remote_socket.Connect(IPHost.AddressList[0], 80);

                    //sending the request
                    remote_socket.Send(ASCIIEncoding.ASCII.GetBytes(request));
                 
                    Console.WriteLine("SENDING REQUEST FROM:" + _socket.LocalEndPoint.ToString()+ " -TO-> " + ":" + hosturl);

                    //set response buffer as the size of the socket buffer
                    byte[] response = new byte[remote_socket.ReceiveBufferSize];

                    int bytesReceived = 1;

                    //check if connection is still alive after sending request
                    if (!remote_socket.Connected)
                    {
                        remote_socket.Close();
                       _socket.Close();
                        _taskStatus = TaskStaus.DONE;
                        return;
                    }

                    //set timeout for socket release socket faster
                    remote_socket.ReceiveTimeout = 2000;
                    _socket.SendTimeout = 2000;

                    //getting the first buffer of the response from the remote
                    bytesReceived = remote_socket.Receive(response, 0, response.Length, SocketFlags.None);

                    //running until end of response
                    while (bytesReceived > 0)
                    {
                        //release CPU time
                        Thread.Sleep(10);
                       
                        //sending packets from remote to the client
                        _socket.Send(response, bytesReceived, SocketFlags.None);
                       
                        //release CPU time
                        Thread.Sleep(10);

                        //reload the next buffer of the response
                        bytesReceived =remote_socket.Receive(response);

                        //release CPU time
                        Thread.Sleep(10);

                        Console.WriteLine("SENDING RESPONSE FROM:" + hosturl + " --TO--> " + _socket.RemoteEndPoint.ToString());
                       
                    }

                    //close response socket
                    remote_socket.Close();

                }

                //close the client socket
               _socket.Close();

                Thread.Sleep(100);

            }
            catch (Exception ex)
            {

                //close request socket
                _socket.Close();
            }
            finally
            {
                _socket.Close();
                _taskStatus = TaskStaus.DONE;
            }
      
        }


        /// <summary>
        /// return the host name from request
        /// removing unnecessary chars
        /// </summary>
        /// <param name="pkt_line">the first row in the request</param>
        /// <returns>host address</returns>
        private static string get_hostname(string pkt_line)
        {
            return pkt_line.Split(' ')[1].Replace("http://", "").Split('/')[0];
        }

        /// <summary>
        /// return the requested page from request
        /// fixing row as packet send from proxy
        /// remove the original hostname.
        /// </summary>
        /// <param name="pkt_line"></param>
        /// <param name="hostname"></param>
        /// <returns></returns>
        private string get_page(string pkt_line, string hostname)
        {
            return pkt_line.Replace("http://", "").Replace(hostname, "");
        }

        /// <summary>
        /// rebuild new request 
        /// </summary>
        /// <param name="_lines">edited request lines array</param>
        /// <returns>complete request string</returns>
        private static string rebuild_pkt(string[] _lines)
        {
            string _pkt = "";
            for (int i = 0; i < _lines.Length; i++)
            { _pkt = _pkt + _lines[i];}
            
            return _pkt;    
        }

       
        /// <summary>
        /// split the request to lines array
        /// </summary>
        /// <param name="pkt">complete request string</param>
        /// <returns>array of lines</returns>
        private string[] split_packet(string pkt)
        {
            string endLine = "\r\n";
            ArrayList _list = new ArrayList();

            string _line = "";
            for (int i = 0; i < pkt.Length; i++)
            {
                _line = _line + pkt[i];
                if (_line.EndsWith(endLine))
                {
                    _list.Add(_line);
                    _line = "";
                }
            }

            return (String[])_list.ToArray(typeof(string));
        }

        /// <summary>
        /// optinal write to log
        /// </summary>
        /// <param name="message">message to write</param>
        private  void writelog(string message)
        {
            FileStream _file = new FileStream(Environment.CurrentDirectory + "\\log.txt", FileMode.Append, FileAccess.Write);
            StreamWriter _writer = new StreamWriter(_file);
            _writer.WriteLine(message + "\n\r");
            _writer.Close();
            _file.Close();
            _writer.Dispose();
            _file.Dispose();

        }

    }
}

Program.cs

פונקציית Main מפעילה TcpListener של ה Proxy על פורט מסויים, ונכנסת ללואה אין סופית שה TcpListener מתחיל למלאות חיבורים במערך, לאחר מכן מופעלת הפונקציה ClearStack שמפעילה חיבורים ומנקה חיבורים שהושלמו.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Collections;
using System.Net;
using System.Threading;

namespace Proxy
{
    class Program
    {
     
        static TcpListener _listener;
        static ArrayList _arr = new ArrayList();

        static void Main(string[] args)
        {
            //set the proxy port
            _listener = new TcpListener(IPAddress.Any, 8666);
            _listener.Start();

            //infinity loop
            while (true)
            {
                if (!_listener.Pending())
                {
                    clearStack();
                    continue;
                }
             
                Socket D = _listener.AcceptSocket();
                ProxyHandler _handler = new ProxyHandler(D);
                _arr.Add(_handler);
            }

        }

     

        /// <summary>
        /// stack of socket active the handle routine
        /// remove close handlers
        /// </summary>
        static void clearStack()
        {
            for (int i = _arr.Count - 1; i > 0; i--)
            {
                ProxyHandler _tmp = (ProxyHandler)_arr[i];

                switch (_tmp._taskStatus)
                {
                    case TaskStaus.STANDBY:
                       _tmp.run();
                        break;
                 
                    case TaskStaus.DONE:
                       _arr.Remove(_tmp);
                        break;
                    default:
                        break;
                }

                //release CPU time
                Thread.Sleep(10);
            }
        }

    }
}




סיכום:

אפשר להגיד ש Proxy הוא סוג של סוכן כפול לטוב ולרע והשימוש בו הוא על הגבול של חדירה לפרטיות מצד שני הוא מאפשר לשמור על תכנים ולסנן בקשות חשודות לכן השימוש בו משתנה מצורך לצורך, אבל מדובר באחד הכלים החשובים בעולם הרשתות.

תשתמשו בו בחוכמה!