Kabuk komut dosyalarında setuid'e izin ver


184

setuidİzni bit yerine uygulamakla sahibinin etkin kullanıcı kimliği ile bir programı çalıştırmak için Linux söyler:

> cat setuid-test.c

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv) {
    printf("%d", geteuid());
    return 0;
}

> gcc -o setuid-test setuid-test.c
> ./setuid-test

1000

> sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test
> ./setuid-test

65534

Ancak, bu yalnızca çalıştırılabilirler için geçerlidir; kabuk komutları setuid bitini yok sayar:

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

Wikipedia diyor ki :

Artan güvenlik açığı olasılığı nedeniyle, birçok işletim sistemi çalıştırılabilir kabuk komut dosyalarına uygulandığında setuid özelliğini yok sayar.

Bu riskleri kabul etmeye istekli olduğumu farz edersek, Linux'a setuid bitine çalıştırılabilir dosyalardaki gibi kabuk komut dosyalarında olduğu gibi davranmasını söylemenin bir yolu var mı?

Değilse, bu sorun için ortak bir geçici çözüm var mı? Mevcut çözümüm, şifre istemini önlemek için , çalışmasını istediğim kullanıcı olarak belirli bir komut dosyasını çalıştırmaya sudoersizin vermek ALLiçin bir giriş eklemek NOPASSWD. Bunun en büyük dezavantajı, sudoersbunu yapmak istediğim her seferde bir girişe ihtiyaç duyulması sudo some-scriptve sadece arayan yerine arayanın yapması gerektiğisome-script

Yanıtlar:


202

