Bitcoin Forum
May 19, 2026, 07:35:36 PM *
News: Latest Bitcoin Core release: 31.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: « 1 2 3 [4]  All
  Print  
Author Topic: [OPEN] Mobit.Exchange Bug Bounty Campaign | Get Paid for Reporting Bugs  (Read 866 times)
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 17, 2026, 05:38:54 AM
 #61

Title: Misleading Error Message Returned for Expired Orders

While testing expired exchange orders on MoBit, I noticed that expired orders return the message:

Quote
Error: No currency was provided, order timed out.

However, the order had already been created successfully with a valid currency pair before expiration. The issue was actually a timeout/expiration event, not a missing currency parameter.

This is a confusing message since users may think they submitted the form incorrectly when the real reason is simply that the order expired. A clearer message such as

Quote
“Order expired due to timeout”
would improve usability and reduce support confusion.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 17, 2026, 06:02:10 AM
 #62

Title: Refund Endpoint Returns Internal Server Error for Expired Orders

While testing order expiration behavior on MoBit, I attempted to initiate a refund request for an expired order using the refund endpoint.

Instead of returning a clear validation message explaining that the order was already expired or no longer refundable, the backend returned a generic internal server error:

Code:
{
  "error":"internal-server-error",
  "error_msg":"Server encountered an error, contact support.",
  "result":null
}

From my testing, the order expiration itself appears to work correctly, but the refund flow does not appear to properly handle expired order states. A properly handled application  should normally return a controlled error message such as:

  • "order expired"
    "refund unavailable for expired orders"
    "invalid order state"

This is an  indication of missing server-side validation or an unhandled exception path in the refund logic. Even if refunds are intentionally unavailable after expiration, the API should return a controlled error response rather than a 500 internal server error.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 17, 2026, 11:22:21 AM
Merited by hugeblack (2)
 #63

Title: Inconsistent Parameter Handling Documentation for GET /api/v1/estimate Endpoint

I just noticed that the
Code:
/api/v1/estimate
endpoint is defined as a GET request, but its parameter section is labeled “Request Body”.

This appears inconsistent with standard HTTP behavior and with the earlier “Passing Parameters” section of the documentation, which correctly states that GET requests should use query parameters.

During testing, I observed that this inconsistency could confuse developers integrating with the API, since GET request bodies are not reliably supported across many HTTP clients, proxies, CDNs, and frameworks. Some clients ignore or strip GET bodies entirely.

Current documentation example:

Code:
GET /api/v1/estimate

followed by:

Quote
“Request Body”

Expected behavior/documentation:
Parameters for this endpoint should be documented as query parameters, for example:

Code:
/api/v1/estimate?from_currency=bitcoin&to_currency=ethereum&send_amount=1.0&rate_type=dynamic

Alternatively, if a request body is actually intended, the endpoint should use POST instead of GET.

This inconsistency may lead to failed integrations, malformed requests, and unnecessary confusion for developers.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 17, 2026, 11:59:36 AM
 #64

Title: Inconsistent API Documentation References Non-Existent /api/v1/calculate Endpoint

While still testing the Mobit API documentation, I noticed an inconsistency between the documented calculation endpoints. In the “Passing Parameters” section, the documentation provides the following example:

Code:
GET /api/v1/calculate?from_currency=bitcoin&to_currency=ethereum&receive_amount=100.000

However, the main endpoint reference later documents the calculation functionality under:

Code:
GET /api/v1/estimate

To verify the behavior, I tested the /api/v1/calculate endpoint directly and received a 404 response, indicating that the endpoint does not exist. This creates integration problem for developers because the documentation appears to reference two different endpoint names for the same functionality. From testing,
Code:
/api/v1/calculate
seems invalid, while
Code:
/api/v1/estimate
appears intended to handle exchange amount calculations.

The implication of this is that developers following the documentation example may build integrations against a non-existent endpoint and encounter unexpected failures.

Suggested fix:

  • Remove references to /api/v1/calculate if it is deprecated or non-existent
    Use a single endpoint name consistently throughout the documentation
    Clarify whether /api/v1/calculate and /api/v1/estimate are intended to behave differently or if this is simply a documentation mismatch


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 17, 2026, 12:25:24 PM
 #65

Title: API Returns HTML Error Pages Instead of JSON for Invalid Endpoints

