# Syzkaller 102: Practical Guide to Fuzzing Real-World Targets. ###### tags: `syzkaller` --------------------------------- ![image](https://hackmd.io/_uploads/HydK7Vjbeg.png) ## Trước khi bắt đầu - Ở bài viết lần trước [Syzkaller-101](https://hackmd.io/jnsARl3zSFem_Pg-0FULmQ) mình đã khái quát một cái nhìn toàn diện về Syzkaller: cấu trúc, cách hoạt động và ứng dụng vào việc fuzzing một custom vulnerable driver. Nếu bạn chưa từng đọc hoặc chưa tìm hiểu về Syzkaller, mình khuyến khích nên đọc bài viết đó trước để có nền tảng kiến thức tổng quát. - Hãy nói sơ qua về Syzkaller. Như đã đề cập trong bài viết trước, Linux Kernel có khoảng `450-500` syscall tổng cộng. Con số này nghe có vẻ không nhiều để làm surface attacking, nhưng thực tế, mỗi syscall trong Linux Kernel được triển khai với hàng trăm communicate channel khác nhau với các subsystem. Ví dụ, một syscall cơ bản như `open` có thể được triển khai khác nhau tùy thuộc vào từng filesystem driver hoặc từng node trong `/dev/`. Chính vì vậy, `Syzkaller` thực sự RẤT MẠNH, với khả năng tự động sinh mã hiệu quả và tạo corpus dựa trên coverage của bitmap. Tuy nhiên, với kích thước khổng lồ của mã nguồn Linux Kernel, vẫn sẽ có những "đường đi" mà Syzkaller không thể bao quát hết. Do đó, để đạt hiệu quả tốt nhất, nên kết hợp cả Fuzzing tự động và Manual Auditing. ## I. Tìm cho mình một Target để fuzzing ### 1. Think like a Hacker - Trước khi lựa chọn một target để có thể fuzzing thì chúng ta phải ngó qua một vòng "thị trường" bug hiện tại. Ở thời điểm mình viết bài này thì có khoảng cỡ `10.318` CVE về Linux Kernel. Ở trên [Syzbot](https://syzkaller.appspot.com/upstream) đang có 1591 Bug đang open, 6234 Bug đã được fixed. Con số này chiếm khoảng hơn 1/2 số lượng CVE ở trên. ![image-2](https://hackmd.io/_uploads/rydBUVo-xx.png) - Để tìm target fuzzing thì cũng tương tự với việc tìm các lỗ hổng ở các application khác. 1. Tìm những target ít ai để ý tới: Thường những target này khó, ít người tìm được bug hoặc bounty thấp nên không hấp dẫn. 2. Tìm những target được xem là mỏ "bug": Ví dụ như là các Subsystem liên quan tới mạng chẳng hạn, `netfilter`, `nf_tables` năm nào cũng bị đấm. Ngoài ra ta xem ở trên [Syzbot-heatmap](https://syzkaller.appspot.com/upstream/coverage/subsystems?period=month), page này cho ta thấy Google, họ đã fuzzing khá là nhiều Subsystem, bao quát gần hết. Một số keynote: + Syzbot chủ yếu fuzz các subsystem thông thường, không yêu cầu phần cứng đặc biệt. Những subsystem nào trên heatmap có coverage 0% thường là do Syzkaller/Syzbot không thể cover được (do thiếu hardware hoặc lý do kỹ thuật khác), nên có thể bỏ qua các subsystem này. + Nên tránh các subsystem đã có coverage quá cao (>50%), ví dụ: `ext4` (74% với 18k block), `exfat` (92% với 4236 block), `mm` (67% với 40k block), v.v. + Những subsystem nằm trong khoảng 10-20% thì có vẻ là một target tốt để lựa chọn: `ceph` (5% với 17k block), `cifs` (2% với 23k block, không hiểu sao nó thấp như vậy), `damon` (14% recently với 1173 block). + Ngoài ra những subsystem có bug gần đây, ví dụ như `bluetooth` cũng không cần yêu cầu nặng về hardware và tài liệu để đọc nó cũng rất rất nhiều. Cũng đáng để hướng tới. - Bài viết lần này mình sẽ hướng tới subsystem `bluetooth`. Why? + `Bluetooth` thường bật mặc định trên nhiều thiết bị (laptop, điện thoại, IoT...), trở thành surface attack lý tưởng. + Nó bao gồm nhiều thành phần: HCI, L2CAP, RFCOMM, SMP... Mỗi thành phần có hàng loạt syscall và ioctls riêng. + Các state machine phức tạp, `điều kiện race` và xử lý memory (buffer, socket, pointer) không chặt chẽ → rất nhiều lỗi kiểu `UAF, OOB, NULL dereference`, rất phù hợp để fuzzing. + Bluetooth nó dễ tiếp cận với Syzkaller so với một số subsystem khác vì networking dễ hơn để fuzz: data flow mạch lạc, tất cả làm thông qua socket. + còn đối một số subsystem liên quan với filesystem, thì phải handle một đống binary blobs, mount, unmount,... thì sẽ khiến cho việc fuzzing phức tạp hơn ## II. Cách mà Bluetooth hoạt động trong Linux Kernel - Trước khi đến với viết `Syzlang description` để fuzzing bluetooth thì ta nên hiểu rõ cách nó hoạt động ra sao, như trong [document](https://github.com/google/syzkaller/blob/master/docs/syscall_descriptions.md#describing-new-system-calls) có nói ``` To enable fuzzing of a new kernel interface: 1. Study the interface, find out which syscalls are required to use it. Sometimes there is nothing besides the source code, but here are some things that may help: - Searching the Internet for the interface name and/or some unique constants. - Grepping Documentation/ dir in the kernel. - Searching tools/testing/ dir in the kernel. - Looking for large comment blocks in the source code. - Finding commit that added the interface via git blame or git log and reading the commit description. - Reading source code of or tracing libraries or applications that are known to use this interface. ``` - Bluetooth trong Linux kernel được triển khai chủ yếu qua BlueZ, gồm nhiều module kernel nối tiếp để xử lý từ phần cứng đến giao diện người dùng. - Dữ liệu Bluetooth đi qua lần lượt các layer: `HCI → L2CAP → RFCOMM` (hoặc các protocol khác như `BNEP`, `HIDP`) → `người dùng`, trong đó mỗi layer đảm nhận nhiệm vụ riêng về điều khiển, đóng gói, bảo mật và dịch vụ khám phá. ### 1. Các thành phần chính của Bluetooth Stack trong Linux Kernel - `Host Controller Interface (HCI)`: + `HCI` là giao diện chuẩn giữa phần mềm host (trong kernel) và Bluetooth controller (phần cứng). + Kernel định nghĩa các socket `AF_BLUETOOTH`, `BTPROTO_HCI` cho phép gửi lệnh (commands) và nhận sự kiện (events) từ `controller`. + Các driver HCI như `hci_usb.ko`, `hci_uart.ko` triển khai transport layer cho USB hay UART, tương ứng với module kernel riêng biệt. - `Logical Link Control and Adaption Protocol (L2CAP)`: + `L2CAP` cung cấp kênh truyền dữ liệu đa kết nối, thực hiện "phân mảnh" và "tái hợp" (segmentation và reassembly). + Nó nằm trên HCI và chịu trách nhiệm mapping các kênh (channels) cho các protocol cấp cao hơn như `RFCOMM`, `BNEP`. - `RFCOMM`: + `RFCOMM` là giao thức mô phỏng cổng nối tiếp (RS-232) chạy trên L2CAP, cho phép emulation “serial port” với kiểu truyền tin stream tương tự TCP. + Trong kernel, RFCOMM được triển khai bằng `rfcomm.ko`, kèm theo daemon kernel thread `krfcommd` để xử lý các kết nối đến - Các module bổ trợ (`BNEP`, `HIDP`, …): + `BNEP` (Bluetooth Network Encapsulation Protocol) để network-bridge (PAN), được triển khai trong `bnep.ko` + `HIDP` (Human Interface Device Protocol) cho các thiết bị bàn phím, chuột Bluetooth, được hỗ trợ qua `hidp.ko` và cho phép truy cập raw qua `hidraw.ko` - Ngoài ra còn một số protocol khác như: `Service Discovery Protocol (SDP)`, `Security Manager Protocol (SMP)` nhưng nó nằm ngoài phạm vi của bài viết này nên mình sẽ không nhắc tới. ### 2. Luồng dữ liệu và xử lý trong kernel 1. Khởi tạo: Khi module HCI load, kernel mở device node `/dev/bluetooth/hciX`, upload firmware nếu cần qua subsystem firmware loader. 2. Kết nối: Người dùng hoặc daemon BlueZ gửi lệnh HCI (HCI Command) qua socket, controller phản hồi với HCI Event. 3. Mở kênh L2CAP: BlueZ user-space tạo socket L2CAP, kernel module `l2cap.ko` thiết lập kênh và cấu hình độ tin cậy. 4. Tạo dịch vụ: Ví dụ RFCOMM – BlueZ tạo socket RFCOMM, kernel spawn thread `krfcommd` để accept, rồi dữ liệu được đưa qua L2CAP. 5. Bảo mật: Trước khi tạo kênh, SMP được gọi để pairing và thiết lập key mã hóa. 6. Shutdown: Khi disconnect, kernel giải phóng cấu trúc dữ liệu, đóng kênh L2CAP, ngắt HCI socket. ### 3. From userspace to kernel ***a. User-space tương tác với Bluetooth*** - User-space tương tác với Bluetooth thông qua `socket API` và các công cụ như `bluetoothctl` hoặc các daemon như `bluetoothd` aka `BlueZ`. 1. Mở socket Bluetooth: - User-space sử dụng socket với domain `AF_BLUETOOTH` và các protocol như `BTPROTO_HCI`, `BTPROTO_L2CAP`, hoặc `BTPROTO_RFCOMM` - Ví dụ, để giao tiếp với HCI: ```c int sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); ``` 2. Gửi lệnh HCI: - User-space gửi command cho HCI thông qua socket. Kernel gửi command này đến Bluetooth controller - Ví dụ: command `HCI_OP_INQUIRY` để quét các thiết bị Bluetooth gần đó. 3. Nhận event HCI: - Kernel nhận respone từ controller dưới dạng HCI Event và chuyển nó lại cho user-space qua socket. 4. Tương tự với L2CAP hoặc RFCOMM ***b. Các hàm callback trong Kernel*** - Trong kernel, các module Bluetooth sử dụng `hàm callback` để xử lý event không đồng bộ. Một số ví dụ: 1. HCI Layer - Khi nhận lệnh từ user-space, kernel gọi các hàm xử lý tương ứng: ```c int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len); ``` - Hàm này sẽ xử lý command HCI từ user-space và gửi đến controller. - Khi nhận sự kiện từ controller, kernel sẽ gọi callback để xử lý: ```c void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); ``` - `skb` chứa dữ liệu sự kiện từ controller. 2. L2CAP Layer với RFCOMM Layer cũng tương tự như trên ***c. Tổng kết lại quy trình xử lý dữ liệu trong kernel*** - User-space tương tác với Bluetooth qua socket API (`AF_BLUETOOTH`). - Kernel xử lý lệnh và dữ liệu qua các module như `hci.ko`, `l2cap.ko`, `rfcomm.ko`. - Các hàm callback trong kernel được sử dụng để xử lý sự kiện không đồng bộ từ controller hoặc user-space. ### 4. Fuzzing Strategy 1. Xác định mục tiêu Fuzzing: - HCI Layer: + Fuzz các lệnh HCI (HCI Commands) được gửi từ user-space đến kernel qua socket AF_BLUETOOTH. + Các syscall/operation chính cần fuzz bao gồm: `bind`, `release`, `getname`, `sendmsg`, `recvmsg`, `ioctl`, `setsockopt`, `getsockopt`, `poll` Đây là các entrypoint mà user-space có thể tác động vào kernel thông qua socket của HCI. + Các hàm callback tương ứng trong kernel được định nghĩa trong struct proto_ops hci_sock_ops như sau: Ví dụ với HCI: ```c static const struct proto_ops hci_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = hci_sock_release, .bind = hci_sock_bind, .getname = hci_sock_getname, .sendmsg = hci_sock_sendmsg, .recvmsg = hci_sock_recvmsg, .ioctl = hci_sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = hci_sock_compat_ioctl, #endif .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = hci_sock_setsockopt, .getsockopt = hci_sock_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .mmap = sock_no_mmap }; ``` + Ta nên bao quát hết toàn bộ những `cmd` trong `HCI` để có thể có những corpus tốt hơn (thà fuzz nhầm còn hơn bỏ xót). - L2CAP Layer, RFCOMM Layer và các protocol bổ trợ còn lại như `BNEP`, `CMTP`, `HIDP` thì viết desctiption tương tự với `HCI Layer`. ## III. Writing Syzkaller Description for Bluetooth's Layer. - Ở bài viết này mình chỉ hướng dẫn viết description cho `HCI Layer` và `L2CAP Layer`, tại vì cách thức và concept viết cũng tương tự cho các protocol cũng như các layer còn lại. Mình sẽ xem chúng là "bài tập về nhà" cho reader. ### 1. HCI Layer - Các developer của Syzkaller cũng đã viết sẵn description cho [Bluetooth](https://github.com/google/syzkaller/blob/master/sys/linux/socket_bluetooth.txt) - Let's take a look: ```c resource sock_bt[sock] resource sock_bt_hci[sock_bt] syz_init_net_socket$bt_hci(fam const[AF_BLUETOOTH], type const[SOCK_RAW], proto const[BTPROTO_HCI]) sock_bt_hci bind$bt_hci(fd sock_bt_hci, addr ptr[in, sockaddr_hci], addrlen len[addr]) ioctl$sock_bt_hci(fd sock_bt_hci, cmd flags[bt_hci_ioctl], arg buffer[inout]) ioctl$HCIINQUIRY(fd sock_bt_hci, cmd const[HCIINQUIRY], arg ptr[in, hci_inquiry_req]) setsockopt$bt_hci_HCI_DATA_DIR(fd sock_bt_hci, level const[0], opt const[HCI_DATA_DIR], arg ptr[in, int32], arglen len[arg]) setsockopt$bt_hci_HCI_TIME_STAMP(fd sock_bt_hci, level const[0], opt const[HCI_TIME_STAMP], arg ptr[in, int32], arglen len[arg]) setsockopt$bt_hci_HCI_FILTER(fd sock_bt_hci, level const[0], opt const[HCI_FILTER], arg ptr[in, hci_ufilter], arglen len[arg]) getsockopt$bt_hci(fd sock, level const[0], opt flags[bt_hci_sockopt], arg buffer[out], arglen ptr[inout, len[arg, int32]]) write$bt_hci(fd sock_bt_hci, data ptr[in, vhci_command_pkt], size bytesize[data]) ``` - Các hàm callback của hci_sock được định nghĩa ở trong `net/bluetooth/hci_sock.c` như sau: ```c static const struct proto_ops hci_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = hci_sock_release, .bind = hci_sock_bind, .getname = hci_sock_getname, .sendmsg = hci_sock_sendmsg, .recvmsg = hci_sock_recvmsg, .ioctl = hci_sock_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = hci_sock_compat_ioctl, #endif .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = hci_sock_setsockopt, .getsockopt = hci_sock_getsockopt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .mmap = sock_no_mmap }; ``` - Để nhận xét thì các developer đã viết các description cho các hàm callback đã khá đầy đủ: + `syz_init_net_socket$bt_hci` tương ứng với việc gọi syscall `sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);` + `bind$bt_hci`, `write$bt_hci`, và `ioctl` với đầy đủ các `cmd` flag, `bt_hci_ioctl = HCIDEVUP, HCIDEVDOWN, HCIDEVRESET, HCIDEVRESTAT, HCIGETDEVLIST, HCIGETDEVINFO, HCIGETCONNLIST, HCIGETCONNINFO, HCIGETAUTHINFO, HCISETRAW, HCISETSCAN, HCISETAUTH, HCISETENCRYPT, HCISETPTYPE, HCISETLINKPOL, HCISETLINKMODE, HCISETACLMTU, HCISETSCOMTU, HCIBLOCKADDR, HCIUNBLOCKADDR, HCIINQUIRY` + `write$bt_hci` tương ứng với `.sendmsg = hci_sock_sendmsg` trong kernel. - Tuy nhiên nó vẫn thiếu một vài description cho các hàm callback: + `release`: `.release = hci_sock_release`. Mô phỏng thao tác đóng socket -> `close()`. + `getname`: `.getname = hci_sock_getname`. Mô phỏng thao tác lấy thông tin địa chỉ socket -> `getsockname()` hoặc là `getpeername()`. + `recvmsg`: `.recvmsg = hci_sock_recvmsg`. Mô phỏng thao tác nhận dữ liệu từ socket -> `recvmsg()` hoặc `read()`. Nên implement cả 2 trong file description luôn. + `poll`: `.poll = datagram_poll`. Mô phỏng thao tác kiểm tra trạng thái socket -> `poll()` hoặc `select()`. Cái này thì nên viết `poll` thôi. + Về `getsockopt` và `setsockopt` thì các developer cũng đã làm khá kỹ rồi, nhưng nên kiểm tra 1 vòng xem thử ở tham số thứ 3 (tham số `arg` trong syzlang) ở tất cả các trường hợp `opt` có khác nhau về struct hay không, nếu có struct nào khác thì nên implement riêng cho từng trường hợp của `opt` đó. struct của `sockaddr_hci` thì họ cũng đã implement rồi, nên không cần phải làm lại. ```c type hci_dev_t int16[-1:4] sockaddr_hci { hci_family const[AF_BLUETOOTH, int16] hci_dev hci_dev_t hci_channel flags[bt_hci_chan, int16] } bt_hci_chan = HCI_CHANNEL_RAW, HCI_CHANNEL_USER, HCI_CHANNEL_MONITOR, HCI_CHANNEL_CONTROL, HCI_CHANNEL_LOGGING ``` tương ứng trong kernel ```c struct sockaddr_hci { sa_family_t hci_family; unsigned short hci_dev; unsigned short hci_channel; }; ``` #### a. `release` ***Description cho Syzkaller***: ```c close$bt_hci(fd sock_bt_hci) ``` - `close` -> syscall `close()` - `$bt_hci` -> để syzlang nhận biết rằng mình đang viết syscall `close()` cho socket `sock_bt_hci`: `resource sock_bt_hci[sock_bt]` - `fd sock_bt_hci` -> tham số thứ nhất cho syscall `close()` tương ứng với ptr trỏ tới `fd` đang mở: `syz_init_net_socket$bt_hci(fam const[AF_BLUETOOTH], type const[SOCK_RAW], proto const[BTPROTO_HCI]) sock_bt_hci` -> Description ở trên khi convert qua hàm ở user-space thì sẽ là: `close(sock_bt_hci);` #### b. `getname` ***Description cho Syzkaller***: ```c getsockname$bt_hci(fd sock_bt_hci, addr ptr[out, sockaddr_hci], addrlen len[addr]) ``` - `getsockname$bt_hci` -> Tên syscall được định nghĩa cho Syzkaller, cụ thể cho socket Bluetooth HCI `$bt_hci`. - `fd sock_bt_hci` -> tham số đầu tiên là file descriptor (fd) của socket HCI, được định nghĩa từ resource `sock_bt_hci`. - `addr ptr[out, sockaddr_hci]` -> addr là con trỏ trỏ đến cấu trúc `sockaddr_hci`, được định nghĩa trong Syzkaller như: ```c sockaddr_hci { hci_family const[AF_BLUETOOTH, int16] hci_dev hci_dev_t hci_channel flags[bt_hci_chan, int16] } ``` + out chỉ ra rằng kernel sẽ ghi dữ liệu vào addr - `addrlen len[addr]`: `addrlen` là độ dài của cấu trúc `sockaddr_hci`, được truyền từ user-space. -> tương ứng trong user-space: ```c struct sockaddr_hci addr; socklen_t addrlen = sizeof(addr); getsockname(sock_bt_hci, (struct sockaddr*)&addr, &addrlen) ``` - Tương tự với `getpeername()` ```c getpeername$bt_hci(fd sock_bt_hci, addr ptr[out, sockaddr_hci], addrlen len[addr]) ``` - Mình nghĩ 1 trong 2 đều y chang nhau, cũng không cần phải viết cả 2. #### c. `recvmsg()` and `read()` - Trước khi viết `recvmsg()` cho syzkaller, thì tốt nhất nên viết thêm cả `sendmsg()` cho hci_sock. Trong kernel, hàm callback `sendmsg()` được implement như sau: ```c static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct hci_mgmt_chan *chan; struct hci_dev *hdev; struct sk_buff *skb; int err; const unsigned int flags = msg->msg_flags; BT_DBG("sock %p sk %p", sock, sk); if (flags & MSG_OOB) return -EOPNOTSUPP; if (flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE | MSG_CMSG_COMPAT)) return -EINVAL; if (len < 4 || len > hci_pi(sk)->mtu) return -EINVAL; skb = bt_skb_sendmsg(sk, msg, len, len, 0, 0); if (IS_ERR(skb)) return PTR_ERR(skb); lock_sock(sk); switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: case HCI_CHANNEL_USER: break; case HCI_CHANNEL_MONITOR: err = -EOPNOTSUPP; goto drop; case HCI_CHANNEL_LOGGING: err = hci_logging_frame(sk, skb, flags); goto drop; default: mutex_lock(&mgmt_chan_list_lock); chan = __hci_mgmt_chan_find(hci_pi(sk)->channel); if (chan) err = hci_mgmt_cmd(chan, sk, skb); else err = -EINVAL; mutex_unlock(&mgmt_chan_list_lock); goto drop; } hdev = hci_hdev_from_sock(sk); if (IS_ERR(hdev)) { err = PTR_ERR(hdev); goto drop; } if (!test_bit(HCI_UP, &hdev->flags)) { err = -ENETDOWN; goto drop; } hci_skb_pkt_type(skb) = skb->data[0]; skb_pull(skb, 1); if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { /* No permission check is needed for user channel * since that gets enforced when binding the socket. * * However check that the packet type is valid. */ if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT && hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT && hci_skb_pkt_type(skb) != HCI_SCODATA_PKT && hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) { err = -EINVAL; goto drop; } skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } else if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) { u16 opcode = get_unaligned_le16(skb->data); u16 ogf = hci_opcode_ogf(opcode); u16 ocf = hci_opcode_ocf(opcode); if (((ogf > HCI_SFLT_MAX_OGF) || !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && !capable(CAP_NET_RAW)) { err = -EPERM; goto drop; } /* Since the opcode has already been extracted here, store * a copy of the value for later use by the drivers. */ hci_skb_opcode(skb) = opcode; if (ogf == 0x3f) { skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } else { /* Stand-alone HCI commands must be flagged as * single-command requests. */ bt_cb(skb)->hci.req_flags |= HCI_REQ_START; skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } } else { if (!capable(CAP_NET_RAW)) { err = -EPERM; goto drop; } if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT && hci_skb_pkt_type(skb) != HCI_SCODATA_PKT && hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) { err = -EINVAL; goto drop; } skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } err = len; done: release_sock(sk); return err; drop: kfree_skb(skb); goto done; } ``` - Ta cần viết struct `msghdr` với syzlang để fuzzer có thể hiểu được chúng ta làm gì với nó: ```c struct msghdr { void *msg_name; /* Address to send to/receive from. */ socklen_t msg_namelen; /* Length of address data. */ struct iovec *msg_iov; /* Vector of data to send/receive into. */ size_t msg_iovlen; /* Number of elements in the vector. */ void *msg_control; /* Ancillary data (eg BSD filedesc passing). */ size_t msg_controllen; /* Ancillary data buffer length. !! The type should be socklen_t but the definition of the kernel is incompatible with this. */ int msg_flags; /* Flags on received message. */ }; ``` - Convert qua syzlang: ```c msghdr { msg_name ptr[inout, void] msg_namelen len[msg_name, int32] msg_iov ptr[inout, iovec[in, array[int8]]] msg_iovlen len[msg_iov, int64] msg_control ptr[inout, array[int8]] msg_controllen len[msg_control, int64] msg_flags flags[msg_flags, int32] } msg_flags = MSG_OOB, MSG_DONTWAIT, MSG_NOSIGNAL, MSG_ERRQUEUE, MSG_CMSG_COMPAT //struct iovec đã được define sẵn trong syzkaller nên mình không viết lại nữa ``` ***Description cho sendmsg() Syzkaller***: ```c sendmsg$bt_hci(fd sock_bt_hci, msg ptr[in, msghdr], flags const[0]) ``` - Vì param `flags` trong hci_sock không được sử dụng tới nên mình để hằng số là 0 luôn. - Convert qua C: ```c struct msghdr msg = {0}; struct iovec iov; char buf[1024]; iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; sendmsg(sock_bt_hci, &msg, 0); ``` ***Description cho recvmsg() Syzkaller***: ```c recvmsg$bt_hci(fd sock_bt_hci, msg ptr[out, msghdr], flags flags[recvmsg_flags]) recvmsg_flags = MSG_OOB, MSG_TRUNC ``` - `fd sock_bt_hci`: file descriptor của socket HCI. - `msg ptr[inout, msghdr]`: con trỏ tới cấu trúc `msghdr`. - `flags flags[recvmsg_flags]`: các recv flag cho recvmsg(). - Ví dụ khi convert qua C: ```c struct msghdr msg = {0}; struct iovec iov; char buf[1024]; iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; recvmsg(sock_bt_hci, &msg, 0); ``` - Nếu Syzkaller đã implement syscall write cho socket Bluetooth HCI như: ```c write$bt_hci(fd sock_bt_hci, data ptr[in, vhci_command_pkt], size bytesize[data]) ``` thì nên implement thêm cả syscall read để kiểm tra chiều nhận dữ liệu từ kernel về user-space. Ví dụ: ```c read$bt_hci(fd sock_bt_hci, buf buffer[out], size bytesize[buf, intptr]) ``` - `fd sock_bt_hci`: file descriptor của socket HCI. - `buf buffer[out]`: bộ đệm nhận dữ liệu từ kernel. - `size bytesize[buf, intptr]`: kích thước bộ đệm. #### d. `poll()` - prototype của `poll` trong user-space: ```c int poll(struct poll_fd *ufds, nfds_t nfds, int timeout); ``` - `fds`: mảng các struct `pollfd`. - `nfds`: số lượng phần tử trong mảng. - `timeout`: thời gian chờ (ms). - Vì `poll` đã được define trước đó trong syzkaller, nên mình sẽ không cần viết lại các struct của nó mà chỉ cần viết cho hci layer. ***Description cho poll() Syzkaller***: ```c poll$bt_hci(fds ptr[inout, pollfd_bthci], nfds const[1], timeout int32) pollfd_bthci { fd sock_bt_hci events flags[poll_events, int16] revents flags[poll_events, int16] } poll_events = POLLIN, POLLPRI, POLLOUT, POLLERR, POLLHUP, POLLNVAL, POLLRDNORM, POLLRDBAND, POLLWRNORM, POLLWRBAND ``` - `fds`: con trỏ tới mảng các pollfd (có thể chứa fd của socket HCI). - `nfds`: số lượng phần tử trong mảng fds. - `timeout`: thời gian chờ. Khi convert sang C: ```c struct pollfd pfd; pfd.fd = sock_bt_hci; pfd.events = POLLIN | POLLOUT; poll(&pfd, 1, 1000); // timeout 1000ms ``` #### e. `ioctl` - Về syscall `ioctl` thì có vẻ các developer của syzkaller đã define khá đầy đủ, tuy nhiên vẫn có một số trường hợp ngoại lệ, ví dụ như với case `cmd = HCISETSCOMTU`, thì trong kernel implement một hàm riêng cho nó ```c int hci_dev_cmd(unsigned int cmd, void __user *arg) { struct hci_dev *hdev; struct hci_dev_req dr; __le16 policy; int err = 0; if (copy_from_user(&dr, arg, sizeof(dr))) return -EFAULT; hdev = hci_dev_get(dr.dev_id); if (!hdev) return -ENODEV; if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) { err = -EBUSY; goto done; } if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { err = -EOPNOTSUPP; goto done; } if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { err = -EOPNOTSUPP; goto done; } switch (cmd) { case HCISETAUTH: err = hci_cmd_sync_status(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &dr.dev_opt, HCI_CMD_TIMEOUT); break; case HCISETENCRYPT: if (!lmp_encrypt_capable(hdev)) { err = -EOPNOTSUPP; break; } if (!test_bit(HCI_AUTH, &hdev->flags)) { /* Auth must be enabled first */ err = hci_cmd_sync_status(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &dr.dev_opt, HCI_CMD_TIMEOUT); if (err) break; } err = hci_cmd_sync_status(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &dr.dev_opt, HCI_CMD_TIMEOUT); break; case HCISETSCAN: err = hci_cmd_sync_status(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &dr.dev_opt, HCI_CMD_TIMEOUT); /* Ensure that the connectable and discoverable states * get correctly modified as this was a non-mgmt change. */ if (!err) hci_update_passive_scan_state(hdev, dr.dev_opt); break; case HCISETLINKPOL: policy = cpu_to_le16(dr.dev_opt); err = hci_cmd_sync_status(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy, HCI_CMD_TIMEOUT); break; case HCISETLINKMODE: hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT); break; case HCISETPTYPE: if (hdev->pkt_type == (__u16) dr.dev_opt) break; hdev->pkt_type = (__u16) dr.dev_opt; mgmt_phy_configuration_changed(hdev, NULL); break; case HCISETACLMTU: hdev->acl_mtu = *((__u16 *) &dr.dev_opt + 1); hdev->acl_pkts = *((__u16 *) &dr.dev_opt + 0); break; case HCISETSCOMTU: hdev->sco_mtu = *((__u16 *) &dr.dev_opt + 1); hdev->sco_pkts = *((__u16 *) &dr.dev_opt + 0); break; default: err = -EINVAL; break; } done: hci_dev_put(hdev); return err; } ``` Nhưng họ lại gộp chung lại và khiến cho case này cần một chút thay đổi ```c ioctl$sock_bt_hci(fd sock_bt_hci, cmd flags[bt_hci_ioctl], arg buffer[inout]) ``` - Mình sẽ implement riêng cho case `cmd = HCISETSCOMTU`. Để ý trong hàm `hci_dev_cmd()` thì tham số thứ 2: `arg()`sẽ copy giá trị của nó vào trong `struct hci_dev_req dr;` bởi hàm `copy_from_user(&dr, arg, sizeof(dr))`. Thì trong description của syzlang, tham số `arg` phải là một con trỏ, trỏ tới struct `hci_dev` ***Description ioctl với case cmd = HCISETSCOMTU***: ```c ioctl$sock_bt_hci_HCISETSCOMTU(fd sock_bt_hci, cmd const[HCISETSCOMTU], arg ptr[in,hci_dev_struct]) hci_dev_struct { dev_id int16 dev_opt int32 } ``` #### f. `setsockopt()` and `getsockopt()` - Ở trong syzkaller, `setsockopt()` được implement như sau: ```c setsockopt$bt_hci_HCI_DATA_DIR(fd sock_bt_hci, level const[0], opt const[HCI_DATA_DIR], arg ptr[in, int32], arglen len[arg]) setsockopt$bt_hci_HCI_TIME_STAMP(fd sock_bt_hci, level const[0], opt const[HCI_TIME_STAMP], arg ptr[in, int32], arglen len[arg]) setsockopt$bt_hci_HCI_FILTER(fd sock_bt_hci, level const[0], opt const[HCI_FILTER], arg ptr[in, hci_ufilter], arglen len[arg]) ``` - Có vẻ là đã khá đầy đủ, tuy nhiên ở trong source kernel, với `level const[0]`, thì nó sẽ gọi tới hàm `setsockopt_old` ```c if (level == SOL_HCI) return hci_sock_setsockopt_old(sock, level, optname, optval, optlen); ``` Ở phần này thì chúng ta không bàn tới vì họ đã làm tốt phần này rồi. Cái chúng ta cần quan tâm là phần còn lại. ```c if (level != SOL_BLUETOOTH) return -ENOPROTOOPT; lock_sock(sk); switch (optname) { case BT_SNDMTU: case BT_RCVMTU: switch (hci_pi(sk)->channel) { /* Don't allow changing MTU for channels that are meant for HCI * traffic only. */ case HCI_CHANNEL_RAW: case HCI_CHANNEL_USER: err = -ENOPROTOOPT; goto done; } err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen); if (err) break; hci_pi(sk)->mtu = opt; break; default: err = -ENOPROTOOPT; break; } done: release_sock(sk); return err; ``` - Phần này mình sẽ gọi là `setsockopt$bt_hci_NEW` với `level` sẽ có giá trị bằng `SOL_BLUETOOTH (274)`, với 2 case của `optname` là `BT_SNDMTU`, và `BT_SNDMTU`. ***Description setsockopt cho Syzkaller***: ```c setsockopt$bt_hci_NEW(fd sock_bt_hci, level const[SOL_BLUETOOTH], opt flags[bt_hci_mtu_opt], val ptr[in, int32], vallen bytesize[val, int32]) SOL_BLUETOOTH = 274 bt_hci_mtu_opt = BT_SNDMTU, BT_RCVMTU ``` - `fd sock_bt_hci`: file descriptor của socket HCI. - `level const[SOL_BLUETOOTH]`: level phải là 274. - `opt flags[bt_hci_mtu_opt]`: optname là một trong hai giá trị `BT_SNDMTU` hoặc `BT_RCVMTU`. - `val ptr[in, int32]`: con trỏ tới giá trị MTU kiểu int32. - `vallen bytesize[val, int32]`: độ dài của giá trị truyền vào, kiểu int32. - Điều này cũng xảy ra tương tự với `getsockopt()` nên mình không cần phải mô tả nữa ***Description setsockopt cho Syzkaller***: ```c getsockopt$bt_hci_NEW(fd sock_bt_hci, level const[SOL_BLUETOOTH], opt flags[bt_hci_mtu_opt], val ptr[out, int32], vallen bytesize[val, int32]) SOL_BLUETOOTH = 274 bt_hci_mtu_opt = BT_SNDMTU, BT_RCVMTU ``` #### g. Compile lại những gì đã làm - `Make extract`: ![image-3](https://hackmd.io/_uploads/ByQlOViWxl.png) - `Make generate`: ![image-4](https://hackmd.io/_uploads/rJvlO4sWgg.png) - `make`: ![image-5](https://hackmd.io/_uploads/rynlONj-xl.png) + gặp một chút trục trặc, nhưng nó không liên quan đến bài viết này, chỉ cần bỏ hàm đó đi là được. ![image-6](https://hackmd.io/_uploads/H1M-d4iZge.png) - Đôi lúc trong quá trình compile mượt mà quá, mình không quen với điều này. Nhìn vào trong file socket_bluetooth.txt.const sẽ thấy có một số "thứ mới" được thêm vào là đã biết build thành công: ![image-7](https://hackmd.io/_uploads/rkU-u4jblx.png) ![image-8](https://hackmd.io/_uploads/rkF-O4s-eg.png) ### 2. L2CAP Layer Nhìn 1 vòng L2CAP trong syzkaller thì họ cũng define gần đủ hết các callback function trong kernel ```c resource sock_bt_l2cap[sock_bt] syz_init_net_socket$bt_l2cap(fam const[AF_BLUETOOTH], type flags[bt_l2cap_type], proto const[BTPROTO_L2CAP]) sock_bt_l2cap bind$bt_l2cap(fd sock_bt_l2cap, addr ptr[in, sockaddr_l2], addrlen len[addr]) connect$bt_l2cap(fd sock_bt_l2cap, addr ptr[in, sockaddr_l2], addrlen len[addr]) accept4$bt_l2cap(fd sock_bt_l2cap, peer ptr[out, sockaddr_l2, opt], peerlen ptr[inout, len[peer, int32]], flags flags[accept_flags]) sock_bt_l2cap setsockopt$bt_l2cap_L2CAP_OPTIONS(fd sock_bt_l2cap, level const[SOL_L2CAP], opt const[L2CAP_OPTIONS], arg ptr[in, l2cap_options], arglen len[arg]) getsockopt$bt_l2cap_L2CAP_OPTIONS(fd sock_bt_l2cap, level const[SOL_L2CAP], opt const[L2CAP_OPTIONS], arg ptr[out, l2cap_options], arglen ptr[inout, len[arg, int32]]) setsockopt$bt_l2cap_L2CAP_LM(fd sock_bt_l2cap, level const[SOL_L2CAP], opt const[L2CAP_LM], arg ptr[in, flags[bt_l2cap_lm, int32]], arglen len[arg]) getsockopt$bt_l2cap_L2CAP_LM(fd sock_bt_l2cap, level const[SOL_L2CAP], opt const[L2CAP_LM], arg ptr[out, int32], arglen ptr[inout, len[arg, int32]]) setsockopt$bt_l2cap_L2CAP_CONNINFO(fd sock_bt_l2cap, level const[SOL_L2CAP], opt const[L2CAP_CONNINFO], arg ptr[in, l2cap_conninfo], arglen len[arg]) getsockopt$bt_l2cap_L2CAP_CONNINFO(fd sock_bt_l2cap, level const[SOL_L2CAP], opt const[L2CAP_CONNINFO], arg ptr[out, l2cap_conninfo], arglen ptr[inout, len[arg, int32]]) ``` ```c static const struct proto_ops l2cap_sock_ops = { .family = PF_BLUETOOTH, .owner = THIS_MODULE, .release = l2cap_sock_release, .bind = l2cap_sock_bind, .connect = l2cap_sock_connect, .listen = l2cap_sock_listen, .accept = l2cap_sock_accept, .getname = l2cap_sock_getname, .sendmsg = l2cap_sock_sendmsg, .recvmsg = l2cap_sock_recvmsg, .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .gettstamp = sock_gettstamp, .mmap = sock_no_mmap, .socketpair = sock_no_socketpair, .shutdown = l2cap_sock_shutdown, .setsockopt = l2cap_sock_setsockopt, .getsockopt = l2cap_sock_getsockopt }; ``` - Chỉ thiếu "một vài" `options`: + `.release = l2cap_sock_release` + `.listen = l2cap_sock_listen` + `.getname = l2cap_sock_getname` + `.sendmsg = l2cap_sock_sendmsg` + `.recvmsg = l2cap_sock_recvmsg` + `.poll = bt_sock_poll` + `.ioctl = bt_sock_ioctl` + `.gettstamp = sock_gettstamp` + `.shutdown = l2cap_sock_shutdown` - Mình sẽ chỉ mô tả Description cho từng options, tại vì nó cũng tương tự với HCI Layer ở trên, mình đã mô tả khá là chi tiết rồi. Nếu như bạn không hiểu, thì hãy dừng lại một chút, đọc ở trên và suy nghĩ lại một xíu trước khi làm tiếp. #### a. `release()` and `shutdown()` ***Description release() trong Syzkaller***: ```c close$bt_l2cap(fd sock_bt_l2cap) ``` ***Description shutdown() trong Syzkaller***: ```c shutdown$bt_l2cap(fd sock_bt_l2cap, how flags[shutdown_flags]) shutdown_flags = SHUT_RD, SHUT_WR, SHUT_RDWR ``` #### b. `listen()` ***Description listen() trong Syzkaller***: ```c listen$bt_l2cap(fd sock_bt_l2cap, backlog int32) ``` - `fd sock_bt_l2cap`: file descriptor của socket L2CAP (được định nghĩa từ resource). - `backlog int32`: số lượng kết nối chờ tối đa. #### c. `getname()` ***Description getname() trong Syzkaller***: ```c getsockname$bt_l2cap(fd sock_bt_l2cap, addr ptr[out, sockaddr_l2], addrlen len[addr]) getpeername$bt_l2cap(fd sock_bt_l2cap, addr ptr[out, sockaddr_l2], addrlen len[addr]) ``` #### d. `sendmsg()` and `recvmsg()` ***Description sendmsg() trong Syzkaller***: ```c sendmsg$bt_l2cap(fd sock_bt_l2cap, msg ptr[in, msghdr], flags const[0]) ``` ***Description recvmsg() trong Syzkaller***: ```c recvmsg$bt_l2cap(fd sock_bt_l2cap, msg ptr[out, msghdr], flags flags[recvmsg_flags]) recvmsg_flags = MSG_ERRQUEUE, MSG_OOB, MSG_WAITALL, MSG_DONTWAIT, MSG_PEEK, MSG_TRUNC ``` #### e. `poll()` ***Description poll() trong Syzkaller***: ```c poll$bt_l2cap(fds ptr[inout, pollfd_btl2cap], nfds const[1], timeout int32) pollfd_btl2cap { fd sock_bt_l2cap events flags[poll_events, int16] revents flags[poll_events, int16] } poll_events = POLLIN, POLLPRI, POLLOUT, POLLERR, POLLHUP, POLLNVAL, POLLRDNORM, POLLRDBAND, POLLWRNORM, POLLWRBAND ``` #### f. `ioctl()` ***Description ioctl() trong Syzkaller***: ```c ioctl$bt_l2cap(fd sock_bt_l2cap, cmd flags[bt_sock_ioctl_flag], arg buffer[inout]) bt_sock_ioctl_flag = TIOCOUTQ, TIOCINQ ``` #### g. `gettstamp()` ***Description gettstamp() trong Syzkaller***: ```c ioctl$bt_l2cap_gettstamp(fd sock_bt_l2cap, cmd flags[bt_l2cap_gettstamp_flag], arg ptr[out, timespec]) bt_l2cap_gettstamp_flag = SIOCGSTAMP, SIOCGSTAMPNS ``` - `fd sock_bt_l2cap`: file descriptor của socket L2CAP. - `cmd`: có thể là SIOCGSTAMP hoặc SIOCGSTAMPNS (tùy kernel hỗ trợ). - `arg ptr[out, timespec]`: con trỏ tới struct timespec để kernel ghi timestamp. #### h. `setsockopt()` and `getsockopt()` Xảy ra tương tự với HCI layer, thì trong syzkaller chỉ mới implement hàm `l2cap_sock_setsockopt_old()`, các phần còn lại thì không đụng tới. ***Description setsockopt() và getsockopt() trong Syzkaller***: ```c setsockopt$bt_l2cap_new_BT_SECURITY(fd sock_bt_l2cap, level const[274], optname const[BT_SECURITY], optval ptr[in,bt_l2cap_btsecurity], optlen len[optval]) getsockopt$bt_l2cap_new_BT_SECURITY(fd sock_bt_l2cap, level const[274], optname const[BT_SECURITY], optval ptr[out, bt_l2cap_btsecurity], optlen len[optval]) bt_l2cap_btsecurity { level int8 key_size int8 } setsockopt$bt_l2cap_new_BT_DEFER_SETUP(fd sock_bt_l2cap,level const[274], optname const[BT_DEFER_SETUP], optval ptr[in,int32], optlen len[optval]) getsockopt$bt_l2cap_new_BT_DEFER_SETUP(fd sock_bt_l2cap,level const[274], optname const[BT_DEFER_SETUP], optval ptr[out,int32], optlen len[optval]) setsockopt$bt_l2cap_new_BT_FLUSHABLE(fd sock_bt_l2cap,level const[274], optname const[BT_FLUSHABLE], optval ptr[in,int32], optlen len[optval]) getsockopt$bt_l2cap_new_BT_FLUSHABLE(fd sock_bt_l2cap,level const[274], optname const[BT_FLUSHABLE], optval ptr[out,int32], optlen len[optval]) setsockopt$bt_l2cap_new_BT_POWER(fd sock_bt_l2cap,level const[274], optname const[BT_POWER], optval ptr[in,bt_l2cap_BT_POWER], optlen len[optval]) getsockopt$bt_l2cap_new_BT_POWER(fd sock_bt_l2cap,level const[274], optname const[BT_POWER], optval ptr[out,bt_l2cap_BT_POWER], optlen len[optval]) bt_l2cap_BT_POWER{ force_active int8 } setsockopt$bt_l2cap_new_BT_CHANNEL_POLICY(fd sock_bt_l2cap,level const[274], optname const[BT_CHANNEL_POLICY], optval ptr[in,int32], optlen len[optval]) getsockopt$bt_l2cap_new_BT_CHANNEL_POLICY(fd sock_bt_l2cap,level const[274], optname const[BT_CHANNEL_POLICY], optval ptr[out,int32], optlen len[optval]) setsockopt$bt_l2cap_new_BT_SNDMTU(fd sock_bt_l2cap,level const[274], optname const[BT_SNDMTU], optval ptr[in,int32], optlen len[optval]) getsockopt$bt_l2cap_new_BT_SNDMTU(fd sock_bt_l2cap,level const[274], optname const[BT_SNDMTU], optval ptr[out,int32], optlen len[optval]) setsockopt$bt_l2cap_new_BT_RCVMTU(fd sock_bt_l2cap,level const[274], optname const[BT_RCVMTU], optval ptr[in,int16], optlen len[optval]) getsockopt$bt_l2cap_new_BT_RCVMTU(fd sock_bt_l2cap,level const[274], optname const[BT_RCVMTU], optval ptr[out,int16], optlen len[optval]) setsockopt$bt_l2cap_new_BT_MODE(fd sock_bt_l2cap,level const[274], optname const[BT_MODE], optval ptr[in,int8], optlen len[optval]) getsockopt$bt_l2cap_new_BT_MODE(fd sock_bt_l2cap,level const[274], optname const[BT_MODE], optval ptr[out,int8], optlen len[optval]) getsockopt$bt_l2cap_new_BT_PHY(fd sock_bt_l2cap,level const[274], optname const[BT_MODE], optval ptr[out,int32], optlen len[optval]) ``` #### i. Compile lại những gì đã làm Các bước thì tương tự như ở trên. ## IV. Fuzzing - Config để fuzzing bằng Syzkaller, thì chỉ cần thêm tất cả những syscall mà mình define trong socket_bluetooth.txt, bao gồm cả syscall được define sẵn và syscall mình thêm vào. ví dụ với HCI Layer và L2CAP Layer ở trên ```c { "target": "linux/amd64", "http": "127.0.0.1:56741", "workdir": "/home/cpu12462/p2o_2025/test_wr/workdir", "kernel_obj": "/home/cpu12462/p2o_2025/linux_test/", "image": "/home/cpu12462/p2o_2025/test_wr/image/bullseye.img", "sshkey": "/home/cpu12462/p2o_2025/test_wr/image/bullseye.id_rsa", "syzkaller": "/home/cpu12462/syzkaller_/", "procs": 4, "type": "qemu", "vm": { "count": 4, "kernel": "/home/cpu12462/p2o_2025/linux_test/arch/x86/boot/bzImage", "cpu": 2, "mem": 2048 }, "enable_syscalls": [ "mmap", "syz_init_net_socket$bt_hci", "bind$bt_hci", "ioctl$sock_bt_hci", "ioctl$HCIINQUIRY", "setsockopt$bt_hci_HCI_DATA_DIR", "setsockopt$bt_hci_HCI_TIME_STAMP", "setsockopt$bt_hci_HCI_FILTER", "getsockopt$bt_hci", "write$bt_hci", "close$bt_hci", "getsockname$bt_hci", "getpeername$bt_hci", "sendmsg$bt_hci", "recvmsg$bt_hci", "read$bt_hci", "poll$bt_hci", "ioctl$sock_bt_hci_HCISETSCOMTU", "setsockopt$bt_hci_NEW", "getsockopt$bt_hci_NEW", "syz_init_net_socket$bt_l2cap", "bind$bt_l2cap", "connect$bt_l2cap", "close$bt_l2cap", "shutdown$bt_l2cap", "accept4$bt_l2cap", "listen$bt_l2cap", "getsockname$bt_l2cap", "getpeername$bt_l2cap", "sendmsg$bt_l2cap", "recvmsg$bt_l2cap", "poll$bt_l2cap", "ioctl$bt_l2cap", "ioctl$bt_l2cap_gettstamp", "setsockopt$bt_l2cap_L2CAP_OPTIONS", "getsockopt$bt_l2cap_L2CAP_OPTIONS", "setsockopt$bt_l2cap_L2CAP_LM", "getsockopt$bt_l2cap_L2CAP_LM", "setsockopt$bt_l2cap_L2CAP_CONNINFO", "getsockopt$bt_l2cap_L2CAP_CONNINFO", "setsockopt$bt_l2cap_new_BT_SECURITY", "getsockopt$bt_l2cap_new_BT_SECURITY", "setsockopt$bt_l2cap_new_BT_DEFER_SETUP", "getsockopt$bt_l2cap_new_BT_DEFER_SETUP", "setsockopt$bt_l2cap_new_BT_FLUSHABLE", "getsockopt$bt_l2cap_new_BT_FLUSHABLE", "setsockopt$bt_l2cap_new_BT_POWER", "getsockopt$bt_l2cap_new_BT_POWER", "setsockopt$bt_l2cap_new_BT_CHANNEL_POLICY", "getsockopt$bt_l2cap_new_BT_CHANNEL_POLICY", "setsockopt$bt_l2cap_new_BT_SNDMTU", "getsockopt$bt_l2cap_new_BT_SNDMTU", "setsockopt$bt_l2cap_new_BT_RCVMTU", "getsockopt$bt_l2cap_new_BT_RCVMTU", "setsockopt$bt_l2cap_new_BT_MODE", "getsockopt$bt_l2cap_new_BT_MODE", "getsockopt$bt_l2cap_new_BT_PHY" ] } ``` - Sau đó chạy fuzzing tương tự ở [Syzkaller-101](https://hackmd.io/jnsARl3zSFem_Pg-0FULmQ). Mình chạy ở trên máy của công ty, thì chạy cỡ 1 tuần thì cũng được kha khá bug ![image-9](https://hackmd.io/_uploads/rJ6MdVs-eg.png) - Thời điểm mình được như trên là mình cũng đã viết được kha khá description, không chỉ mỗi bluetooth subsystem, mà còn net-ipv4, usb, mm,... ## V. Final Word - Theo mình nghĩ reproducing là phần quan trọng nhất trong việc fuzzing. Fuzzing có ý nghĩa gì nếu như nó không thể reproduce được crash. Trong [document](https://github.com/google/syzkaller/blob/master/docs/reproducing_crashes.md) của syzkaller cũng đã nói khá rõ trong cách để reproduce bug. - Tuy nhiên không phải crash nào của syzkaller cũng có thể reproduce được, phần lớn là như vậy. Vậy nên cũng cần có thêm kỹ năng đọc crash log để có thể reproduce được bug, tuy nhiên mình vẫn chỉ mới tới dừng chân được ở level fuzzing, vẫn còn research thêm về reproduce bug thông qua đọc crash log. Một ngày nào đó kỹ năng mình cao hơn mình sẽ viết thêm phần reproducing. Cảm ơn bạn đã đọc tới đây. Peace! Out