1 puan yazan GN⁺ 4 시간 전 | 1 yorum | WhatsApp'ta paylaş
  • Minimal konteyner imajlarında curl veya wget çoğu zaman bulunmaz; bu yüzden paket kurmadan iç servis bağlantısını doğrulamak için alternatif bir yöntem faydalıdır
  • Bash içindeki /dev/tcp/host/port yönlendirmesi bir TCP soketi açabildiğinden, HTTP/1.1 istek metnini doğrudan yazıp göndererek yanıt okunabilir
  • /dev/tcp, dosya sistemi yolu değil Bash’in dahili bir özelliği olduğundan ls /dev/tcp ya da başka kabuklardaki normal dosya erişim yöntemiyle çalışmaz
  • Bu yöntem; yönlendirmeler, chunked yanıtlar, sıkıştırma, yeniden deneme ve TLS’yi ele almayan basit bir hata ayıklama tekniğidir ve Connection: close olmadan cat beklemede kalabilir
  • Günlük HTTP işleri için doğru araç curl’dür; ancak araç eklemenin zor olduğu küçük konteynerlerde hızlı bağlantı kontrolü için yeterlidir

Bash dosya tanımlayıcısıyla HTTP isteği yazmak

  • Dahili Docker ağı içinde başka bir servisin /health endpoint’ine erişilebildiğini doğrulamak gerekiyordu, ancak imajda curl veya wget yoktu
  • Bash, TCP soketlerini dosya tanımlayıcısına bağlayabildiği için HTTP isteğini aşağıdaki gibi doğrudan yazıp göndermek mümkündür
exec 3<>/dev/tcp/service/8642
printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3
cat <&3
  • service, komutun çalıştığı yerden çözümlenebilen ve erişilebilir bir ana makine adı olmalıdır
    • Docker ağında tanımlı bir konteyner veya servis adı olabilir
    • Çözümlenebilir bir DNS adı da kullanılabilir
    • Ana makine ve port ortama göre değiştirilmelidir
  • Yanıt çıktısı durum satırını, başlıkları, boş satırı ve gövdeyi birlikte içerir
  • Başlık eklemek için isteği bitiren boş satırdan önce \r\n ile biten satırlar eklemek yeterlidir
exec 3<>/dev/tcp/service/8642
printf 'GET /v1/models HTTP/1.1\r\nHost: service\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n' "$API_KEY" >&3
cat <&3

/dev/tcp neden gerçek bir dosya değil

  • /dev/tcp, gerçek bir aygıt dosyası değil, Bash’in işlediği bir yönlendirmedir
    • Diskte böyle bir yol bulunmadığı için ls /dev/tcp başarısız olur
    • Başka bir kabukta cat /dev/tcp/... çalıştırmak da hata verir
  • Bash manual’a göre /dev/tcp/host/port içinde host geçerli bir ana makine adı veya internet adresi, port ise tamsayı bir port numarası ya da servis adıysa Bash bir TCP soketi açmayı dener
  • Bash, DNS sorgusunu ve connect(2) çağrısını yapar; exec 3<> ise soketi dosya tanımlayıcısı 3e bağlayarak okuma ve yazmayı mümkün kılar

HTTP istemcisinin yerine geçen bir araç değil, geçici bir kontrol aracı

  • Bu yaklaşım gerçek bir HTTP istemcisi olmadığı için yönlendirmeleri, chunked yanıtları, sıkıştırmayı, yeniden denemeleri, TLS’yi vb. işlemez
  • Connection: close başlığı önemlidir
    • Olmadığında sunucu HTTP/1.1 varsayılanına göre bağlantıyı açık tutabilir
    • Bu durumda cat <&3, EOF beklediği için sonlanmayabilir
  • timeout 6 bash -c '...' gibi bir sarmalayıcı kullanmak, bağlantının kapanmadığı durumlara karşı da koruma sağlayabilir
  • /dev/tcp ham soket açtığı için yalnızca düz HTTP için geçerlidir; https için openssl s_client gerekir
  • POSIX özelliği değil, Bash özelliğidir; bu nedenle Debian’daki /bin/sh olan dash veya zsh içinde kullanılamaz, bash doğrudan çağrılmalıdır
  • Bash derlenirken --enable-net-redirections ile etkinleştirilen bir derleme zamanı seçeneğidir
  • Sonuç olarak bu, curl’ün yerini alacak genel amaçlı bir araçtan çok, ek paket kurulamayacak küçük konteynerlerde hızlı bağlantı doğrulaması için uygun bir yöntemdir

