{"id":"RUSTSEC-2026-0199","aliases":[],"status":"published","title":"Panic in `bcrypt::verify` on non-ASCII hash input","description":"`bcrypt::verify(password, hash)` and `HashParts::from_str(hash)` panic in\n`str::slice_error_fail` when given a 60-byte `\u0026str` containing a multi-byte\nUTF-8 character at certain byte positions.\n\n## Impact\n\nAny Rust code that calls `bcrypt::verify` (or `HashParts::from_str`) with an\nattacker-controlled hash string will panic. The `bcrypt` crate is\n`#![forbid(unsafe_code)]`, so this is limited to a denial-of-service and\ncannot lead to memory corruption.\n\nRealistic attack contexts include:\n\n- Rust authentication services reading hashes from a database that was\n  previously compromised via, e.g., SQL injection. The attacker can then\n  crash the service on every login attempt against the tampered account.\n- CLI tools accepting hashes from stdin or command-line arguments.\n- Password managers or vault services loading hashes from untrusted\n  configuration sources.\n\n## Root cause\n\n`split_hash` performed five `\u0026str` slicing operations on the input hash:\n`\u0026hash[1..3]`, `\u0026hash[4..6]`, `\u0026hash[7..]`, `\u0026salt_and_hash[..22]`, and\n`\u0026salt_and_hash[22..]`. None of these were char-boundary-checked. Any input\nwhere a multi-byte UTF-8 character spanned one of those byte positions\ncaused a panic.\n\nThis is a regression of the fix originally shipped in 2021 for issue #62\n(commit `0833509`). The regression was introduced in the parser rewrite in\nPR #95 (commit `e9a8394`, released as 0.19.0).\n\nThe pre-existing regression test `does_no_error_on_char_boundary_splitting`\nwas not removed, but was silently rendered ineffective by the new\n`bytes[0] != b'$'` guard, which rejected its input earlier and prevented\nit from reaching the buggy slices — leaving CI green through the regression.\n\n## Fix\n\n`split_hash` now rejects any hash string containing non-ASCII bytes up\nfront. A valid bcrypt hash is always exactly 60 ASCII bytes, so this\ncloses the entire class of byte-boundary panics rather than guarding each\nslice individually.\n\nThe fix was merged in PR #103 and released as `bcrypt 0.19.2` on 2026-06-20.\n\n## Downstream impact\n\n`pyca/bcrypt` (which depended on `bcrypt 0.19.1`) is **not** affected. Its\nPython-side wrapper performs its own byte-level salt parsing before\ninvoking `bcrypt::hash_with_salt`, and never reaches the buggy code path\nin `split_hash` or `verify`.","severity":{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium","from":"osv"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"published":"2026-06-20T12:00:00Z","modified":"2026-07-04T08:30:04Z","fixes":[{"ecosystem":"crates.io","package":"bcrypt","fixed_in":["0.19.2"],"sources":["osv"]}],"affected":[{"source":"osv","ecosystem":"crates.io","package":"bcrypt","ranges":[{"type":"SEMVER","events":[{"introduced":"0.19.0"},{"fixed":"0.19.2"}]}]}],"references":[{"url":"https://crates.io/crates/bcrypt","tags":["package"]},{"url":"https://rustsec.org/advisories/RUSTSEC-2026-0199.html","tags":["advisory"]},{"url":"https://github.com/Keats/rust-bcrypt/pull/103","tags":["web"]},{"url":"https://github.com/Keats/rust-bcrypt/issues/62","tags":["report"]},{"url":"https://github.com/Keats/rust-bcrypt/pull/95","tags":["web"]},{"url":"https://github.com/Keats/rust-bcrypt/commit/f5f1ee2862c1198a85afe3c2f8cd80835162b7e9","tags":["web"]}],"sources":{"osv":[{"id":"RUSTSEC-2026-0199","title":"Panic in `bcrypt::verify` on non-ASCII hash input","description":"`bcrypt::verify(password, hash)` and `HashParts::from_str(hash)` panic in\n`str::slice_error_fail` when given a 60-byte `\u0026str` containing a multi-byte\nUTF-8 character at certain byte positions.\n\n## Impact\n\nAny Rust code that calls `bcrypt::verify` (or `HashParts::from_str`) with an\nattacker-controlled hash string will panic. The `bcrypt` crate is\n`#![forbid(unsafe_code)]`, so this is limited to a denial-of-service and\ncannot lead to memory corruption.\n\nRealistic attack contexts include:\n\n- Rust authentication services reading hashes from a database that was\n  previously compromised via, e.g., SQL injection. The attacker can then\n  crash the service on every login attempt against the tampered account.\n- CLI tools accepting hashes from stdin or command-line arguments.\n- Password managers or vault services loading hashes from untrusted\n  configuration sources.\n\n## Root cause\n\n`split_hash` performed five `\u0026str` slicing operations on the input hash:\n`\u0026hash[1..3]`, `\u0026hash[4..6]`, `\u0026hash[7..]`, `\u0026salt_and_hash[..22]`, and\n`\u0026salt_and_hash[22..]`. None of these were char-boundary-checked. Any input\nwhere a multi-byte UTF-8 character spanned one of those byte positions\ncaused a panic.\n\nThis is a regression of the fix originally shipped in 2021 for issue #62\n(commit `0833509`). The regression was introduced in the parser rewrite in\nPR #95 (commit `e9a8394`, released as 0.19.0).\n\nThe pre-existing regression test `does_no_error_on_char_boundary_splitting`\nwas not removed, but was silently rendered ineffective by the new\n`bytes[0] != b'$'` guard, which rejected its input earlier and prevented\nit from reaching the buggy slices — leaving CI green through the regression.\n\n## Fix\n\n`split_hash` now rejects any hash string containing non-ASCII bytes up\nfront. A valid bcrypt hash is always exactly 60 ASCII bytes, so this\ncloses the entire class of byte-boundary panics rather than guarding each\nslice individually.\n\nThe fix was merged in PR #103 and released as `bcrypt 0.19.2` on 2026-06-20.\n\n## Downstream impact\n\n`pyca/bcrypt` (which depended on `bcrypt 0.19.1`) is **not** affected. Its\nPython-side wrapper performs its own byte-level salt parsing before\ninvoking `bcrypt::hash_with_salt`, and never reaches the buggy code path\nin `split_hash` or `verify`.","status":"published","published":"2026-06-20T12:00:00Z","modified":"2026-07-04T08:30:04.254971708Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"updated_at":"2026-11-02T15:53:40.430679Z"}]}}
