Phân tích lỗi Dirty COW  -  CVE-2016-5195

ping

VIP Members
19/06/2013
57
101 bài viết
Phân tích lỗi Dirty COW  -  CVE-2016-5195
Về Dirty Cow

Gần đây tôi có thấy nhiều thông tin về lỗ hổng này, được đánh giá là Serious, cho phép leo thang đặc quyền, ảnh hưởng đến nhiều distro Linux… Tôi cũng không hiểu sao một lỗ hổng sau khi được công bố thì có logo, website riêng, Twitter, Facebook Page thậm chí là có cả một cái shop bán đồ lưu niệm :| Vì tò mò nên cũng muốn tìm hiểu cơ chế hoạt động của vuln này, dù tôi không phải người chuyên research OS Kernel.

Dirty COW .png

Dirty COW được gán mã lỗi CVE-2016-5195, là một cái bug Linux Kernel Race condition cho phép leo thang đặc quyền thông qua Local Exploit (nghĩa là kẻ tấn công phải vào được server nạn nhân trước với quyền Normal rồi dùng lỗi này để nâng lên quyền root). Dirty COW ảnh hưởng đến nhiều phiên bản Kernel(2.6.22–3.9) và hầu hết các distro Linux thông dụng, nhưng cách khai thác thì rất đơn giản và hiệu quả.

Người phát hiện ra lỗi này, Phil Oester, công bố sau khi một server anh này quản lý bị tấn công bằng chính Dirty COW. Có nghĩa là mã khai thác hiện có đã bị dùng để tấn công thực tế từ rất lâu rồi. Cha đẻ Linux, Linus Torvalds cũng đề cập trong bản vá lỗi rằng anh đã mường tượng lỗi này có thể xảy ra, và đã đưa ra bản vá vào năm 2005 nhưng nó đã không thực sự phát huy tác dụng.

Trong bài viết này, tôi sẽ cố gắng giải thích chi tiết cách lỗi này hoạt động. Và nếu có gì sai sót, hy vọng ai đó có thể góp ý thêm.

Chi tiết lỗi

Một cách phân tích bug hiệu quả là dựa vào mã khai thác đã có. Dưới đây là đoạn mã được cung cấp từ repository github “chính thức” của Dirty COW.


Mã khai thác này cho phép sửa nội dung một file đã được set permision Read Only, hoạt động được trên hầu hết các distro Linux trừ Red Hat Enterprise Linux 5 và 6 (lý do sẽ giải thích ở dưới). Cách dùng thì có thể thấy dễ hiểu thế này:

Mã:
$ sudo -s
# echo this is not a test > foo
# chmod 0404 foo
$ ls -lah foo-r — — -r — 1 root root 19 Oct 20 15:23 foo
$ cat foo this is not a test
$ ./dirtyc0w foo m00000000000000000
$ cat foo m00000000000000000

Theo luồng xử lý của chương trình, chúng ta có thể chia nhỏ các công việc mà nó thực hiện như sau.

1. Load file và map vào Virtual Address Space

Tại dòng 87, chương trình mở file input và đọc với chế độ Read Only. Tiếp đến lấy thông tin file ghi vào biến con trỏ st và lưu lại tên file vào biến name. Mọi thứ khá rõ ràng.

Mã:
87: f=open(argv[1],O_RDONLY);
88: fstat(f,&st);
89: name=argv[1];

Ở dòng 101, nội dung file được map vào Virtual Address Space bằng hàm mmap()

Mã:
101: map=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0 );

mmap() không copy nội dung file được vào RAM, mà ánh xạ nội dung này vào vùng nhớ ảo của tiến trình đang chạy.

1.png

Hãy xem qua đặc tả hàm:

Mã:
[B]void *mmap(void *[/B][I]addr[/I][B], size_t [/B][I]length[/I][B], int [/B][I]prot[/I][B], int [/B][I]flags[/I][B], int [/B][I]fd[/I][B], off_t [/B][I]offset[/I][B]);[/B]

Tham số addr trong trường hợp này là NULL, nghĩa là kernel tự quyết định địa chỉ lưu trữ, độ dài vùng nhớ length bằng độ lớn của file st.st_size, chế độ truy cập prot vẫnđược để là Read Only PROT_READ vì thế vùng mapping này chỉ có thể được đọc, fd offset tương ứng với file input và vị trí bắt đầu load file. Và tham sốquan trọng nhất ở đây, flags được gán bằng MAP_PRIVATE.

Theo như tài liệu của linux:

MAP_PRIVATE: Create a private copy-on-write mapping