Standard API errors have returned structured JSON responses, but I just noticed that requests to non-existent API endpoints return a full HTML error page instead.

For example, most documented API errors follow a consistent format such as:

Code:
{
  "result": null,
  "error": "internal-server-error",
  "error_msg": "Server encountered an error"
}

However, requesting an invalid API route returns an HTML 404 page rather than JSON. This is an inconsistent behavior for API users and may cause issues for applications that expect all API responses such as errors to be returned in JSON format. This inconsistency could break automated parsing, handling of exception or client-side integrations that rely on a consistent response structure from API.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 17, 2026, 01:30:49 PM
Merited by hugeblack (1)
 #66

Title: Misleading Use of to_address Field in /api/v1/refund Endpoint

A closer look at the API documentation, I noticed inconsistency in how the
Code:
to_address
field is described in the refund endpoint compared to the exchange endpoint.
In the
Code:
/api/v1/exchange
endpoint,
Code:
to_address
is clearly defined as the destination wallet where funds will be sent after a successful exchange.

However, in the
Code:
/api/v1/refund
endpoint, the same field name is reused in a different way. In this context,
Code:
to_address
is not used as a destination for funds but instead serves as a verification value to confirm ownership of the original order.

The current documentation does not clearly highlight this difference in meaning, which may lead to confusion. A developer reading only the refund endpoint description may incorrectly assume that
Code:
to_address
is where refunded funds will be sent.

I would expect a clearer distinction in the documentation, explicitly stating that in the refund endpoint,
Code:
to_address
refers to the original address used during order creation and is used strictly for verification purposes, not for fund delivery.

Suggested clarification:
Quote
“Original destination address provided during order creation. Used for verification of refund requests and not used as a payout destination.”


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 10:42:08 AM
 #67

Title: Exchange Form Uses Relative init_tx Action Path Instead of Absolute Endpoint

While reviewing the exchange form behavior, I noticed that the form submits using a relative path:

Code:
<form action="init_tx" method="POST">

Because the action does not begin with a leading slash (
Code:
/
), the form submission URL depends on the current page instead of always going to the same endpoint.

During testing, I noticed this could cause problems because if the form is opened from a different page or URL path, the browser may submit the request to a wrong location such as:

Code:
/some/path/init_tx
instead of:
Code:
/init_tx

This could lead to failed submissions, unexpected 404 errors, or requests being routed to the wrong endpoint depending on how Mobit navigation or URL structures evolve over time.

Expected behavior would be to use an absolute path for the form action so submissions consistently resolve to the intended endpoint regardless of the current URL context, for example:

Code:
<form action="/init_tx" method="POST">

Using an absolute path would ensure the form always submits to the correct endpoint and avoid possible routing errors.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 12:08:19 PM
 #68

Title: Two Main Submit Buttons, "Calculate" and "Exchange" Have No Differentiation Logic

While reviewing the frontend form logic, I noticed that both the “Calculate” and “Exchange” buttons are implemented as separate
Code:
type="submit"
inputs within the same form and both submit to the same
Code:
init_tx
endpoint.
Code:
<input id="calculate" name="calculate" type="submit" value="Calculate">
<input id="exchange" name="exchange" type="submit" value="Exchange">
The server has to guess which button was clicked based on which
Code:
name
field appears in the POST body. This is non-standard practice considering that with disabled Javascript, the server-side logic distinguishing Calculate vs Exchange depends entirely on this pattern working correctly.
 
From testing, it appears the backend distinguishes the requested action based on which button name is included in the POST body.
Even though browsers normally handle this correctly, this is a fragile logic because both operations share the same endpoint and completely rely on submit-button field detection.

This stood out especially because the platform promotes “No JS” compatibility:
Code:
<strong>Fast, Secure, No KYC, No JS, No Logs.</strong>

meaning the reliability of server-side form handling becomes more important. If a browser, accessibility tool, proxy, automated client, or malformed request omits the submit button field, the backend may not reliably determine whether the user intended to calculate an exchange or actually create one.

