Challenge 07

7 - re_crowd

Hello,

Here at Reynholm Industries we pride ourselves on everything. It's not easy to admit, but recently one of our most valuable servers was breached. We don't believe in host monitoring so all we have is a network packet capture. We need you to investigate and determine what data was extracted from the server, if any.

Thank you

Ở bài này chúng ta có 1 file .pcap, dùng wireshark để xem file này:

Ở đây mình thấy gói số 7 có protocol là HTTP, nên mình sẽ extract các http object capture được như hình sau:

Sau khi extract hết ra, ta có được file “5c” là file nặng nhất (11 kb), mở nó lên bằng Google chrome, ta được một webpage về đoạn chat của một số nhân viên trong công ty.

Ở gần cuối của đoạn chat có 1 đoạn nói chuyện khá thú vị

Theo hình trên thì Jen đã lưu danh sách account ở “C:\accounts.txt”, nên mình đoán là, máy công ty đã bị nhiễm malware này, con malware này đã lấy cắp file “accounts.txt” và gửi về cho attacker.

Bây giờ ta sẽ vào phân tích các gói tin trong wireshark.

Ở trên là một gói tin được gửi đến server, phương thức PROPFIND cũng khá lạ so với mình, nên mình đã google để tìm PROPFIND và tìm được một số bài viết hay ho.

Trong loạt bài trên có phân tích rõ về lỗi Buffer Overflow dẫn đến RCE. Vì vậy ta chỉ cần tập trung vào đoạn shellcode bắt đầu từ “VVYAIAI…”.

Đoạn shellcode này chỉ bao gồm các kí tự chữ số, chữ thường và chữ in hoa nên mình nghĩ ngay tới Alphanumeric shellcode. Đoạn shellcode trên được tạo ra bằng thư viện alpha2.

Ta viết 1 đoạn code để decode đoạn shellcode này (lưu ý đoạn shellcode này là Unicode shellcode):

# python3

if __name__ == '__main__':
    with open('shell.txt', 'rb') as f:
        text = f.read().decode('utf-16le')
    text = text[text.find('ZBABABABABkMAGB9u4JB') + len('ZBABABABABkMAGB9u4JB')::]
    r = ''
    for i in range(len(text) // 2):
        x, y = ord(text[2*i]), ord(text[2*i+1])
        c, d = (x & 0xF0) >> 4, x & 0xF
        e, f = (y & 0xF0) >> 4, y & 0xF
        b = f
        a = d + e
        while a >= 16:
            a = a - 16
        r += chr((a<<4) | b)
    with open('dec.bin', 'wb') as f:
        f.write(bytearray([ord(i) for i in r]))
        print ('Done')

Trong đó, shell.txt chính là đoạn shellcode copy ra từ file .pcap.

Tiếp theo ta dùng blobrunner để chạy và debug đoạn shellcode mới.

blobrunner là tool dùng để chạy 1 đoạn shellcode, dùng để debug các shellcode dễ dàng hơn.

shellcode mới có 1 pattern lặp đi lặp lại rất nhiều (push arg, push arg, push hash, call ebp):

Chỗ “call ebp” sẽ nhảy đến hàm ở 0x10000006, hàm này sẽ tính hash của các hàm Windows API, so sánh với hash được push lên stack. Sau khi tìm được hàm có hash tương ứng, shellcode sẽ nhảy tới hàm Windows API đó qua lệnh “jmp eax”.

Đoạn shellcode này không dài quá, nhưng mình lười đọc nên đã làm như sau:

  • Breakpoint tại “jmp eax”.
  • Chạy shellcode.
  • Log lại những hàm và tham số được gọi để có cái nhìn tổng quát về chương trình.

Và thứ tự các hàm và tham số được gọi như sau:

  • LoadLibraryA , "ws2_32".
  • WSAStartup.
  • WSASocketA, AF_INET, SOCK_STREAM, …
  • WSAConnect, (random number) socket handle, sockaddr (family: AF_INET, port: 4444, IP: 192.168.68.21).
  • Đến đây chương trình không chạy nữa, lý do là vì nó cố gắng kết nối đến 192.168.68.21:4444 nhưng không có server nào ở địa chỉ này cả.

Ta thử tìm port 4444 trong wireshark.

Có 1 gói tin từ 192.168.68.21:4444 gửi về server với độ dài phần data là 1243 bytes.

Vậy ta dựng lại server để giả lập gói tin đó, làm lại 1 lần như trên, nhưng đến đoạn WSAConnect thì patch IP thành 127.0.0.1 để theo dõi tiếp xem nó gọi thêm hàm WinAPI nào.

Code server:

# python3
import socket
from binascii import a2b_hex

if __name__ == '__main__':
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('0.0.0.0', 4444))
    s.listen(5)
    c, v = s.accept()
    data = '9c5c4f52a4b1037390e4c88e...' # truncated
    data = a2b_hex(data)
    c.send(data)
    c.close()
    s.close()
    print ('[+] Done')

