26 Mart 2017

Kali Linux - Bölüm 13: Sistem Boot Süreci ve Servis Yönetimi

Sistem boot ve shutdown süreçleri

Linux’un boot sürecini anlamak ne işinize yarayabilir? Sisteminiz açıldığında otomatik olarak başlatılan servislerin tam olarak nasıl başlatıldıklarını (ve shutdown sırasında kapatıldıklarını öğrenebilirsiniz). Linux tabanlı bir gömülü sistem oluşturmayı düşündüğünüzde kurgulamanız gereken temel sistem bileşenlerini daha rahat anlarsınız.
Sisteminiz boot etmiyorsa hatayı tespit edebilme kabiliyeti kazanırsınız. MBR (Master Boot Record) virüs’ünün sahip olduğu avantajı anlarsınız. İşletim sisteminin ne olduğunu daha iyi anlamaya yaklaşırsınız. Ve tabi herşeyin nasıl çalıştığını merak eden bir kişiliğiniz varsa, rahatlarsınız.
Önce teorik olarak süreci anlatalım. Linux boot süreci aşağıdaki temel adımlarla gerçekleşir:

BIOS

  • BIOS anakart üzerindeki ROM çipindeki kod ile Power On Self Test (POST) işlemini gerçekleştirir.
  • BIOS konfigürasyonundaki boot edilebilir cihazları sırayla tarar ve bulduğu ilk cihazın ilk sektöründe (MBR – Master Boot Record) bulunan kodu çalıştırır

Boot Loader

  • MBR kodu GRUB’ı (GRand Unified Bootloader) çalıştırır.
  • GRUB kullanıcının seçtiği seçeneğe göre Linux kernel’ının çalıştırır.

Kernel ve Initrd

  • GRUB temel sürücüleri barındıdan Kernel’ı çalıştırır
  • GRUB Kernel’ın diğer modüllerini de barındıran ve /boot partition’ını içeren init ramdisk (initrd) imajını hafızaya yükler
  • Kernel diğer modüllerini ve dosya sisteminin tamamını aktif hale getirir

Servislerin başlatılması

  • Kernel, tüm kernel modülleri ve dosya sistemi yüklendikten sonra, Kernel initialization (yani servislerin başlatılması) sürecini başlatır
  • “init”, “upstart” ve “systemd” yöntemlerinden birisi kullanılarak servisler başlatılır (Kali için kullanılan yöntem Debian tabanlı bir dağıtım olması nedeniyle “systemd”dir)
BIOS kodu donanıma özel bir koddur, yani her donanım üreticisi kendi ihtiyaçlarına göre geliştirilen bir yazılımı anakart üzerindeki bir ROM (read only memory) çipine yerleştirir. Bilgisayarın düğmesine basıldığında bu kod çalışmaya başlar ve Power On Self Test (POST) sürecini başlatır. Bu test ekran, klavye, v.d. temel donanım parçalarının çalışabilir durumda olup olmadıklarını kontrol eder. BIOS konfigürasyonu yine üreticinin tercihine göre farklılaşabilecek bir tuş ile değiştirilebilir (bilgisayar başlatıldığında belli bir zaman dilimi içinde bu tuşa basılması gerekir). Bu tuş F2, F10, Esc veya farklı bir tuş olabilir.


Yukarıda gördüğünüz ekran VMWare’de makinemizi çalıştırırken F2 tuşu ile ulaştığımız BIOS konfigürasyon ekranı. Bu ekranda bilgisayar açılırken hangi cihazları ve hangi sırada taraması gerektiğini tanımlayabildiğimiz ekran. Gördüğünüz gibi ilk sırada Floppy Disk sürücüsü var, bu cihaza rastlanmayınca sabit disk üzerindeki MBR kaydı boot işlemi için kullanılacak.
MBR verisini incelemek için boot edilmiş olan Kali dağıtımımızın içinden raw disk dosyasının başından itibaren 1 adet 512 byte’lık bir alanı okuyup dosya olarak kaydedeceğiz. Unix ve linux sistemlerde disk cihaz dosya adları genellikle “sda” veya “hda” ile başlar.


