Web Security

SMF’s Image Proxy Signed an Internal Request for a Forum User (CVE-2026-61520)

With its image proxy enabled, Simple Machines Forum turned an authenticated user's image address into an HMAC-authorized server fetch; incomplete destination validation let that server-minted request cross DNS and redirect boundaries toward non-public network targets.

A warm hand-drawn view of the SMF 3.0 repair: DNS A and AAAA results converge into one address set, the global-range gate rejects private and NAT64-to-private destinations, and a 3xx response returns to the URL for another check.
In this article

Research basisSOSEC web security research · source pinned at 90ff9400d641 ; request creation, HMAC validation, resolution, connection, redirect, and cache paths reviewed across release-2.1 and release-3.0

SourceGitHub Advisory Database unreviewed entry / CVE record / SMF 2.1 and 3.0 repair commits / SMF source / SOSEC source review

1 An ordinary forum image ultimately used the server's network position

A browser normally downloads a remote forum image directly from the image host. Simple Machines Forum can take a different route. When the image proxy is enabled, SMF fetches the object into a local cache and serves that cached representation to readers. The design solves mixed-content problems, shields reader addresses from remote image operators, and provides a place to enforce size and media rules.

CVE-2026-61520 sits inside the identity change created by that proxy. An authenticated user selects the destination in forum content. SMF then computes an HMAC with a site secret and creates a local proxy URL. When that URL is requested, proxy.php or its 3.0 class equivalent validates the HMAC and performs a server-side fetch. The remote endpoint sees the forum server, not the reader.

The old checks recognized some obvious local forms, yet they did not consistently turn a hostname into its complete connection set and re-evaluate each redirect. A hostname that resolved to a private, loopback, link-local, reserved, IPv6, or translated destination could cross the intended rule. A public first hop could also select a different destination through an HTTP Location header.

The request did not need to become a valid displayed image for the network action to matter. SMF could inspect MIME type and size only after DNS, TCP, optional TLS, and HTTP had already occurred. An internal service that returned text would fail the image test, but it would still have received a request from a trusted server-side network position.

The GitHub Advisory Database published an unreviewed entry sourced from NVD on July 14, 2026 and records a Moderate, CVSS 6.3 assessment. It is not a Simple Machines vendor advisory. This review pins the repository at 90ff9400d6415bc9368fc91c61123bd7bca885d8 and examines repair commits 4bf35cf9e45573a5f55a6f52995086c1da89c096 and b4d23dfd74a511587c605f9d294cefc3a75b4b26. Every causal claim below maps to the request fields, functions, or branch contents in those snapshots.

1.2 The proxy turns a BBCode URL into a server-signed fetch and changes whose network performs it

Forums preserve old content for years. A post may reference an HTTP image long after the forum itself moves to HTTPS. Modern browsers warn about or block active mixed content, and remote hosts can collect an address, user agent, timestamp, and referrer for every reader. A local proxy gives the operator one controlled HTTPS origin and prevents the image host from observing each visitor.

The proxy also reduces repeat traffic. Once SMF has fetched and classified an image, later page views can read a cached body. Administrators can set a maximum object size and a cache lifetime. Those product benefits are real; they also mean the feature owns DNS resolution, network connections, redirects, response parsing, storage, and expiry refresh.

A browser and a forum process do not see the same network. Browser controls include same-origin policy, private-network restrictions, and the reader's route. The forum process may reach a VPC, container services, a host gateway, cloud metadata, monitoring endpoints, or interfaces that trust internal source addresses. The proxy turns a content field into an action within that broader view.

Threat modeling therefore asks more than whether a user controls a URL. It asks which component resolves the host, how many A and AAAA answers it receives, which answer the connection library chooses, whether a redirect changes the host, where the process runs, and when response validation occurs. The vulnerability is the combined answer to those questions.

In the 2.1 branch, image processing lives in Sources/Subs.php. The BBCode parser extracts and normalizes the URL supplied between image tags. When image proxying is active, the parser calls get_proxied_url($url). At that point, the destination is still selected by the content author.

get_proxied_url() reads the board URL, the proxy flag, $image_proxy_secret, and current-user state. Under the normal proxy path it constructs a local HTTPS URL with two query fields. request contains the encoded remote URL. hash is hash_hmac('sha1', $url, $image_proxy_secret).

