Root tulajdonú fájl írása PHP-ból.

Fórumok

Adott egy szerver amin root jogokkal írni kellene fájlok tartalmát ( /proc/net/nf_condition/ könyvtár állományai a root tulajdonában vannak). Hogy mi legyen a fájlokba írva, és melyikbe írjunk azt egy webfelületről vezérelném.

A webfelület mondjuk lehet PHP. PHP-ból nem szokás írni a fájlrendszert, ott ahol root jog kell. Ezért
arra gondoltam, hogy írok pl. C nyelven egy démont ami írja fájlokat. Hogy mit kell írni, azt meg a démon egy TCP socketen figyelné. A PHP program pedig a démon portjára küldi, mit kell tenni.

Hogyan szokás ezt kulturáltan megoldani? Van esetleg más megoldás?

Egyébként a fájlokba csak 1-est vagy 0-át írunk. Ha bashból írom, akkor így néz ki:


echo 1 > /proc/net/nf_condition/valmifajl

Szóval van megoldásom, de nem tudom így szokás-e ezt csinálni.

Hozzászólások

Szerintem(!) a PHP scripted azt írja át amihez joga van.
Pontosabban amihez a scriptet futtató usernek joga van.
Ebből kifolyóan én tisztán php-ban oldanám meg.
<?php echo exec('echo 1 > /proc/net/nf_condition/valmifajl'); ?>

--------------
“If there were no hell, we would be like the animals. No hell, no dignity.”

Luther: Mondd, problémáid vannak a szövegértelmezéssel? A téma indtója nem-root jogosultságú PHP szkripttel szeretne írni olyan fájl(oka)t, amihez csak root-nak van jogosultsága.

Termih: amit leírsz (daemonos megoldás), az szerintem overkill, erre biztosan nem írnék démont. Én magam sajnos nem tudok kulcsrakész megoldást, de az alábbi kereső kifejezéseknek néznék utána: "suphp", illetve "phpsuexec".

Szerk.: illetve PHP-ból gondolom SUDO segítségével meg lehet hívni például egy másik {bash|python|perl|php|stb.} szkriptet, aminek paraméterként megadod a módosítandó fájl nevét, és a beíandó értékeket. Ez azonban biztonsági kockázatot jelenthet, ha a szkriptben nem ellenőrzöd szigorúan a bejövő paramétereket.

...nem-root jogosultságú PHP szkripttel szeretne írni olyan fájl(oka)t, amihez csak root-nak van jogosultsága.

A root jogot akkor is meg kell szerezni, mert anélkül ugye nem megy a dolog.
Akkor minek külön egy C progi?

--------------
“If there were no hell, we would be like the animals. No hell, no dignity.”

Mert a C program 99.999%-ban azt csinálja, amit beleírsz.
A PHP meg nem azt, amit a scriptbe beleírtál, hanem azt, amit a PHP binárisába beleírtak (azaz kb. bármit).
Annak az esélye, hogy meg tudod írni a C programodat biztonsági résektől mentesre, elég nagy, annak az esélye, hogy a PHP-ban holnapután nem találnak valami full-remote-root-azt-csinálok-a-gépeddel-amit-akarok jellegű biztonsági rést, kb. nulla.

minden program tartalmaz legalább egy változót és egy hibát. :)

szerintem ennyire azért nem vészes a helyzet, hogy bármi csak ugy kibe járkálna a php-n. ezt akkor el lehetne mondani szinte bármire. kernelre, sshra. egyesek szerint simán lehallgatják az ssht, és el tudom hinni. de annak az ellenkezőjét is.

a C-s megoldás egyébként szép lenne...

Én ennyire nem vagyok üldözési mániás, szerintem minden program az utasításaink szerint működik (és nem a kívánságaink szerint). A bug-ról vagy sechole-ról 99.999%-ban a programozó tehet, nem a programozási nyelv.
--------------
“If there were no hell, we would be like the animals. No hell, no dignity.”

