Chiều 2/11:
Hôm nay tôi bận rộn kinh khủng nên không vào HVA. Mãi đến lúc gần tan sở, tôi mới log vào diễn đàn để lướt nhanh qua xem có mục gì mới và hấp dẫn không. Điều đầu tiên tôi nhận ra là truy cập đến diễn đàn HVA cực chậm. Tôi nhận được vài cái PM trong đó có hai cái từ JAL. JAL cho biết là diễn đàn bị DoS liên tục từ khuya hôm qua đến bây giờ, JAL cũng đã nhận định rằng các cú GET ở dạng
/forum/?&time=0.13435 có lẽ là các tấn công hiện đang xảy ra trên diễn đàn. Tôi vội log vào HVA server để xem xét tình hình.
Ngay khi tạo cái đuôi tail đầu tiên đến web server log của HVA, tôi choáng váng vì tốc độ dồn dập của loại GET trên. Bởi web server log không có khả năng tường trình các chi tiết cần thiết để đánh giá loạt tấn công hiện đang xảy ra, tôi chạy ngay một phát tcpdump và chọn option ra lệnh cho tcpdump ghi nhận đầy đủ hết tất cả các thông tin hàm chứa trong mọi tầng giao thức
-53-. Sau vài phút, tôi hối hả tải mớ "dump" này về laptop, tiện tay sao chép luôn cái log của web server. Sau đó, tôi điều chỉnh cho firewall giảm bớt số lượng connection cho phép để tạm giảm thiểu độ tải của server và phóng ngay ra cửa cho kịp chuyến tàu về nhà.
Trên tàu lửa, tôi mở đoạn dump vừa tạo ra để xem xét sự thể. Dán chặt đôi mắt vào màn hình của laptop, tôi thật sự choáng khi nhìn thấy nội dung của mớ tcpdump vừa lấy được lúc nãy. Tôi tự rủa mình không ngớt vì đã xem nhẹ mấy cái
/forum/?&time=0.xxxxx ngày hôm qua nhưng lại tự an ủi là đã thắt chặt connection limit trước khi rời sở. Dẫu HVA server không bị "xụm" đi chăng nữa thì bà con thành viên muốn vào diễn đàn cũng sẽ rất khổ sở vì số lượng GET theo dạng này có con số kinh khủng. Theo tính toán sơ khởi, tôi ước tính có chừng xấp xỉ trên dưới 1200 SYN request mỗi giây đến HVA server và các cú SYN này hoàn toàn hợp lệ vì nó hồi báo bằng cú ACK rồi tiếp tục dùng ASK-PSH để đẩy cú GET đến web server. Điều đáng nói là hơn một nửa request dạng này hoàn toàn không có "x-flash" header và cấu trúc HTTP header được gởi đến rất đơn giản:
Code: GET /forum/?&time=0.05536 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Host: quangvinh-vn.net
Connection: Keep-Alive
Có những điều đáng nói một cách tổng quát ở đây là:
1. Giá trị
/forum/?&time=x.xxxxx hoàn toàn ngẫn nhiên (random). Tôi cho rằng tính random ở đây có dụng ý để đánh lừa những cơ chế detect dựa trên pattern. Nếu một cơ chế cản lọc quá cụ thể cho từng URI như dạng /forum/?&time=0.05535 thì sẽ trở nên vô ích vì nó chẳng "match" gì cả và chúng có thể tự do đi vào.
2. Giá trị
Connection: Keep-alive lần này có tác dụng rõ rệt vì nó không cần mở nhiều sockets, nó chỉ cần dùng socket đã được mở và cứ thế mà gởi
/forum/?&time=x.xxxxx với giá trị x.xxxxx thay đổi ngẫu nhiên. Điểm ảnh hưởng nặng nề đối với firewall của HVA ở chỗ cứ mỗi IP chỉ cần 1 connection (trong 6 connection cho phép) đã đủ để liên tục gởi GET request. Giới hạn 6 connections ở đây trở nên thừa thãi nếu dựa trên nguyên tắc này. Thật tế cả 6 connection cho phép đã được tận dụng triệt để. Bởi thế mới có trên dưới 1200 SYN xảy ra trong 1 giây. Tôi không rõ giá trị "keep-alive" này được "chủ nhân" của chúng ấn định rõ ràng hay vô tình mà gán cho chúng. Nếu quả thật đây là một dụng ý cụ thể thì phải nói rằng khả năng cân nhắc và chuẩn bị cho việc tấn công HVA lần này khác rất xa những lần trước.
3. Các cú GET này không mang "x-flash" header cho nên nó qua khỏi snort và mod_security dễ dàng. Tôi không rõ là chúng có bị một proxy server nào đứng trước "lột" mất cái header hay không vì không có gì chứng mình điều này. Tuy nhiên, chuyện này rất có thể có vì việc thực hiện "anonymize"
-54- của proxy nào đó có thể "lột" các header đến mức độ không còn dấu tích gì (ngay cả dấu tích của chính proxy server thực hiện chuyện "lột").
4. Song song với các cú GET với URI "ngẫu nhiên" như trên, hàng loạt các cú GET đến cái banner swf cũng đồng thời xảy ra và chỉ có GET cái banner ấy mà thôi. Theo tôi, đây là đợt tấn công tổng hợp, liên hoàn, cố tình làm tê liệt mọi tài nguyên của máy chủ HVA: GET HVA banner để tạo load trên bình diện static image và chiếm socket, GET URI "ngẫu nhiên" để tạo load trên máy chủ trên bình diện tác động đến database.
Đối với một shared hosting server bình thường nào đó, chỉ cần 1/3 khối lượng "cơn lũ" này cũng đủ đưa server ấy đi nghỉ mát nếu cơ chế phòng thủ server này không chặt chẽ. Có lẽ bạn vẫn còn thắc mắc tại sao 4 điểm trên lại kinh khủng đến như vậy? Tôi sẽ cố gắng giải thích trong giới hạn cho phép như sau:
1. ảnh hưởng của URI ngẫu nhiên:
Một request ở dạng
/forum/?&time=0.05536 sẽ đi qua ba giai đoạn:
- qua FW: passed - bởi vì nó hoàn toàn hợp lệ
- qua snort: passed - bởi vì nó không match cái gì cả
- qua mod_security: passed - bởi vì nó không match gì cả luôn
Khi lên đến tầng web server, có mấy chuyện xảy ra:
- trước tiên web server vui vẻ tiếp nhận vì nó nhận thấy cú request này chẳng có gì sai cả, có lẽ request ở dạng thuộc phần hành php lo. Bởi thế, web server nhận lấy và chuyển nó đến php.
- php được giao cho request từ web server, nó bèn tiếp nhận. Để hoàn tất request này, nó bèn liên hệ database để xem cú GET này nên dùng "skin" nào để hiển thị.
- khi nhận ra là cú request này chẳng ăn nhập vào đâu, nó bèn tạo ra một trang mặc định với tài khoản guest rồi trả lại cho web server.
- bước kế tiếp, web server trả về kết quả cho trình duyệt nào gởi request, đồng thời tường trình access status 200 trong web log, cộng thêm dăm ba cái error 404 nếu như có vài cái gif nào đó không tồn tại (khi dùng một trang mặc định). Một cú GET như thế đi vào sẽ lưu lại trong log của web server như sau:
Code: xxx.xxx.xxx.xxx - - [02/Nov/2004:06:26:32 -0500] "GET /forum/?&time=0.13435 HTTP/1.0" 200 12933 "-" "Mozilla/4.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7)"
Bạn có thể thấy:
* status
200 trong dòng log ở trên biểu thị web server đã tiếp nhận cú GET.
* biểu thị
"-" đứng trước "Mozilla/4.0" cho thấy, đối với web server, cú request này đi trực tiếp từ bên ngoài mà chẳng hề được "refer" từ link của diễn đàn HVA cả.
Kết cục, một GET ngẫu nhiên hoàn toàn thành công trên phương diện tạo load cho máy chủ trên mọi tầng.
Hạn chế rõ rệt của URI ngẫu nhiên này chính là giá trị bất biến đứng trước dấu =. Đối với snort, detect giá trị này khá đơn giản, ở dạng HEX nó có giá trị:
2F 3F 26 74 69 6D 65 và nó thuộc vị trí offset 0040 - 0050 trong gói HTTP chứa cú GET này. Đối với mod_security, một regex (xem chú thích 42) có thể dễ dàng bắt và cản nó trước khi nó có thể đi sâu vào bên trong. Điều cần nêu lên ở đây là sáng kiến tạo ra giá trị ngẫu nhiên cho URI của "tác giả" của các cú GET nhằm phần nào gây khó khăn trong việc cản trở chúng. Nếu web admin chỉ dựa trên web log mà đánh giá sự thể thì sẽ khó lòng nắm bắt vấn đề.
2. ảnh hưởng của giá trị connection: keep-alive:
Giá trị keep-alive có tác dụng trực tiếp hỗ trợ những gì xảy ra ở phần 1 ở trên. Cứ mỗi cú truy cập thành công và dựa vào đòi hỏi "keep-alive" này, nó nắm giữ socket và liên tiếp gởi hết cú GET này đến cú GET khác xuyên qua một ESTABLISHED connection. Bởi lẽ có nhiều cú gởi từ một IP (IP của proxy server nào đó chẳng hạn) đến quá dồn dập, nên không hề có khoảng thời gian gián đoạn, bởi thế "keep-alive" hoàn toàn bền bỉ trong trường hợp này. Từ máy con, trình duyệt không cần phải tạo thêm connection mà vẫn đều đặn đẩy GET đến HVA server. Nếu mỗi connection có khả năng "keep-alive" và liên tục gởi hàng trăm cú GET thì giới hạn 6 connections sẽ cho phép chúng gởi hàng ngàn cú GET nếu mức độ bền bỉ quả thật... bền bỉ (xem chú thích 51). May thay, đường dẫn từ các gateway của các cú GET kia đến HVA server không ở tình trạng tuyệt hảo. Có những cú GET gởi đến, dẫu có ấn định "keep-alive", chúng vẫn bị gián đoạn vì bị nghẽn mạng ở đâu đó. Thời gian gián đoạn này dài hơn thời gian ấn định để "keep-alive" nên chúng phải tạo ra connection mới để tiếp tục đi vào HVA web server và mỗi lần gián đoạn xảy ra, dường như sự gián đoạn này tạo nên ảnh hưởng dây chuyền. Các cú GET khác đang nối đuôi đi vào cũng bị chậm lại và có những trường hợp hoàn toàn bị tan biến (dựa theo thông tin từ các tcp stream lấy được qua tcpdump). Mỗi khi phương tiện "keep-alive" này gián đoạn, chúng lại bị rơi vào vòng quản chế của firewall connection limit vì phải tạo socket mới.
Giả sử các cú GET trên không ấn định giá trị "keep-alive" và đặc biệt HVA web server không hỗ trợ tính chất "keep-alive" thì sao? Thì mỗi cú GET đi vào đều phải được tạo một socket riêng cho nó. Với giới hạn 6 connection limit, mỗi IP dùng để gởi cú HTTP GET này chỉ có thể gởi tối đa 6 cú GET trong bất cứ lúc nào. Tính chất "keep-alive" được hỗ trợ trên web server của HVA trong lúc này không ngoài lý do cải thiện vận tốc truy cập cho người dùng, mỗi cú "bấm" trên trình duyệt sẽ cho phép họ tải về trọn bộ một trang của HVA xuyên qua một socket đã được hình thành. Vì lý do "cải thiện" cho người dùng hợp lệ mà HVA web server phải rơi vào tình trạng "è lưng ra chịu đấm" của cơn DoS.
Hạn chế của ấn định "keep-alive" này ở đâu? Hiển nhiên là phụ thuộc vào sự hỗ trợ của web server. Để giới hạn nhiều cú GET đi xuyên qua một ESTABLISHED connection, "keep-alive" trên web server của HVA phải hoàn toàn tắt bỏ. Chỉ với cách này mới có thể "ép" và điều tác đúng theo quy chế connection limit từ firewall.
3. ảnh hưởng HTTP header không có "x-flash":
Bởi các cú GET ngẫu nhiên này hoàn toàn không có dấu hiệu gì thuộc về "x-flash" (tôi vẫn nghi ngờ đây là chuyện vô tình các x-flash bị proxy server nào đó anonymize), mọi cơ chế cản lọc của HVA hoàn toàn mở ngỏ cho chúng vào. Cái này cũng dễ hiểu vì chúng hoàn toàn hợp lệ trên bình diện giao thức. Điểm ảnh hưởng thứ 3 này hoàn toàn tương thích với hai ảnh hưởng trên. Tôi không thể tìm ra được một bằng cớ nào chứng tỏ các cú GET ở dạng không có "x-flash" header đi vào là cố tình không có header này hay vô ý bị proxy server nào đó "lột" mất. Ngay cả việc truy ngược lại IP gởi cú GET này cũng chỉ có thể tiết lộ các hops nó đã đi xuyên qua và thông tin thu lượm được quá mơ hồ để có thể xác định rõ ràng là nó được tạo ra một cách có chủ định hay không. Cho dù tôi có thể xác định được đường đi đến HVA server đã phải qua ít nhất là một proxy server nhưng để xác định nó có chức năng "anonymize" hay không thì không thể được. Dù gì đi chăng nữa, gần một nửa số lượng GET đi vào với URI ngẫu nhiên hoàn toàn không mang "x-flash" header và hiển nhiên các cơ chế cản trở hiện có trên HVA server không có tác dụng bao nhiêu và hiển nhiên chúng đã đóng góp không ít đến việc làm cho HVA server đứ đừ.
Nói về mặt hạn chế thì các cú GET không có "x-flash" header này ngay trước khi điều chỉnh lại firewall, snort, mod_security và một số cơ chế khác thì chúng không bị hạn chế gì cả. Những cú request này tương tự như các cú request "vô tội" đến từ trình duyệt của người dùng bình thường và mang theo thông tin sai sót trên thanh địa chỉ. Theo đúng nguyên tắc, web server phải tiếp nhận và thông báo đến trình duyệt nếu nó không thoả mãn được request (tường trình một error status nào đó). Kiện toàn được "cơn lũ" này ở hai điểm 1 và 2 ở trên tự nhiên sẽ khắc phục được ảnh hưởng thứ 3 này.
4. ảnh hưởng các cú GET đến HVA banner:
Tôi cho rằng các cú GET đến HVA banner (swf file) khá vô ích trong trường hợp này. Ấn định "keep-alive" này chỉ tạo một socket để lấy swf file và hiển nhiên proxy server nào đứng trước client gởi GET request này sẽ tự động dùng bản có trong cache để cung cấp cho client. Đối với HVA server, số lượng sockets được tạo ra cho cú GET này tạo ảnh hưởng "chen lấn" đến các cú GET với URI ngẫu nhiên ở trên chớ không mang lại một tác dụng gì rõ rệt. Thực tế, chúng còn tạo ảnh hưởng đối nghịch với các cú GET với URI ngẫu nhiên vì chúng làm giảm cơ hội mở thêm socket của các cú GET với URI ngẫu nhiên. Đặc biệt khi phương tiện "keep-alive" không thể duy trì vì đường dẫn xấu hoặc bị gián đoạn vì lý do nào đó. Nói về mặt ảnh hưởng thì các cú GET banner chẳng là gì so với các cú GET với URI ngẫu nhiên. Lý do:
- mỗi cú GET lấy static image đối với server chỉ là một cú request đơn giản: client hỏi, server trả lời, hoàn tất là phủi tay. Nếu client hỏi tiếp, server trả lời tiếp và cũng cùng tấm hình static này sẽ tiếp tục đi xuyên qua socket đang có sẵn. Dịch vụ ở dạng này hoàn toàn static và ở giới hạn đơn tầng.
- mỗi cú GET dùng URI ngẫu nhiên kia không ấn định cụ thể nó cần "lấy" cái gì nhưng lại tạo ra ảnh hưởng đa tầng (như đã phân tích ở phần 1 phiá trên).
Sau khi phân tích khá kỹ lưỡng các điểm lợi hại, tôi quyết định hình thành sẵn các "rules" để đối phó. Tiếc thay, một giờ mười lăm phút trên tàu lửa trôi qua nhanh chóng. Tôi đã đến nhà, đành phải xếp laptop lại và tiếp tục sau bữa tối vậy.
Tối 2/11:
Ăn tối xong, tôi hăm hở mở laptop ra. Tôi đang ở đâu nhỉ? À, phần snort signature dang dở. Phần này tôi không mất quá nhiều thời gian, chỉ phải đưa lên HVA server và chạy thử rồi chỉnh ngay tại chỗ thôi. Tuy nhiên tôi cần hoàn tất nó rồi chỉ cần "cut & paste" lên server cho nhanh. Tôi dành thời gian để hình thành vài cái filters cho mod_security vì theo tôi, đây là cửa ngõ tối hậu sau khi các cú GET có URI ngẫu nhiên đã lọt qua khỏi quy định connection limit của firewall. Rất tiếc tôi không thể đưa các filter này vào bài viết vì tính nhạy cảm của nó. Bạn chỉ cần biết tinh thần cản lọc được áp đặt để tạo hiệu năng cao nhất là đủ.
Chưa hoàn tất điều tôi muốn thì...
bíp bíp bíp. Đúng là "phước bất trùng lai, hoạ vô đơn chí", tôi nhận được cú gọi khẩn cấp từ sở vì một server quan trọng ngưng hoạt động. Tôi lẩm nhẩm:
"sorry hva, work comes first!". Tôi logoff HVA server, logon VPN của sở trong khi trận DoS vẫn tuôn vào ào ạt. Tôi biết chắc "cơn lũ" này không đủ để "giết chết" HVA server nhưng chắc chắn rất có nhiều người đang không thể vào diễn đàn. Tôi phải giải quyết công việc của sở xong rồi mới quay lại.
Hơn ba giờ đồng hồ trôi qua, rốt cuộc tôi cũng đã hoàn tất công việc ở sở, tìm ra, khắc phục được sự cố và đưa máy chủ bị ngưng hoạt động kia trở lại bình thường. Tôi lẩm bẩm:
"mẹ khỉ, ngày mai lại mất cả giờ đồng hồ để viết tường trình nữa rồi."
Tôi vội vã log vào HVA server. Chậm quá! Cực kỳ chậm! sau hai lần thử tôi mới có thể log vào được. Console hiển thị các thông tin chậm như thể nó đang chiếu chậm một đoạn phim nền đen chữ trắng. Tôi cảm thấy mệt mỏi lắm, đã quá nửa đêm sau một ngày làm việc dài dằng dặt từ 7 giờ sáng. Tôi quyết định chỉ tắt bỏ ấn định "keep-alive" trên HVA web server để khắc chế điểm đã phân tích trong phần 2 ở trên. Tái khởi động web server mất hơn ba phút vì có quá nhiều processes và connections còn đang hoạt động. Web server phải đợi cho từng connection hoàn tất mới tắt bỏ và khởi tạo mother process / child processes từ đầu.
Cố gắng nán thêm vài phút để xem server log, tôi hài lòng khi thấy tình hình được cải thiện rõ rệt sau khi "keep-alive" không còn tác dụng nữa. Server load xuống thấy rõ và truy cập vào diễn đàn nhanh hơn. Tôi vương vai, ngáp dài rồi logoff khỏi HVA server. Tôi lẩm bẩm:
"phải đi ngủ không thì ngày mai dậy không nổi. Có chuyện gì thì ngày mai tính tiếp."
Các bạn có thể theo dõi tiếp phần 10 tại http://hvaonline.net/hvaonline/posts/list/211.html
Chú thích:
-53- tcpdump cho phép sniff và tường trình các gói tin ra vào ở nhiều chế độ. Chế độ tôi đã dùng để bắt lấy các gói tin lần này là dùng snaplen có giá trị bằng 0 (-s0):
# tcpdump -s0 port 80 -w /tmp/dump
Căn bản mà nói, snaplen là số bytes của dữ liệu của mỗi gói tin. Theo mặc định, snaplen có giá trị là 68 bytes (riêng SunOS có giá trị tối thiểu là 96 bytes). Với giá trị mặc định nhỏ bé này, nếu không xác định đúng snaplen thì gói tin được capture sẽ bị "xén" (truncated). Bởi thế,
-s0 ấn định sẽ dùng bất cứ giá trị nào thích hợp để bắt lấy trọn gói tin. Chỉ nên dùng chọn lựa này trong trường hợp cần thiết vì hồ sơ "dump" được tạo với chọn lựa này sẽ rất lớn.
-54- anonymize là "vô danh hoá", xem thêm chú thích 52.