forum content: [img]USER_SELECTED_URL[/img]
  → get_proxied_url(USER_SELECTED_URL)
  → /proxy.php?request=urlencode(USER_SELECTED_URL)
              &hash=HMAC-SHA1(USER_SELECTED_URL, site_secret)

The author never needs to learn or guess the secret. The application takes a low-privilege message and endorses it with a high-value credential. The HMAC remains cryptographically valid. Its message domain is the problem: a destination chosen through the content path receives a proxy ticket that the network-facing endpoint will accept.

The generated URL appears in rendered content. A preview, reader request, moderation workflow, or later cache refresh can trigger it. The public database entry describes an authenticated attacker. Operators should enumerate every content feature that reuses BBCode or the proxy helper, including posts, messages, signatures, profile fields, calendar items, and third-party extensions.

Four stages show a controlled HTTP BBCode image URL, HMAC creation in get_proxied_url, checkRequest verification, and the server-side cache fetch.
The HMAC proves that SMF issued the URL. Destination safety must be established independently at the point where the server prepares to connect.

A public image proxy without authorization would be an obvious open fetch service. SMF therefore requires both query fields and recomputes the HMAC with the local secret. If an external visitor edits the request value, the digest no longer matches. This is an appropriate integrity control for a proxy ticket.

An authenticated content author does not attack that comparison. The author reaches a legitimate minting path. Once get_proxied_url() signs the selected address, the downstream endpoint cannot distinguish a harmless public image from a non-public destination merely by checking who created the signature. It needs a destination policy as a second decision.

This pattern recurs in thumbnail services, document renderers, link previews, webhook relays, email scanners, and export jobs. Signed URLs prevent tampering and unmetered direct invocation. They do not validate the user-controlled message before signing. A source review must search backward from verification to every ticket issuer.

Rotating $image_proxy_secret would invalidate old tickets and leave the causal path intact. New content would receive signatures from the new secret. A durable fix changes what can be signed or rejects unsafe destinations at the proxy and connection layers. SMF chose the latter and applied it broadly.

2 checkRequest() moves from ticket validation to network I/O, and a cache miss creates the connection

The 2.1 proxy class initializes from the feature flag, maximum image size, cache directory, and site secret. checkRequest() then requires both hash and request, validates URL syntax, excludes the forum's own host, and verifies the HMAC. Those decisions establish that a ticket is well formed and unmodified; they do not yet establish that its remote address is suitable for a server-side connection.

Order determines how caching changes exposure. Neither vulnerable parent has the new destination predicate: a valid cache hit can return locally, while a miss enters the network path. The repaired 2.1 entry calls is_fetch_safe($request) before its cache decision, and repaired 3.0 calls WebFetchApi::isFetchSafe($request) in the same position. A repaired cache hit may still cause safety-related DNS work, but it does not reconnect to the remote host or read a new body.

On a miss, cacheImage() derives the cache name and invokes the shared fetcher. MIME and length checks run only after a response body arrives. They can reject an unsuitable cache object, but they cannot reverse resolution, connection, and request transmission that have already happened. That is why a page that never displays an image can still have exercised the SSRF path.

Release 2.1 uses fetch_web_data() across several PHP configurations; release 3.0 routes WebFetchApi::fetch() into CurlFetcher or SocketFetcher. The business entry knows that the request is an image-proxy ticket, the shared entry selects a protocol implementation, and the concrete fetcher sits closest to the socket. The patch evaluates the destination at those different states so a caller cannot escape merely by entering at another layer.

The reviewed claim is intentionally limited to HTTP/HTTPS. Release 3.0 covers proxy and shared entries, Curl/Socket request paths, and redirects; release 2.1 covers proxy and shared entries plus its legacy cURL redirect. FTP/FTPS is a separate calling surface. Disable it when unused or verify control and data connections independently under egress policy.

Deployment review must therefore look beyond one new condition in proxy.php. Confirm that the PHP backend actually enabled in production comes from the complete repaired package, that themes or extensions did not copy an old helper, and that no plugin directly instantiates an unguarded fetcher. Staging and production must agree on cURL, socket, IPv6, and network-policy behavior.

2.1 Both repairs inspect the complete DNS result, but each branch keeps its own address semantics