Device dosyalarının altında bulunduğu /dev dizini altında “sd” ve “hd” ile başlayan dosyaları aradığımızda “sda” ile başlayan dosyaların bulunduğunu görüyoruz. “sda” tüm sabit diski (0 sektöründe bulunan MBR kaydı ve bunun içerdiği partition tablosu dahil) ifade eder. Diğer dosyalar ise bu disk üzerinde bulunan partition’ları ifade eder. Partition en kaba tabiri ile sabit diskin belli bir bölümünde yer alan ve belli bir dosya sistemine (filesystem) uygun bir veri yapısına sahip (NTFS, FAT, EXT, v.b.) bir alanı ifade eder.
Bizim amacımız sabit disk üzerindeki 0 sektörünü okumak olduğundan ve buna partition device dosyalarından değil tüm diski ifade eden “sda” dosyasından erişmemiz gerektiği için aşağıdaki gibi bir okuma yapıyoruz.


Daha sonra bu 512 byte’lık veriyi parse edebilmek için R-Studio aracından faydalanalım.


R-Studio normal olarak şikayet ediyor, çünkü ilk 512 byte’ın bir MBR kaydı olduğunu anlıyor, fakat partition tablosunda belirtilen disk alanlarının mevcut olmadığını görüyor. Biz yine de bu veriyi anlamlandırmak için bu verinin üzerinde sağ klik yaparak Edit / View seçeneği ile yeni bir pencerede veriyi inceleyelim.


Bu alanın ilk bölümünde bootstrap kodu bulunuyor. Bunun sonlarına doğru da GRUB kelimesinin geçtiği bir bölümü görebiliyoruz. Bu kodun tam olarak ne yaptığını anlamak için disassemble edebiliriz, ancak bunun tersine mühendisliğini yapmak bizi odağımızdan saptıracaktır. Merak eden arkadaşlar linux üzerinde çalışan objdump ile veya varsa lisanslı IDA Pro araçları ile bunu yapmaya çalışabilirler. Burada özetle şunu söylememiz lazım, MBR içinde bulunan kod alanı oldukça sınırlı olduğu için burada ancak basit bir kod bulundurulabilir. GRUB bir grafik arayüz sunuyor ve bazı konfigürasyon dosyalarını okuyor, daha sonra da kernel’ı ve ram disk’i hafızaya yüklüyor. Dolayısıyla grub’ın asıl işi yapan bileşenleri partition alanlarında bulunmalı.
Amacımız disk mimarisini açıklamak değil ama MBR kaydının geri kalanı hakkında da kısa bilgi vereyim.


Bir DOS partition tablosunda 4 adet kayıt vardır. Elbette 4’ten fazla partition’da oluşturabilirsiniz, bunun için son partition extended partition olarak kullanılır, ancak bunun detayına girmeyeceğim.
Yukarıda gördüğünüz ilk partition boot partition’dır, çünkü “0x80” olarak işaretlenmiştir. Boot partition işletim sisteminin (yani kernel imajının) bu partition da olması anlamına gelir. Diğerlerinde bu alan “0” olduğundan boot partition olmadıkları anlaşılabilir. Daha sonraki alanlar partition’ın hangi sektörden başladığı ve boyutunun ne olduğu gibi bilgileri barındırır. Bootstrap kodu ve partition tablosu ilk 512 byte’lık alana sığmak zorundadır. Bu alanın sonunda da aşağıda gördüğünüz gibi MBR kaydının signature’ı yer alır.


MBR’da bulunan bootstrap kodu zamanında zararlı yazılımlar için bir hedef olarak kullanılmıştır. Bu çok da mantıklıdır çünkü bu kod çalıştığında henüz işletim sistemi bile ayağa kalkmamıştır. Dolayısıyla anti virüs de aktif hale gelmemiştir.
Şimdi GRUB çalıştığında nasıl bir arayüzle karşılaşıyoruz bunu gözlemleyelim.


“e” tuşuna bastığımızda ayarlanmış GRUB komutlarını görebiliriz:


Komutların en sonunda GRUB’ın önce Kernel’ı (yani işletim sistemini) yüklediğini ve sonra da initial ram disk’i (initrd) yüklediğini görebiliriz.