However, live network traffic testing revealed that the actual implementation contradicts both the HTML source and the "No JS" claim. "Calculate" does not POST to
Code:
init_tx
.  it sends a GET request to /?calc=1 with parameters appended as query strings:
Code:
GET https://mobit.exchange/?calc=1&from_currency=bitcoin&to_currency=ethereum&from_amnt=1&rate_type=flat
"Exchange" correctly sends
Code:
POST https://mobit.exchange/init_tx
and returns a
Code:
302 Found
redirect to the order tracking page.
This means JavaScript is running to override the form's declared
Code:
method="POST"
and
Code:
action="init_tx"
before submission. This is a direct contradicting the advertised "No JS" compatibility.

The form as written in HTML would POST both buttons to
Code:
init_tx
, but JavaScript intercepts the Calculate action and converts it to a GET request instead. Users or clients running without JavaScript would therefore submit Calculate as a POST to
Code:
init_tx
just like Exchange, with no
Code:
calc=1
flag, giving the server no reliable way to distinguish the intended action.

Expected behavior would be either:

  • separate endpoints for calculation and exchange creation or
    an explicit hidden action parameter validated server-side.

Using a more explicit action-handling approach would make the request flow more reliable and reduce uncertainty in non-JavaScript environments. Additionally, the
Code:
calc=1
flag being exposed as a plain URL query parameter means anyone can call
Code:
/?calc=1
directly with arbitrary parameters, bypassing the form and captcha entirely.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 12:46:18 PM
 #69

Raw IP Access Bypasses Host Boundary Enforcement

The application is accessible directly through its raw IP address (
Code:
https://111.90.159.167
) and returns the full production website without any restriction, redirection or validation of the Host header. This practically means the server treats requests made to the IP exactly the same as requests made to the official domain (mobit.exchange).

Looking at this from a security design perspective, this is an intended origin boundary weakness. This is atypical since a production system is expected to enforce a single canonical entry point. This is especially so when handling sensitive flows like order creation, payments or session initialization. In this case, no such restriction has been applied by Mobit.

During testing, the IP-based request still behaved like a normal session-enabled request, including generation of CSRF tokens and captcha values, confirming that the backend fully processes the request instead of rejecting or isolating it.

I could think of several impacts of this anomaly and list the following:
  • The system is reachable through an unintended origin (raw IP)
    No enforcement of canonical domain access
    Same application state is exposed across multiple entry points
    Potential inconsistency if future protections depend on hostname rules

Expected Behavior
  • Reject requests where Host is no equivalent to mobit.exchange or
    Redirect all IP-based traffic to the canonical domain
    Ensure only the configured domain is treated as a valid entry point

What Are the Risks?
I wouldn't see this as an immediate exploit on its own, nevertheless, it weakens a basic trust assumption that all user traffic enters through a controlled domain boundary. In simple terms, the server has no IP-based access restriction but relies entirely on SSL to block direct IP access. This is not a proper server-side control. Generally, systems that handle financial or transactional flows typically enforce these standards to prevent misrouting. Allowing this misconfiguration points to inconsistent security rules and unintended exposure of backend configuration.

Recommendations
Enforce strict host validation at reverse proxy level
Add canonical redirects from IP to domain
Block unknown Host headers at infrastructure layer


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 01:06:08 PM
 #70

Bug 3: Static captcha is weakness.

The captcha image is generated dynamically, but it is unclear whether it is based on re-captcha or hCAPTCHA, the line:

Code:
refresh page for new Captcha

This suggests that it is not securely bound to the session, if the captcha token

Code:
captcha_token


is predictable or reusable, it can be bypassed and a bot using ocr or captcha solving can easily bypass the control.

Title: Captcha Token Exposed and Reusable

I found the captcha token hardcoded in two places: the first as the image
Code:
src
and as a hidden field. Here it is:
Code:
captcha_token: eyJpZCI6ImE1MmRhN2UwLTM0MTAtNDhlYS1iZjVjLWIzYzRlY2UwOTdmZCJ9...
The page also says "refresh page for new Captcha" . This means that the same token persists across page loads until manually refreshed. Because the token remains valid and unchanged until a refresh action, it behaves more like a static session identifier than a one-time CAPTCHA challenge. This increases the risk that a previously obtained token could be reused in multiple submissions.

🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 01:28:38 PM
 #71

CSRF Failure Returns Raw Plain Text Response Instead of Structured Error Page

When a request is submitted with a missing or invalid CSRF token, the server responds with a raw plain text message:
Quote
CSRF validation failed: The CSRF session token is missing.
Instead of returning a properly formatted HTML error page or a structured JSON response (like other errors in the system), the application exposes a bare technical message directly to the client.