The vulnerable code was not unguarded. A request needed recognizable URL structure, the proxy excluded its own host, and some literal addresses or exact localhost forms failed. The missing step was the network meaning behind an ordinary hostname.

A resolver can return several A and AAAA records, and the connection library may select among them according to protocol preference, ordering, or failure. A public candidate cannot authorize a set that also contains loopback, private, link-local, or another prohibited address. Both repairs therefore form one complete candidate set and make one branch-specific decision over it.

Release 3.0's WebFetchApi::isFetchSafe() first checks registered schemes and any narrower set supplied by the caller, then rejects an empty host and reserved suffixes. A literal IP becomes a candidate; a hostname is queried with dns_get_record(..., DNS_A | DNS_AAAA); no answer fails closed. Candidates then pass through the project IP semantics and FILTER_FLAG_GLOBAL_RANGE, with one failing value rejecting the host.

Release 3.0 also normalizes the well-known 64:ff9b:: NAT64 form in Sources/IP.php, converting its final 32 bits to dotted IPv4 before classification. A syntactically IPv6 destination that carries private or loopback IPv4 meaning is thus judged by that embedded route rather than its surface notation.

Release 2.1's is_fetch_safe($url) uses the legacy IRI parser, permits HTTP, HTTPS, FTP, and FTPS, and applies FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_NO_PRIV_RANGE to its candidates. It also rejects an empty result, but it does not have 3.0's GLOBAL_RANGE definition or NAT64 change. The two branches must not be flattened into one phrase such as “globally routable.”

A diagram of the SMF 3.0 repair and target defensive model: a URL becomes A and AAAA candidates, the global-range gate rejects private and NAT64-to-private destinations, and HTTP redirects return for another destination decision.
This figure depicts the 3.0 global-range and NAT64-aware model. The 2.1 predicate uses different PHP flags and must be accepted against its own runtime behavior.

Backports and tests must preserve that distinction. Record the normalized candidate array, the exact predicate result, and whether the selected fetcher attempted a connection. DNS64/NAT64, IPv4-mapped IPv6, and interpretation differences among PHP, cURL, and the operating system belong in an isolated matrix. A stricter deployment egress rule remains an independent control; it must not be reported as semantics implemented by the application function itself.

2.2 An HTTP redirect creates a new destination; permission from the previous hop does not carry forward

A public image can return 301, 302, or 307, and Location can be absolute, protocol-relative, or relative to the current path. The fetcher completes that value and prepares another request. A safe first connection says nothing about the second destination selected by its response.

CurlFetcher keeps libcurl auto-follow disabled. sendRequest() reads status and Location, getRedirectUrl() completes the target, and redirect() calls WebFetchApi::isFetchSafe(). A rejection stops recursion and prevents the current response from becoming a successful image result.

SocketFetcher constructs a new Url and re-enters guarded request(); the 2.1 legacy cURL class calls its own branch predicate before following. The 3.0 cURL redirect decision does not carry the initial narrow HTTP/HTTPS scheme list, so cross-protocol Location values remain negative tests backed by egress policy.

Acceptance observes network facts: the public recorder receives the first request, while the prohibited recorder receives none. Relative and cross-host targets, protocol-relative values, multiple hops, loops, malformed Location, and hop exhaustion are separate cases. A final false return is weaker evidence than proving that no second socket was opened.

2.3 MIME, size, and cache rules protect the response, but arrive too late to authorize the destination

After a body arrives, cacheImage() determines MIME type, requires an image/ prefix, and enforces the configured length limit. Those are necessary content controls that keep obvious non-images or oversized objects out of the cache.

The SSRF-relevant work occurs earlier: DNS, TCP, optional TLS, and the HTTP request all precede body classification. An internal text endpoint has already received traffic from the forum's network position when its response fails the image test. If a legacy endpoint changes state on GET, the caller may not need any readable body.

A real image response does not establish public origin. Internal cameras, monitoring, and chart services can return images, while a public attacker can forge content type. Destination policy answers whether the server may connect; response policy answers whether the returned object may be stored and displayed. Both decisions must pass independently.

The mechanism is now complete: SMF mints a ticket for an author-selected address, a cache miss sends that ticket into server-side fetching, DNS and redirects can alter the effective target, and response checks intervene only after connection. The next chapter discusses how the two maintained branches close that path without replaying it.