GRUB disk üzerinde birden fazla işletim sistemi varsa (bunlar aynı veya farklı partition’larda olabilir, ama Linux ve Windows işletim sistemlerini ele alırsak bunlar farklı partition’lara kurulmuş olmalıdır) bu tür durumlarda da kullanılır. Kullanıcı isterse Linux işletim sistemini isterse Windows işletim sistemini ayağa kaldırabilir. Ancak kafanız karışmasın, çalışan GRUB uygulaması (en azından MBR’da bulunan küçük bölümün dışında kalan önemli bölümü) ve bunun kullandığı konfigürasyon dosyaları Linux partition’ı içinde yer alır.
Aşağıda Kali’nin kurulumu sırasında GRUB’ın MBR bölümünün kurulumu ile ilgili uyarıyı görebilirsiniz:


GRUB küçük kernel diyebileceğimiz kernel’ı yükledikten ve ram diski hafızaya yükledikten sonra Kernel ram disk’te bulunan diğer modüllerini hayata geçirir. Bundan sonra işletim sisteminin uygulama servislerini başlatmasına sıra gelir.
Bu servislerin başlatılması için “init”, “upstart” veya “systemd” yöntemlerinden birisi kullanılır.
Unix dünyasından gelen “init” yöntemi ile servis scriptleri çalıştırılırken script’ler arasındaki bağımlılıkları sistem yöneticisi olarak sizin düşünmeniz gerekecektir. Servis script’leri sıra ile çalıştırılır ve bir script tamamlanmadan bir sonraki başlayamaz. Servis script’lerinin hangi sıra ile çalıştırılacağı bu scriptlerin adlarının başında bulunan rakamlardan anlaşılır. “init”ten sonra “upstart” yöntemi geliştirilmiştir. Son olarak “systemd” güncel işletim sistemi dağıtımlarında kullanılmaktadır. Mevcut Kali dağıtımının altyapı olarak kullandığı Debian dağıtımı “systemd” yöntemini kullandığından “init” yönteminin detayına girmeyeceğim. Ancak “systemd” yönteminin “init” yönteminden temel farkları şunlardır:
  • Script’lerin eşzamanlı çalıştırılabilmesi (ve daha hızlı boot edebilme).
  • Script’ler arasındaki bağımlılıkların sistematik biçimde ele alınması (ki eşzamanlı çalıştırma için böyle bir ihtiyaç var).
  • SysV init script’leri birer bash script’idir (/etc/init.d/ dizini altında yer alırlar) ve uzun dosyalardır. systemd ile daha kısa ve anlaşılır birer konfigürasyon dosyası ile servisler (unit’ler) yönetilmektedir.
Upstart içinde yukarıdakine benzer değerlendirmeler yapılabilir, ancak systemd bu yöntemden farklıdır ve tabi yukarıda bahsedilenlerin ötesinde özellikleri de bulunmaktadır. Debian dağıtımı wheezy versiyonu ile birlikte “systemd” altyapısıyla dağıtılmaya başlandı.


SysV “init” yöntemiyle başlatılan bir dağıtımda başlayan servislerle ilgili kayıtların ekranda aktığını görürdük. Burada ise hem paralel olarak servislerin başlatılması etkisiyle getty servisi kısa sürede başlıyor ve başlar başlamaz ekran kayıtlarını temizliyor, hem de GRUB linux kernel’ını başlatırken “quiet” opsiyonuyla başlatıyor. Bu nedenle Kali’yi başlatırken fazla bir mesaj göremiyoruz.
Eğer “systemd” startup süreci ile ilgili bir debug ihtiyacınız varsa şu linkte Debugging bölümünde geçen ayarları uygulayarak tespitinizi daha sağlıklı yapabilirsiniz: https://wiki.debian.org/systemd
“systemd” başlangıç yöntemine geçmeden önce “init” yöntemi ile servislerin başlatılmasına ilişkin bilgi vermek istiyorum:
  • “init” yönteminde kernel ve file system aktif hale geldikten sonra ilk olarak “/sbin/init” uygulaması çalıştırılır.
  • “init” uygulaması “/etc/inittab” dosyasına bakarak buradaki default runlevel’ı tespit eder. Run level konusunun detayına girmeyeceğim ancak kısaca her bir seviyede başlatılacak ve durdurulacak (kill edilecek) servislere ilişkin “bash” script’leri yer alır.
  • Tespit edilen default run level’la ilgili başlatılacak ve durdurulacak servislerle ilgili bash script’leri “/etc/rc.d/rc[run level]” dizini altında bulunur.
  • Bu dizinler içindeki bash script dosya isimleri aslında “/etc/init.d” dizini altındaki benzer isimli bash script’lerine verilen sembolik linklerdir. “S” ile başlayan script dosyaları çalıştırılırken bunlar “start” parametresi ile çalıştırılır. “K” ile başlayan script dosyaları çalıştırılırken bunlar da “stop” parametresi ile çalıştırılır.