szerintem minden program az utasításaink szerint működik (és nem a kívánságaink szerint

Pontosan. Ebből, és a leírásomból pedig leszűrhető, hogy a PHP-t író "programozók" által leírt kód (az utasításaik) minőségéről milyen véleménnyel vagyok.
Ha a PHP scriptet tökéletesen állítod is elő, akkor sincs esélyed a PHP bináris sechole-jait elkerülni, arról nem is beszélve, hogy mennyi esélyed van a tökéletes PHP script előállítására (= mennyire tudhatod, hogy a leírt scripted mit is fog csinálni).

sudo echo 1

:D:D

ezzel kiiratod az 1-et jó rendszergazdásan és teljes uralommal az stdout felett, viszont a shellnek továbbra se lesz joga írni a /proc-ba :D

ugyanis az exec(), system() php-ban valójában ezt csinálja:
IF van magic redirection, vagy pipe karakter a paraméterben?
THEN akkor futtatom /bin/sh -át -c kapcsolóval, majd õ tudja milégyen vele...
ELSE a paraméterben megadott futtatható közvetlenül az én child-em lesz.

~~~~~~~~
http://www.youtube.com/watch?v=VbUVqODL1nE

root tulajdonában van... ha a webszervered nobody-ként fut, akkor próbáld ki, hogy: chown root:nobody /proc/net/nf_condition/valmifajl && chmod 0664 /proc/net/nf_condition/valmifajl

Mert ugye az apache (ugyan azt nem írtad, hogy apache a webszerver, de most egyszerűsítettem...) nevében fut a php is, és az nobody user nobody group. Ha fájlt a csoporttagok írhatják, akkor a webszerver tud bele így írni. Nem emlékszem már hol és mire, de használtam ezt a megoldást nem kis sikerrel.

--
openSUSE 11.4

Nem tudom hogy szokás ezt csinálni, de amennyiben megfelel (nem kell valósidejű reakció), én a két program közti kommunikációt adatbázison keresztül tenném. Így marad egy log-od is arról, hogy ki mit mikor kért.

A "daemon" része pedig legyen egy script ami vagy állandóan fut, vagy cronból van ütemezve.

a démonos végülis kultúrált, de nem feltétlenül kell C, mert


$ mkfifo input        # a php szkriptből ebbe fogsz írni
$ tail -f input | (   # ez pedig root-ként fut
  declare -A files    # és a bash tud asszociatív tömböt, éljen
  files["v1"]="/proc/net/nf_condition/valami1"
  files["v2"]="/proc/net/nf_condition/valami2"

  while read fileid value
  do
    if [ "$fileid" ] && [ "${files[$fileid]}" ] && [ "$value" = 0  -o "$value" = 1 ]
    then
      echo "$value" > "${files[$fileid]}"
    fi
  done
)

$ echo v1 1 > input
$ echo v2 0 > input

de nem feltétlenül kell C, mert

Egy nagy baja van a bash scriptnek: baromi nehéz security szempontból halálbiztos paraméter ellenőrzést írni benne. Márpedig csak akkor van értelme a root jogokat nem közvetlenül a PHP scriptnek adni, ha a PHP és a rootként futó cucc között interfészen kőkeményen ellenőrizzük, hogy hülyeségeket ne lehessen injektálni.

Lehet hogy hülyeséget írok de nem ártana megnézni pl: a Webmin -t. Ha jól emlékszem hasonló képen működik mint amit te szeretnél.
Meg kell nézni hogy ott hogy van megoldva.Vagy akár írni kell hozzá egy Plugin -t és kész.
Bocs ha hülyeséget írtam annyira nem vagyok jártas a témában meg még korán van nagyon.

http://doxfer.webmin.com/Webmin/CustomCommands

"This page covers Webmin's Custom Commands module, which can be used to create buttons to run frequently used shell commands."

Vagy kombináljuk össze a fentieket.
Létrehozol egy csoportot, "valamicsoport" néven. A csoportnak engedélyezed a fájl írását, ahogy fent írták. Majd írsz egy C programot, amit a php exec függvénnyel hívsz meg, és SETGID attribútuma van a bináris állománynak, és a "valamicsoport"-hoz tartozik(oda setgid-ol).

Megj:

Ezt a trükköt a postfix sendmail parancstól lehet ellesni, amit egyébként előszeretettel használ a php.

Akkor most mar elmondom, mert ugy latszik, senkinek nem tunt fel a fajl furcsa eleresi utvonala.

A /proc a memoriaban tarolodik (kicsit kozelebbrol a kernel exportal ide dolgokat), es ketto dolgot nagyon ketlek vele kapcsolatban:
- hogy implementalva vannak rajta a fajlrendszer jogok (hulyeseg, felesleges, stb)
- hogy a valtoztatas egy reboot utan is ugy marad.

Ilyen szemszogbol kellene ujravizsgalni a feladatot.

Ja, es a SUID root (mert ehhez az kellene) az ordogtol valo, es mindenestol kerulendo.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

Igen, komplikált, és akkor már jobb a bash szkript, pipe-on keresztül. Bár be lehet azt is fűzni. Mondjuk a SETGID-elt C program írja a pipe-ot amit a root jogú bash olvas, stb. :)