Copy-on-write là một khái niệm quan trọng của hệ điều hành, tư tưởng của nó là nếu một tiến trình đọc một tài nguyên dạng copy-on-write thì sẽ vẫn như đọc tài nguyên thường, nhưng nếu thực hiện thao tác ghi/sửa thì hệ điều hành sẽ tạo một bản sao của tài nguyên đó và cập nhật các nội dung mới vào đây, bản gốc không bị thay đổi.

2.png

Với cờ MAP_PRIVATE, bản mapping được xử lý riêng với tiến trình sở hữu nó. Các thay đổi về nội dung trên mapping không ảnh hưởng đến bản gốc và cũng không chịu sự tác động của bất kỳ tiến trình nào khác. Cụ thể hơn, nếu chúng ta sửa nội dung file input sau khi đã map vào Virtual Address Space thì file gốc không bị thay đổi.

Cụm Copy-on-write cũng chính là dạng đầy đủ của từ COW trong cái tên Dirty COW

2. Race Condition

Như đã nói ở phần đầu, Dirty COW là một lỗi Race Condition, và lỗi này được tạo ra bởi 2 dòng lệnh sau:

Mã:
106: pthread_create(&pth1,NULL,madviseThread,argv[1]);
107: pthread_create(&pth2,NULL,procselfmemThread,argv[2]);

Về cơ bản thì Race Condition xảy ra khi mà có nhiều hơn một luồng thực thi cùng đọc/ghi vào tài nguyên dùng chung dẫn đến xung đột và những tình huống không đoán được trước. Trong trường hợp của Dirty COW, các luồng thực thi này là 2 thread madviseThread procselfmemThread, còn tài nguyên dùng chung là bản mapping của file input. Ta sẽ đi cụ thể vào từng thread.

1. madviseThread()

Chú ý vào dòng 45, đây là lệnh quan trọng nhất của thread này. Còn vòng lặp for chỉ giúp cho Race được diễn ra.

Mã:
45: c+=madvise(map,100,MADV_DONTNEED);

madvise là một system call, nó đưa ra chỉ thị cho kernel biết phải làm gì với không gian nhớ bắt đầu từ addr và có độ dài length

Mã:
[B]int madvise(void *[/B][I]addr[/I][B], size_t [/B][I]length[/I][B], int [/B][I]advice[/I][B]);[/B]

Ở đây, chỉ thị là MADV_DONTNEED, nó yêu cầu kernel không sử dụng không gian nhớ này nữa và có thể giải phóng tài nguyên liên kết đến file input. Lúc này vùng nhớ trên trở thành dirty, đây là từ đầu tiên trong tên Dirty COW. Với vùng nhớ dirty, lại có thêm chỉ thị MADV_DONTNEED (do thực hiện vòng lặp), dữ liệu ghi vào sẽ bị “ thrown away”, còn dữ liệu đọc ra vẫn sẽ được lấy từ file gốc.

Tóm lại, mục đích thread này là ngăn cản việc sử dụng vùng mapping.

2. procselfmemThread()

Mã:
61: int f=open("/proc/self/mem",O_RDWR);
62: int i,c=0;
63: for(i=0;i
 
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
Bài phân tích chi tiết quá.

WhiteHat cung cấp công cụ kiểm tra kết hợp cập nhật bản vá theo link sau đây: http://tools.whitehat.vn/dirtyCow.zip

Người dùng và quản trị hệ thống tải công cụ và chạy với lệnh [python DirtyCOW] để khắc phục vấn đề sớm nhất có thể. Công cụ sẽ tự động kiểm tra xem hệ thống có lỗ hổng hay không, nếu có sẽ tự động cài đặt bản vá lỗi (Yêu cầu hệ thống cài đặt Python).

Hoặc:

Mã:
#!/usr/bin/python

# Copyright (c) Bkav Corporation....
import platform, os, subprocess, re

def pm():
    pms = ['apt-get', 'yum', 'dnf']
    for pm in pms:    
        c = subprocess.Popen(['which', pm], stdout=-1, stderr=-1)
        if c.communicate()[0]:
            return pm


kernel_ver = int( ''.join(list(re.findall("(\d+).(\d+).(\d+)", platform.uname()[2])[0])) )
kernel_ver *=10 if kernel_ver= 2622:
    os.system ('sudo '+ pm() + ' update')
else:    
    print kernel_ver
    print "Kernel: ", platform.uname()[2], " safe"
Mã:
 
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
Comment
Thẻ
cve-2016-5195 dirty cow
Bên trên