Linux, yorumlanmış tüm çalıştırılabilir dosyalardaki (örneğin bir #!satırdan başlayarak çalıştırılabilen çalıştırmalar) ayar bileşenini yok sayar . Comp.unix.questions SSS setuid kabuk komut ile güvenlik sorunlarını açıklar. Bu problemler iki türdür: shebang ve shell ile ilgili; Aşağıda daha fazla ayrıntıya giriyorum.

Güvenliği umursamıyorsanız ve setuid komut dosyalarına izin vermek istiyorsanız, Linux altında çekirdeği düzeltmeniz gerekir. 3.x çekirdeklerin olarak sana bir çağrı eklemek gerektiğini düşünüyorum install_exec_credsiçinde load_scriptçağrısı önce, işlevi open_exec, ama ben test etmedim.


Setuid Shebang

Shebang ( #!) 'nin tipik olarak uygulanma biçimine özgü bir yarış koşulu vardır :

  1. Çekirdek yürütülebilir dosyayı açar ve onunla başladığını bulur #!.
  2. Çekirdek yürütülebilir dosyayı kapatır ve bunun yerine yorumlayıcıyı açar.
  3. Çekirdek betiğe yolu argüman listesine (as argv[1]) ekler ve yorumlayıcıyı çalıştırır.

Bu uygulamayla setuid komut dosyalarına izin verilirse, bir saldırgan mevcut bir setuid komut dosyasına sembolik bir bağlantı oluşturarak, uygulayarak ve çekirdeğin adım 1'i tamamladıktan sonra ve tercüman dolaşmadan önce bağlantıyı değiştirerek düzenleme yaparak rasgele bir komut dosyası çalıştırabilir ilk argümanını açıyor. Bu nedenle, çoğu sendika bir shebang tespit ettiklerinde setuid bitini yok sayarlar .

Bu uygulamayı güvence altına almanın bir yolu, çekirdeğin yorumlayıcı açılıncaya kadar komut dosyasını kilitlemesi olacaktır (bunun yalnızca dosyanın bağlantısını kesmesini veya üzerine yazmasını değil, aynı zamanda yoldaki herhangi bir dizini yeniden adlandırmasını da engellemesi gerektiğini unutmayın). Ancak, unix sistemleri zorunlu kilitlerden uzak durma eğilimindedir ve sembolik bağlantılar özellikle zor ve istilacı olarak doğru bir kilitleme özelliği sağlar. Kimsenin böyle yaptığını sanmıyorum.

Birkaç unix sistemi (temel olarak OpenBSD, NetBSD ve Mac OS X, hepsi etkin olmak üzere bir çekirdek ayarı gerektirir) , ek bir özellik kullanarak güvenli setuid shebang uygular : yol , önceden tanımlayıcı N'de açılmış olan dosyaya atıfta bulunur (açılış , kabaca eşittir ). Pek çok unix sistemi (Linux dahil) ayarlanmış komut dosyalarına sahiptir ancak bu komutları kullanmamıştır./dev/fd/N/dev/fd/Ndup(N)/dev/fd

  1. Çekirdek yürütülebilir dosyayı açar ve onunla başladığını bulur #!. Çalıştırılabilir dosya tanımlayıcısının 3 olduğunu varsayalım.
  2. Çekirdek tercümanı açar.
  3. Çekirdek /dev/fd/3bağımsız değişken listesini (as argv[1]) ekler ve yorumlayıcıyı yürütür.

Sven Mascheck'in shebang sayfası , setuid desteği de dahil olmak üzere, sendikalar arasında shebang hakkında birçok bilgi içermektedir.


Setuid tercümanlar

İşletim sisteminizin setuid shebang'ı desteklediğinden veya yerel bir ikili sarmalayıcı kullandığınızdan (örneğin sudo) , programınızın root olarak çalışmasını sağladığınızı varsayalım . Bir güvenlik deliği açtın mı? Belki . Buradaki sorun, derlenmiş programların yorumlanmasıyla ilgili değildir . Sorun, çalışma zamanı sisteminizin ayrıcalıklarla yürütüldüğünde güvenli davranıp davranmamasıdır.

  • Dinamik olarak bağlanmış herhangi bir yerel ikili çalıştırılabilir program, program tarafından istenen dinamik kütüphaneleri yükleyen dinamik yükleyici (örn. /lib/ld.so) Tarafından yorumlanır . Birçok ünitede, ortamdaki dinamik kitaplıklar için arama yolunu yapılandırabilir ( LD_LIBRARY_PATHortam değişkeni için ortak bir addır) ve hatta yürütülen tüm ikili dosyalara ek kitaplıklar yükleyebilirsiniz ( LD_PRELOAD). Programın invoker özel hazırlanmış bir yerleştirerek bu programın bağlamında rasgele kod yürütebilir libc.soiçinde $LD_LIBRARY_PATH(diğer taktik arasında). Tüm mantıklı sistemler LD_*, setuid çalıştırılabilir değişkenlerini görmezden gelir .

  • Gelen kabukları gibi sh csh'ın ve bunların türevleri gibi, çevre değişkenleri otomatik olarak kabuk parametresi olur. Gibi parametreler aracılığıyla PATH, IFSve daha birçok senaryonun invoker kabuk dosyalarınızın en bağlamında rasgele kod çalıştırmak için birçok fırsat vardır. Bazı mermiler, betiğin ayrıcalıklarla çağrıldığını algılarlarsa, bu değişkenleri varsayılan değerlere ayarlarlar, ancak güvenebileceğim herhangi bir uygulama olduğunu bilmiyorum.

  • Çoğu çalışma zamanı ortamı (yerel, bayt kodu veya yorumlanmış olsun) benzer özelliklere sahiptir. Bazıları setuid çalıştırılabilir işlemlerinde özel önlemler alır, ancak yerel kodu kullananlar genellikle dinamik bağlantıdan (bu önlemleri alır) daha fazla bir şey yapmazlar.

  • Perl önemli bir istisnadır. Bu açıkça Setuid komut dosyalarını destekleyen güvenli bir şekilde. Aslında, işletim sisteminiz komut dosyalarındaki setuid bitini dikkate almamış olsa bile betiğiniz setuid komutunu çalıştırabilir. Bunun sebebi perl'in gerekli kontrolleri yapan ve tercümanı istenen komut dosyası üzerinde istenen yetkilerle yeniden dizginleyen bir setuid root yardımcı ile birlikte gönderilmesidir. Bu perlsec kılavuzunda açıklanmıştır . Eskiden #!/usr/bin/suidperl -wTbunun yerine gerekli olan setuid perl scriptleriydi #!/usr/bin/perl -wT, ancak çoğu modern sistemde #!/usr/bin/perl -wTyeterliydi.

Yerel bir ikili sarmalayıcı kullanmanın bu sorunları önlemek için kendi içinde hiçbir şey yapmadığını unutmayın . Aslında, durumu daha da kötüleştirebilir , çünkü çalışma zamanı ortamınızın ayrıcalıklarla başlatıldığını algılamasını ve çalışma zamanı yapılandırılabilirliğini atlamasını engelleyebilir.

Yerel bir ikili sarmalayıcı, sarmalayıcı çevreyi temizlediğinde kabuk kodunu güvenli hale getirebilir . Senaryo çok fazla varsayımda bulunmamaya özen göstermelidir (örn. Mevcut dizin hakkında), ancak bu geçerli. Bunun için sudo'yu, çevreyi sterilize edecek şekilde ayarlanması koşuluyla kullanabilirsiniz. Kara liste değişkenleri hataya açıktır, bu nedenle her zaman beyaz listedir. Sudo ile env_resetseçeneğin açık setenv, kapalı, env_fileve env_keepsadece zararsız değişkenleri içerdiğinden emin olun .


TL, DR:

  • Setuid Shebang güvensiz ama genellikle göz ardı edilir.
  • Ayrıcalıklı bir program çalıştırıyorsanız (sudo veya setuid aracılığıyla), yerel kod veya perl yazın veya çevreyi dezenfekte eden bir sarmalayıcıyla ( env_resetseçenekle sudo gibi) programı başlatın .

Discussion “setuid” yerine “setgid” yerine geçtiyseniz, bu tartışma aynı şekilde geçerlidir; ikisi de Linux çekirdeği tarafından senaryolarda yoksayılır


2
@Josh: Güvenli setuid kabuk komut dosyaları mümkündür, ancak yalnızca hem kabuk uygulayıcısı hem de komut dosyası yazarı çok dikkatliyse. Yerel kodlardan ziyade, uygulayıcıların setuid betiklerinin senaryo yazarı tarafında çok az çaba harcayarak güvenli olmaları gerektiğine dikkat ettikleri Perl'i öneririm.
Gilles

3
Görünüşe göre suidperlmalzeme
itiraz edildi

7
Aslına bakarsanız suidperlperl 5.11'den (5.12 kararlı) çıkarıldı: perl5110delta:> "suidperl" kaldırıldı. Düzgün desteklemeyen sistemlerde ayarsız izin bitlerini taklit eden bir mekanizma sağlamak için kullanılır. perl5120delta:> "suidperl" artık Perl’in bir parçası değil. Düzgün desteklemeyen sistemlerde ayarsız izin bitlerini taklit eden bir mekanizma sağlamak için kullanılır.
Randy Stauner

5
Ayrıca, bu satırı 5.6.1 belgelerin (yaklaşık on yıl önce) perl561delta'dan aldığına dikkat edin:> suidperl'in perl'ün son sürümlerine eklenmemiş veya varsayılan olarak yüklenmemiş olduğuna dikkat edin. Suidperl kullanımı kesinlikle önerilmez . İhtiyacınız olduğunu düşünüyorsanız, önce sudo gibi alternatifleri deneyin. Bkz courtesan.com/sudo .
Randy Stauner

3
Anlamıyorum: bu sorunların nedenlerinin bir açıklaması gibi görünüyor, ancak OP'nin buradaki sorusuna gerçek bir cevap var mı? İşletim sistemime setuid shell scriptleri çalıştırmasını söylememin bir yolu var mı?
Tom

55

Bu sorunu çözmenin bir yolu, kabuk betiğini setuid bitini kullanabilen bir programdan çağırmaktır.
sudo gibi bir şey. Örneğin, bir C programında bunu nasıl başaracağınız:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

Setuid-test2.c olarak kaydedin.
derleme
Şimdi bu programın setuid'lerini ikili yapın:

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

Şimdi, onu çalıştırabilmelisin ve senaryonun kimsenin izni olmadan çalıştırıldığını göreceksin.
Ancak burada ayrıca komut dosyası yolunu kodlamanız veya yukarıdaki exe'ye komut satırı argümanı olarak geçirmeniz gerekir.


25
Senaryoyu komut satırı argümanı olarak geçmesine izin verme önerisine karşı öneride bulunacağım, çünkü aslında programı çalıştırabilecek herhangi bir komut dosyasını tanımlanmış kullanıcı olarak çalıştırabilecek birini verir.
dsp

39
Komut dosyasının tam yolu kodlanmış olsa bile BU GÜVENİLİRDİR. Kabuk, değişkenleri çevreden devralır ve birçoğu istilacının keyfi kodlar eklemesini sağlar. PATHve LD_LIBRARY_PATHaçık vektörlerdir. Bazı kabuklar yürütmek $ENVveya $BASHENVya ~/.zshenvsen dosyasindan, bu hiç de gelen koruyamaz böylece onlar komut düzgün yürütülmesi başlamadan önce bile. Ayrıcalıklı bir kabuk betiğini çağırmanın tek güvenli yolu çevreyi temizlemek . Sudo güvenli bir şekilde nasıl yapılacağını bilir. Bu yüzden kendi paketleyicinizi yazmayın, sudo kullanın .
Gilles

28
Bunun için birden bire düşürüldüğü için kendimi kötü hissediyorum - özellikle güvensiz sürümleri de duymak istediğimi söyledim ve bunu söylediğimde bir kabuk betiği argümanı alan bir yürütülebilir dosya hayal ediyordum. Açıkçası, bu büyük ölçüde güvensiz, ama hangi olasılıkların mevcut olduğunu bilmek istedim
Michael Mrozek

8
@Gilles: FYI, Linux LD_LIBRARY_PATHsetuid biti ile karşılaştığında diğer şeylerin arasında bozuluyor .
Şubat'ta 15:12

3
Kullanmak yerine system, execaileden birini kullanmayı daha basit (ve daha verimli) bulabilirsiniz - büyük olasılıkla execve. Bu şekilde, yeni bir işlem oluşturmaz veya bir kabuk başlatmazsınız ve argümanları iletebilirsiniz (ayrıcalıklı komut dosyanızın argümanları güvenle işleyebileceğini varsayarak).
Toby Speight

23

Bu yüzden bu teknede bulunan bir kaç senaryo ekledim:

#!/bin/sh
[ "root" != "$USER" ] && exec sudo $0 "$@"

Bunun kullanılmadığını setuidancak sadece mevcut dosyayı ile birlikte çalıştırdığını unutmayın sudo.


4
Bu setuid kullanmaz ama sadece size bir bilgi verir sudo. (Benim için, setuid tüm mesele sudo gerekmeden şeylerin kök olarak çalışmasına izin veriyor.)
Luc

12

Eğer arama yapmaktan kaçınmak sudo some_scriptistiyorsanız sadece şunları yapabilirsiniz:

  #!/ust/bin/env sh

  sudo /usr/local/scripts/your_script

SETUID programlarının, kök ayrıcalıklarıyla çalıştıkları ve kullanıcılar üzerinde büyük kontrol sahibi oldukları için büyük özenle tasarlanması gerekir. Her şeyi kontrol altına almaları gerekiyor. Scriptlerle yapamazsınız çünkü:

  • Kabuklar, kullanıcı ile yoğun şekilde etkileşime giren büyük yazılım parçalarıdır. Her şeyi kontrol ettirmek neredeyse imkansızdır - özellikle de kodun çoğu bu modda çalıştırılmak istenmediğinden.
  • Senaryolar çoğunlukla hızlı ve nihayetinde bir çözümdür ve genellikle setuide izin verecek kadar özenle hazırlanmazlar. Potansiyel olarak tehlikeli birçok özelliğe sahiptirler.
  • Onlar büyük ölçüde diğer programlara bağlı. Kabuğun kontrol edilmesi yeterli değildir. sed, awkvb. de kontrol edilmelidir.

sudoBazı akıl sağlığı kontrolleri sağladığını ancak yeterli olmadığını lütfen unutmayın - kendi kodunuzdaki her satırı kontrol edin.

Son bir not olarak: yeteneklerini kullanmayı düşünün. Kullanıcıya çalışan bir işlemi, normalde kök ayrıcalıklar gerektiren özel ayrıcalıklar vermenizi sağlar. Bununla birlikte, örneğin, pingağı yönetmesi gerekmekle birlikte, dosyalara erişmesi gerekmez. Ancak miras kaldıklarından emin değilim.


2
Sanırım /ust/bin/envolmalıdır /usr/bin/env.
Bob,

4

Sudo + komut dosyasının adı için bir diğer ad oluşturabilirsiniz. Tabii ki, bu daha kurmak için bir iş, çünkü o zaman da bir takma ad oluşturmak zorundasınız, ancak sudo yazmak zorunda kalmamanızdan sizi kurtarıyor.

Ancak korkunç güvenlik risklerine aldırış etmiyorsanız, kabuk betiği için tercüman olarak setuid bir kabuk kullanın. Bunun senin için işe yarayıp yaramayacağını bilmiyorum, ama sanırım olabilir.

Yine de bunu yapmama konusunda tavsiyede bulunduğumu söylememe izin verin. Sadece eğitim amaçlı söylüyorum ;-)


5
Çalışacak. Dediğin gibi korkunç. SETUID biti, hak sahibi ile yürütülmesine izin verir. Setuid kabuğu (setuid ile çalışmak üzere tasarlanmadıysa), herhangi bir kullanıcı için root olarak çalışacaktır. Yani herhangi biri koşabilir rm -rf /(ve serideki diğer komutları EVDE YAPMAYIN ).
Maciej Piechotka

9
Tarafından @MaciejPiechotka evde YAPMAYIN Şunu o iş yerinde yapmak için çekinmeyin? :)
peterph