Chạy lại và quan sát danh sách các hàm WinAPI được gọi (chỉ liệt kê hàm được gọi sau WSAConnect):

  • recv, (random number above) socket handle, buffer, 4, 0.
  • VirtualAlloc, lpBase = 0, size = 0x4D7, MEM_COMMIT, PAGE_EXECUTE_READWRITE.
  • recv, (random number above) socket handle, buffer, 0x4D7, 0.
  • Đến đây chương trình lại không chạy nữa, đã đến lúc ta phải phân tích đoạn shellcode này.

Sau khi nhận 0x4D7 byte, thì dữ liệu đó được đưa vào trong hàm mà mình đặt tên là “decr” ở 0x10000143. Ta thử xem hàm “decr” này có gì.

Đoạn code trên chính là RC4.

Mẹo nhận biết RC4: nhìn thấy đoạn code tương tự như for (i = 0; i < 256; ++i) a[i] = i , thì gần như chắc chắn là RC4, đó chính là 1 trong các bước tạo key của RC4.

Trong đoạn code trên, dòng asm ở địa chỉ 0x10000144 chính là pattern nói ở trên.

Key ở lần RC4 này là “killervulture123” , được lấy ở thanh ghi esi (hình ở dưới):

Sau khi RC4 decrypt đoạn data, thì ta nhận được một đoạn shellcode mới, đoạn shellcode này cũng thực hiện việc dynamically resolve các function để phục vụ việc gọi các hàm WinAPI. Sau đó, nó thực hiện 1 số công việc hay ho:

Đầu tiên là CreateFile C:\\accounts.txt với quyền đọc (GENERIC_READ). Ta cũng fake file “C:\accounts.txt” để program có thể đọc.

Tiếp theo, ReadFile để đọc 0x100 bytes từ file này.

Sau đó đoạn data vừa được đọc bởi hàm ReadFile lại được đưa vào 1 hàm khác để mã hoá, và hàm này rất quen thuộc:

Không biết bạn đọc có thấy pattern "for (i = 0; i < 256; ++i) a[i] = i" trong đoạn trên không nhỉ ^^!.

Lần này key được push trên stack: “intrepidmango”.

Tiếp theo, một socket mới lại được tạo ra, connect tới 192.168.68.21:1337

Như vậy, nội dung của file sẽ được RC4 encrypt trước khi gửi về port 1337 cho attacker.

Tóm lại, luồng thực thi của chương trình như sau:

  • Connect tới 192.168.68.21:4444 để nhận 1243 bytes dữ liệu.
  • Dữ liệu vừa nhận được sẽ được giải mã RC4 bằng key “killervulture123”.
  • Đoạn dữ liệu vừa giải mã được chính là một shellcode.

Sau đó, đoạn shellcode trên sẽ được thực thi, luồng thực thi của đoạn shellcode như sau:

  • Đọc file “C:\accounts.txt”.
  • Dữ liệu vừa đọc sẽ được mã hoá RC4 bằng key “intrepidmango”.
  • Dữ liệu vừa được mã hoá sẽ được gửi đến server ở 192.168.68.21:1337.
  • Kết thúc.

Vậy việc cuối cùng cần làm là, lấy data được gửi (ở port 1337) trong wireshark ra, decrypt và lấy flag.

# python3
from binascii import a2b_hex
from Crypto.Cipher import ARC4

if __name__ == '__main__':
    key = b'intrepidmango'
    data = a2b_hex(b'truncated ...')
    rc4 = ARC4.new(key)
    d = rc4.decrypt(data)
    print (d)

Run:

b'roy:h4ve_you_tri3d_turning_1t_0ff_and_0n_ag4in@flare-on.com:goat\r\nmoss:Pot-Pocket-Pigeon-Hunt-8:narwhal\r\njen:Straighten-Effective-Gift-Pity-1:bunny\r\nrichmond:Inventor-Hut-Autumn-Tray-6:bird\r\ndenholm:123:dog'
h4ve_you_tri3d_turning_1t_0ff_and_0n_ag4in@flare-on.com

Fact: vì RC4 là thuật toán mã hoá đối xứng nên ta có thể lấy file bị mã hoá trong wireshark ra để làm giả file “C:\accounts.txt”, khi đó program sẽ mã hoá file này 1 lần nữa và cho ra luôn file gốc.

[+] Source code dùng để giải cho tất cả các bài nằm ở đây