Debian’ın “systemd” geçişi “init” scriptlerin tamamen terk edilmesi şeklinde olmamıştır. Öncelikle Debian’da “init” süreci ile ilgili kalıntılara bir göz atalım.
Bir defa “/sbin/init” uygulaması halen mevcuttur:


Ancak ilginç olan şey bu dosyanın aslında “/lib/systemd/systemd” dosyasına verilmiş olan bir sembolik link olmasıdır. Buna göre kernel yine ilk olarak “/sbin/init” uygulamasını başlatacaktır, ancak aslında başlattığı proses “systemd” prosesi olacaktır.


Yukarıda gördüğünüz gibi “1” numaralı proses “init” tabanlı bir başlangıç senaryosunu uygulayan sistemlerde olduğu gibi “/sbin/init” prosesidir. Ancak gerçekte “systemd” tabanlı bir başlangıç senaryosu işlemiştir.
Debian tabanlı Kali’de “/etc/inittab” dosyası bulunmamaktadır.


Ancak run level scriptleri var olmaya devam etmektedir (her ne kadar “rc.d” dizini altında olmasalar da):


Örnek olarak Run Level 3’ü (yani multi-user run level’ı) temsil eden “rc3.d” dizinine bir göz atalım:


“init” yönteminde burada çalışma sırasıyla “K” ile başlayan script’ler ise bu run level’a ulaşıldığında “stop” parametresi ile çalıştırılacak servis durdurma scriptleri, “S” ile başlayan script’ler ise bu run level’a ulaşıldığında “start” parametresi ile çalıştırılacak servis başlatma script’leridir. Bunların kendi aralarındaki sıralama ise “K” ve “S” harflerinden sonra gelen rakamlarla belirlenmektedir. “init” yönteminde her bir script çalışmadan önce bir öncekinin tamamlanmasını bekler.
Buradaki dosyaları “ls –al” komutuyla listelediğimizde, bu dosyaların aslında “/etc/init.d” dizini altındaki script’lere verilen sembolik linkler olduğunu görürüz:


Örnek olarak “apache2” script dosyasına erişelim:


Gördüğünüz gibi (aslında “/etc/init.d/apache2” dosyasına sembolik link veren) K01apache2 script’i, comment’lenmiş ancak belli bir disipline göre yazılmış bilgileri içeriyor. Bu alanlar LSB (Linux Standard Base) uyumlu script başlık alanlarıdır. Bildiğiniz gibi comment alanları bir script’in çalışmasını etkilemez, ancak bu alanlar ilgili script’in bağımlılıkları ve diğer bilgileri içerdiğinden “systemd” kullanan bir sistemde SysV “init” yönetminde var olan bir script’in karşılığı (ki bunlara unit diyoruz) yok ise, çalışma anında “systemd” yöntemi için bir wrapper unit dosyası oluşturulması için kullanılmaktadır. İşte temelde Debian üzerinde “init” yönteminin kalıntılarını görmemizin sebebi budur.