Megj: Ez akkor már a postfix teljes működését imitálja.

Ha egyszerű megoldást akar az ember, akkor úgy nézem arra jó lehet a sudo.

Az incron-t javaslom. Figyeli a megadott könyvtárat, ha pakolsz bele állományt elindul, meghívja a scriptedet és te örülsz. Természetesen a vizsgálatokat ne hagyd el, nehogy valaki jót szórakozzon.

--
A főnököm mindig megtartja amit ígér, ha pénzt ígér azt is!

És itt az fog történni, hogy a root-ként futó echo parancs betetolja a kimenetét a bash uid-jével futó pipe-ba, ami ugyanígy megpróbálja beírni a fájlba. Nem fog neki sikerülni.
Én ilyen workaroundot találtam, szóljon, aki tud elegánsabbat:
echo "blabla" | sudo tee /proc/...

Bár van már pár éve, de anno én ezt úgy oldottam meg, hogy apache írt saját könyvtárán belül egy fájlba. Egy "init" érték, tehát hogy van-e változás + a szükeséges adatok. És egy, a root user nevében futtatott cron feladat pedig figyelte, hogy van-e teendője. Természetesen nem árt némi szintaktikai és egyéb ellenőrzés, nehogy kártékony és/vagy hibás adatot vegyen át.
/Tényleg nem emlékszem már pontosan, de nekem mintha apache vhostok létrehozásához + ugye apache reload -hoz kellett volna. Vagy bind? Na mindegy, tényleg nem emlékszem már :$/

Mindenkinek köszönöm a hozzászólásokat. Csak a vitákat olvasva is nagyon tanúságos volt.

raziiii:
Ezt nem egészen értem. Belépek ssh-t használva, és web felület helyett parancssorban vezérlek? Vagy tud valamit a ssh2 amiről nem tudok? Ha erre gondoltál a gond az, hogy nem én fogom vezérelni, hanem olyanok akiknek nem lesz linuxos hozzáférésük.

Az exec() parancs működik, de azt tiltani kellen használat helyett. Bár végeredmény csak
LAN-on fog futni.

imp megoldása kiváló és gyors:


$ mkfifo /var/www/dir/input

Az input csővezetéket a www-data tulajdonába adom a dir könyvtárral együtt.

Egy m.sh scriptet készítve ahogy imp írta:


#!/bin/bash
tail -f /var/www/dir/input | (  
  declare -A files    
  files["v1"]="/proc/net/nf_condition/valami1"
  files["v2"]="/proc/net/nf_condition/valami2"

  while read fileid value
  do
    if [ "$fileid" ] && [ "${files[$fileid]}" ] && [ "$value" = 0  -o "$value" = 1 ]
    then
      echo "$value" > "${files[$fileid]}"
    fi
  done
)

Futtatom a háttérben:


$ ./m.sh &

Ezek után írtam egy PHP progit:

A fájlba írás így működik:


<?php
exec('echo v1 1 > /var/www/dir/input');
?>

