# bitcoinj has a ScriptExecution P2PKH/P2WPKH Verification Bypass

bitcoinj's correctlySpends() fast paths for P2PKH and P2WPKH verify an attacker keypair's signature but never bind the pubkey to the output's hash, so any keypair passes validation.

- Payout: Research
- Severity: high
- Program: bitcoinj
- Found by: jmecom
- Disclosed: 2026-05-08
- CVE: CVE-2026-44714
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-hfcf-v2f8-x9pc
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-hfcf-v2f8-x9pc
- Tags: area:oss domain:oss impact:integrity kind:research platform:ghsa sev:high tech:java technique:source-review vuln:auth-bypass vuln:crypto

**Target:** ScriptExecution.correctlySpends() in bitcoinj-core (Java library)
**Root cause:** The optimized P2PKH and P2WPKH branches verify pubkey.verify(sigHash, signature) using the attacker-supplied pubkey but omit the HASH160(pubkey) == output hash check; for P2WPKH the scriptCode is also built from the attacker pubkey, so the attacker controls both key and sighash preimage.
**Steps:**
1. Locate ScriptExecution.correctlySpends() fast paths at ScriptExecution.java:1042 (P2PKH) and :1023 (P2WPKH).
2. Craft a scriptSig with two pushes: a signature and an arbitrary attacker pubkey (P2PKH), or a witness with signature and pubkey (P2WPKH).
3. Sign the sighash computed against the victim output's scriptPubKey (P2PKH) or against `ScriptBuilder.createP2PKHOutputScript(pubkey)` (P2WPKH) with the attacker key.
4. Observe correctlySpends() returns success despite HASH160(pubkey) not matching the spent output's committed hash.
**How found:** Code review of the optimized fast-path branches in correctlySpends(), noticing the OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY semantics were not re-implemented and that P2WPKH derives scriptCode from the untrusted witness pubkey.
**Exploitation:** Any application relying on correctlySpends() (TransactionInput.java:546, Wallet.java:4520, LocalTransactionSigner.java:84, CustomTransactionSigner.java:77) as proof of spend validity can be fed a forged input that passes local verification for arbitrary P2PKH/P2WPKH outputs. Not exploitable against SPV/PoW trust model since input signatures aren't checked there.
**Insight:** When a library reimplements script semantics as an optimized fast path, diff it against the full interpreter: every opcode constraint (here OP_EQUALVERIFY binding pubkey to output hash) must be re-enforced, and any sighash preimage built from attacker-supplied data (P2WPKH scriptCode) is a red flag.