Yukarıda bu script’in “start” parametresi ile başlatılması halinde çalışacak kod bölümünün bir kısmı görülmektedir. “init” yönteminin terk edilmesinin bir sebebi de bu script’lerin uzunluğu ve bakım yapılabilirliklerinin düşük olmasıdır. Daha sonra göreceğiniz gibi “systemd” unit dosyaları sadece birer konfigürasyon dosyalarıdır ve boyutları daha küçük olduğu için okunabilirlikleri de daha yüksektir.
“systemd” yöntemini anlamak malesef “init” kadar basit değildir. “systemd” script’lerin paralel başlatılmasını destekler, ancak bağımlılıkları elbette takip eder. Bunun yanı sıra run level kavramı “systemd” için (en azından aynı keskinlik ve manada) geçerli değildir. “target” unitleri kabaca run level’lara benzeyebilir, ancak aynı anda birden fazla “target” aktif olabilir. “default.target” kabaca default run level’a benzetilebilir, ancak default target ile birlikte diğer target’lar da aktif olabilir.


Yukarıda görüldüğü gibi “/lib/systemd/system/default.target” unit dosyası “graphical.target” dosyasına verilen bir sembolik link. “systemd” ile birlikte scriptlerin yerini “unit” dosyaları alıyor ve bu dosyalar “.service”, “.target”, “.socket” gibi uzantılar alan konfigürasyon dosyaları. “graphical.target” run level 5’e paralel olarak düşünülebilir. Ancak daha önce de söylediğimiz gibi “systemd” tek bir run level’da çalışmak zorunda değil, “multi-user.target” gibi farklı target’ların da aynı anda aktif olması beklenebilir.
Kali’nin default kurulumunda “ssh” gibi servisler aktif değildir. Bunu çalışan ağ servislerini inceleyerek netleştirebiliriz:


Görüldüğü gibi UDP 68 portu dışında hiçbir porta bir servis bağlanmış değil. Bu durumda “/etc/systemd/system” dizini altındaki “target.wants” dizinlerinin altındaki “service” unit isimlerini listeleyelim. Bu dizinler altındaki servislerin startup sırasında aktif hale gelmesi gerektiğini düşünebiliriz (bu varsayımımız her zaman doğru değildir, ancak bu örnek için geçerlidir).


Şimdi “systemd” ile birlikte gelen “systemctl” komutunu kullanarak “ssh” servisini her reboot’tan sonra aktif hale gelecek şekilde konfigüre edelim ve daha sonra tekrar “wants” dizinleri altındaki “service” unitlerini inceleyelim.


“ssh” servisini enable ettikten sonra “/etc/systemd/system” dizini altında “sshd.service” unit’inin “/lib/systemd/system” dizini altındaki “ssh.service” isimli unit dosyasına verilen bir sembolik link olduğunu görüyoruz. Ayrıca “wants” dizinleri altındaki “service” unit’lerini incelediğimizde de yine “ssh.service” unit dosyasına bir sembolik link görüyoruz.
Ancak bu işlemi yaptıktan sonra “ssh” servisimiz halen çalışmıyor. Biz sadece “ssh” servisinin reboot sonrasında hayata geçmesini sağlamış olduk. Bunu “systemctl” komutunu “status” parametresi ile çağırdığımızda da görebiliriz.


“ssh” servisini başlatmak için “systemctl” komutunu “start” parametresi ile çalıştırabiliriz.


“ssh” servisi şu anda hem reboot edildiğinde çalışacak şekilde ayarlanmış durumda hem de çalışmakta.
“systemctl” komutunun sağladığı bilgilerden birisi de ilgili servisle ilgili üretilmiş syslog kayıtları.
“systemctl” sadece servislerin yönetilmesi işine yaramıyor. Normalde “shutdown” komutu ile yaptığımız sistemin kapatılması, yeniden başlatılması gibi işlemleri ve diğer işlemleri de bu komutla yapabiliriz. Şimdi sistemi yeniden başlatarak “ssh” servisimizin reboot sonrası aktif hale gelip gelmeyeceğini inceleyelim.


Reboot sonrasında görüyoruz ki “ssh” servisimiz çalışıyor.


Yeri gelmişken Kali sistemimize “root” kullanıcısı ile “ssh” servisi üzerinden bağlanmaya çalışalım:


