RIPSTech presents
Java Security Calendar 2019
Day 1 - Candy Cane
Can you spot the vulnerability?
|
|
The function extractString
opens an OpenOffice document which was previously uploaded by an attacker and extracts the text from it.
An OpenOffice document is actually a ZIP file which contains multiple resources. Amongst other things the document contains a file named content.xml which contains
the text information in XML representation. This file is parsed by the method org.jdom2.input.SAXBuilder.build()
which is vulnerable to
XXE injection. We can exploit this XXE by adding the following DOCTYPE declaration to the XML document and reference the XXE entity in the document. This will insert
the contents of the file /etc/passwd into the document.
|
|
Day 2 - Eggnog Madness
Can you spot the vulnerability?
|
|
The parameter rawJson
in line 20 is user-controlled and parsed as JSON. The data extracted from the JSON string is then passed to this
which invokes the second constructor in line 21. Here, the parameter controllerName
and data
can be misused to instantiate any object and to control the first parameter of a constructor in line 26. In line 28, the parameter task
is used as function name that is executed on the previously created object. As a result, we can instantiate any object, control the first argument of the constructor, and we can invoke any function without parameters.
For exploitation, an attacker can instantiate a ProcessBuilder
with a shell command like touch hacked.jsp
and then call the start()
function which invokes the shell command: rawJson={"controller":"java.lang.ProcessBuilder","task":"start","data":["touch","hacked.jsp"]}
Note that the "logging" code in line 31 was a feint and is not vulnerable.
Day 3 - Christmas Carols
Can you spot the vulnerability?
|
|
A temp
parameter is received in line 23 and passed to the renderFragment()
function in line 24. Here, the first argument fragment
leads to a Code Injection vulnerability in the Velocity template. In line 16, the fragment (template) is evaluated as Java code by Velocity. An exploitation is not that easy though. The limitation of this Template Injection is that the attacker can't execute Java code directly. However, Java reflection can be used to access interesting Java classes and to finally execute arbitrary shell commands:
|
|
Day 4 - Father Christmas
Can you spot the vulnerability?
|
|
This code challenge receives the user input via the GET or POST parameter url
in line 6. The url
parameter can be used to exploit an open redirect vulnerability. The redirect happens in line 10 if url
starts with /
. However, a URI starting with 2 slashes ( //attacker.org
) is not a relative URI but an absolute URI without a scheme. Therefore, the intended check if the URI is relative can be bypassed with the url
parameter //attacker.org
.
Finally, the Location response header looks like:
Location: //attacker.org
and the server redirects the victim to attacker.org.
Day 5 - Wintertime
Can you spot the vulnerability?
|
|
In this code challenge the function toString
iterates over all HTTP parameters and formats them into HTML representation. In real world scenarios this can be used for logging purposes.
In line 7 the value delimiter
is received and is appended in line 16 to the StringBuilder
instance after each parameter value. A denial of service issue may arise due to the internals
of java.util.StringBuilder
. By default the StringBuilder object is initialized with an array of size 16. Each time a new value is appended, the StringBuilder instance checks if the data
fits into the array. If not, the size of the array is doubled. In this case there is a large amplification which can cause the Java heap to run out of memory. By default Apache Tomcat
has a 2MB limit for POST requests and a maximum amount of 10000 parameters. If we submit a very large value for parameter delim
(e.g. 1.8 MB) in combination of an array with multiple
(e.g. 10000) HTTP parameters we have a maximum amplification of the factor ~20000, considering the StringBuilder internals.
Day 6 - Yule
Can you spot the vulnerability?
|
|
In line 9 untrusted user input is received from the parameter url
. A java.nio.file.Path
instance is created from the given value and
the contents of that file are read by the method java.nio.file.Files.readAllBytes()
. This functionality can be used to read arbitrary files through path
traversal. The contents of the file are not reflected into the response of the request, so it is not possible to access the content. However, by sending the value /dev/urandom
the application can be interrupted because the method Files.readAllBytes()
will not terminate until the Java heap is
out of memory. This leads to an infinite file read and finally to a memory exhaustion (DoS) which is not catched by the IOException
handler.
Day 7 - Jingle Bells
Can you spot the vulnerability?
|
|
In this challenge the username
parameter in line 25 is user-controlled and flows into jGenerator.writeRawValue()
without sanitization. For example, an attacker could inject the payload ?username=foo","permission":"all
which results in:
|
|
loadJson()
(line 13), the user foo
has escalated his privileges to
all
permissions through a JSON injection. Also, a successful exploitation depends on the implementation of loadJson()
.
To successfully exploit this issue, the method loadJson()
must deserialize only the first occurrence of every key such that the duplicate keys are ignored.
Day 8 - Icicles
Can you spot the vulnerability?
|
|
The parameter icons
from line 8 flows into a File
object in line 11. It is validated in line 14, and finally it is concatenated into a
file path in line 22. The check in line 14 tries to prevent a path traversal vulnerability by comparing the file name to the parameter icons
.
The method getName()
turns input like /../../../foo.txt
into foo.txt
and prevents a simple path traversal attack. However, if the input is only ..
nothing is removed by getName()
and thus it is possible to bypass the
security check. In combination with the parameter filename
we are able to traverse one directory level higher and can download any file we want there.
Day 9 - Chestnuts
Can you spot the vulnerability?
|
|
The parameter whitelist
from line 12 controls a part of the regular expression pattern in line 21, and the parameter value
from line 12
is validated against this expression in line 22. Because we can inject an arbitrary expression and have control over the value that the expression is matched against, we can produce
heavy CPU consumption with a complex regular expression (ReDoS). This can lead to a CPU exhaustion and results in a DoS at least in Java 8.
Proof of Concept:
|
|
Day 10 - Anticipation
Can you spot the vulnerability?
|
|
In this code challenge the user input is mapped from the GET or POST parameter name
to the function parameter name
via the annotation @RequestParam
. In line 3 the Content-Type of the response is set to text/xml
.
If untrusted user input flows into a response with the Content-Type text/xml
an attacker can inject a script tag with the xml namespace attribute
"http://www.w3.org/1999/xhtml"
. The browser interprets the content as JavaScript and executes it. To solve this challenge the attacker first has to
escape the CDATA element. The filter can be bypassed by inserting a space bewteen ]]
. This space character is stripped by the filter. For all other
spaces that are needed for payload the attacker can use tabs.
The following payload can be used to exploit this challenge:
|
|
Day 11 - Carolers
Can you spot the vulnerability?
|
|
The code challenge extracts a TAR file into a temporary directory. A user controls the contents of the file /tmp/uploaded.tar
in line 11.
A TAR file is an archive which holds the contents of various files or folders. Each file or folder in the archive is mapped to a TarArchiveEntry
object. An attacker can control the file name of such an entry which can be accessed with TarArchiveEntry.getName()
in line 16. Since the user
input reaches the sensitive sink java.io.File
in line 16 a path traversal attack (zip slip) can be triggered.
The sanitization in line 16 removes the ../
character sequence from the name which is insufficient and can be bypassed with the following payload
on a Linux system running a Tomcat server:
|
|
Day 12 - Evergreen
Can you spot the vulnerability?
index.jsp
|
|
init.jsp
|
|
A class is assigned to the div-element in line 5 dynamically, originating from the variable customClass
. Said variable
is initialized with the static string default
but is then overwritten in init.jsp when it is included in line 3. Since
init.jsp fetches the user input and assigns it directly to customClass
without any sanitization or verification, the
contents of the div's class attribute can be controlled by an attacker. Breaking out of the attribute is trivial by using a double quote. All other
instances of user input are propertly sanitized using an ESAPI encoder.
Day 13 - Epiphany
Can you spot the vulnerability?
|
|
In this challenge we have a multi-part file upload and the uploaded file has to belong to the content type text/plain
, otherwise the file upload is aborted (line 26).
The developer tries to prevent that a user uploads dangerous files like .jsp
however the content type is controlled by the attacker and thus this check can be easily bypassed.
Another user controlled input is the filename of the uploaded file (item.getName()
in line 28). It leads to a Path Traversal vulnerability because a string like
/../
is a valid file name in Java. Finally, we can upload any file we want because the content type check is insufficient and via the Path Traversal vulnerability
we can set the destination of the uploaded file which leads to a Remote Command Execution in the end.
Proof of Concept:
|
|
Day 14 - Chimney
Can you spot the vulnerability?
|
|
This servlet exports a CSV file which contains unfiltered user input, and thus it contains a Formular Injection vulnerability.
The parameter description
in line 22 flows into the variable rows
and finally into the StringBuilder
in line 33.
However, we have no sanitization and an attacker can inject a payload like =cmd|'/C calc.exe'!Z0
via description
that finally leads to a exported CSV
file of the structure:
|
|
=
will be interpreted by the software as a formula.
An attacker could exfiltrate data / execute OS commands, or do other malicious actions.
Day 15 - Mistletoe
Can you spot the vulnerability?
|
|
This servlet uses the find
system command and exposes the directories of the current folder to the user.
This leads to an information leak but an attacker can do even more harm with this code. In line 9/10 the basic command is build
(find . -type d
) and in line 12-15 the parameter options
is appended to the
find
command. Finally, java.lang.ProcessBuilder
is called in line 17/18 to execute the command.
It is important to say that a direct command injection is not possible because everything after find
is treated as an argument.
However, the user controls some options/arguments of this command which leads to an Argument Injection vulnerability. By injecting the find
parameter -exec
an attacker is still able to execute arbitrary system commands in the end.
A payload like ?options=-exec cat /etc/passwd ;
leads to the final shell command find . -type d -exec cat /etc/passwd ;
.
Day 16 - Candles
Can you spot the vulnerability?
|
|
|
|
In this challenge the user input is received via the @RequestParam
annotation and is mapped to the parameter name of method findUsers
(line 10).
The code snippet creates a Hibernate Session using the MySQL driver and queries UserEntity
objects from the database with a user-supplied filter.
In Hibernate, single-quotes within a string literal are escaped using double single quotes. The method escapeQuotes
(line 5) seems to correctly apply this escaping.
However, we can escape the HQL context and execute plain MySQL queries by injecting the following payload:
|
|
sleep
.
This (truncated) query will be sent to the database:
|
|
Day 17 - Carol Singers
Can you spot the vulnerability?
|
|
The method loadEnv()
is called in line 39 and processes the cookie env
which is user controlled
(lines 26-29). In line 30 the method setEnv()
is called with two user controlled parameters (key/value).
The key
variable is split by .
into an array and the first entry is checked against a blacklist (line 15)
to prevent the injection of malicious system properties like java
.
The previously mentioned blacklist check can be bypassed with a payload like .java.xxx
because only the first entry of the split key is validated against the blacklist
check in line 15 and an empty string is accepted (see line 9/10). After the check is passed all empty string or null values are removed from the list in line 20 and
joined together with .
again to build the final property.
The goal of an attacker is to set the system property java.library.path
(line 22)
to the value /var/myapp/data
because it is possible to upload files there (line 34) and thus it is vulnerable to a Library Injection.
An attacker has to upload a malicious library called libDEOBFUSCATION_LIB.so
.
The file requires the prefix lib
and the suffix .so
, otherwise System.loadLibrary()
in line 44 won't load it.
Proof of Concept:
|
|
Day 18 - Reindeer
Can you spot the vulnerability?
|
|
In this snippet we have a Session Fixation vulnerability which leads to a Command Injection vulnerability.
The first thing that happens if you visit the application is that a new session is created and session variables are set (lines 43-54).
However, an attacker has some control over the session parameters: the user input config
is split into key-value pairs in line 49.
After that the processed config
parameter is merged with the whitelist on line 51.
This leads to full control over the session variables of the own session. The goal is to reach the "execute last command" part in lines 23-26 which is only accessible
if a valid password is provided through the authorization header (line 21) and the session variable last_command
is set. As mentioned above we have full control
over the session variables but only in the own session and we do not have a password, so we can not reach it. Luckily, there is a Session Fixation vulnerability in the lines 35-39 that leads to full control over a victims cookies.
We can send the admin of the app a link and set his session to our known session via the parameter config
and execute our last stored shell command.
The authentication header is send automatically, so the password check is passed.
- curl "http://victim.org/?config=last_command@ls" -v
-
Send link to admin and execute the following requests in the background:
- http://victim.org/?config=JSESSIONID@D4E9132DB9703009B1C932E7C37286ED&save_session=yes
- http://victim.org/?home=yes
Day 19 - Gingerbread
Can you spot the vulnerability?
|
|
In line 13 the parameter p
is received. It gets evaluated in line 24 which leads to an Expression Language Injection.
In an attempt to prevent this, a check for quotes was added (line 14-16) since strings usually start with quotes in this expression language.
Additionally, a regular expression check is applied (lines 18-22).
This blacklist attempts to prevent the injection of dangerous classes and language constructs that could be used to execute arbitrary Java instructions.
Due to the flexibility of Java there are countless ways to bypass the blacklist. For example, it is possible to call the javax.scripts.ScriptEngineManager
class via reflection. Eval expects a string but
there are many ways to encode this string which finally leads to a Code Injection. A possible payload could look like this:
|
|
Day 20 - Ornaments
Can you spot the vulnerability?
|
|
In line 54 the user input username
is passed to the userExists()
method and in line 32-49 it is checked if this user exists in the LDAP directory.
The username is added to the filter string in line 40. The filter checks if the simpleSecurityObject
contains an attribute with the given uid
. Then
the query is sent to the LDAP server in line 43 and if this user exists true
is returned in line 46. This query would lead to a Blind LDAP Injection
because in line 54 the attacker receives information if the query was successful or not. To prevent this the given username is checked in lines 33-38
against a blacklist. This does not prevent the LDAP Injection but only limits it because attributes like userpassword
cannot be read. If you pay
attention to the comment in line 20 there is the attribute createtimestamp
which is not included in the blacklist and therefore can be read. With the
createtimestamp
of the admin user an API token was generated as you can see in line 10 and with this token it is possible to execute a shell command
in the method executeCommand()
in line 14. A possible injection could look like this:
|
|
Day 21 - Snowman
Can you spot the vulnerability?
|
|
In this code challenge, the method decrypt
decrypts a user provided hex-encoded cypher text using an insecure AES algorithm (line 27).
The previously encrypted cypher text is known to the attacker (line 10). However, the attacker has no knowledge about the encryption key and therefore the encrypted content is not known.
Since the cypher text is not protected by a MAC or a signature the attacker can manipulate the IV (first 16 bytes of the cypher text) and abuse the CBC malleability to cause a BadPaddingException
.
The cypher text can be decrypted without knowing the key with the Padding Oracle attack. In the worst case 16*256 requests are needed to decrypt one block. More information about the Padding Oracle attack can be found here: https://www.owasp.org/images/e/eb/Fun_with_Padding_Oracles.pdf.
Day 22 - Fruitcake
Can you spot the vulnerability?
|
|
The parameter url
is turned into an URLConnection
object in line 31 through the getUrl()
method.
This method checks if the URL starts with http
(line 13),
if it is a valid external URL (line 17), and also redirects are not followed as you can see in line 10. However, this challenge allows a single redirect through
the Location
header (lines 33-43). If an attacker controlled URL sends an attacker controlled location header this can be exploited.
There is a second getUrl()
check in line 39, but
since http://
only has to occur somewhere in the string the payload is pretty obvious:
victim.org?url=http://evil.com/
. The server has to send the HTTP header Location: https://localhost/?x=http://google.com
.
In the check in line 39 only the URL after http
is checked, so google.com
in this case.
The request is sent to https://localhost/
though with the query string x=http://google.com
(line 40/41).
The response body of the requested URL is printed in line 45. This is a classic Server-Side Request Forgery (SSRF) vulnerability but if you take a closer look you also see that it is a File Read vulnerability:
Location: file:///etc/passwd#http://google.com
Day 23 - Ivy
Can you spot the vulnerability?
|
|
This challenge contains a Format String Injection vulnerability. The user input name
is escaped against Cross-Site Scripting (XSS) in line 17 and concatenated into a format string in line 18.
Since calendar
is an object, several format strings can be used here and our user input can also contain input format strings without producing an error. A format string %s
calls the internal toString()
function from the class java.util.Calendar
which in turn calls toString()
from the respective objects the Calendar object
contains. In line 12 a java.util.SimpleTimeZone
object is created with an unfiltered user controlled input id
which is added to the Calendar object (line 15). Thus
the format string in line 18 can contain a XSS payload which is inserted via the format string %s
and printed in line 20 which results in a Reflective XSS vulnerability.
Proof of Concept:
|
|
Day 24 - Nutcracker
Can you spot the vulnerability?
|
|
|
|
This code challenge contains an Object Injection vulnerability.
The user input is supplied via the @RequestBody
annotation of the Spring framework and is mapped to parameter xml
in line 2.
The input is then parsed into a org.w3c.dom.Document
instance in line 8.
After parsing, the XPath expression //com.rips.demo.web.User[@serialization='custom'][1]
is used to filter out the first of all com.rips.demo.web.User
nodes with the attribute serialization='custom'
(line 10/12). In line 17 the filtered node is then transformed back into a string and then deserialized in line 20.
There are 2 classes in the challenge, both implementing the Serializable
interface, meaning that objects of these classes can be serialized.
In both classes the default deserialization is overridden using the readObject
method in lines 18-24 and 38-42.
The method defaultReadObject()
in line 19 and line 40 reads the non-transient and non-static fields of the classes from the stream.
In line 41 the transient field password
of the class User
is manually read from the stream.
This introduces a security risk since we can hide another object (of any type, not only string) within the User
object which is not filtered out by the XPath expression.
The sink is in line 19 in the readObject
method of the Invoker
class. This gadget allows us to create an arbitrary object by calling the
constructor with a string array and invoking a user-controlled method of this object without arguments.
With the following payload we can create a ProcessBuilder
instance and execute an arbitrary shell command.
|
|