corCTF 2024 - the-conspiracy

  • Author: jammy
  • Date:

the-conspiracy

Description: Our intelligence team created a chat app, and secretly distributed it to the lemonthinker gang. We’ve given you the application source and a capture taken by one of our agents - can you uncover their plans?

Solves: 283

Solution:

The challenge gives 2 files, challenge.pcap and source.py. Let’s start off by looking at the source code:

 1import random
 2from scapy.all import *
 3import csv
 4
 5sources, destinations, messages = [], [], []
 6
 7with open('chatlogs.csv', mode='r') as file:
 8    csv_reader = csv.reader(file)
 9    for row in csv_reader:
10        sources.append(row[0])
11        destinations.append(row[1])
12        messages.append(row[2])
13
14def encrypt(message):
15    messagenums = []
16    for character in message:
17        messagenums.append(ord(character))
18    keys = []
19    for i in range(len(messagenums)):
20        keys.append(random.randint(10, 100))
21
22    finalmessage = []
23    for i in range(len(messagenums)):
24        finalmessage.append(messagenums[i] * keys[i])
25
26    return keys, finalmessage
27
28for i in range(len(messages)):
29    finalmessage, keys = encrypt(messages[i])
30    print(finalmessage, keys)
31    packet1 = IP(src=sources[i], dst=destinations[i])/TCP(dport=80)/Raw(load=str(finalmessage))
32    send(packet1)
33    packet2 = IP(src=sources[i], dst=destinations[i])/TCP(dport=80)/Raw(load=str(keys))
34    send(packet2)

Breaking this down bit by bit, we see that the script first reads from a CSV file chatlogs.csv, which has source IPs, destination IPs, and the messages that are being sent. From here, these are then loaded into 3 lists: sources, destinations, and messages.

Next, we have an encrypt function. This function goes through each character in a message, converts it to its ASCII value, and then multiplies it by a random number between 10 and 100. The random numbers used to multiply are stored in a list called keys, and the final encrypted message is stored in a list of numbers called finalmessage.

Finally, the script goes through each message and encrypts it. It then creates 2 packets for each message: one with the encrypted message and one with the keys used to encrypt the message. These packets are then sent to the destination IP.

Now, let’s look at the pcap file. We can see from the script that that the pcap file contains a lot of TCP packets, sent between 3 IP addresses. (I’m using wireshark for the purposes of this writeup, but you can use any pcap viewer of your choice.)

wireshark

As hinted by the script, the messages are sent in pairs, so we can look at adjacent packets for messages and keys, as seen below with the TCP payloads in packets 7 and 8:

Packet 7: [6344, 1919, 3996, 7128, 9102, 1152, 4802, 8208, 4305, 6710, 5029, 5106, 3675, 10000]
Packet 8: [61, 19, 37, 66, 82, 36, 49, 76, 41, 61, 47, 46, 35, 100]

We can see that the first packet contains the encrypted message, and the second packet contains the keys used to encrypt the message. We can write a script to decrypt the messages, given we know the method use to encrypt them from the script:

1enc_message = [6344, 1919, 3996, 7128, 9102, 1152, 4802, 8208, 4305, 6710, 5029, 5106, 3675, 10000]
2keys = [61, 19, 37, 66, 82, 36, 49, 76, 41, 61, 47, 46, 35, 100]
3clean_message = []
4for i in range(len(enc_message)):
5    clean_message.append(chr(enc_message[i] // keys[i]))
6print(''.join(clean_message))

Running this script, we get the output “hello blinkoid”. This shows us that our method of decryption is correct, and we can now decrypt all the messages in the pcap file. To extract all payloads, you can use the below script:

1from scapy.all import rdpcap
2packets = rdpcap('challenge.pcap')
3for packet in packets:
4    if packet.haslayer('TCP'):
5        payload = bytes(packet['TCP'].payload)
6        if payload:
7            print(payload.decode('utf-8', errors='ignore'))

We can then write these to a file, and read off of the file and decrypt the messages, by dividing each encrypted character by its corresponding key. The final script to decrypt all the messages is below:

 1f = open("messagecodes.txt", "r")
 2collection = []
 3for x in f:
 4	line = x.strip('[]').split(',')
 5	final_in_line = line[-1].strip(']\n') # This is to remove annoying new line characters, that were the result of how I handled codes.
 6	clean_line = line[:-1] + [final_in_line]
 7	numbers = [int(num.strip()) for num in clean_line]
 8	collection.append(numbers)
 9
10for i in range(26): # This is because the length of collections is 52 - so we take half of this.
11	message = collection[i * 2]
12	keys = collection[(i * 2) + 1]
13	finalmessage = ""
14	for j in range(len(message)):
15		finalmessage += chr(int(message[j] / keys[j]))
16	print(finalmessage)

As a result, you get the output below:

hello blinkoid
hello night
how do we eliminate the msfroggers
idk i'll ask slice1
how do we eliminate the msfroggers
we can send them to the skibidi toilet
or we can deprive them of their fanum tax
slice1 is being useless
what's new
blinkoid? message back :(
oh errr... this sounds great! any more ideas
we could co-conspire with the afs
and get them to infiltrate the msfroggers
that way team lemonthink reins supreme
your a genius!
alright night
i have my own idea
let's hear it
so yk about the afs
if we send our secret code over to them
they can use it to infiltrate the afs
what's our code again?
i think it's corctf{b@53d_af_f0r_th3_w1n}
hey night did you hear my idea
you had an idea? blinkoid just told me you were being useless
what the sigma

Flag: corctf{b@53d_af_f0r_th3_w1n}

Bonus: Creating the challenge and unintendeds

The inspiration/theming for this challenge came from 2 things - the first being a Darknet Diaries episode (146) about the FBI building a backdoor into a chat app that was to be distributed to criminals, allowing them to read all messages being sent, and using this to catch criminals. The second was making “gangs” out of 3 of the emotes used in the CoR discord - the lemonthinkers, the msfroggers, and the afs. This emote theme was also used in other challenges, such as infiltration, and msfrogofwar3.

Secondly, a slight unintended solution which we discovered during testing was being able to run strings on the file, which gets the message codes and keys, without having to use scapy to extract it.

strings

In the end, we decided to keep this in, as the main complexity with the challenge was in decrypting the messages.