“ssh” servisi “root” kullanıcısıyla bağlanmaya çalıştığımızda parolamızın hatalı olduğunu söyleyerek bağlantıyı kabul etmiyor. Aslında sorun parolamızda değil, root kullanıcısına “ssh” bağlantısı için izin vemediğimizden bizi kabul etmiyor. Normalde biliyorsunuz sistem yöneticilerinin kendilerine ait düşük haklara sahip kullanıcı kodları ile sistemlere bağlanmaları, daha sonra (linux ortamında sudo yaparak ve loglanarak) yönetici hesabına geçiş yaparak işlem yapmaları beklenir. Ancak Kali böyle bir canlı ortam türünden bir sunucu değil. O yüzden biz “root” kullanıcısı ile doğrudan bağlanabilmek için gerekli ayarı yapalım.
Bunun için öncelikle “/etc/ssh/sshd_config” dosyasındaki “PermitRootLogin prohibit-password” satırını comment’leyelim, daha sonra da “PermitRootLogin yes” satırını ekleyelim.


Daha sonra “systemctl”yi kullanarak “ssh” servisini yeniden başlatalım.


Bu işlemden sonra Kali bilgisayarımıza “ssh” bağlantısı kurduğumuzda başarılı olabiliriz.


Daha önce eğer “systemd” service unit’i bulunmayan bir “init” script’ini çalıştırmak istersek “systemd”nin bunu desteklediğini söylemiştik. “systemd” bunun için “init” scriptlerinin başında bulunan “LSB” comment alanlarındaki bilgileri kullanıyor ve bir wrapper hazırlıyordu. Bunu yapabilmemiz için “/lib/systemd/system-generators/systemd-sysv-generator” uygulaması ile init script’i için bir wrapper (kabuk) oluşturulur. Bu wrapper’lar “/run/systemd/generator.late/” dizini içinde bulunur.


Bu dizine göz attığımızda Kali dağıtımında bu dizinin çoktan doldurulduğunu ve “init” script’leri için service unit wrapper’larının hazır olduğunu görürüz.
Bunlardan “apache2.service” unit’ine göz atalım.


Bu dosyanın içine baktığımızda servisi başlatmak için çalıştırılacak olan komutun yine “init” yönteminde olduğu gibi “init.d” dizini altındaki bash script’inin “start” parametresi ile çağrılması olduğunu görürüz. Yani “systemd” bu unit’i kendi anladığı dile çevirmiş, ancak halen eski yöntemi korumuştur.
Native formatta bir service unit’ine göz attığımızda ise doğrudan çalıştırılabilir dosya isminin bu konfigürasyon parametresine yer aldığını görebiliriz:


Debian’ın “systemd” uygulaması SysV init yöntemini tamamen yok saymadığından dolayı “init.d” script’lerine alışkın olanlar halen servis başlatma ve kapatma için bu script’leri kullanabilirler.
Örneğin aşağıdaki örnekte “ssh” servisini “init” yöntemiyle ve bu yöntemle gelen “service” komutu ile başlatıp kapatacağız
Önce init script’ini doğrudan çağırarak servisi başlatalım ve kapatalım:


Şimdi de “service” komutunu kullanalım:




Kali’nin ağ servisleri hakkındaki genel politikasından makalenin başında bahsetmiştik. Kali bir pentest platformu olduğundan öntanımlı olarak servisleri başlatmadığı gibi bazı servisleri sonradan (apt-get install ile) kurduğunuzda da bunları otomatik olarak başlamaktan alıkoyar. Bunu tam olarak şu şekilde yapar; kurulum sırasında çağrılan “update-rc.d” uygulamasının yerini alacak bir Perl script’i geliştirilmiştir. Bu script eğer söz konusu servis kara listedeyse bunun için bir init script’i oluşturmaz.






Ancak kara listede olsa da servisi yine de reboot sonrası yaşayacak hale getirmek istiyorsanız “systemctl” komutuyla bunu yapabilirsiniz, veya “update-rc.d” scriptine “update-rc.d ssh enable” komutu göndererek servisi aktif hale getirebilirsiniz.

<<Önceki Bölüm                                                                                               Sonraki Bölüm>>