3 Two maintained branches close the same HTTP control point through different code structures

SMF maintains 2.1 and 3.0 in parallel. Release 3.0 uses namespaces, typed methods, Url, and WebFetchApi; release 2.1 retains global functions and traditional file layout. Both repairs make a destination decision before connection, but one relies on the project global-range model while the other uses PHP's non-private and non-reserved flags.

Repository containment gives stable attribution. git branch --contains 4bf35cf9... identifies release-3.0, with changes in ProxyServer, WebFetchApi, Url, IP, and namespaced fetchers. git branch --contains b4d23df... identifies release-2.1, with changes in Subs.php, proxy.php, and the legacy cURL class.

The unreviewed GitHub Advisory Database text appears to transpose the two hashes when associating them with release families. This report does not speculate about the cause; it follows reproducible repository evidence and writes 4bf35cf9→3.0 and b4d23df→2.1.

That correction affects inventory. A scanner using the reversed map can both miss vulnerable trees and flag repaired ones. A downstream commit may use another hash, but it must preserve the functions, predicate, call positions, and behavior of its own branch. The 2.1 and 3.0 fixes are not interchangeable substitutes.

3.1 Release 3.0 closes an object-oriented shared control point; release 2.1 adds the same timing to its legacy call chain

In 3.0, ProxyServer::checkRequest() rejects an unsafe target at business entry, WebFetchApi::fetch() repeats the decision before backend selection, and CurlFetcher or SocketFetcher checks closest to connection. Every redirected Url re-enters a guard. HMAC continues to provide ticket integrity; the destination function provides network authorization.

The branch decision combines WebFetchApi::isFetchSafe() and IP.php: scheme and reserved-suffix checks precede A/AAAA classification under GLOBAL_RANGE, with NAT64 normalized first. CurlFetcher's next-hop check uses the general predicate rather than the initial narrow scheme set, leaving cross-protocol behavior to negative tests and egress enforcement.

Release 2.1 adds is_fetch_safe($url) in Sources/Subs.php and calls it from proxy.php::checkRequest(), common fetch_web_data(), and the legacy cURL redirect. It blocks at the same business, shared-library, and destination-change moments, but its address result remains exactly PHP's non-private and non-reserved decision.

Long-lived forums commonly modify Subs.php, proxy.php, or fetchers. A merge can retain HMAC and MIME behavior while dropping one safety call. An upgrade or distributor backport is acceptable only when symbols, call positions, and recorder tests agree; a version banner or one copied function body is insufficient.

The shared HTTP invariant is concise: a URL rejected by its branch predicate must never reach a cURL or HTTP-socket connection, either initially or after a redirect. FTP/FTPS is outside that proof and must be disabled or supported by separate evidence. The source mechanism ends here; the remaining chapters cover exposure, response, and acceptance.

4 Concrete impact follows the forum process's routes and the way each reachable service trusts that source

SSRF does not automatically mean complete internal data access. Outcomes depend on available protocols, response visibility, caching, target authentication, metadata hardening, network ACLs, and endpoint semantics. Source establishes that the server can send a request; asset and event evidence determine what that request can obtain.

One consequence is discovery through timing, status, and cache differences. Another is access to internal HTTP services that trust a source subnet. A third is contact with local management or cloud metadata. Modern platforms may require tokens, special methods, or hop constraints, which narrow consequences without repairing the forum path.

If an internal interface changes state on GET, the connection itself can matter. If a body also passes image and size checks, it may become observable through the proxy cache. A non-image body normally fails display, but timing, error state, and target logs can still prove that network activity occurred.

Impact assessment should therefore be asset-specific: record the SMF host, container or Pod, outbound routes, reachable subnets, metadata mode, service mesh, internal HTTP dependencies, and source-address trust, then overlay proxy state and content permissions. That intersection sets priority more accurately than one universal worst-case statement.

4.1 Five conditions can be verified separately: identity, content path, proxy state, outbound reachability, and target trust

The first is a user or workflow that can create content passing through image BBCode and get_proxied_url(). Group permissions, moderation, messages, signatures, imports, and extensions all change that population. A source call graph and route test are more reliable than assuming only public posts use the helper.