Szenti ajánlása alapján a Debianban ezeket találtam:
suPHP keresés után:
libapache2-mod-suphp - Apache2 module to run php scripts with the owner permissions

PhpSuExec keresés után:
apache2-suexec-custom - Configurable suexec program for Apache 2 mod_suexec
apache2-suexec - Standard suexec program for Apache 2 mod_suexec

Ezeket nem volt még időm átnézni.

Az incron is nagyon jónak tűnik. Sőt ezt eddig nem ismertem, akár más célra is jó lesz.

A C nyelvű megoldás overkill lenne?
Egyébként már a kérdés feltevése előtt megírtam egy C programot amely fogadja az adatokat egy tetszőleges porton. 90 sor. Igaz jelenleg nem tud többet még meg kellen írni, milyen adatokat fogadjon és azzal mit kezdjen. De gondoltam az már nem sok. Ehhez is készen van egy PHP program ami csomagokat küld a C program adott portjára. Mellesleg nekem azért tetszik egy C program megírása legjobban mert a legkevésbé függ más programoktól, apache moduloktól, PHP beállításoktól, és szeretném ha ezen a téren hordozható lenne.

kreszan:
Igen volt már suexec.

A sudo használata is jó ötlet, ez eszembe sem jutott.

Köszönöm a válaszokat újra!

---
http://szit.hu

Mivel biztonságról van szó, az még belefér, hogy megadjuk, hogy a bash max. mennyi karaktert olvasson be:

echo 12 34 56 78 90 |(read -n 10 a b; echo $a - $b)

Az persze kérdés, hogy véd-e bármiféle buffer overflow szerűségtől, és hogy esetleg csak plusz problémákat okoz, ha az azonosító egyszer csak valamiért hosszabbra sikerül, de ugye minél biztonságosabb valami, annál nehezebb használni :)

Épp így a

while read fileid value

sorok helyett én - ha nem felejtem el - akkor a

while read fileid value garbage

megoldást szeretem használni.

Illetve gondolom az exec - echo csak egy példa, nem végleges :D

Mi lenne ha PHP helyett inkább C-ben írnál egy CGI programot? Ha abban megbízol eléggé akkor az futhatna root-ként.

Nem jo otlet. Barmit, aminek a 80-as porthoz koze van es nem webszerver, rootkent futtatni nem jo otlet. A "megbizol" egy eleg relativ dolog. En peldaul sokaig megbiztam a Wordpressben. Egy ido utan nem. A sajat magam altal irt program ennel meg egy fokkal rosszabb, de valahol meg kell huzni a hatart. A CGI viszont nem azert rossz, mert en irtam, hanem mert van benne egy plusz reteg. Egy hello world is veszelyes, ha a webszerver hibas, es shellhez enged. Nem kene ezt azzal tetezni, hogy egybol root shellt kap a tamado.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

A webszerver továbbra sem kell, hogy root legyen. A C program setuid-dal futhat root-ként, tehát ha valamit elront a webszerver (amúgy is valószínűtlen), akkor sem tud root jogosultságokhoz jutni egy támadó. (Pont ezért mondtam C-t és nem Perl-t vagy bármi más nyelvet.)
Azért is tartom ezt jobb megoldásnak mint a többit, mert sokkal kevesebb rétegen megy át az adat mielőtt a root-ként futtatott programot eléri, így csökken annak az esélye, hogy azokban hiba lesz. Tulajdonképpen az egész PHP-s bohóckodás lényege az lenne, hogy a webszervertől kapott adatot megrágja és átadja egy root-ként futtatott programnak. Setuid-dal és CGI-vel a webszerver és a program között nincs extra réteg ami hibázhatna. Még fontosabb, hogy nincs interpretált réteg, tehát a code injection is ki van zárva.

Nem ertem, hogy miert gondolod, hogy utana root-kent futna barmi is. A C programod nem fork-ol, tehat a root jogosultsagait sem orokiti tovabb. A webszerver meg a parent process, tehat az sem kap root jogosultsagokat. Tehat ha valamit elront a C program a jogosultsagait akkor sem lehet megszerezni.

