{"id":"CVE-2025-54590","aliases":["GHSA-8xq3-w9fx-74rv"],"url":"https://o3.security/vulnerability/CVE-2025-54590","summary":"webfinger.js is vulnerable to Blind SSRF attacks through localhost","details":"### Description\nThe lookup function takes a user address for checking accounts as a feature, however, as per\nthe ActivityPub spec (https://www.w3.org/TR/activitypub/#security-considerations), on the\nsecurity considerations section at B.3, access to Localhost services should be prevented while\nrunning in production. The library does not prevent Localhost access (neither does it prevent\nLAN addresses such as 192.168.x.x) , thus is not safe for use in production by ActivityPub\napplications. The only check for localhost is done for selecting between HTTP and HTTPS\nprotocols, and it is done by testing for a host that starts with the string “localhost” and ends with\na port. Anything else (such as “127.0.0.1” or “localhost:1234/abc”) would not be considered\nlocalhost for this test.\n\nIn addition, the way that the function determines the host, makes it possible to access any path\nin the host, not only “/.well-known/...” paths:\n\n```javascript\nif (address.indexOf('://') > -1) {\n  // other uri format\n  host = address.replace(/ /g,'').split('/')[2];\n} else {\n  // useraddress\n  host = address.replace(/ /g,'').split('@')[1];\n}\n\nvar uri_index = 0; // track which URIS we've tried already\nvar protocol = 'https'; // we use https by default\n\nif (self.__isLocalhost(host)) {\n  protocol = 'http';\n}\n\nfunction __buildURL() {\n  var uri = '';\n  if (! address.split('://')[1]) {\n  // the URI has not been defined, default to acct\n    uri = 'acct:';\n  }\n  return protocol + '://' + host + '/.well-known/' +URIS[uri_index] + '?resource=' + uri + address;\n}\n```\n\nIf the address is in the format of a user address (user@host.com), the host will be anything\nafter the first found @ symbol. Since no other test is done, an adversary may pass a specially\ncrafted address such as user@localhost:7000/admin/restricted_page? and reach pages that\nwould normally be out of reach. In this example, the code would treat\nlocalhost:7000/admin/restricted_page? as the host, and the created URL would be\nhttps://localhost:7000/admin/restricted_page?/.well-known/webfinger?resource=acct:use\nr@localhost:7000/admin/restricted_page?. A server listening on localhost:7000 will then\nparse the request as a GET request for the page /admin/restricted_page with the query string\n/.well-known/webfinger?resource=acct:user@localhost:7000/admin/restricted_page?.\n\n### PoC and Steps to reproduce\nThis PoC assumes that there is a server on the machine listening on port 3000, which receives\nrequests for WebFinger lookups on the address /api/v1/search_user, and then calls the lookup\nfunction in webfinger.js with the user passed as an argument. For the sake of the example we\nassume that the server configured webfinger.js with tls_only=false.\n\n\n1. Activate a local HTTP server listening to port 1234 with a “secret.txt” file:\n\n```\npython3 -m http.server 1234\n```\n\n2. Run the following command:\n\n```\ncurl\n\"http://localhost:3000/api/v1/search_user?search=user@localhost:1234/secret.txt\n?\"\n```\n\n3. View the console of the Python’s HTTP server and see that a request for a\n“secret.txt?/.well-known/webfinger?resource=acct:user@localhost:1234/secret.txt\n?” file was performed.\nThis proves that we can redirect the URL to any domain and path we choose, including\nlocalhost and the internal LAN.\n\n\n### Impact\nDue to this issue, any user can cause a server using the library to send GET requests with\ncontrolled host, path and port in an attempt to query services running on the instance’s host or\nlocal network, and attempt to execute a Blind-SSRF gadget in hope of targeting a known\nvulnerable local service running on the victim’s machine.\n\n\n### References\nThe vulnerability was discovered by Ori Hollander of the JFrog Vulnerability Research team.","published":"2025-08-01T18:03:41.619Z","modified":"2026-10-28T02:30:50.199641675Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"webfinger.js","fixedVersion":"2.8.1"}],"fix":{"url":"https://github.com/silverbucket/webfinger.js/commit/b5f2f2c957297d25f4d76072963fccaee2e3095a","label":"silverbucket/webfinger.js@b5f2f2c"},"references":[{"type":"WEB","url":"https://github.com/silverbucket/webfinger.js/releases/tag/v2.8.1"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2025/54xxx/CVE-2025-54590.json"},{"type":"ADVISORY","url":"https://github.com/silverbucket/webfinger.js/security/advisories/GHSA-8xq3-w9fx-74rv"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-54590"},{"type":"FIX","url":"https://github.com/silverbucket/webfinger.js/commit/b5f2f2c957297d25f4d76072963fccaee2e3095a"},{"type":"PACKAGE","url":"https://github.com/silverbucket/webfinger.js"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:50.199641675Z"}}