Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
drupal-oembed-ssrf — Drupal 11.4.8 - High - Remote - SSRF - oEmbed discovery GETs an unscoped second-hop URL | Kitploit
Tools/GitHubGitHub/abraxas/drupal-oembed-ssrf
Vulnerability AnalysisExploitationWeb Application ExploitationSecurity VirtualizationWeb SecurityPenetration Testing
GitHubabraxas/drupal-oembed-ssrf

drupal-oembed-ssrf

Drupal 11.4.8 - High - Remote - SSRF - oEmbed discovery GETs an unscoped second-hop URL

View Repository
74 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - drupal-oembed-ssrf

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  drupal-oembed-ssrf

drupal-oembed-ssrf

Class: SSRF Reach: Remote

Drupal core 11.4.8 (61044508) - Drupal

SA-CORE-2026-008 gated the discovery document host with media_oembed_discovery_trusted_host_patterns. UrlResolver::findUrl still returns the raw link href. ResourceFetcher::fetchResource then GETs that URL with no host or private-IP check. oEmbed thumbnail_url is the same class of GET. Default patterns are empty, so discovery is off until an admin enables it.

After an admin turns discovery on, a user who can create oEmbed media can point Drupal at an allowlisted page whose JSON oEmbed link href is loopback, RFC1918, or metadata.

IDno CVE yet
ClassSSRF (second-hop leftover of SA-CORE-2026-008)
ReachRemote (authenticated media create, after admin sets discovery hosts)
CWECWE-918
CVSSMedium: 6.1 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N
ProductDrupal core
Affected11.4.8 (61044508071749629aaf3380aa85971e94df9e84)
Authauthenticated media create, after an admin sets discovery hosts. AC:H is that admin toggle.
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

Only after an admin has set discovery trusted-host patterns. A media creator is enough after that. A complete stranger with no account cannot do this on default. This is SSRF, not RCE and not an admin login.

PrivilegeMethod / sinkKindLab-proven or inferred
Make Drupal GET an attacker-chosen URL (loopback in this lab)UrlResolver::findUrl raw href then ResourceFetcher::fetchResource GETrecon (server-side fetch)Lab-proven. http://127.0.0.1:18080/oembed returned DRUPAL-OEMBED-SSRF-WITNESS
Read the HTTP body of that URL into PHPSame GET; JSON/XML parsed as oEmbedreconLab-proven. Witness was in the 200 body
Same GET for thumbnail_url after a resource is builtOEmbed::getLocalThumbnailUri ~417-456reconInferred from the pin. This lab stopped at the discovered href
Reach RFC1918 / link-local / metadata if the PHP netns canUnscoped Guzzle GETreconInferred. Lab used loopback, not 169.254.169.254

Closed on this leftover: first hop when patterns are empty (lab-proven); first hop when the HTML host does not match (lab-proven); unauthenticated media create on Standard without a media role; RCE; admin session theft.

How I found it

I pinned Drupal 11.4.8 after SA-CORE-2026-008. The first hop is gated:

$patterns = \array_map(static fn ($hostPattern) => \sprintf('{%s}i', $hostPattern), Settings::get('media_oembed_discovery_trusted_host_patterns', []));
// ...
if (!$allowed) {
  return FALSE;
}
$response = $this->httpClient->get($url);
return $this->findUrl($xpath, 'json') ?: $this->findUrl($xpath, 'xml');

findUrl returns the raw href. ResourceFetcher GETs it with a timeout and nothing else:

$result = $xpath->query("//link[@type='application/$format+oembed']");
return $result->length ? $result->item(0)->getAttribute('href') : FALSE;
$response = $this->httpClient->request('GET', $url, [
  RequestOptions::TIMEOUT => $this->timeout,
]);

The lab is a faithful gadget of those 11.4.8 functions, not a live drupal:11.4.8 site install. Last line uses how=gadget. Empty patterns did not fetch. A non-matching host did not fetch. With ^firsthop$ allowed, discoverResourceUrl returned http://127.0.0.1:18080/oembed and the unscoped GET returned the witness.

Wrong turns already recorded: treating default-off as a miss (it is the intended first-hop gate; the leftover is hop two); calling a live Standard install the oracle (this pack is the pin functions); claiming RCE because Guzzle ran. Theatre: a reverse shell. The oracle is default-off plus wrong-host blocked plus the second-hop witness.

Same pin leftovers: CKEditor 5 FileIsImage skip.

Lab

python3 drupal-oembed-ssrf-Abraxas-Labs.py

or

cd lab
./run.sh

Loopback only: first-hop HTML on http://127.0.0.1:19510. Second hop is http://127.0.0.1:18080/oembed inside the gadget netns.

SUCCESS drupal-oembed-ssrf how=gadget pin=11.4.8 first-hop=allowed second-hop=127.0.0.1:18080 default-off=blocked DRUPAL-OEMBED-SSRF-WITNESS

The fix

Run the same host / private-IP policy on the discovered endpoint and on thumbnail_url. Do not fetch an href whose host would have failed the first hop.

References

  • github.com/drupal/drupal tag 11.4.8 (61044508071749629aaf3380aa85971e94df9e84)
  • UrlResolver.php · ResourceFetcher.php
  • Nearby patched (not this hop): SA-CORE-2026-008
  • Same tag: drupal-ckeditor-fileisimage
  • CWE-918

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool