openwrt / openwrt/luci

Lack of Anti-CSRF Protection in OpenWRT – /ubus Accepts Unauthorized Requests

Open
#7,635 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
7.9k
Forks
2.9k
Avg merge
4d 17h
Merged PRs (30d)
28

Description

Is there an existing issue for this?
  • I have searched among all existing issues (including closed issues)
screenshots or captures

Image

Image

Image

Image

Actual behaviour

During security testing with OWASP ZAP, an alert was triggered indicating Absence of Anti-CSRF Tokens in OpenWRT's /ubus API.
The issue was classified as Risk: Medium with Confidence: Low by OWASP ZAP.

Upon further manual verification, it was confirmed that no CSRF protection mechanism is enforced, allowing API calls to be executed with just a valid sessionid.

  • In OpenWRT's JavaScript frontend (L.env), both sessionid and token are present.
  • However, the token is never sent in any POST requests, meaning the API does not check for CSRF protection.
  • Requests to /ubus are processed successfully as long as the attacker possesses the sessionid.

Proof of Concept (PoC)

I successfully executed the following PowerShell script CSRF TEST:

$body = '[{"jsonrpc": "2.0", "id": 11, "method": "call", "params": ["SESSION_ID", "file", "write", {"path": "/etc/sysupgrade.conf", "data": "## CSRF TEST\n"}]}]'

Invoke-WebRequest -Uri "https://openwrt.home/ubus" `
  -Method POST `
  -Headers @{"Content-Type"="application/json"} `
  -Body $body

🔹 Note: Replace SESSION_ID in the script above with the actual value of sessionid, which can be found in OpenWRT’s JavaScript frontend (L.env) or extracted from the sysauth_https session cookie in the browser.

As a result, the /etc/sysupgrade.conf file was modified, confirming that no CSRF validation occurs.

The tests were conducted both with and without an Nginx proxy, and the results remained the same.
Regardless of whether requests were sent directly to OpenWRT or proxied through Nginx, the API did not enforce CSRF protection.

If I misunderstood anything about the authentication or security model, please feel free to correct me. I appreciate any feedback!

Expected behaviour
  • The /ubus API should require a CSRF token to prevent unauthorized actions.
  • Requests without a valid CSRF token should be rejected with Access Denied.
  • sessionid should not be enough to execute API requests without additional verification.
Steps to reproduce

1️⃣ Log into OpenWRT Web UI.
2️⃣ Open Developer Tools (F12) and locate sessionid using one of the following methods:

  • Option 1: Elements Tab – Search for sessionid inside the JavaScript object L = new LuCI({...}).
  • Option 2: Console Tab – Enter the following command:
    console.log(L.env);
    
    This will print the LuCI object where sessionid and token are defined.

3️⃣ Execute the PowerShell script above, replacing SESSION_ID with the extracted value.
4️⃣ The request succeeds, modifying the router’s configuration with ## CSRF TEST.

Additional Information
🔹 Tested on OpenWRT Versions:

OpenWrt 23.05.4 r24012-d8dd03c46f / LuCI openwrt-23.05 branch git-24.332.79522-a493155
OpenWrt 24.10.0 r28427-6df0e3d02a / LuCI openwrt-24.10 branch 25.014.55016~7046a1c

🔹 Backend Analysis:

The CSRF token is generated in dispatcher.uc by session_setup() using randomid(16).
However, it is never validated in /ubus requests, likely due to the test_post_security() function not being invoked.

🔹 Potential Impact:

If an attacker steals sessionid (via XSS, MITM, or session hijacking), they can execute arbitrary API requests.
Lack of CSRF validation allows full router compromise if sessionid is leaked.
What browsers do you see the problem on?

Firefox, Chrome, Microsoft Edge

Relevant log output

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading dispatcher.uc, especially session_setup() and the mentioned test_post_security() path, then trace how /ubus requests are handled alongside the LuCI frontend's sessionid and token. Reproduce the request on the listed OpenWrt versions and determine the intended validation boundary. Done means requests without a valid CSRF token are rejected while authenticated requests continue to work.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
api, authentication, backend, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.