# Permission escalation by adding the same permission twice

LSP6Utils.combinePermissions sums permission bitmasks instead of OR-ing, so adding _PERMISSION_STATICCALL twice yields _PERMISSION_SUPER_DELEGATECALL, enabling full storage control and asset theft.

- Payout: Audit contest
- Severity: medium
- Program: LUKSO
- Found by: gpersoon
- Disclosed: 2023-09-18
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2023-06-lukso#m-08
- Library page: https://bugbountylibrary.com/r/c4%3A2023-06-lukso%3AM-08
- Tags: area:web3 domain:web3 impact:funds-loss impact:privesc kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** LSP6Utils.combinePermissions library function (Solidity)
**Root cause:** combinePermissions uses arithmetic addition (`result += uint256(permissions[i])`) on permission bitmasks rather than bitwise OR, so duplicate bits carry into higher permission bits.
**Steps:**
1. Deploy LSP0ERC725Account and LSP6KeyManager, transfer ownership to the key manager.
2. Build `ownerPermissions` array with `_PERMISSION_STATICCALL` twice.
3. Call `givePermissionsToController` for a controller with that array.
4. Read the address-permissions data key via `getData`; observe value `0x00..4000` = `_PERMISSION_SUPER_DELEGATECALL` instead of `_PERMISSION_STATICCALL`.
**How found:** Code review of LSP6Utils.combinePermissions noticed arithmetic addition of permission bitmasks; hypothesized duplicate entries would carry into adjacent bits and confirmed with a Foundry PoC.
**Exploitation:** A controller granted a duplicated benign permission (e.g. STATICCALL twice) silently receives SUPER_DELEGATECALL, letting them setData all storage parameters of the Universal Profile and steal all assets.
**Escalation:** Low-privilege permission grant escalates to full storage control over the Universal Profile, enabling asset theft.
**Insight:** When permissions are bitmask constants, any accumulation via `+` instead of `|` lets duplicate entries carry into higher bits; check every bitmask combiner for arithmetic vs bitwise ops.