This creates a noticeable inconsistency in error-handling across the application. Most other failures return either styled HTML pages or JSON-formatted responses. This suggests that this CSRF error is being triggered at a lower-level security layer before the normal response formatting pipeline is applied.

Expected Behavior

The application should handle CSRF failures like other errors and return either:

A styled HTML error page with a clear, user-friendly message or
A structured JSON error response consistent with the API format

Recommendation
Route CSRF validation errors through the standard error handler
Suppress internal security terminology in user-facing responses
Provide a consistent UI fallback for authentication/session-related failures


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 04:57:55 PM
 #72

Static and Predictable form_hash Weakens Request Integrity Protection

While reviewing the frontend code, I noticed a hidden field called
Code:
form_hash
that appears to be intended as an extra layer of request integrity verification on top of the existing CSRF token:
Code:
<input type="hidden" name="form_hash" value="eb8130fd89d44958">

I wanted to understand how this value was generated, so I ran a series of tests to see whether it was unique per user, per session, or per page load. What I found was that it is none of those things. it turns out to be a time-based global token that every user receives simultaneously regardless of who they are or how they got to the page.

How I Tested It
In my first test, I opened the page in two normal browser tabs and one incognito window which has no cookies, no session data, nothing shared with my regular browser and compared the values across all three. All three returned the same hash:
Code:
Normal tab:    eb8130fd89d44958
Same session:  eb8130fd89d44958
Incognito:     eb8130fd89d44958
If the hash were tied to my session or generated fresh on each page load, the incognito window would have returned something different. It didn't.So I waited about two hours and repeated the test to understand how often the value changes. This time I got:
Code:
Normal tab:    d110014ee5fd6c5f
Same session:  bb76cf99930ed394
Incognito:     bb76cf99930ed394
Two different values appearing in the same test window was unexpected. The most likely explanation is that I caught the hash mid-rotation, one tab got the outgoing value while the other two had already received the new one. This tells me the hash rotates on a fixed time schedule, and that the boundary between rotation windows is not cleanly defined.

Why This Is a Problem
Because the hash is global and time-based, any attacker can simply load the page once to get a valid
Code:
 form_hash
and then reuse it in automated or scripted requests for the entire duration of that rotation window. They can do this without needing a real user session or any special access.
The mid-rotation behaviour also introduces a reliability concern for legitimate users. If the hash rotates between the moment a user loads the page and the moment they submit the form, their request could be rejected with a validation error through no fault of theirs. For a cryptocurrency exchange where users are entering wallet addresses and initiating real transactions, that kind of unpredictable failure could be frustrating.

Why I'm flagging this is that the existing CSRF token, if properly configured should handle per-session protection properly. The
Code:
form_hash
appears to have been added as an extra security measure on top of that, but because it is implemented as a shared global value rather than something tied to the individual user, it does not actually add any meaningful protection but just creates the appearance of one.

What I Would Expect Instead
A
Code:
form_hash
that genuinely strengthens request integrity should be unique per user session and per page load, generated server-side using a cryptographically secure random value. It should be validated against the specific session that originally requested the page. That way it cannot be harvested from one session and replayed against another.





🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 05:21:08 PM
 #73

CSRF Token Exposed in HTML Form Source

The CSRF token is embedded directly in the HTML form and is visible in the page source:
Code:
value="IjI3MWYzYzA3NTFjYjA3ODQ2ZTNlOTI5Y2FmOGI1MjQ0OTZjMDQ0ZDgi.ags9yQ.si-awR3SJVrz5bBagcQ3YPHBhEc"
This means the token is not protected from client-side visibility and can be extracted by anyone who can access the rendered HTML or DOM, including through browser inspection or any client-side script execution.

While CSRF tokens are normally expected to be present in HTML forms, their security depends entirely on being unpredictable, session-bound, and protected from cross-context leakage such as XSS. If an attacker is able to inject or execute scripts on the page, the token can be harvested and reused to forge authenticated requests on behalf of the user.

Recommendation
Ensure CSRF tokens are session-bound and single-use where possible
Rotate tokens regularly and invalidate after sensitive actions
Add additional safeguards such as strict Origin/Referer validation
Treat CSRF tokens as non-secret but integrity-bound values, not standalone secrets


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
May 18, 2026, 07:00:46 PM
 #74

