Case Study: How a B2B Software Company Found and Fixed Critical CMS and CRM Vulnerabilities
In two weeks, AI Hacker assessed the external attack surface, gained access to a CMS administrator account, and discovered an exposed CRM key. Retesting confirmed that the critical vulnerabilities were fixed.
Sentra TeamSeptember 20, 20265 min read
A B2B software company is responsible for more than its own data. A successful attack on its product affects customers’ business processes and their trust in the vendor.
The attack surface changes with every release, API version, and connected service. Security therefore needs continuous assessment: spotting new risks, determining whether they can be exploited in practice, and checking that fixes work.
A large B2B software developer launched a two-week pilot of Sentra AI Hacker to assess its external attack surface and web applications.
Pilot scope
Asset and finding counts have been changed to protect the customer’s identity. Technical scenarios and validation results are preserved.
The scope and testing boundaries were agreed before the pilot began. Over two weeks, AI Hacker analyzed 868 assets.
Asset overview
868Total assets
The assessment used a black-box approach, from an external attacker’s perspective. AI Hacker recorded 423 findings: 22 vulnerabilities of varying severity and 401 informational findings.
Vulnerabilities
423Total
A Sentra expert also manually reviewed all 10 critical, high, and medium severity vulnerabilities. The most significant scenarios were validated in practice to establish the access they provided and their potential impact. The first critical findings were recorded the day after active testing began.
Confirmed attack chains
API routes, parameters, and technical field names in the examples have been changed. The sequence of checks and the boundaries of confirmed results are preserved.
1. Access to a Strapi administrator account
Sentra AI Hacker
Objective
Test administrative access through the public Strapi API.
Confirmed
3 of 3 confirmed
Content APIcms.example
Filter oracleeditor_link
Reset coderecovery_code
CMS administratorLogin confirmed
01AnalysisThe public API exposes /example-api/cms/records. I will start with a baseline and a filter on editor_link.contact_address: if the hidden field participates in the query, the record count should depend on the condition even when the field itself is not returned.
The public API exposes /example-api/cms/records. I will start with a baseline and a filter on editor_link.contact_address: if the hidden field participates in the query, the record count should depend on the condition even when the field itself is not returned.
02Replay HTTP requestContent API responds without a session Completed
dast_replayAnonymized excerpt
Input
GET /example-api/cms/records
No Cookie or Authorization
Result
HTTP 200
match_count: N (N > 0)
03Input validation testingFilters return different match counts Completed
dast_fuzzAnonymized excerpt
Input
filters[editor_link][contact_address][$contains] = @
Control: same field, value <non-matching control>
A nested filter may participate in a database query even when the field is absent from the JSON response. In LIKE, the character _ is a wildcard. The recovered email must be checked using $eq; acceptance of a token filter alone does not mean a token already exists.
06AnalysisThere is a match/no-match difference, while an unknown field returns 500. The server processes the nested user field. I will check whether it accepts recovery_code: a value may be recoverable through responses even if it is never returned in JSON.
There is a difference between matches found → no matches, while an unknown field returns 500. The server processes the nested user field. I will check whether it accepts recovery_code: a value may be recoverable through responses even if it is never returned in JSON.
07Replay HTTP requestThe recovery_code filter is accepted Completed
08Replay HTTP requestNo reset code before the recovery request Completed
dast_replayAnonymized excerpt
Input
filters[editor_link][recovery_code][$notNull] = true
Before the account/recovery request
Result
HTTP 200
match_count: 0
09AnalysisEmail extraction has a trap: _ in LIKE matches any character. I will verify the recovered address with $eq to avoid accepting a similar but incorrect value.
Email extraction has a trap: _ in LIKE matches any character. I will verify the recovered address with $eq to avoid accepting a similar but incorrect value.
10Input validation testingEmail recovered through suffix matching Completed
dast_fuzzAnonymized excerpt
Input
Test editor_link.contact_address using $endsWith
Refine the address after ambiguous LIKE matching
Result
Candidate email: [redacted]
Suffix match found
11Replay HTTP requestFull email matches using $eq Completed
HTTP 200
match_count: N (N > 0)
Exact match confirmed
12
Decision·Obtain approval for the recovery request
The request sends an email; changing the password modifies the account. The customer approved these actions in this case.
13Replay HTTP requestReset code appears after the recovery request Completed
dast_replayAnonymized excerpt
Input
POST /example-api/cms/account/recovery
{"contact_address":"[redacted]"}
Repeat recovery_code[$notNull]
Result
HTTP 204
Before recovery: match_count 0
After recovery: match_count N > 0
14AnalysisThe code now exists and is accessible through the same oracle. I will complete the approved password change and login. Further CMS vulnerabilities are a separate scope and are not part of this result.
The code now exists and is accessible through the same oracle. I will complete the approved password change and login. Further CMS vulnerabilities are a separate scope and are not part of this result.
15Input validation testingReset code recovered through the oracle Completed
dast_fuzzAnonymized excerpt
Input
Bounded recovery_code prefix testing
The recovered value is not published
Result
recovery_code: [redacted]
Code passed to the approved validation step
16
Finding·Access to the administrator account
The approved password change and administrator login were completed. Critical vulnerabilities were retested after remediation.
Attack chain
Confirmed
CMS administrator takeover through the Content API
An external party gains administrative CMS access
Business risk
80/100
Exploitation likelihood
95%
Attack path
Initial access
An external visitor without an account. The customer approved the password change.
01Filtering on hidden administrator fieldsContent API Filter oracleConfirmed
Prerequisite
The Content API is accessible without a session and accepts nested filters.
Result
Record counts depend on the editor_link condition; the internal recovery_code field is accepted.
Content API responds without a session
Anonymized research evidence excerpt
Request / action
GET /example-api/cms/records
No Cookie or Authorization
Administrator login was confirmed with approval. Retesting of critical vulnerabilities passed after remediation.
Success criterion
Administrator login confirmed after an approved password reset.
Remediation
Block filtering on internal fields
Prevent the public Content API from filtering on administrator relations and update the CMS.
Revoke exposed codes and review access
Invalidate recovery codes and retest administrator access.
The chain began with CVE-2026-27886. The public API accepted filters on hidden fields and returned different responses depending on whether a value matched. AI Hacker used these differences to recover the administrator’s email address and then a password reset code.
With the customer’s approval, the password was changed and the account was accessed. Administrator account takeover was confirmed in practice.
2. An exposed administrative Bitrix24 webhook
Sentra AI Hacker
Objective
Check the privileges of a webhook found in public client configuration.
Confirmed
3 of 3 confirmed
Public pageportal.example
Webhookclient_settings
AdministratorIntegration profile
CRM APIAllowed methods
01AnalysisThe app-bootstrap contains client_settings.crm_hook_url. The URL resembles an incoming webhook: /example-api/integration/<actor>/<key>/. I will start with actor-info without a session to check that the key is active and identify its user.
Within app-bootstrap there is client_settings.crm_hook_url. The URL resembles an incoming webhook: /example-api/integration/<actor>/<key>/. I will start with actor-info without a session to check that the key is active and identify its user.
02Inspect HTTP trafficWebhook found in the public page HTML Completed
actor-info identifies the webhook user, permission-list shows the allowed scope, and operation-list lists available methods. These responses must be considered together: is_admin does not replace a CRM scope check or prove that every operation was executed.
04AnalysisThe secret is already in the path /example-api/integration/<actor>/<key>/; this access method requires no browser session. I will remove Cookie and Authorization when replaying the request so the administrator profile cannot be attributed to my own session.
The secret is already in the path /example-api/integration/<actor>/<key>/; this access method requires no browser session. I will remove Cookie and Authorization when replaying the request so the administrator profile cannot be attributed to my own session.
GET /example-api/integration/<actor>/[key]/actor-info
No Cookie or Authorization
Result
HTTP 200
{"actor_context":{"is_admin":true}}
06AnalysisThe webhook is active and the profile returned is_admin: true. Next I will check permission-list: the administrator flag alone does not establish which scopes this key can access.
The webhook is active and the profile returned is_admin: true. Next I will check permission-list: the administrator flag alone does not establish which scopes this key can access.
GET /example-api/integration/<actor>/[key]/operation-list
Result
HTTP 200
List of methods for this webhook retrieved
09Execute commandThe list includes CRM operations and batch requests Completed
computer_executeAnonymized excerpt
Input
Local analysis of the saved operation-list
Identify CRM object operations and batch requests; do not invoke the operations
Result
Operations on CRM leads, deals, contacts, companies, and activities
Batch requests
10AnalysisThe secret is in HTML, actor-info confirms an administrator, and permission-list and operation-list confirm CRM access. This is enough for the report. Creating or deleting customer objects is unnecessary to verify these privileges.
The secret is in HTML, actor-info confirms an administrator, permission-list and operation-list confirm CRM access. This is enough for the report. Creating or deleting customer objects is unnecessary to verify these privileges.
11
Decision·Stop after metadata requests
Only actor-info, permission-list, and operation-list were called. No CRM data was created, modified, or deleted.
12
Finding·Exposed administrative Bitrix24 webhook
An external visitor obtains an active integration secret with CRM access. The key must be revoked and removed from client configuration.
Attack chain
Confirmed
Exposed administrative Bitrix24 webhook
An external party obtains an integration key with CRM access
Business risk
80/100
Exploitation likelihood
95%
Attack path
Initial access
An external visitor to the public page without a session.
01Extracting a secret from HTMLPublic page WebhookConfirmed
Prerequisite
client_settings is included in the public page’s app-bootstrap.
Result
A webhook URL containing an integration secret was found in the configuration.
An external party obtains an integration key with CRM access
is_admin=true, access_areas=crm. Metadata methods were tested; CRM objects were not modified.
Success criterion
The webhook is active: actor-info returns is_admin=true and permission-list includes CRM. CRM objects were not modified.
Remediation
Revoke the exposed webhook
Rotate the integration secret and review its usage.
Move the secret to the server
Remove the webhook from HTML and client configuration; restrict integration privileges.
A valid Bitrix24 webhook was found in client-side configuration available to any visitor. A few metadata requests confirmed that the key was active, had administrator privileges, and provided access to CRM and its API methods.
Such a key allows CRM objects to be read and modified, creating a risk of both data disclosure and tampering. No CRM records were accessed or changed during the assessment; metadata requests were sufficient.
Pilot outcome
The customer received a detailed technical report covering:
vulnerabilities and their severity;
evidence and reproduction steps for confirmed scenarios;
attack chains and their potential consequences;
remediation guidance, divided into immediate and additional measures.
Sentra automatically prioritized vulnerabilities based on confirmed exploitation scenarios and business risk. The customer fixed the critical vulnerabilities, and retesting confirmed that the fixes worked.
The company is now preparing to integrate Sentra AI Hacker into continuous security assessment.