Phân tích lỗ hổng leo thang đặc quyền (CVE-2016-8655) trong Linux kernel

DDos

VIP Members
22/10/2013
524
2.191 bài viết
Phân tích lỗ hổng leo thang đặc quyền (CVE-2016-8655) trong Linux kernel
Hôm 6/12, nhà nghiên cứu bảo mật Philip Pettersson đã tiết lộ một lỗi nguy hiểm trong kernel của Linux cho phép một user có thể leo thang đặc quyền. Thông tin về lỗ hổng này các bạn có thể tham khảo tại đây.

CVE-2016-8655.png

Trong topic này, mình sẽ phân tích chi tiết nguyên nhân gây ra lỗi này.

Để tạo ra AF-PACKET sockets, CAP_NET_RAW là yêu cầu cần thiết trong Network Namespace. Trong một hệ thống, các namespace với đặc quyền thấp có thể được tạo ra khá dễ dàng. Các namespace ở đặc quyền thấp này thường được cho phép trong các bản phân phối của Linux (Ubuntu, RedHat...). Việc tạo ra các namespace này được kích hoạt từ bên trong cáccontainers để xâm nhập vào kernel. Trên Android, các quá trình với gid=3004/AID_NET_RAW có thể tạo ra socket AF_PACKET (mediaserver) và có thể gây ra lỗi này.

Cốt lõi của vấn đề nằm ở hai hàm packet_set_ring()packet_setsockopt(). Chúng ta có thể sử dụng hàm packet_set_ring() bằng cách gọi hàm setsockopt() trong socket sử dụng PACKET_RX_RING option.
Nếu phiên bản của packet socket là TPACKET_V3, một đối tượng timer_list sẽ được khởi tạo bởi hàm packet_set_ring() khi nói gọi hàm init_prb_bdqc().

Mã:
...[/INDENT]
[INDENT]switch (po->tp_version) {[/INDENT]
[INDENT]case TPACKET_V3:[/INDENT]
[INDENT]/* Transmit path is not supported. We checked[/INDENT]
[INDENT]* it above but just being paranoid[/INDENT]
[INDENT]*/[/INDENT]
[INDENT]if (!tx_ring)[/INDENT]
[INDENT]init_prb_bdqc(po, rb, pg_vec, req_u);[/INDENT]
[INDENT]break;[/INDENT]
[INDENT]default:[/INDENT]
[INDENT]break;[/INDENT]
[INDENT]}[/INDENT]
[INDENT]...
Fuction flow thiết lập timer theo thứ tự packet_set_ring()->init_prb_bdqc()->prb_setup_retire_blk_timer()->
prb_init_blk_timer()->prb_init_blk_timer()->init_timer()


Khi socket bị đóng, packet_set_ring() được gọi trở lại để giải phóng ring buffer và xóa các timer được khởi tạo trước đó nếu packer version là lớn hơn TPACKET_V2

Mã:
...[/INDENT]
[INDENT]if (closing && (po->tp_version > TPACKET_V2)) {[/INDENT]
[INDENT]/* Because we don't support block-based V3 on tx-ring */[/INDENT]
[INDENT]if (!tx_ring)[/INDENT]
[INDENT]prb_shutdown_retire_blk_timer(po, rb_queue);[/INDENT]
[INDENT]}[/INDENT]
[INDENT]...
Vấn đề ở đây là nếu chúng ta có thể thay đổi packet version tới giá trị TPACKET_V1 với hàm packet_setsockopt() sau khi hàm init_prb_bdqc() được thực hiện và trước khi hàm packet_set_ring() được trả về.

Việc thay đổi này sẽ bị từ chối sau khi ring bufter đã được khởi tạo nhưng nó là không đủ.

Mã:
...[/INDENT]
[INDENT]case PACKET_VERSION:[/INDENT]
[INDENT]{[/INDENT]
[INDENT]...[/INDENT]
[INDENT]if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)[/INDENT]
[INDENT]return -EBUSY;[/INDENT]
[INDENT]...
Giữa lệnh gọi hàm init_prb_bdqc()swap(rb->pg_vec, pg_vec) trong packet_set_ring() có nhiều cách để race đường dẫn code này.

Khi socket bị đóng, hàm packet_set_ring() sẽ không xóa timer khi socket version là TPACKET_V1. Các timer_list struct mà diễn tả các đối tượng timer được xác định bên trong packet_sock struct cho chính socket tuy nhiên sẽ được giải phóng với việc gọi hàm kfree()

Sau đó, chúng ta có use-afer-free trong một timer object mà nó có thể được khai thác bằng một vài tấn công trong SLAB allocator. Cuối cùng dẫn tới việc kernel sẽ nhảy vào hàm đã được thay đổi khi timer hết hạn.

Lỗi này được sửa bằng cách sử dụng lock_sock(sk) trong packet_setsockopt() khi thay đổi packet version đồng thời sử dụng lock ở thời điểm bắt đầu của hàm packet_set_ring()

Mã khai thác lỗi này, các bạn có thể tải về tại đây.

Để vá lỗi này, các bạn cập nhật kernel sử dụng lệnh dưới đây:

Ubuntu:

sudo apt-get update
sudo apt-get dist-upgrade


Red Hat:

yum -y update kernel
 
Chỉnh sửa lần cuối bởi người điều hành:
Mời các bạn tham gia Group WhiteHat để thảo luận và cập nhật tin tức an ninh mạng hàng ngày.
Lưu ý từ WhiteHat: Kiến thức an ninh mạng để phòng chống, không làm điều xấu. Luật pháp liên quan
Thẻ
cve-2016-8655 linux kernel
Bên trên