1 yorum

 
GN⁺ 4 시간 전
Hacker News görüşleri
  • 90’ların sonunda çocukken telnet ile 80, 25, 110 portlarına bağlanıp sunucuyla doğrudan konuşulabildiğini öğrenince çok sarsılmıştım
    Basit bir GET / HTTP/1.1 isteğini elle yazabiliyor, 25 numaralı portta HELO, mail-from, mail-to ile e-posta gönderebiliyor, POP3 ile posta kutusu listesini ve tek tek iletileri çekebiliyordunuz
    Bu deneyim, “sihir diye bir şey yok” farkındalığının başlangıcıydı; bilgisayarın her parçasının insanlar tarafından yapıldığını ve emek verirseniz belli bir seviyeye kadar anlaşılabileceğini gösterdi
    Gelecekte bunların çoğunu ajanlara bırakacağız muhtemelen, ama model ve güvenlik katmanlarının filtreleri olmadan gerçek işleyişi öğrenmek isteyenler için çeşitli sistemlerde ilginç açıklıklar kalacak gibi görünüyor

    • DKIM/SPF yokken ve SMTP sunucu doğrulaması gevşekken, jacques.chirac@elysee.fr gibi bir adresten e-posta gönderip arkadaşlarınıza hacker gibi görünebiliyordunuz
    • O zamanlar sadece DKIM veya SPF yok değildi, SMTP sunucularının çoğu da herkesten herkese posta kabul eden birer open relay idi
    • Sonuçta hepsi sadece metin dosyası
      Yapılandırılmış metin dosyaları oluşturmanın, göndermenin ve okumanın çeşitli yollarının üzerine bir sürü kısaltma eklenmiş bir yapıydı
      Bir gün veritabanlarının bile aslında metin dosyası olduğunu fark edince kısa süreliğine oturup düşünmem gerekti
    • Geçen yüzyılda şirkette kişisel e-posta okuyup göndermek gerektiğinde, her biri için telnet ile POP3 ve SMTP’ye bağlanırdık
    • HTTP/2 ile bu şekilde yapamazsınız ama neyse ki neredeyse tüm sunucular hâlâ HTTP/1 de konuşuyor
      TLS de telnet ile çalışmıyor ve birçok sunucu HTTP isteklerine sadece yönlendirme döndürüyor
      telnet yerine openssl s_client kullanırsanız TLS içinde metni tünelleyebilirsiniz ama biraz hile gibi hissettiriyor
      Modern protokollerin çoğunun ikili kodlamayı tercih etmesi yüzünden, özel araçlar olmadan hat seviyesinde kurcalamanın zorlaşmış olması da üzücü
      Yine de gelecekte de bunları kurcalayan insanlar olacaktır; çubukla ateş yakmak ya da kilden tuğla pişirmek gibi eski teknikler eğlencelidir ve bazen gerçekten işe yarar
      Hatta AI sayesinde deney yapmak daha da kolaylaştı; RFC’leri didiklemek yerine LLM’ye sorarak örneğin yaygın IMAP komutlarının çoğunu öğrenebilirsiniz
  • zsh içinde, Bash’teki /dev/tcp’den ayrı olarak zsh/net/tcp ve zsh/zftp modülleri var
    https://zsh.sourceforge.io/Doc/Release/TCP-Function-System.h...
    https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#The-...
    https://zsh.sourceforge.io/Doc/Release/Zftp-Function-System....

  • Plan 9’da gerçek bir sentetik dosya sistemi olan /net vardı; bu sayede herhangi bir programdan bunları ve daha fazlasını yapabiliyordunuz
    Hatta başka bir makinenin /net’ini 9P protokolüyle mount edip anlık bir VPN gibi kullanabiliyordunuz; 9front ile Linux üzerinde de deneyebilirsiniz
    Go kütüphanesinde de Plan 9 tarzı /net izleri görünüyor; sanki Rob Pike’ın mirası gibi

  • example.com üzerinde gayet iyi çalışıyor
    exec 3<>/dev/tcp/example.com/80 ile açıp printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' >&3 gönderdikten sonra cat <&3 derseniz HTTP/1.1 200 OK alırsınız
    Bugünlerde HTTPS zorlamayan alan adı çok az olduğu için, böyle testlerde insanın yolu sonunda example.com’a düşüyor

    • Halka açık WiFi captive portal bozulduğunda da example.com faydalı
      Tarayıcıda http://example.com adresine giderseniz captive portal sayfasına yeniden yönlendirilip internet erişim sürecini tekrar tamamlayabilirsiniz
    • printf içine gerçek satır sonları koysanız da çalışıyor
      \r olması teknik olarak doğru ama olmasa da çalışıyor
  • Bir arkadaşın bilgisayarıyla konuşmak için herkesin bash -i >& /dev/tcp/IP/PORT 0>&1 kullandığına dair şaka yapılabilir

  • Bash'in yaptığı şey HTTP konuşmak değil, TCP soketi açabilmeyi sağlaması
    Burada yapılan şey HTTP'yi doğrudan konuşmak; test ya da debug için idare eder ve elle denemesi eğlencelidir, ama gerçek insansız ortamlarda böyle sahte bir HTTP istemcisi kullanmak başınıza iş açar
    Bu oyuncak kod HTTP'yi düzgün parse edemediği için bozulabilir
    Elbette Bash ile tam teşekküllü bir HTTP/1.1 istemcisi yazmak da mümkündür, hatta saf Bash ile bir HTTP sunucusu da yapılabilir: https://github.com/bahamas10/bash-web-server
    Daha az çılgın bir seçenek olarak genelde nc vardır ve çoğu durumda daha mantıklı olan da budur

    • “Saf Bash ile tam teşekküllü bir HTTP sunucusu” ifadesi teknik olarak yanlış
      Bash, gelen bağlantıları kabul etmek için TCP/UDP soketlerini dinleyemez
      bash-web-server projesi C ile yazılmış bir soket dinleyicisini derliyor ve çalışma zamanında bunu bir “yerleşik” modül olarak dinamik biçimde yükleyerek bu işlevi sağlıyor
      [0] https://github.com/bahamas10/bash-web-server/tree/main/loada...
    • Bu düzeltme doğru; yazıdaki ifade fazla kaçmış, daha doğru olacak şekilde güncelleyeceğim
      nc ya da benzeri netcat araçları daha iyi bir seçim olurdu ama o sırada kullandığım imajda böyle bir araç yoktu
    • O kadar da çılgın sayılmaz
      HTTP/1.1 ve zorunlu Host başlığı ortaya çıkmadan önce de insanlar HTTP isteklerini elle giriyordu
      Ciddi kullanım için bunu yapmak delilik olur, Bash ile web sunucusu yazmak da öyle; ama hızlı testler için gayet iyidir
    • Hatta biri saf Bash Minecraft sunucusu bile yaptı
      https://sdomi.pl/weblog/15-witchcraft-minecraft-server-in-ba...
    • Bash için Rails benzeri bir framework de var: https://github.com/jneen/balls
  • Bauhinia ekibinin bir CTF sorusunu çözerken bunu kullandığını görüp öğrenmiştim
    Bu, birkaç aşamadan oluşan bir CTF'ti; ilk aşamada ROP zinciriyle bir system shell elde ediyorsunuz ama ortam fiilen Bash dışında neredeyse hiçbir şey çalıştıramadığınız bir hapishane gibiydi
    Kullanılabilen şeyler aşağı yukarı sadece read ve cat idi; bu yüzden cat /dev/tcp kullanıp bunu sanal terminale yönlendirdiler, ardından iç sistemi işaret eden URL'yi alıp flag'i bulmak için içeriğini okudular

  • Dahili Docker ağı içinde container'lar arası bağlantıyı kontrol ederken, imajda ne curl ne de wget olduğu için bu yöntemi keşfettim
    Şaşırtıcı olan, Bash'te /dev/tcp bulunması ve biraz shell büyüsüyle HTTP isteğine benzer bir şey üretilebilmesiydi
    Örneğin exec 3<>/dev/tcp/service/8642 ile açıp printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3 gönderdikten sonra cat <&3 yapabilirsiniz
    Buradaki service, bağlanılacak ana makine adıdır; 8642 ise HTTP konuşmayı denediğiniz porttur

    • Havalı görünüyor ama neden doğrudan curl destekleyen bir imaj kullanmıyorsunuz diye merak ediyorum
      Aklıma gelen bir dezavantaj yok; hatta production imajlarında bile neredeyse şart sayarım
  • Eski Debian ve Debian türevi dağıtımlarda bu özellik çalışmıyordu; çünkü sanal dosya üzerinden TCP erişimi varsayılan olarak devre dışıydı
    Bildiğim kadarıyla 2009'da bu konudaki tutum değişti ve özellik etkinleştirildi; tartışmalar ve bağlantılar Bug #146464 içinde yer alıyor
    <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464#37>
    Shell araçlarıyla ağ işlevlerine doğrudan erişmenin başka yolları da var; örneğin curl, wget, Perl'in HEAD ve GET komutları, netcat/nc, socat, telnet vb.

  • Gençlik yıllarımda başkalarının /dev/ptty aygıtına echo ile ürkütücü mesajlar gönderip onları korkuttuğumu hatırlıyorum
    Benim gönderdiğim mesajlar karşı tarafın açık terminalinde sihirli şekilde belirirdi
    Bilgisayar laboratuvarında neden her istemci için farklı hesaplar kullanıp onları kilitlemediklerini hâlâ bilmiyorum; belki de bu, o zamanın VAX sınırlamalarından kaynaklanıyordu