Welcome to another story in the “Lessons Learned” series where we discuss real-world vulnerabilities from the perspective of an application security engineer focusing on the underlying root causes and the measures we can take to prevent similar issues in our applications.

In today’s story, we discuss a write-up by the security researcher Jonathan Bouman showing how an SSTI (Server-side template injection) vulnerability affecting an error message was used to expose all user data of an application. You can view the full write-up here.

Affected Application

Today’s vulnerability affected FileSender, a popular open-source application mainly used for file-sharing.

Impact of the vulnerability

Exposure of credentials (database and S3) potentially allowing an unauthenticated user to access and tamper with all customer files managed by the application.

What went wrong?

  • The security researcher explored the /download.php route which allows unauthenticated users to download files (FileSender allows authenticated users to share download links with unauthenticated users), and found that if the download link is expired the HTTP request is redirected to another unauthenticated route like /?s=exception&exception=eyJtZXNzYWdlIjoidHJhbnNmZXJfcHJlc3VtZWRfZXhwaXJlZCIsInVpZCI6IjY3MGE4N2M0YmQ0ODAiLCJkZXRhaWxzIjpudWxsfQ==

  • Obviously, the exception query parameter is base64 encoded, which implies the backend decodes this and uses it to apply some logic, specifically to generate the error message and display it as shown below:

  • When decoding the message we get the below JSON
$ echo "eyJtZXNzYWdlIjoidHJhbnNmZXJfcHJlc3VtZWRfZXhwaXJlZCIsInVpZCI6IjY3MGE4N2M0YmQ0ODAiLCJkZXRhaWxzIjpudWxsfQ==" | base64 -d  | jq  

{  
  "message": "transfer_presumed_expired",  
  "uid": "670a87c4bd480",  
  "details": null  
}
  • When changing the message in the JSON to something like xxxxx, re-encoding, and calling the same route, the new message was shown between curly brackets instead of the original message, e.g. encoding the below JSON to give a new base64 string, then replacing the value of the exception query parameter with this new base64 string shows the error message {xxxxx} as shown below.
$ echo '{"message":"xxxxx","uid":"670a87c4bd480","details":null}' | base64  
eyJtZXNzYWdlIjoieHh4eHgiLCJ1aWQiOiI2NzBhODdjNGJkNDgwIiwiZGV0YWlscyI6bnVsbH0K

Backend logic

  • When checking the code of the application to understand the logic of how the backend decodes and handles the exception query parameter, the security researcher came across the below function.

  • As shown this unserialize function starts with decoding the base64 string, and then extracting the fields from the JSON (line 362).
  • Then it replaces any {cfg:some_option} , {conf:some_option} , or {config:some_option} values in the exception with an empty string (line 365).
  • This suggests that these cfg/conf/config values would be parsed by a templating engine and substituted with the value of the config option, and it seems the goal of line 365 is to prevent this from happening.
  • However, this only replaces these cfg/conf/config values if they are enclosed in curly brackets (e.g. {cfg:some_option}).

Exploit

  • The security researcher checked the documentation of FileSender and found some potentially sensitive configuration options such as db_username and db_password which hold the database credentials and also cloud_s3_key and cloud_d3_secret which hold the AWS credentials that are used to access all customer files uploaded to FileSender.
  • The security researcher then tried modifying the message in the JSON as shown above (decode, modify, re-encode and then send in HTTP request) to cfg:db_username (without curly brackets), and the validation step in line 365 above didn’t replace this (as it doesn’t have a curly bracket), then as we have seen earlier a set of curly brackets was added in another place in the code, leading to the templating engine now replacing the config option with its value and exposing the database username as shown below:

  • This allowed exposing the values of all the config values mentioned above including the database and S3 credentials using this unauthenticated route, which then could be used to expose or tamper with all customer files directly on S3.

The Fix

FileSender fixed this issue by adding validation which only allows a list of allowed exception message identifiers in the message field such as the value transfer_presumed_expired used in the original decoded string.

Lessons Learned

  • Keep error messages generic: This vulnerability is a strong reminder to keep error messages in your web application as generic as possible, as any extra information while helpful for troubleshooting, gives more context to any potential attacker.
  • Secure Design: In this case, I would argue that using a templating engine to parse the error message is a design mistake. The developers tried to compensate for this design mistake through the validation applied in line 365 above, but using validation to compensate for design issues doesn’t always work as we have seen in this case. Your best bet to catch such design mistakes is integrating threat modeling in your SDLC process. For more about threat modeling you can check my series Threat Modeling Handbook.
  • Input validation: Another important missing control here was input validation, the message field here should be one of a list of expected values (exception identifiers such as transfer_presumed_expired), and if the backend validated that the passed message was in fact one of these allowed values before applying any logic, this would have prevented the issue (this was also the actual fix FileSender ended up using for this vulnerability as mentioned above). Generally, input validation is one of the most powerful security controls that is usually easy to implement and is effective against a wide range of security attacks (such as SSTI in this case). You can find more details about input validation in this story I’ve posted earlier.
  • Client-side encryption: If you are hosting customer files, it is always a good idea to allow your customers to encrypt their files with their own keys (client-side), in addition to any server-side encryption you apply, this gives extra protection in case the files do get exposed through a vulnerability like this one. In this case, FileSender did provide a client-side encryption feature, and customers using this feature had that extra protection.
  • Pentests and Bug Bounty: It seems from the validation in line 365 above that the developers did know that such issue could exist but thought that the validation they had was sufficient, this is why it is important to schedule periodic pentests and/or allow bug bounty hunters to test your application to verify whether your assumptions are correct.

Conclusion

The most innocent-looking features of your application such as error messages, coupled with suboptimal design decisions and insufficient validation could lead to severe security issues. That is why developers and security engineers need to understand and discuss the implications of the design and implementation decisions as early as possible in the SDLC process and perform proper testing to confirm their assumptions are correct.

Stay tuned for another story, and another set of Lessons Learned!

Author Of article : Mohamed AboElKheir Read full article