Gửi các HTTP header bằng Flash ================================================ Mã ActionScript 2.0 dưới đây sẽ thực hiện việc gửi một request dạng GET (giả sử đến website http://www.vuln.site/some/page.cgi?p1=v1&p2=v2) cùng với một HTTP header bất kì (Foo:Bar). Mã này hoạt động trong Flash 7 và 8 (cũng như là Flash 6) Code: var req:LoadVars=new LoadVars(); req.addRequestHeader( "Foo", "Bar" ); req.send( "http://www.vuln.site/some/page.cgi?p1=v1&p2=v2", "_blank", "GET" ); Đoạn mã dưới đây cũng tương tự như trên nhưng với request dạng POST (cùng URL và tham số là a=b&c=d) Code: var req:LoadVars=new LoadVars(); req.addRequestHeader( "Foo", "Bar" ); req.decode( "a=b&c=d" ); req.send( "http://www.vuln.site/some/page.cgi?p1=v1&p2=v2", "_blank", "POST" ); (chú ý: phương thức LoadVars.decode() được bổ sung trong Flash 7, nếu không dùng phương thức này ta cũng có thể tự thực hiện, ở Flash 6 thì làm khác nhau một chút). Đối tượng Flash nằm trong trình duyệt thực hiện việc gửi request, đồng thời nó cũng gửi kèm theo cookie (giống như trình duyệt vẫn làm khi gửi request). Flash cũng gửi User-Agent của trình duyệt cùng với mọi header chuẩn khác. Nó cũng hỗ trợ liên kết HTTPS. Các công việc trên đã thực hiện thành công ở Microsoft IE 6.0, Microsoft IE 6.0 SP2 và Firefox 1.5.0.4, các trình duyệt đó đều chạy Flash 8.0.22.0 và Flash 7.0.19.0 Trong IE, lời gọi hàm addRequestHeader sẽ thay thế giá trị các header thông thường của trình duyệt, bao gồm cả Referer và User-Agent. Trong Firefox 1.5.0.4, khi dùng các header đó trong hàm addRequestHeader thì chúng (header) sẽ được đưa vào cuối sector header của HTTP request Code: // tạo một header UA trong IE 6.0 SP2 và 2 header UA trong FF 1.5.0.4 req.addRequestHeader("User-Agent","Hacker/1.0"); // tạo một header Referer trong IE 6.0 SP2 và 2 header Referer trong FF 1.5.0.4 req.addRequestHeader("Referer","http://somewhere/"); Cũng có thể thay giá trị của nhiều header quan trọng khác trong IE bằng cách thêm dấu hai chấm ( : ) sau tên header (kĩ thuật này sẽ mô tả ở [3] trong nội dung XmlHttpRequest) Code: req.addRequestHeader("Host:","foobar.site"); Tuy nhiên cách này sẽ không làm được trên Firefox 1.5.0.4 Chú ý: khi URL truy cập đến là nằm trong cùng domain với Flash, ta có thể dùng LoadVars để đọc dữ liệu HTTP response, thấy rõ rằng cách dùng LoadVars là điểm cơ bản trong nhiều framework dạng AJAX mà dựa trên Flash (tựa như đối tượng XmlHttpRequest trong Javascript) The security implications ======================== Khả năng một kẻ tấn công buộc trình duyệt của nạn nhân gởi HTTP request chứa các header bất kì đến các site khác đã tạo ấn tượng mạnh đến sự hiểu biết về bảo mật ứng dụng web của chúng tôi - kể cả việc đánh giá hiện tượng liên quan bảo mật lẫn tính minh xác của một số cơ chế bảo mật. Điều quan trọng là phải hiểu những cách tấn công được mô tả ở đây không phải là tấn công cross-site scripting, không phải dạng cross-site-scripting (nói một cách chính xác) mà cũng chẳng phải dạng xâm phạm tín thực liên vùng (cross-domain trust) trong object của Flash hoặc giữa các Flash object và các trang nhúng HTML Sự thật thì đối tượng Flash có thể gửi request đến bất kì URL nào và với bất kì HTTP header nào mà kẻ tấn công muốn. Vấn đề chính là điều này, nó cho phép kẻ tấn công gửi một liên kết (đến một trang HTML có nhúng đối tượng Flash, hay đến thẳng đối tượng Flash nằm trên website của kẻ tấn công), liên kết này cho phép đối tượng Flash sẽ chạy trên trình duyệt của nạn nhân. Chính đối tượng Flash này sẽ gửi HTTP request (cùng với các HTTP header mà kẻ tấn công đã chọn trước) đến website mong muốn và điều này sẽ làm hại đến trình duyệt (của nạn nhân) Nói cách khác, nhiều nhà phát triển phần mềm (và cũng có thể là các nhà nghiên cứu bảo mật) luôn cho rằng hầu hết các HTTP header không thể bị kẻ tấn công thay đổi bằng một giá trị bất kì. Bài viết sẽ chỉ ra sự sai lầm và cũng là lỗi trong cách hiểu này VD 1: header "Expect" Trong [4], một lỗi nằm trong trong Apache 1.3.34, 2.0.57 và 2.2.1 xem là nguy hiểm vì có thể chèn dữ liệu HTML vào trong header Expect. Tại [5], những lời góp ý chân thành, liên quan đến "Nói về XSS, hiện tượng này không phải là XSS như đã thấy. Trừ phi ai đó cho tôi thấy rằng làm thế nào để làm cho trình duyệt gửi header Expect đến website mong muốn". Nhưng khi dùng đối tượng Flash, việc tấn công trở nên dễ dàng. Hãy xem khi nạn nhân click vào link dưới đây: http://www.evil.site/attack.swf URL này là một đối tượng Flash, đối tượng này sẽ chạy các ActionScript sau Code: var req:LoadVars=new LoadVars(); req.addRequestHeader( "Expect", "<script>alert('gotcha!')</script>" ); req.send( "http://www.target.site/", "_blank", "GET" ); ActionScript này gửi một request từ trình duyệt nạn nhân đến website www.target.site, trong request có header Expect chứa mã HTML (Javascript) gây hại. Nếu website bị tấn công (www.target.site) chạy phiên bản Apache bị lỗi, kết quả tiếp theo sẽ là cross-site scripting. Vì thế đã rõ ràng - Apache phiên bản 1.3.34, 2.0.57 và 2.2.1 sẽ bị tấn công XSS (cùng với trình duyệt là IE hay Firefox và hỗ trợ Flash 6, 7). Trong [5] cũng lưu ý với Aapche 2.0/2.2, server trả về XSS response chỉ sau khi hết thời gian timeout của request. Xin nói thêm là Apache đã sửa lỗi này trên cả 3 phiên bản trên VD 2 - CSRF và Referer Tấn công CSRF (Cross Site Request Forgery) là kĩ năng cần thiết để một kẻ tấn công buộc trình duyệt nạn nhân thực hiện một thao tác (gửi HTTP request) đến website mong muốn. Khái niệm này được nói đến một vài lần, đầu tiên là trên mailing list Zope (với tiêu đề "Client Side Trojans", [6], và một lần khác tại BugTraq, dưới tiêu đề này, bây giờ nó đã trở thành tên bán tiêu chuẩn, CSRF [7]. Cả 2 bài góp ý trên, đều dựa trên header Referer như là dấu hiệu để trình duyệt sinh ra HTTP request có liên kết bắt nguồn từ trong website. Thật ra, nếu chỉ dùng HTML và Javascript thì không thể giả mạo Referer (trừ ngoại lệ trong [8], nhưng điều này chỉ hiệu quả trong một số điều kiện giới hạn; như khi sử dụng HTTPS thì không hiệu quả). Tuy nhiên, rõ ràng rằng với Flash thì đièu này không thành vấn đề, và Referer có thể bị giả mạo để yêu cầu các tài nguyên HTTPS, mọi thứ trong khi trình duyệt gởi cookie của site với request. Như phía trên đã minh hoạ, cả request dạng GET (với host và query bất kì) và POST (với phần host, query và phần thân theo định dạng chuẩn Content-Type là "application/x-www-form- urlencoded") đều gửi đi được. Chú ý: đoạn phân tích trên không có nghĩa là chỉ có header Referer là bị giả mạo. Nên thấy rõ rằng bất kì header nào cũng có thể bị giả mạo. Trong trường hợp của IE, kẻ tấn công tạo các header giả để thay đổi header của trình duyệt (như là Referer). Với Firefox, kỹ thuật giả mạo Referer không thể làm được vì Firefox thêm header vào bên dưới cùng của các header HTTP request. Tuy nhiên, một số ứng dụng web vẫn có thể vẫn dùng giá trị cuối của header, và như thế vẫn có thể bị nguy hiểm đối với kỹ thuật này Hiện nay, mọi khả năng tấn công cross-site scripting qua các header HTTP request (Referer, User-Agent, Expect, Host, Content-Type) đều làm được. Flash 9 ============== Flash 9 được thông báo vào ngày 28/06/2006 [9]. Ở Flash 9, những kĩ thuật đã nói trên (như dùng lớp LoadVars) không hỗ trợ các header mà trình duyệt dùng (như User-Agent, Host and Referer) và các header được bảo vệ như Content-Length. Tuy nhiên, các header như Expect vẫn được gữi đi và một vào kiểu tấn công (như VD 1 trên) vẫn hiệu quả trong Flash 9 Những giới hạn =========================== * URL và phần body luôn ở dạng URL-encoded (URL được mã hoá). Tức là không thể cho các kí tự SP, HT, CR và LF (và nhiều kí tự khác) hiển thị ở dạng raw trong request URL và body. * Chỉ có thể dùng GET và POST * Trong IE, chỉ có header của mỗi lần truy cập được gởi một lần * Tổng quát, không thể điều chỉnh hoàn toàn section header (kẻ tấn công sẽ gặp rắc rối khi muốn gửi những kí tự đặc biệt trong các header) Giải pháp đơn phần =============== Hãy chú ý hạn chế đầu tiên của kĩ thuật này - kí tự CR và LF không thể đặt trong section body ở định dạng raw. Nghĩa là cơ chế này không thể dùng để gửi request POST mà phần body của request này được biên dịch thành content-type là "multipart/form-data" (định dạng này dùng các kí tự CR,LF ở dạng raw để đánh dấu phần header với body). Nói cách khác, một Flash player không thể gửi đi POST request mà phần body của nó là một luồng "multipart/form-data" hợp lệ được. Vì thế, tác giả của những ứng dụng web có thể dùng các HTML form và đặt thuộc tính ENCTYPE là "multipart/form-data", điều này đảm bảo rằng thông tin đưa lên web với body multipart/form-data hoàn toàn hợp lệ. Một khi dùng cách này thì ta có thể biết được request đi đến có xuất phát từ Flash player hay không, và việc phá hoại sẽ trở nên vô tác dụng. Giải pháp này dĩ nhiên hơi áp đặt - cả trang HTML và script phải được sửa đồi để dùng multipart/form-data. Với vài nền tảng phát triển web, điều này là bình thường, còn với số khác (raw Perl, ASP) thì không. Hơn nữa, trong trường hợp như VD 1 trên, web server (Apache) thông dịch rồi sử dụng các HTTP header, và tiến trình này sẽ không đi đến lớp ứng dụng web, vì vậy trong những trường hợp như thế, giải pháp này không áp dụng được . nó là một luồng "multipart/form-data" hợp lệ được. Vì thế, tác giả của những ứng dụng web có thể dùng các HTML form và đặt thuộc tính ENCTYPE là "multipart/form-data", điều. gửi request POST mà phần body của request này được biên dịch thành content-type là "multipart/form-data" (định dạng này dùng các kí tự CR,LF ở dạng raw để đánh dấu phần header với. IE 6.0 SP2 và 2 header UA trong FF 1.5.0.4 req.addRequestHeader("User-Agent"," ;Hacker/ 1.0"); // tạo một header Referer trong IE 6.0 SP2 và 2 header Referer trong FF 1.5.0.4