Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/network-services-pentesting/pentesting-web/wordpress.md
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,56 @@ Issues introduced by this snippet:
The response discloses the result of the injected query or alters the database, proving SQLi.


### Unauthenticated SQL Injection via PayPal IPN (Paid Membership Subscriptions ≤ 2.15.1)

Paid Membership Subscriptions exposed a public listener for PayPal Standard IPN at any front‑end URL gated by `?pay_gate_listener=paypal_ipn`. After IPN verification, the handler extracted `$_POST['custom']` as a payment ID and loaded a payment object, which concatenated the ID directly into a SQL string.

Data flow

- `GET ?pay_gate_listener=paypal_ipn` → IPN verifier → `$payment_id = $_POST['custom']`
- `pms_get_payment($payment_id)` → `PMS_Payment::get_data($id)`
- `$wpdb->get_row("SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = {$id}")`

Vulnerable sink (simplified)

```php
public function get_data( $id ) {
global $wpdb;
return $wpdb->get_row(
"SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = {$id}",
ARRAY_A
);
}
```

Exploitation notes

- Context is a SELECT, so use boolean/UNION‑based extraction; stacked queries typically won’t execute via WordPress DB APIs.
- Craft an IPN POST that passes the plugin’s verification (PayPal Sandbox works if test mode is enabled) and inject via `custom`, e.g. `1 OR 1=1-- -`.
- Match UNION column count with the original `SELECT * FROM ...` to leak arbitrary values (e.g., MySQL `version()`, other table data).

Patched pattern (2.15.2)

```php
public function get_data( $id ) {
global $wpdb;
$id = absint( $id );
return $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = %d",
$id
), ARRAY_A
);
}
```

Detection and hardening

- Log and review requests to `?pay_gate_listener=paypal_ipn` from non‑PayPal IPs and look for non‑numeric `custom` values or SQL metacharacters.
- Restrict IPN/webhook reachability, validate against provider endpoints, and strictly type/validate identifiers before DB access.
- Always use `$wpdb->prepare()` (or parameter binding in your stack) instead of concatenation.


### Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)

Another task, **downloadcustomfile**, allowed visitors to download **any file on disk** via path traversal. The vulnerable sink is located in `modules/customfield/model.php::downloadCustomUploadedFile()`:
Expand Down Expand Up @@ -722,5 +772,6 @@ The server responds with the contents of `wp-config.php`, leaking DB credentials
- [Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses](https://patchstack.com/articles/hosting-security-tested-87-percent-of-vulnerability-exploits-bypassed-hosting-defenses/)
- [WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)](https://patchstack.com/database/wordpress/plugin/woocommerce-payments/vulnerability/wordpress-woocommerce-payments-plugin-5-6-1-unauthenticated-privilege-escalation-vulnerability)
- [Hackers exploiting critical WordPress WooCommerce Payments bug](https://www.bleepingcomputer.com/news/security/hackers-exploiting-critical-wordpress-woocommerce-payments-bug/)
- [SQL Injection via PayPal IPN in Paid Membership Subscriptions (Patchstack)](https://patchstack.com/articles/sql-injection-vulnerability-patched-in-paid-membership-subscriptions-plugin/)

{{#include ../../banners/hacktricks-training.md}}
60 changes: 58 additions & 2 deletions src/pentesting-web/sql-injection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ true
-1/**/oR/**/1=1
1/**/aND/**/1=1
1'
1'>'0
1'>''0
2'-'1
0'+'1
1'*'1
Expand Down Expand Up @@ -531,6 +531,59 @@ Example:
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
```

## Abusing unauthenticated webhooks/IPN to reach SQL sinks (WordPress PayPal IPN case study)

Many web apps expose unauthenticated “listener” endpoints for third‑party payment providers (e.g., PayPal IPN, Stripe webhooks). If those handlers take a field from the webhook and concatenate it into a SQL query, you get pre‑auth SQL injection reachable from the internet.

Generic hunting flow

- webhook_handler() reachable unauthenticated (often gated by a GET switch like `?listener=...`).
- The handler parses POST JSON/form fields, then calls a model loader with a user‑supplied identifier.
- The model function builds a query like `SELECT * FROM <table> WHERE id = {$input}` via string concatenation instead of a prepared statement.
- Sometimes an upstream “verifier” gate exists (e.g., IPN signature check or sandbox mode). You must satisfy it so execution reaches the SQL sink.

Real‑world example (WordPress – Paid Membership Subscriptions ≤ 2.15.1)

- Public endpoint: any front‑end URL with `?pay_gate_listener=paypal_ipn` receiving an HTTP POST that resembles a PayPal IPN.
- Data flow: `$_POST['custom']` → `$payment_id` → `PMS_Payment::get_data($id)` → `$wpdb->get_row("SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = {$id}")`.
- Vulnerable sink (simplified):

```php
public function get_data( $id ) {
global $wpdb;
$result = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = {$id}", ARRAY_A );
return $result;
}
```

- Exploitation notes:
- Context is a SELECT; expect boolean/UNION‑based reads, not stacked queries.
- Craft a valid IPN POST that passes verification (use PayPal Sandbox if test mode is enabled) and inject via `custom`, e.g. `1 OR 1=1-- -`.
- Target table typically `{prefix}pms_payments`; align UNION columns with the original SELECT.

Mitigation pattern

- Enforce numeric type and use prepared statements/bind parameters:

```php
public function get_data( $id ) {
global $wpdb;
$id = absint( $id );
return $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}pms_payments WHERE id = %d",
$id
), ARRAY_A
);
}
```

Detection and hardening

- Monitor access to webhook listeners from non‑provider IPs with non‑numeric identifiers; correlate with DB error/slow logs.
- If using PayPal IPN, restrict to sandbox in test environments, validate against provider endpoints, and reject requests with malformed fields.
- Never derive identifiers used in SQL directly from webhook bodies without strict validation and binding.

## WAF Bypass

[Initial bypasses from here](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
Expand Down Expand Up @@ -674,5 +727,8 @@ https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
## References

- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
- [SQL Injection via PayPal IPN in Paid Membership Subscriptions (Patchstack)](https://patchstack.com/articles/sql-injection-vulnerability-patched-in-paid-membership-subscriptions-plugin/)
- [WordPress $wpdb->prepare() documentation](https://developer.wordpress.org/reference/classes/wpdb/prepare/)
- [PayPal Instant Payment Notification (IPN) overview](https://developer.paypal.com/docs/api-basics/notifications/ipn/)

{{#include ../../banners/hacktricks-training.md}}
{{#include ../../banners/hacktricks-training.md}}