Nem azzal szokott lenni a baj, hogy továbbörökíti a jogosultságait, hanem azzal, hogy ráveszik olyasmire, amit a programozója nem óhajtott. És mivel rootként fut, nincs további akadály előtte...
Ha valaki egy setuidos programot meg tud hívni, akkor sokkal több esélye van arra, hogy a programot olyasmire rávegye, amit a programozója nem szerett volna. Egy socketon keresztül sokkal kisebb esély van erre.

Nem latom, hogy ez miert lenne igy, sot.
A CGI-s megoldasnal:
Webszerver -> C program root-kent.
A PHP-s megoldasnal:
Webszerver -> PHP -> C program root-kent.

Az hogy a parameterek hogyan kerulnek at majdnem mindegy, hiszen ugyanolyan hibakat lehet vele veteni. Viszont a plusz retegben tovabbi hibak lehetnek. Ezen meg ront az is, hogy a socket tovabbi komplexitast ad a programnak.

Viszont a plusz retegben tovabbi hibak lehetnek.

Nem, nem lehetnek, vannak :) Ezt már csak abból is lehet tudni, hogy a PHP-ról beszélünk...

Az hogy a parameterek hogyan kerulnek at majdnem mindegy, hiszen ugyanolyan hibakat lehet vele veteni.

Ez addig igaz, amíg a paramétereken kívül mást nem tudsz átadni a programnak. Csakhogy command line program indításakor a command line paramétereken felül még a következő dolgok is összekötik az indítót az indított programmal:
- environment
- current wd
- std in/out/err file descriptorok
- esetleges egyéb nyitvahagyott file-ok
- controlling tty

Ha egy socketon keresztül éred el a programot, akkor ezekre a dolgokra nincs ráhatásod, ergó befolyásolni sem tudod a program működését ezeken keresztül.

Persze, lehet tökéletes programokat írni, a fenti dolgokat is lehet kezelni egy C programból, csak éppen érteni kell hozzá. Ha egy socketon keresztül beszélsz a daemonnal, akkor ezekkel akkor sem lesz baj, ha nem csinálsz velük semmit.

Nem hiszem, hogy a webszerver a fork utan nem zarna a sajat nyitott fajlait mielott meg elinditana a CGI-t, de javits ki ha tevedek.
A tamadonak meg mindenkeppen a parametereken keresztul kene tamadnia, mert mast nem tud befolyasolni. Tehat a tamadasi felulet ugyanaz. Ha meg mar root vagy akkor a legkevesbe sem szamit, hogy a parent process mi volt.

A tamadonak meg mindenkeppen a parametereken keresztul kene tamadnia, mert mast nem tud befolyasolni.
A CGI-knek standard esetben is átad a webszerver pl. environment változókban olyan dolgokat, amik a kérésből származnak. Plusz a webszerverekben is szokott lenni bug, tehát nem zárható ki, hogy a támadó olyasmire veszi rá a webszervert, amit az rendes körülmények között nem csinálna.

Dehogyis. A webszervernek kéréseket küldesz, ha nem jól kezeli a kéréseket (bugos), akkor olyan dolgokat is meg lehet vele csináltatni, amit az írói nem akartak. De ez még nem jelenti azt, hogy root lennél, ezen a ponton még csak a webszerver nevében tudsz futtatni valamit. Mondjuk ha ügyes vagy, akkor egy shellt össze tudsz szedni a webszerver nevében (ráveszed egy exec(/bin/sh)-ra), amit a HTTP socketon keresztül tudsz távirányítani. Na ezen a ponton nagyon "hasznos", ha a webszerver usere el tud érni setuid root-os programokat, amik arra számítanak, hogy az őket meghívó valaki nem lesz gonosz hozzájuk, azaz nem védik magukat abból az irányból, "hiszen csak a webszerver hívhatja meg őket, benne pedig megbízunk".

A felvetés az volt, hogy szerinted nem lényeges a setuid root-os CGI-nél, hogy védett-e a webszerver felől, hiszen onnan a támadó nem tudja meghívni neki tetsző módon, csak a paramétereket tudja befolyásolni, ergó ez semmivel nem lesz így kevésbé biztonságos, mint ha tényleg csak a paramétereket adnánk át egy socketen.

