Những trận tấn công đầy hiểm họa - Cách phá vỡ Windows
trang này đã được đọc lần
Những trận tấn công đầy hiểm họa - Cách phá vỡ Windows
trang này đã được đọc lần
Giới thiệu
Bài viết này minh hoạ một phát sinh tấn công mới đến Microsoft Windows, và có thể đến các hệ
thống thông điệp khác (message-based systems). Những lỗi được minh hoạ trong bài viết này, ở
thời điểm bài này được viết, thuộc dạng không thể chữa được. Giải pháp duy nhất để loại trừ
những loại tấn công này đòi hỏi chức năng không có sẵn trong hệ điều hành Windows, và ngay
cả trong những ứng hoạt khác cho hệ Windows. Lâu nay Microsoft biết rõ những lỗi này; khi tôi
cảnh báo họ loại tấn công trên, họ cho biết rằng họ không xếp loại phương thức tấn công đó như
một dạng lỗi của Windows - bạn có thể xem bức điện thư ở đây -1-. Khảo cứu trên bùng ra từ lời
phát biểu của ông Jim Allchin, phó chủ tịch Microsoft, cho rằng có quá nhiều lỗi trong hệ
Windows đến nỗi, nếu mã nguồn của Windows bị tiết lộ sẽ trở nên mối đe doạ đến an ninh quốc
gia. Ông ta đề cập đến “xếp hàng thông điệp” (Message Queuing), và ngay sau đó hối hận vì đã
đề cập đến vấn đề này. Tuy nhiên, dựa trên số lượng khảo sát trên toàn thế giới hiện nay đến
vấn đề trên sau lời phát biểu của ông Allchin, đây là lúc cộng đồng “mũ trắng” -2- lột tả thực
trạng này.
Bài viết này thuộc dạng từng bước một chỉ dẫn cách khai thác một điển hình của lỗi ở trên. Có
nhiều phương pháp tấn công khác đã được bàn thảo, tuy nhiên ví dụ của các loại tấn công khác
không được trình bày ở đây. Có rất nhiều cách khai thác các lỗi trên và nhiều biến thái của từng
giai đoạn được trình bày. Ðây chỉ là một ví dụ.
Nền tảng - hệ thống thông điệp của Win32
Các nội ứng trình của Windows hoàn toàn được điều hành xuyên qua phương pháp ứng dụng
thông điệp. Khi một phím được nhấn, một thông điệp được gởi đến khung động hiện tại (current
active window), thông điệp này cho biết một phím nào đó đã được “nhấn”. Khi Windows xác định
một ứng trình cần vẽ lại phạm vi của client, Windows gởi một thông điệp đến ứng trình này. Thật
ra, khi một biến cố (event) xảy ra và một ứng trình nào đó cần biết, nó sẽ gởi đi một thông điệp.
Những thông điệp này được đặt vào một hàng đợi (queue) và được ứng trình tuần tự xử lý.
Ðây là một cơ chế hết sức đáng tin cậy để điều hành các ứng trình. Tuy nhiên, cơ chế dùng để
điều hành các thông điệp trên Win32 bị lỗi. Bất cứ một ứng trình nào cũng có thể gởi thông điệp
đến bất cứ khung (window) nào trên cùng desktop, bất chấp ứng trình gởi thông điệp có làm chủ
khung đó hay không, và bất chấp ứng trình được nhận thông điệp có muốn nhận các thông điệp
này hay không. Hoàn toàn không có một cơ chế nào dùng để xác minh nguồn gốc của các thông
điệp; không có cách nào để phân biệt sự khác nhau giữa một thông điệp được gởi từ một ứng
trình hiểm độc và một thông điệp từ lõi (kernel) của Windows. Ðó là thiếu sót cơ chế xác minh
mà chúng ta sẽ khai thác với chủ đích: sử dụng các thông điệp này để thao túng các khung và
các quy trình làm chủ các window trong Windows.
Tổng quát
Trong ví dụ này, tôi sẽ khai thác Network Associate VirusScan phiên bản 4.5.1, chạy trên
Windows 2000 Professional. Tôi truy nhập như “guest” và VirusScan Console chạy trên desktop là
“LocalSystem”, mục đích dùng để lừa VirusScan chạy mã riêng để gia tăng chủ quyền cho tôi.
Mục đích này đạt được qua nhiều phân đoạn đơn giản:
1. Tìm một khung thích hợp trong VirusScan (một hòm chỉnh [edit box] là hoàn hảo), và lấy một
khung đó để xử dụng.
2. Bỏ đi giới hạn phân độ trong edit box để tôi có thể đánh vào một số lượng dữ kiện tùy ý.
3. Dán vào một số mã nhị phân (binary code).
4. Buộc VirusScan thi triển mã của tôi (như LocalSystem)
Những bước trên thực sự rất dễ làm. Windows cung cấp trọn bộ các chức năng tiện dụng mà
chúng ta cần. Tôi viết một ứng trình nhỏ gọi là Shatter -3- nó thực hiện chức năng gia tăng chủ
quyền này. Bạn cũng sẽ cần một hex editor có khả năng chép dữ liệu nhị phân vào clipboard (tôi
dùng UltraEdit (www.ultraedit.com)), và một debugger (tôi dùng WinDbg
(www.microsoft.com/ddk/debugging/installx86.asp)).
XIN NHỚ: Vài bộ quét virus cảnh báo người dùng virus “Win32/Beavuh” trong hồ sơ sploit.bin
thuộc phần nén Shatter.zip. Ðây không phải là virus. Bộ quét đánh dấu đúng hồ sơ này
(sploit.bin) - mã nguồn của hồ sơ này được thiết kế dùng để mở một command shell và tra nó
vào một socket của mạng. Một cách tổng quát mà nói, làm như vậy không tốt (đối với bảo mật),
bởi thế bộ quét có phản ứng đúng trong quy trình tạo cảnh báo. Mã nguồn này quả thật được
thiết kế với dụng ý đen tối, tuy nhiên bộ quét nhận diện nó như một thứ virus là sai.
Các thông điệp của Windows bao gồm 3 phần, một phần nhận diện cho thông điệp và hai tham
số. Các tham số được sử dụng một cách khác nhau tùy thuộc vào thông điệp gì được gởi. Ðiều
này tạo điều kiện đơn giản hơn cho chúng ta, vì chúng ta chỉ phải lo đến 4 điểm: một điều dụng
khung (window handle) để nhận thông điệp, một thông điệp và hai tham số. Hãy thử xem
chuyện này dễ như thế nào..
Bước 1: Tìm khung
Chúng ta cần xác định một loại quản trị điều chỉnh (edit control) nào đó - một nơi chúng ta có
thể đánh dữ kiện vào. Ðừng ngại nếu bị giới hạn, chúng ta có thể hoá giải vấn đề này. Khởi động
VirusScan console và đánh vào nút đầu tiên - “New Task”. Một cách thuận tiện, trên đầu của
dialog này hiện ra một edit box. Cái này tuyệt hảo. Bây giờ, chúng ta cần một điều dụng (handle)
để chúng ta có thể tương tác (interact) với nó. Windows sẵn sàng cho chúng ta một handle đến
bất cứ một window nào chúng ta muốn - chúng ta chỉ cần hỏi nó. Khởi động Shatter và điều
chỉnh vị trí để bạn có thể thấy được VirusScan edit control bên dưới. Bấm vào “Get cursor
window” - Shatter sẽ thêm và một đơn vị trong hòm danh sách bên dưới tương tự như “102f2 -
Get cursor window”. Cái này là do chúng ta yêu cấu Windows cho chúng ta một handle đến
window trực tiếp phía dưới cursor. Chuyển cursor sang VirusScan edit control và đánh phím
“Space” để gọi Shatter một lần nữa. Shatter sẽ xoá hòm danh sách và cho bạn biết handle của
mục tiêu window - trường hợp của tôi là 30270. Bây giờ chúng ta có thể tương tác với một
window đang chạy với tính cách lập trình và với chủ quyền cao hơn. Hãy dán vào một ít
shellcode -4-.
Bước 2: Xóa bỏ các hạn chế
Khi đã có một window handle, chúng ta có thể gởi bất cứ thông điệp gì theo ý muốn đến control
này và nó sẽ thừa hành yêu cầu một cách mù loà. Cứ theo từng bước một, đầu tiên chúng ta
phải nắm chắc là có đủ khoảng trống cho shellcode.
Trong Shatter, đánh window handle của bạn vào hòm “Handle”. Thông điệp để chỉnh phân độ tối
đa của các chữ trong một edit box là EM_SERLIMITTEXT. Tham số thứ nhất dành cho phân độ
tối đa của chữ, và khỏi cần quan tâm đến tham số thứ nhì. Ðánh số 4 vào box WPARAM, và 0 và
box thứ 3. Bấm vào EM_SETLIMITTEXT để gởi thông điệp và thử đánh gì đó vào trong edit box
của VirusScan. Bạn chắc hẳn không thể đánh hơn 4 chữ. Thay đổi số 4 thành FFFFFFFF và gởi
thông điệp lại. Bây giờ thử đánh vào edit box của VirusScan; bạn có khoảng trống hơn 4Gb (trên
lý thuyết) trong phần edit control. Nhất định phải đủ chỗ cho cả một shellcode lãng phí nhất.
Bước 3: Tiêm shellcode
Kế tiếp, hãy thử dán cái gì đó vào trong box. Ðúng là bạn có thể dùng right-click và chọn Paste,
nhưng để thoả mãn các tham số cứ nghĩ là chúng ta không thể làm như vậy được. Xoá edit box
của VirusScan và khởi động Notepad. Ðánh vài chữ vào trong Notepad, rồi chép những chữ này.
Trở lại Shatter, chúng ta muốn gởi VirusScan một thông điệp từ trong Clipboard (vừa chép ở
trên) đó là WM_PASTE. Cả hai tham số cho thông điệp này nên là zero, bởi thế phải chỉnh
WPARAM và LPARAM thành zero, để yên handle như vậy. Bấm vào WM_PASTE và xem những
chữ của bạn xuất hiện trên edit box của VirusScan. Bấm nó một lần nữa, hẳn phải có chữ của
bạn xuất hiện lần thứ hai. Vui nhỉ?
Nào, “quậy” vậy là đủ. Xoá edit box của VirusScan một lần nữa và khởi động hex editor của bạn.
Tải sploit.bin trong hồ sơ nén Shatter. Shellcode này lấy từ Jill (Hey, Dark Spyrit!),nó khởi động
một command shell tầm xa trở ngược lại bạn. Nó được hard-coded để gởi command shell đến địa
chỉ loopback tại port 123, lúc này là lúc thích hợp để khởi động một Netcat listener -5- trước khi
bạn quên. Khởi động một cmd (32-bit command line), đánh vào “nc -lp 123” và khỏi lo đến nó
nữa. Trở lại hex edit, chép shellcode vào clipboard, bạn nhất thiết phải chép trọn bộ shellcode
(bao gồm luôn FOON từ đầu - chúng ta sẽ cần đến nó trong vòng 1 giây). Trở lại Shatter và bấm
nút WM_PASTE lần nữa. Bạn hẳn thấy hàng loạt chữ quái đản trong edit box của VirusScan, đó
là shellcode của chúng ta đã được dán vào thành công.
Bước 4: Thi hành mã
Ðây là phần duy nhất của trọn bộ chu trình đòi hỏi kỹ năng. Khởi động debugger của bạn và đính
nó vào chu trình avconsol.exe (dùng lệnh WinDbg: s -a 00000001 10000000 “FOON”, bạn cũng
có thể dùng một debugger khác. Viết xuống vị trí bộ nhớ (memory location) nơi dòng hiển thị vị
trí bộ nhớ xuất hiện; có lẽ nó sẽ xuất hiện một vài lần, đừng hỏi tôi tại sao. Bất cứ dòng nào
cũng được. Trên system của tôi, shellcode xuất hiện ở 0x00148c28, giá trị này có lẽ không khác
mấy nếu bạn dùng cùng một phiên bản. Bây giờ tắt bỏ debugger, truy cập vào như guest và
chuẩn bị đón nhận chủ quyền LocalSystem. Theo từng bước 1 đến 3 lại, nhớ là mọi thứ vẫn làm
việc như guest. Ðừng quên Netcat listener để nhận shell.
Lúc này có lẽ bạn đang cho rằng thủ thuật đính vào một debugger là một thứ hoạt động mang
tính chủ quyền. Ðúng vậy. Tuy nhiên, tương tự như khi viết khai thác phần tràn bộ đệm (buffer
overflow), bạn có thể làm chuyện ấy trên bất cứ bộ phận nào của system; bạn chỉ cần tải địa chỉ,
địa chỉ này nhất định phải làm việc trên bất cứ system nào có cùng phiên bản. Thật ra bạn cũng
chẳng cần phải làm chuyện này. Hầu hết các ứng trình đều có điều dụng ngoại lệ (exception
handlers) (VirusScan nhất định phải có), bởi thế nếu các ứng trình có vi phạm acess violation,
chúng chỉ xử lý với violation này và tiếp tục hoạt động thay vì treo máy. Bởi thế, chẳng có gì
ngăn bạn dán vào vài trăm Kb NOPs rồi cứ lần dò qua bộ nhớ cho đến khi đụng đến đúng địa chỉ
và shellcode của bạn sẽ được thi hành. Chưa hẳn là đẹp mắt, nhưng chạy được.
Thông điệp cuối chúng ta sắp sửa tận dụng là WM_TIMER. Phần này hơi bất thường và khá nguy
hiểm vì nó có thể chứa (đại loại như tham số thứ nhì) địa chỉ của chức năng hồi khuyển timer
(callback function). Nếu tham số thứ nhì không phải là zero, phần thi hành sẽ nhảy thẳng đến vị
trí đã định. Ðúng vậy, bạn không đọc sai; bạn có thể gởi một thông điệp WM_TIMER tới bất cứ
window nào với trị giá không phải là zero cho tham số thứ nhì (tham số thứ nhất là timer ID) và
phần thi hành nhảy thẳng tới địa chỉ đó. Theo tôi biết, thông điệp chẳng đến xếp hàng trong dãy
thông điệp (message queue), bởi thế ứng trình này không có cơ hội để tránh thông điệp đó. Ngớ
ngẩn, ngớ ngẩn, ngớ ngẩn....
Vậy, bên trong Shatter, handle chắc phải được chỉnh dụng edit control của VirusScan để chứa
shellcode của chúng ta. Tham số thứ nhất có thể bất cứ giá trị gì bạn muốn và tham số thứ hai
nên dùng 512 bytes hoặc hơn địa chỉ chúng ta đã chọn ra từ debugger ở trên (chúng ta có 1K
NOP trước shellcode, bởi vậy chúng ta hẳn rớt ngay vào giữa các địa chỉ); trên system của tôi đó
là: 0x148c28 + 0x200 = 0x148e28. Bấm vào WM_TIMER và netcat listener của bạn sẽ thức dậy
với một command prompt. Một lệnh WHOAMI sẽ cho thấy bạn quả thật chuyển hóa từ guest
sang LocalSystem -6-. Enjoy.
Các kỹ thuật khác
Có một số cách khác hơn những bước chúng làm ở trên, sử dụng các cơ chế căn bản tương tự
nhưng có lẽ hơn phức tạp hơn. Thông điệp EM_GETLINE ra lệnh một edit control chép phần
chứa của nó tới một địa điểm định sẵn bên trong thông điệp. Làm cách nào để bạn viết một số
lượng tùy thích dữ liệu vào các địa chỉ trong bộ nhớ theo ý muốn? Bạn muốn khai thác dễ dàng
như thế nào? Chúng ta đã thấy các giới hạn có thể được loại bỏ từ phân độ của một edit control;
chuyện gì xảy ra khi một ứng trình phụ thuộc vào các giới hạn này? Khi một ứng trình dự phỏng
16 bytes dữ liệu từ một edit box có giới hạn 16 bytes, chúng ta có thể đánh vào một vài gigs.
Mọi người, đếm đến ba: 1...2...3.... Tràn Bộ Ðệm! Có lẽ trên stack nữa, bởi 16 bytes dữ liệu chắc
hẳn là không phải từ trữ liệu của bộ nhớ (heap). Hơn nữa, khi chúng ta gởi WM_TIMER đi, tham
số chúng ta ấn định cho timer ID bị đẩy vào trong chồng memory (stack) cùng với cả đống rác
rưởi khác. Ðể tìm một chức năng có thể tận dụng được chức năng của tham số thứ 3 và vô năng
của các tham số khác là điều không thể không mường tượng được, chức năng này cho phép
chúng ta nhảy thẳng vào khai thác chỉ bằng một thông điệp.
Nói về trữ liệu của bộ nhớ, có thêm một điểm ác liệt cho các loại khai thác như trên. Tổng quát
mà nói, các ứng trình thường tạo các dialog box trên trữ liệu của bộ nhớ trước khi những chế
hoạt trong bộ nhớ xảy ra; bởi vậy shellcode của chúng ta nằm ở dạng tĩnh (static). Kinh nghiệm
bản thân tôi thấy hiếm khi có hơn 20 bytes được chuyển dịch giữa các instances. Các địa chỉ tĩnh
có hoán chuyển cũng không thành vấn đề, nhưng, ai mà lo? Gởi một ứng trình một thông điệp
EM_GETLINE để nó viết shellcode của bạn đến vị trí bạn muốn (thật là quỷ quái, viết chồng lên
trữ liệu của bộ nhớ. Ai mà lo?) và rồi chỉ định cùng địa chỉ trong thông điệp WV_TIMER. Một
cách khai thác hoàn toàn không có NOP! Thật là nhộn.
Sửa lỗi
Rồi, kiểu khai thác này khá dễ dàng. Vậy mọi người làm sao sửa lỗi này? Tôi thấy có hai phương
pháp nhanh và ẹ sẽ phá vỡ hàng loạt chức năng khai thác và một giải pháp lê thê, giải pháp này
sẽ không bao giờ là một giải pháp trọn vẹn. Ðể tôi giải thích.
1. Không cho phép thiên hạ lược duyệt (enumerate) windows -7- Nguy hiểm. Ðổ vỡ hàng loạt.
Trên mặt lý thuyết mà nói, có thể chận đứng enumerate, tuy nhiên, bản thân tôi rất ghét khi
thấy thiên hạ cố công cố sức mà chẳng biết loại windows gì đang ở trên desktop khi họ cần.
2. Không cho phép các thông điệp chuyển dịch giữa các ứng trình với những chủ quyền khác
nhau. Ðiều này có nghĩa, bạn bạn không thể tương tác được với những window đang chạy trên
desktop không thuộc chủ quyền của bạn; cũng có nghĩa là ít nhất VirusScan (và có lẽ hầu hết các
tường lửa cá nhân nữa) cần được tái thiết.
3. Thêm chi tiết nguồn gốc vào các thông điệp, và tùy thuộc vào các ứng trình quyết định có xử
lý thông điệp hay không. Ðiều này cần một giải mở rộng (extension) trong Win32 API, và cả
đống công việc cho thiên hạ sử dụng. Lớn chuyện, và thiên hạn vẫn có thể làm sai như thường.
Hãy nhìn vào các điển hình tràn bộ đệm - loại lỗi này đã có hàng nhiều năm và giờ đây vẫn rất
phổ biến.
Căn bản mà nói, chẳng có một giải pháp đơn giản nào, đây là lý do tại sao Microsoft vẫn ếm kỹ
vấn đề này. Khổ nỗi, nếu tôi có khả năng tìm ra lỗi này, tôi dám bảo đảm những người khác
cũng đã tìm ra. Có thể họ đã không cho ai biết và lần sau, khi họ lẻn vào hệ thống của bạn như
một guest, bạn sẽ không biết đường trời nào tại sao họ có được LocalSystem từ guest. Tận cùng
mà nói, bạn luôn luôn cập nhật với các patches đó chớ?
Phụ lục: Tại sao đây là vấn đề?
Khi Microsoft thấy bản sao của bài viết này, họ gởi hồi âm cho tôi nói rõ là họ biết các loại tấn
công này, và họ không xếp loại những lỗi này thuộc dạng yếu điểm bảo mật. Tôi tin quan điểm
của họ là đúng. Có hai lý do Microsoft đưa ra a) loại khai thác trên đòi hỏi khả năng tiếp cận vô
giới hạn đến máy của bạn, hoặc b) loại khai thác trên đòi hỏi bạn chạy một thứ mã đen tối nào
đó trên máy. Tôi hoàn toàn đồng ý cả hai trường hợp, làm chủ máy thì dễ thực hiện khai thác
rồi. Tuy nhiên, họ bị lạc đề. Kẻ tấn công có thể dùng những kỹ thuật trên để gia tăng chủ quyền.
Nếu họ có thể truy cập vào ở mức độ là guest tới máy, những loại tấn công trên cho phép bạn
thu hoạch chủ quyền LocalSystem từ bất cứ account nào. Có ai đã từng nghe đến một món đồ
nghề nhỏ có cái tên là hk.exe không? Vậy còn ErunAsX (AKA DebPloit)? Và rồi iishack.dll? Tất cả
các đồ nghề này khai thác vài lỗi cho phép bạn gia tăng chủ quyền SAU KHI bạn đã truy cập
được vào máy. Tất cả những loại khai thác này đã được Microsoft nhận diện và patched.
Trường hợp máy desktop của công ty, thông thường những máy này được khoá kỹ. Người dùng
trên máy không làm được gì cho lắm bởi họ không được phép. Nếu máy đó bị hổng do Shatter
tấn công, người dùng có thể chiếm chủ quyền LocalSystem và làm gì tùy thích. Trường hợp
Terminal Server (hoặc Citrix) còn tệ hại hơn nữa. Thử mường tượng một công ty cung cấp khách
hàng các chức năng của dịch vụ terminal vì mục đích nào đó. Công ty đó nhất định KHÔNG cho
người dùng của họ bất cứ một chủ quyền “thật” nào cả. Sự tấn công của Shatter sẽ cho phép các
người dùng kia nắm giữ hoàn toàn máy chủ đó; chủ quyền LocalSystem cao hơn cả
Administrator, và trong một chia phân máy chủ (shared server) đó chính là vấn đề. Ồ, và nó
cũng chẳng đòi hỏi truy cập trên console nữa - Tôi đã thử tấn công một Terminal Server thành
công cách xa vài trăm dặm.
Một thực trạng đơn giản là Microsoft BIẾT rõ họ không thể sửa các lỗi này. Cơ chế sử dụng trong
Win32 API từ phiên bản NT 3.5 phát hành năm 1993 đã khá “tĩnh”. Microsoft không thể thay đổi
nó được. Cách duy nhất họ có thể chặn đứng những tấn công này là không cho phép các ứng
trình chạy trên desktop có chủ quyền cao hơn chủ quyền những người truy cập vào system.
Microsoft tin rằng desktop nằm trên biên giới bảo mật, và bất kỳ window nào trên desktop đó
được xếp loại là phi tín (untrusted). Ðiều này đúng, nhưng chỉ đúng đối với Windows, và cũng vì