The second is image proxying or another shared-fetch caller. Disabling image_proxy_enabled removes the named image path, while avatar imports, MIME probes, previews, scheduled jobs, or plugins may still call fetch_web_data() or WebFetchApi::fetch().

The third is an outbound route from the forum process to the destination. A container with no public listener may still reach cluster services, a host gateway, or metadata. Reverse proxies and inbound WAFs usually do not establish that these active requests are blocked.

The fourth is a reachable target that trusts the source or yields a meaningful response without strong identity. A restrictive egress proxy, hardened metadata mode, and authenticated internal APIs narrow risk; services that rely on source IP or “internal only” expand it.

The fifth is code from before the appropriate repair. In 3.0, inspect isFetchSafe() and its ProxyServer, shared-entry, Curl/Socket, and next-hop callers. In 2.1, inspect is_fetch_safe(), proxy.php, fetch_web_data(), and the legacy cURL redirect. A customized package version string cannot replace function evidence.

4.2 Egress policy turns an application mistake into a blocked and observable event

At minimum, deny forum processes access to private, loopback, link-local, reserved, metadata, cluster-control, and management ranges over both IP families. Route permitted public HTTP and HTTPS through a controlled egress proxy that records the final connection IP and repeats destination classification.

A controlled exit can also narrow the interval between DNS review and connection. High-assurance deployments may pin the reviewed address while preserving the original Host header and TLS SNI, with explicit behavior for legitimate multi-address hosts and CDN rotation rather than URL substitution that breaks certificates or application semantics.

Bind policy to the actual SMF process, container, or dedicated fetch identity and test the real packet path. IPv4 ACLs do not automatically cover IPv6 or NAT64, and a configuration file that says “deny” does not prove traffic traverses that firewall. The application repair and infrastructure rule should each produce independent evidence.

4.3 Detection joins content author, proxy ticket, destination decision, connection, and cache into one event

The signer records content object, author, feature, canonical host, scheme, port, and a redacted URL hash, then creates a request ID carried into proxy and fetch logs. Full query strings may contain sensitive data and should be field-redacted rather than copied without limits.

Destination logs record branch, predicate, address classes, and rejection stage; fetch logs record backend, redirect hop, actual connection IP, status, bytes, MIME, duration, and cache state. Resolver, application, egress, and target-side records need the same stable key to prove that rejection preceded socket creation.

One attempt toward metadata or a management range merits escalation. Volume adds context: a new account submitting many hosts, ports, redirectors, or cache misses in a short interval resembles network exploration through the proxy. Detection follows destination and behavior, not one public test string.

4.4 Historical review moves from a minted ticket to contemporaneous DNS, real connection, and impact grade

Fix a time zone and preserve the database, web and PHP logs, proxy cache, resolver records, and outbound flows. Extract proxy.php?request=... values only into offline tooling, normalize without connecting, and cluster by author, host, port, time, and cache key. The analyst workstation must never visit an extracted destination automatically.

Current DNS does not establish event-time routing. Use retained recursive logs, authoritative records, or suitable historical data around content creation and proxy access. Add cache creation, modification, and expiry to the timeline: a vulnerable-parent hit can stay local, while a miss or expiry refresh enters the full fetch path.

Search the forum process, container node, NAT, and egress records for internal HTTP connections and align them with proxy access and cache time. Target-service logs are strongest for method, path, source, and response. A proxy 404 describes an application result; by itself it does not establish whether a connection occurred.

Grade conclusions separately: a dangerous ticket existed; resolution occurred; a connection was attempted or established; a response arrived; sensitive content or state was reached; credentials were later used. If the target is metadata, a secret store, or an internal credential endpoint, rotate and investigate downstream use without waiting for proof that the body displayed as an image.

4.5 Response moves from shutting the entry and preserving evidence to cohort recovery

If an immediate upgrade is unavailable, disable image proxying and unnecessary remote fetching, then deny non-public, metadata, and management destinations at egress. Business configuration reduces new calls; network policy covers missed callers. Record the exact effective time, owner, user impact, and rollback criteria.

Restricting remote-image creation for low-trust users only slows new ticket minting. Existing posts can contain valid proxy URLs, and first viewing or cache expiry can trigger them later. Blocking /proxy.php at the web layer cuts the named path but breaks images and does not cover other shared-fetch callers.

