SqliteAccountInfo profile name validation uses "re.match", so only the first character is checked
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 75/100
Hướng nghiên cứu
Đọc b2sdk/_internal/account_info/sqlite_account_info.py, đặc biệt là get_user_account_info_path và B2_ACCOUNT_INFO_PROFILE_NAME_REGEXP, sau đó kiểm tra bài kiểm thử hiện có test_invalid_profile_name. Cập nhật bước xác thực để từ chối các ký tự không hợp lệ ở bất kỳ vị trí nào trong hồ sơ, đồng thời mở rộng bài kiểm thử với một ký tự đầu hợp lệ theo sau là một hậu tố độc hại; hoàn tất khi những tên đó gây ra ValueError và các hồ sơ hợp lệ vẫn hoạt động.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Description
get_user_account_info_path validates profile with B2_ACCOUNT_INFO_PROFILE_NAME_REGEXP.match(profile) (b2sdk/_internal/account_info/sqlite_account_info.py:112), and the pattern at :30 is unanchored, so re.match accepts any string whose first character is in [a-zA-Z0-9_\-] and never looks at the rest. The value is then interpolated into the database path at :115, :136 and :142 and handed to os.open and sqlite3.connect without normalisation, so a name like a/../../../../../tmp/planted reaches the filesystem with its .. components intact and the credential database, which holds a live application key, is created outside the directory the SDK selected. Over a corpus of 108 names built from valid leading characters and 21 hostile suffixes, 103 were accepted and 50 of those resolved outside {XDG_CONFIG_HOME}/b2. Two things keep this low rather than worse: nothing inside the SDK passes untrusted input as profile, and on POSIX the first injected path component has to already exist as a directory, which the SDK never creates, so an attacker who can create it already has write access to the config directory. We are filing it in the open for that reason. It still seems worth fixing, because the pattern exists specifically to constrain this value and currently does not, and the one test covering it, test_invalid_profile_name, uses '&@(*$', whose first character is already outside the class, so it passes against the unanchored pattern.
Reproduction
On master @ ffa02b32 with Python 3.10.19, in a container built from a fresh clone at that ref, with the ~/.config/b2/db-a directory created to satisfy the POSIX precondition. Build with this Dockerfile, save the script below as poc.py, then run docker build -t b2poc . && docker run --rm -u 1001 -v "$PWD/poc.py:/poc.py:ro" b2poc python /poc.py:
FROM python:3.10.19-slim-bookworm
RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends git >/dev/null
RUN git clone -q https://github.com/Backblaze/b2-sdk-python.git /src \
&& git -C /src checkout -q ffa02b3264bc0e7069ef2c8b25facd9a467e65a6 \
&& pip install --no-cache-dir -q /src
RUN useradd -m -u 1001 victim
import os, sqlite3
from b2sdk.v3 import SqliteAccountInfo
PROFILE = 'a/../../../../../tmp/planted' # accepted by .match(), rejected by .fullmatch()
print('resolved :', SqliteAccountInfo.get_user_account_info_path(profile=PROFILE))
os.makedirs(os.path.expanduser('~/.config/b2/db-a')) # POSIX needs the first component
info = SqliteAccountInfo(profile=PROFILE)
info.set_auth_data(
account_id='acct', auth_token='tok', api_url='https://api', download_url='https://dl',
absolute_minimum_part_size=5000000, recommended_part_size=100, realm='production',
application_key='VICTIM-SECRET-KEY', s3_api_url='https://s3', application_key_id='keyid',
allowed=SqliteAccountInfo.DEFAULT_ALLOWED)
print('created :', os.path.realpath(info.filename))
print('key there:', sqlite3.connect('/tmp/planted.sqlite')
.execute('select application_key from account').fetchone()[0])
resolved : /home/victim/.config/b2/db-a/../../../../../tmp/planted.sqlite
created : /tmp/planted.sqlite
key there: VICTIM-SECRET-KEY
Suggested remediation
Validate the whole string:
--- a/b2sdk/_internal/account_info/sqlite_account_info.py
+++ b/b2sdk/_internal/account_info/sqlite_account_info.py
@@ -109,7 +109,7 @@ class SqliteAccountInfo(UrlPoolAccountInfo):
@classmethod
def get_user_account_info_path(cls, file_name: str | None = None, profile: str | None = None):
- if profile and not B2_ACCOUNT_INFO_PROFILE_NAME_REGEXP.match(profile):
+ if profile and not B2_ACCOUNT_INFO_PROFILE_NAME_REGEXP.fullmatch(profile):
raise ValueError(f'Invalid profile name: {profile}')
One adjacent thing I noticed while reproducing this, which is separate and not addressed by the above: _validate_database (:144) only calls _create_database when the file does not already exist, and _create_database is the only place mode 0600 is set, so a database file that is already present keeps its permissions. Starting from a mode 0666 file, the mode after credentials were stored was still 0o666 and the application key was readable from it.
Credits
- Ngôn ngữ chính
- Python
- Star
- 214
- Fork
- 68
- Merge trung bình
- 51 phút
- Pull request đã merge (30 ngày)
- 6
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của Backblaze/b2-sdk-python
-
Download and copy sync actions print remote file names without escape_control_charsCó thể đã có người làm @dreyfus92 đã nhận 15 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Backblaze/b2-sdk-python#631 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Adopt the AI-native SDLC and repository policiesCó thể đã có người làm @goanpeca đã nhận 18 ngày trước. Đang mởenhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 20/100
Backblaze/b2-sdk-python#634 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 50/100
Backblaze/b2-sdk-python#630 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
ci: cli-compat fails — 4 sse/sync integration tests broken in B2_Command_Line_Tool (all environments)Có thể làm lại được @ffumero2003 đã nhận 37 ngày trước và không có pull request nào đang mở. Đang mởbug testing
Backblaze/b2-sdk-python#627 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 3/5 Nửa ngày Mức phù hợp với người mới 62/100
Backblaze/b2-sdk-python#626 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của Backblaze/b2-sdk-python
Issue tương tự
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
btclib-org/btclib-node#2505 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
open-webui/open-webui#34462 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
gorakhargosh/watchdog#1462 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
openwisp/openwisp-monitoring#889 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
IETF-Hackathon/pqc-certificates#351 ·
Maintainer thường phản hồi trong vòng 2 ngày