Inconsistent Error Field Placement in /api/v1/status Failed Response

The Mobit API documentation generally follows a consistent response structure where errors are returned using a top-level
Code:
"error"
field. However, the
Code:
"failed"
status response introduces a second
Code:
"error"
field inside the
Code:
"result"
object alongside
Code:
"error_msg"
.

This creates an inconsistency in the API design because error information is now represented in two different places depending on the response type. A client consuming the API cannot rely on a single error-handling pattern and may need special-case logic specifically for failed order states.
For example, most responses follow this structure:
Code:
{
  "result": {...},
  "error": null
}
But the failed response instead contains:
Code:
{
  "result": {
    "status": "failed",
    "error": "tx-size",
    "error_msg": "Your order..."
  },
  "error": null
}
This is confusing because the top-level
Code:
"error"
remains null even when the order itself is explicitly in an error state. The API should use a single consistent location for error reporting.
I should either:

  • keep all error information at the top level  or
    standardize business/state errors separately with clearer naming such as "failure_reason" instead of "error" inside "result".

Standardizing the structure would make integrations more predictable and reduce parsing issues for API clients.



🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
Today at 10:12:25 AM
 #75

Session Cookie Missing Secure Attribute

The application’s session cookie is configured with
Code:
HttpOnly
and
Code:
SameSite=Strict
, which is good practice, but it is missing the
Code:
Secure
attribute.

Take a look at this response header:
Code:
Set-Cookie: session=eyJjc3JmX3Rva2VuIjoiYTBjNGQ2MTY0OGI1NDE3NzU2YmY2YWIyMWVjMDRlMjQ4NmQxNGFkNCJ9.agwuUA.6LLB7qofaEHdQsS-x_tf8nkOLyM; HttpOnly; Path=/; SameSite=Strict
The absence of
Code:
Secure
means the browser is not clealy instructed to restrict the cookie to HTTPS connections only. Although the site already enforces HTTPS using HSTS, adding the
Code:
Secure
flag is still standard security practice. It provides an additional layer of protection against accidental or downgraded insecure requests.
Recommended fix:
Code:
Set-Cookie: session=...; Secure; HttpOnly; SameSite=Strict


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
Today at 10:39:14 AM
 #76

GET request to /init_tx returns 405 but with HTML body

Code:
HTTP/1.1 405 METHOD NOT ALLOWED
Content-Type: text/html; charset=utf-8
Content-Length: 2726

A 405 Method Not Allowed response with Content-Length: 2726 means the server is returning a full HTML error page. This is consistent with the earlier bug (API Returns HTML Error Pages Instead of JSON for Invalid Endpoints) where I found 404s also return HTML instead of JSON. This confirms the pattern is not isolated to 404s but applies to all error responses from the frontend. This implied that error handling is inconsistent across the application.


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
ContentWriter
Member
**
Offline

Activity: 475
Merit: 26

Earn from your cryptocurrencies


View Profile
Today at 11:45:57 AM
 #77

Currency Swap Button Is Difficult to Recognize as Interactive

While testing the main exchange form, the currency swap control was difficult to identify as a functional button. The control appears as a standalone image between the currency columns and visually resembles a decorative icon or refresh symbol rather than an interactive swap action. I actually realized it is interactive from the code. This should be redesigned since most users of an exchange never get to look at the code.

Current implementation:
Code:
<input alt="Submit Button" id="swap_order" name="swap_order"
       src="../static/swap.png"
       class="swap-currency-type"
       type="image">
Several factors contribute to the confusion:

The icon uses circular arrows facing the same direction, which resembles a refresh/rotate action more than a currency swap
The control has no visible text label or tooltip
The alt text says "Submit Button" instead of describing the actual action
The image styling does not clearly indicate clickability
This can make the swap feature easy to overlook, especially for new users or users relying on accessibility tools.

Recommendation
Use a more recognizable swap icon (opposite-direction arrows)
Add a tooltip or visible label such as “Swap currencies”
Update the alt attribute to accurately describe the action
Add clearer button styling or hover feedback to improve discoverability


🔐 No KYC Crypto Trading
💸 Earn While You Trade
👉 Join Bridgoro Now
Pages: « 1 2 3 [4]  All
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!