4

super [-r reqpath] komutu [args]

Süper, belirtilen kullanıcıların komut dosyalarını (veya diğer komutları) sanki kökmüş gibi çalıştırmalarına izin verir; veya komutu çalıştırmadan önce kullanıcı başına, gidonda ve / veya yardımcı grupları komut başına belirleyebilir. Scriptlerin setuid kökü haline getirilmesinde güvenli bir alternatif olması amaçlanmıştır. Süper ayrıca sıradan kullanıcıların başkaları tarafından yürütülmesi için komut vermesini sağlar; bunlar komutu sunan kullanıcının uid, gid ve grupları ile yürütülür.

Super, kullanıcının istenen komutu yürütmesine izin verilip verilmediğini görmek için bir `` super.tab '' dosyasına başvurur. İzin verilirse, super, pgm [args] komutunu çalıştırır, burada pgm bu komutla ilişkili programdır. (Kök, varsayılan olarak yürütmeye izin verilir, ancak bir kural kök dışlarsa, yine de reddedilebilir.

Eğer komut, süper programa sembolik bir link (ya da hard link) ise,% command args yazmanız,% super command args yazmanıza eşdeğerdir (Komut süper olmamalıdır, ya da super bir bağlantı.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html


Merhaba ve siteye hoş geldiniz! Yanıtların burada daha ayrıntılı olmasını bekliyoruz. Cevabınızı düzenleyebilir ve bu programın ne olduğunu ve OP'nin problemini nasıl çözebileceğini açıklayabilir misiniz ?
terdon

Teşekkürler Nizam, saatlerce, hesabın başka bir kullanıcı tarafından dosyanın kullanıcısı olarak yürütülmesine izin verilen süper hesaplar gibi bir şey bulmaya çalışıyor.
Chad

2

Herhangi bir sebepten ötürü sudomevcut değilse, C'ye ince bir sarma betiği yazabilirsiniz:

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

Eğer derleme kez Ve olarak ayarlamak setuidile chmod 4511 wrapper_script.

Bu, gönderilen başka bir cevaba benzer, ancak komut dosyasını temiz bir ortamla çalıştırır ve açıkça /bin/bashçağırdığı kabuk yerine kullanır system()ve bu nedenle bazı olası güvenlik açıklarını kapatır.

Bunun çevreyi tamamen attığını unutmayın. Bazı çevresel değişkenleri güvenlik açıkları açmadan kullanmak istiyorsanız, gerçekten kullanmanız gerekir sudo.

Açıkçası, senaryonun kendisinin sadece root tarafından yazılabilir olduğundan emin olmak istiyorsun.


-3

İlk önce tüm soruların ikna edilmediği bu soruyu buldum, burada çok daha iyi bir soru var.

Kendinden açıklamalı olmalıdır.

#include <string>
#include <unistd.h>

template <typename T, typename U>
T &replace (
          T &str, 
    const U &from, 
    const U &to)
{
    size_t pos;
    size_t offset = 0;
    const size_t increment = to.size();

    while ((pos = str.find(from, offset)) != T::npos)
    {
        str.replace(pos, from.size(), to);
        offset = pos + increment;
    }

    return str;
}

int main(int argc, char* argv[])
{
    // Set UUID to root
    setuid(0);

    std::string script = 
R"(#!/bin/bash
whoami
echo $1
)";

    // Escape single quotes.
    replace(script, std::string("'"), std::string("'\"'\"'"));

    std::string command;
    command = command + "bash -c '" + script + "'"; 

    // Append the command line arguments.
    for (int a = 0; a < argc; ++a)
    {
        command = command + " " + argv[a];
    }

    return system(command.c_str());
}

Sonra sen koş

g++ embedded.cpp -o embedded
sudo chown root embedded
sudo chmod u+s embedded

Neden indirgemeler ???
Theodore R. Smith

2
Muhtemelen dize manipülasyonu yapan ve gömülü kabuk koduna sahip aşırı karmaşık bir çözüm olduğundan. Ya basit bir fırlatıcı yaparsın ya da sudo kullanırsın. Bu, tüm seçeneklerin en kötüsü.
Siride
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.