Felreértettél. Arra gondoltam, hogy mindenképpen csak a paramétereken keresztül lehet támadni, annak meg úgyis át kell kerülnie a programhoz, tehát nincs különbség a kettő között. Itt nem a command line paraméterekre gondoltam, hanem bármilyen adatra amit át kell adni a programnak. Ha shell-hez jut, akkor sem tud többet tenni mint elindítani a programot tetszőleges paraméterekkel. Magába a kódba úgysem nyúlhat bele. Ha meg az adatokat rosszul dolgozza fel a program, akkor azon a socket sem segít, sőt.

A STDIN pl. nem kerul zarasra, hanem 1:1 kapja meg a CGI (POST eseten).

Azon felul a kornyezeti valtozokba nagyon sok HTTP header is konvertalodik, ez is egy belepesi pont lehet.

Socketnel az van, hogy ugyan lehet, hogy magat a demon processzt ra tudod venni egy shell futtatasara, de nem lesz kapcsolatod vele, hiszen ahhoz, hogy a shell visszajojjon a socketen keresztul, at a PHP-n es elo kapcsolatod legyen vele, ahhoz nagyon sok es bonyolult kodot kell injectalni - gyakorlatilag ujra kell irni ugy a demont, mind a PHP kodot.
CGI-nel valamivel egyszerubb a dolgod, hiszen a STDIN/OUT-tal folyamatos kapcsolatod van a HTTP request miatt (egyszer olvasd el, hogy mukodnek a CGI-k), igy ha elindul a shell, rogton kapcsolatod is van vele. Nincsenek hatarozott hatarok.

Arrol nem is beszelve, hogy CGI-nel nincs timeout, akarmeddig nyitva lehet a session - a PHP jobb helyeken ot perc utan mindenkit kirugdal az erdobol.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

ami raadasul nem is osszetett

De! Minden C program összetett, mivel van egy bazi nagy C library van hozzálinkelve. Amiket a "nagyon rövid" program is használ implicite. Ezért a bazi nagy C library függvényeit is figyelembe kell venni biztonsági szempontból. A helyzetet rontja, hogy ezt a kódot valaki más írta, a dokumentációt a programozó vagy ismeri vagy nem, abból vagy kiderül, hogy hol vannak a biztonsági szempontból necces dolgok, vagy nem. A tuti az, ha az ember a C library kódjának vonatkozó részeit elolvassa.

Nekem inkább legyen 18 képernyőnyi kód, amit én írtam, és ismerem a működését, mint 5, amit más írt, és nekem kell kitalálni, hogy hol fog megszopatni.

Apache-ban is voltak mar remote exec hibak, pedig az is forditott program.

Nem attol lesz valami biztonsagos, hogy nem script, hanem attol, hogy biztonsagossa tesszuk. Az ultimate megoldas az, hogy kompletten levalasztjuk a webrol, semmilyen kontextet ne tudjon kapni rola.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal 

Én ezt úgy szoktam megoldani, hogy a php vagy maga mellé készít egy sima txt-t vagy sql-be dolgozik, azt pedig egy root joggal futó bash scripttel pl. 5 percenként lecsekkolom.

Ez most nagyon csúnya lesz (de talán kiindulási alapnak jó lehet :) ):

/etc/inet.d-be:

127.0.0.1:555 stream tcp nowait root /path/shellscript

A PHP-ban pedig:


if (fsockopen("localhost","555")) { echo "A restart parancs kikuldese
sikeres"; } else echo "Nem sikerult a restart, socket hiba az 555-os
porton";

Authorizaciorol volt szo, az authentikacio teljesen mas. Az authorizacio fogalmat kimeriti az is, ha kotelezo formatuma van a keresnek, es invalid formatumnal lezarjuk a kapcsolatot hibaval. Authentikacio eseteben elobb ervenyesen kell authentikalni, es utana lehet kereseket kuldeni.
--

Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal