C# ile Basit http-https Servis (Port) Tespiti – httpFinder
Uzun zamandır iş yoğunluğu, projeler vb. konulardan dolayı bloğuma pek zaman ayıramadım. Hazır bloğumu açmışken de bir güzel bir yazı yazmak istedim. Bu yazımda C# kullanarak basit anlamda http ve https servislerinin (port) tespiti ve tespit edilen servislerden dönen cevapların html olarak kaydedilmesini konu aldım. Neden böyle bir şeye ihtiyaç duyarız soruna gelince de; birçok sızma testinin bilgi toplama evresinde, hızlıca ilerlemek gerektiğinden http ve https servislerinin hızlıca tespit edilmesi ve bu servislerden dönen cevapların bir html olarak kaydedilerek hızlıca aksiyon alınması ihtiyacından dolayı böyle bir uygulama yazma gereği duydum. Hoca “nmap” zaten yok mu falan diyen arkadaşlar için; evet nmap var. Ancak multi-threading vs. ile uygulamayı oldukça hızlandırdım. Maksat işleri hızlandırmak zaten. Ayrıca C#’ta bu işler nasıl yapılırı gösterebilmek istedim.
Visual Studio’dan bir C# Form uygulaması oluşturup adını httpFinder koyalım. Oluşturduğumuz projeyede yer alan form’a, form_ekrani.png dosyasındaki gibi 3 adet TextBox, 3 adet Label ve 2 adet Button yerleştirelim. Hedef C Class ip adresi için Target – C Class Network kısmındaki TextBox’ımıza txtTarget adını veriyoruz. Found kısmında yer alan TexBox’a txtFound ismini veriyoruz. Bildirimleri yapacağımız Status kısmındaki TextBox’a txtStatus ismini veriyoruz. Start butonuna btnStart ve Get HTML butonuna btnGetHtml ismini veriyoruz.
Formun Load_events’ına “CheckForIllegalCrossThreadCalls = false;” yazarak, thread’lerin elemanlara vs. erişiminde sorun yaşamamasını sağlıyoruz.
Threadleri bir generic list’te tutarak çalıştırılan treadleri kontrol etmek için globalde bir generic list oluşturuyoruz.
Ardından http servislerini tespit etmek için HttpScan adında bir method tanımlıyoruz.
constint finOctet = 255; //IP adresinin son oktetinin 255 olacağını belirtiyoruz.
constint firstOctet = 1; //IP adresinin ilk oktetinin 1 olacağını belirtiyoruz.
var ip = txtTarget.Text.Trim(); //ip adında bir değişken oluşturup, txtTarget TextBox’ına girilen değerin boşluklarını Trim() ile temizliyoruz.
constint port1 = 80; //80 portunu deneyeceğimizi belirtiyoruz.
Daha sonra try – catch boluğumuzu oluşturuyoruz.
var parse = ip.Split('.');//Gelen değeri ‘.’ Ya göre parçalıyoruz. Buna göre ilk 3 okteti alıp, son oktet için finOctet const değerini kullanacağız. Yani son oktet 1’den başlayıp 255’e kadar test edecek. Bunun için 1 ile 255 arası dönen bir for döngüsü oluşturuyoruz.
for (var i = firstOctet; i < finOctet; i++)
{
var status = $"{parse[0]}.{parse[1]}.{parse[2]}.{i}{":"}{port1}"; //’.’ Karakterine göre parçaladığımız ip oktetlerini birleştirip, döngüden gelen son okteti ekleyerek sonuna port belirtiyoruz.
var t1 = $"{parse[0]}.{parse[1]}.{parse[2]}.{i}";//t1 adında değişken oluşturup oktetlere ayırdığımız IP adreslerini bu değişkende bir string haline getiriyoruz.
txtStatus.Text += $"{Environment.NewLine}{status} için tarama işlemi başladı."; //Yaptığımız işlem ile ilgili kullanıcıya bilgi veriyoruz.
_taskList.Add(Task.Factory.StartNew(() => ConnectTcp(t1, port1))); //Global’de oluşturduğumuz _taskList generic list’ine başlatacağımız thread’i start ederek atıyoruz. Buna göre döngü her döndüğünde ConnectTcp metoduna t1 ve port1 değişkenlerini göndererek metodu bir thread içerisinde çalıştırıyoruz.
}
Tüm bunların dışında http servisine bağlanacağımız ConnectTcp metoduna bakalım.
var client = newTcpClient();//http servisini kontrol etmek için client adında bir TCP connection tanımlıyoruz. Ardından try-catch bloğuna alıyoruz.
try
{
client.SendTimeout = 1000;//Connection için bir timeout değeri giriyoruz.
client.Connect(t1, port1); //Bir önceki method’dan tetiklenen thread ile gelen t1 ve port1 değişkenleri ile hedefin 80 portu ile iletişim kuruluyor. Eğer connection başarılı ise (yani 80 port açıksa) alttaki satırlara devam edecek, başarılı değilse de catch bloğuna (ip ve port ile iletişim yok) zıplayacak.
txtFound.Text += $"{Environment.NewLine}{t1}:{port1}"; //başarılı olan iletişimler (yani 80 portu açık olan ip adresleri) txtFound texboxına dolduruyoruz.
client.Close();//Connection başarılı ile bağlantı sonlandırılıyor.
}
catch (SocketException)
{
//
}
finally
{
GetThreadControl(t1+":"+port1);//her ne olursa olsun GetThreadControl methodu çalıştırılıyor.
}
IP adresleri bulunduktan sonra, bulunan ip adreslerinde servisler ile iletişime geçip html olarak kaydedilmesini sağlamamız gerekiyor. Bunun için Get HTML butonunun click eventi ile HttpRequest metodunu tetikliyoruz.
var urlList = newList<string>(); //Bulunan ip adreslerini doldurmak için bir generic list oluşturuyoruz.
var parseUrlFirst = txtFound.Text.Trim().Split('\n');//Bulunan iplerin boşluklarını temizleyip, diğer kötü karakterleri temizliyoruz.
for (var i = 0; i < parseUrlFirst.Count(); i++)//Temizlediğimiz adresler içinde dönüyoruz.
{
if (parseUrlFirst[i].Replace("\r", "").Contains(":80"))//bulduğumuz ve temizlediğimiz adresler içinde :80 portu varsa, hedefte 80 portunun çalıştığını anlıyoruz.
{
var ip = parseUrlFirst[i].Replace("\r","").Split(':');//porta göre parçalıyoruz
urlList.Add(ip[0]); //temiz ip yi generic liste dolduruyoruz.
}
}
foreach (var t in urlList)//generic liste doldurduğumuz adresi dönüyoruz
{
var url = $"{"http://"}{t}";//ip adresinin başına http://ekleyip değişkene atıyoruz.
txtStatus.Text += Environment.NewLine + url + @" için http sorgusu başladı.";//yaptığımız işlem ile ilgili kullanıcıya bilgi veriyoruz
var uri = newUri(url);//elde ettiğimiz http adresini uri haline çeviriyoruz.
_taskList.Add(Task.Factory.StartNew(() => HttpConnect(url, uri, false)));//uri haline çevirdiğimiz adresi HttpConnect metoduna bir thread ile gönderiyoruz.
}
if (!isHttps)//Eğer gelen istek HttpsRequest metodundan gelmemişse bunun httprequest olduğuna karar veriyoruz.
{
var documentName = url.Replace("http://", "");//doküman oluşturmak için gelen url değerinin başında her alan https:// değerini temizliyoruz.
var saveLocation = Application.StartupPath + @"\Scanner\Saved\" + documentName + ".html"; //her isteği kaydedeceğimiz dosya adı için bir değişken oluşturuyoruz
var request = (HttpWebRequest) WebRequest.Create(uri); //request adında bir httprequest oluşturuyoruz
request.UserAgent = "Googlebot/2.1 (+http://www.adeosecurity.com.tr)";//http isteğinin başıklarını dolduruyoruz.
request.Referer = url;
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
request.AllowAutoRedirect = false;
request.Headers[HttpRequestHeader.AcceptEncoding] = "sdch";
request.Headers[HttpRequestHeader.AcceptLanguage] = "tr-TR,tr;q=0.8,en-US;q=0.6,en;q=0.4";
try
{
using (var response = request.GetResponse())//isteğimizi gönderip, gelen cevabı response değişkenine dolduruyoruz.
{
using (var reader = newStreamReader(response.GetResponseStream()))//bir stream reader oluşturup dönen cevabı stream içine dolduruyoruz
{
var htmlsource = reader.ReadToEnd();//Reader ile gelen html bilgisini okuyup değişkene atıyoruz.
response.Close();//response u kapatıyoruz
reader.Close();//reader ı kapatıyoruz
var createHtmlCopy = newStreamWriter(saveLocation);//dosya olarak kaydetmek için bir stream writer oluşturuyoruz.
createHtmlCopy.Write(htmlsource);//html source değişkenine atadığımız değeri dosyaya yazıyoruz.
createHtmlCopy.Flush();
createHtmlCopy.Close();
txtStatus.Text += Environment.NewLine + url + @" için " + documentName +
@".html dosyası kaydedildi";//yaptığımız işlem ile ilgili kullanıcıya bilgi veriyoruz.
}
}
}
Diğer methodlar benzer olduğu için tek tek anlatmadım. Genel mantık bu şekilde. Kodları Github adresine yükledim. Geliştirmek isteyenler yada kodu incelemek isteyenler github üzerinden erişebilirler.
https://github.com/mindspoof/httpFinder
Güvenli günler.