Snapshot the database, logs, and cache before clearing. Move suspicious objects to read-only isolation, hash them, and restrict access. Once evidence points to a secret or credential endpoint, rotate keys, invalidate sessions, and inspect cloud or target-system records at the corresponding exposure level.

After upgrade and verification, restore proxying by cohort while watching destination rejection, image failure, DNS latency, connection volume, and cache rebuild. Decide whether old entries remain served, are quarantined, or refresh in batches so recovery does not refetch years of content at once.

5 Upgrade acceptance checks function coverage and whether a prohibited destination actually received a connection

A 3.0 instance should contain WebFetchApi::isFetchSafe(), ProxyServer and shared-entry calls, CurlFetcher/SocketFetcher request and next-hop decisions, and IP.php NAT64 normalization. A 2.1 instance should contain is_fetch_safe(), proxy.php, fetch_web_data(), and the legacy cURL redirect change. Distributor backports are accepted by those semantics and file hashes, not by a version banner.

Build isolated authoritative DNS and HTTP recorders: one name points to a permitted test service, one to a prohibited recorder, one returns a mixed set, and one public recorder redirects to the prohibited recorder. The IPv6 group covers loopback, ULA, link-local, mapped, and NAT64 forms. Recorders return only a tiny fixed image or 302 and expose no real management function.

Use a normal authenticated account to create image content and record the generated ticket, branch predicate, DNS array, backend, cache state, and connection count. A normal public image succeeds; a single or mixed target prohibited by deployment policy receives no connection; the redirect case reaches only the first recorder. Keep the application predicate and stricter egress result in separate fields instead of forcing 2.1 and 3.0 to emit the same classification.

Finally force cURL and socket under the production PHP, IPv4-only or dual-stack, and container configuration. Regress HTTPS images, internationalized names, legitimate multi-address CDNs, relative redirects, cache hits and expiry, maximum size, and non-image rejection. Preserve resolver, application, recorder, and firewall logs as the release artifact.

6 The image did not defeat HMAC; signing and network authorization answered different questions

The decisive turn in CVE-2026-61520 occurs when the application mints a ticket for an author-selected address. HMAC proves that SMF generated the proxy URL, not that the host inside it is suitable for a connection from the forum server. The old code protected ticket integrity while leaving resolved and redirected destinations outside that decision.

Release 3.0 closes the path through WebFetchApi::isFetchSafe(), the project IP semantics, and the traced Curl/Socket flows. Release 2.1 adds the missing timing through is_fetch_safe(), the shared fetch entry, and legacy cURL redirects. Each branch needs its own function and behavior evidence; “2.1,” “3.0,” or a database hash label is not enough.

The operational closure follows directly: confirm the correct branch repair, preserve cache and historical logs, constrain forum egress, review previously minted dangerous destinations, and prove the repaired network result with isolated DNS and recorders. Contact with a sensitive target advances credential and target-system response according to evidence.

Image proxying can continue to provide HTTPS consistency, privacy, and caching. A durable design lets signatures authenticate where a message came from, lets destination policy authorize where the server will go, and lets independent egress controls and telemetry prove that decision took effect.

Research record

7Evidence, objects, and sources

The material below preserves the identifiers and references used in this report.

7.1Research objects

Products, actors, techniques, affected objects, and control points discussed in the report.

VulnerabilityCVE-2026-61520

Authenticated SSRF through the SMF image proxy

GitHub Advisory Database entryGHSA-9wvj-rhhh-8292

Unreviewed entry sourced from NVD; published July 14, 2026

SMF 3.0 fix4bf35cf9e45573a5f55a6f52995086c1da89c096

WebFetchApi safety choke point on release-3.0

SMF 2.1 repairb4d23dfd74a511587c605f9d294cefc3a75b4b26

Legacy repair using PHP non-private and non-reserved filters

Reviewed snapshot90ff9400d6415bc9368fc91c61123bd7bca885d8

SOSEC local source pin

Input fieldsproxy.php?request={url}&hash=HMAC-SHA1(url, image_proxy_secret)

The server signed the content author's selected destination

Function chainget_proxied_url → checkRequest → cacheImage → fetch_web_data/WebFetchApi::fetch

Request creation through server-side connection

HTTP repair invariantall A/AAAA answers pass the branch predicate; HTTP next hops rechecked

Application and egress evidence must show zero prohibited Curl/Socket connections

7.2Event chronology

  1. Database entry published

    GitHub Advisory Database published the NVD-sourced unreviewed GHSA-9wvj-rhhh-8292 entry for CVE-2026-61520.

  2. SOSEC completed both-branch review

    The signing, proxy, DNS, connection, redirect, and cache chain was reproduced from source.

  3. SOSEC call-path review completed

    SOSEC pinned the relevant call sites, compared branch-specific repair decisions, and reconciled temporary controls with zero-connection verification.

7.3Sources and material

  1. GitHub Advisory Database unreviewed entry GHSA-9wvj-rhhh-8292https://github.com/advisories/GHSA-9wvj-rhhh-8292
  2. CVE-2026-61520 recordhttps://www.cve.org/CVERecord?id=CVE-2026-61520
  3. Published v2.1.7 tag commit d49255b9https://github.com/SimpleMachines/SMF/commit/d49255b955cbeeb3a702d77009c85aae517ed1e4
  4. Published v3.0-alpha.4 tag commit 49e49a03https://github.com/SimpleMachines/SMF/commit/49e49a035728fbed888b38807d80c1d050c7b014
  5. SMF 3.0 fix commit 4bf35cf9https://github.com/SimpleMachines/SMF/commit/4bf35cf9e45573a5f55a6f52995086c1da89c096
  6. SMF 2.1 fix commit b4d23dfdhttps://github.com/SimpleMachines/SMF/commit/b4d23dfd74a511587c605f9d294cefc3a75b4b26
  7. SMF 3.0 fixed WebFetchApi source at commit 4bf35cf9https://github.com/SimpleMachines/SMF/blob/4bf35cf9e45573a5f55a6f52995086c1da89c096/Sources/WebFetch/WebFetchApi.php#L130-L268
  8. SMF 3.0 fixed ProxyServer source at commit 4bf35cf9https://github.com/SimpleMachines/SMF/blob/4bf35cf9e45573a5f55a6f52995086c1da89c096/Sources/ProxyServer.php#L117-L161
  9. SMF 3.0 fixed CurlFetcher request source at commit 4bf35cf9https://github.com/SimpleMachines/SMF/blob/4bf35cf9e45573a5f55a6f52995086c1da89c096/Sources/WebFetch/APIs/CurlFetcher.php#L226-L253
  10. SMF 3.0 fixed SocketFetcher request source at commit 4bf35cf9https://github.com/SimpleMachines/SMF/blob/4bf35cf9e45573a5f55a6f52995086c1da89c096/Sources/WebFetch/APIs/SocketFetcher.php#L165-L181
  11. SMF 3.0 fixed IP and NAT64 normalization source at commit 4bf35cf9https://github.com/SimpleMachines/SMF/blob/4bf35cf9e45573a5f55a6f52995086c1da89c096/Sources/IP.php#L91-L116
  12. SMF 2.1 fixed shared fetch and safety functions at commit b4d23dfhttps://github.com/SimpleMachines/SMF/blob/b4d23dfd74a511587c605f9d294cefc3a75b4b26/Sources/Subs.php#L6122-L6360
  13. SMF 2.1 fixed image proxy entry at commit b4d23dfhttps://github.com/SimpleMachines/SMF/blob/b4d23dfd74a511587c605f9d294cefc3a75b4b26/proxy.php#L116-L158
  14. SMF 2.1 fixed cURL redirect at commit b4d23dfhttps://github.com/SimpleMachines/SMF/blob/b4d23dfd74a511587c605f9d294cefc3a75b4b26/Sources/Class-CurlFetchWeb.php#L341-L357
  15. RFC 1918: private IPv4 address spacehttps://www.rfc-editor.org/rfc/rfc1918
  16. RFC 4193: IPv6 unique local addresseshttps://www.rfc-editor.org/rfc/rfc4193
  17. RFC 6052: IPv4-embedded IPv6 address formathttps://www.rfc-editor.org/rfc/rfc6052
  18. PHP dns_get_record documentationhttps://www.php.net/manual/en/function.dns-get-record.php
  19. PHP filter_var IP range flagshttps://www.php.net/manual/en/function.filter-var.php