This past week I had a few moments to play the EKOPARTY CTF with Samurai and it was alot of fun. This post will detail some of the solutions for the ones I helped solve as well as a couple others I finished after the fact. The organizers did a good job providing a broad range of problem categories to test a wide range of infosec skills. The only category that seemed to be missing was cryptography, but there was a touch of it in some of the challenges.

Misc 100

This was the first challenge I took a look at and began by running the usual nmap scans to try and connect to it. After having no luck I saw a hint had been posted that hinted the service was running some custom protocol. After a little googling the clues finally made sense, the service was communicating via Datagram Congestion Control Protocol (DCCP). I located some proof of concept code in some of the documentation but was unable to connect my AWS VM to the service. Further documentation revealed that DCCP was unable to function behind NAT, so I spun up another VM on digital ocean as they do not NAT their IP addresses to the internet. Firing up the following code revealed the key,


import socket

socket.SOCK_DCCP                = 6
socket.IPPROTO_DCCP             = 33
socket.SOL_DCCP                 = 269
packet_size                     = 256
address                         = ('',20000)

# Create sockets
client = socket.socket(socket.AF_INET, socket.SOCK_DCCP, socket.IPPROTO_DCCP )
client.setsockopt(socket.SOL_DCCP, socket.DCCP_SOCKOPT_PACKET_SIZE, packet_size)
client.setsockopt(socket.SOL_DCCP, socket.DCCP_SOCKOPT_SERVICE, 'NULL')

# Connect sockets

# Echo
while True:
    client.send(raw_input('IN: '))
    print 'OUT:', client.recv(1024)

Misc 150

The next challenge in this category consisted of a webpage that asked for an IP address that it would send the flag to. Given the clue X-Flag, I assumed the solution had something to do with HTTP headers so I created an AWS VM to catch the requests sent by the webpage. Once I got the VM setup, I started up tcpdump and began sniffing the incoming traffic as I entered the VM’s IP address in the website. Watching the traffic I saw several connection attempts being made to port 6000. With this new information I deduced that the clue was actually referring to X11 and the solution likely involved setting up an X11 server for the challenge to send the flag to. With most cloud services offering headless linux servers, I decided to use SSH tunneling to connect a local Ubuntu Desktop VM running an X11 server to my AWS headless VM. This would forward the connection coming in from the challenge webserver through the AWS headless VM to my local Ubuntu Desktop X11 server. To pull this off I had to make a few changes to the default configuration files for each VM.

Ubuntu Desktop  –    /usr/share/lightdm/lightdm.conf.d/50-xserver-command.conf



Headless AWS Ubuntu Server  –  /etc/ssh/sshd_config

GatewayPorts yes

Once the above configuration changes were made I restarted the X server and sshd and connected the local VM to the AWS VM with the following ssh command.

ssh -R 6000: ubuntu@aws_ubuntu_ip

Putting the IP of the AWS host into the webpage caused the flag to be sent across the connection.

Misc 200

Opening up misc 200 revealed a RAR file that contained two files inside it, cwallet.sso and ewallet.p12. Researching these two files showed they were Oracle PKI containers for single sign-on authentication. A few more searches revealed a tool called ssoDecrypt that could be used to extract the password to open the container. Setting up the tool and running it against the wallet did not yield the key but instead output the following message:

PKCS12 key store mac invalid – wrong password or corrupted file.
PKCS12 key store mac invalid – wrong password, wrong LSSO secret (username + hostname) or corrupted file.

From the error message and reviewing the script, it appeared we needed to correctly guess the username and hostname of the system the wallet was created on. This part had me stumped for a while as I couldn’t determine how we were supposed to get this information. Luckily, cseagle noticed the username and hostname were listed in the RAR metadata from the beginning of the challenge. Adding these to the script returned our flag:


For 100

Reading over the title didn’t give any apparent clues right away until they gave a second hint, Hacker In Disguise Uses Sweet Bubbles. Since this made absolutely no sense, I picked up that it was a mnemonic, HID USB. Combining this clue and reviewing some HID USB documents, I found an HID USB scan code translation table that looked similar to the challenge text.

r2C +17 d17
rF0 r2C -17 u17
r33 +0B d0B
r43 +0C d0C
rF0 r33 -0B u0B
rF0 r43 -0C u0C
r1B +16 d16
rF0 r1B -16 u16 r29 +2C d2C
r43 +0C d0C rF0 r29 -2C u2C

Manually translating the code revealed our flag:


For 175

Opening up this challenge binary revealed a file called PhysicalDrive5.7z. Unzipping the file and running the “file” command on the PhysicalDrive5 file indentified it as a DOS/MBR boot sector. Mounting the file with

mount -o loop PhysicalDrive5 /mnt1

showed several image files and a System Volume Information directory. There didn’t seem to be anything particularly interesting about these files and nothing obvious showed up by looking at hex dumps of each. Since the challenge was a forensics one and we had been given a file system dump, I decided to open the file up with TestDisk. TestDisk and Photorec are extremely useful data recovery tools written by Christophe Grenier. Selecting the Undelete function in TestDisk showed the following:

Analyzing each of the additional files didn’t yield too much new information. This is one of the challenges I came back to after the end of the competition to solve. I decided that the main files I needed to study further were the recovered ones. Since the pictures were all generic drug related ones I decided to focus on the two documents. I ran several metadata extraction tools on the pdf and got nothing. After much inspection of the OFICIO.txt file I noticed an unnecessary number of spaces  and tabs at the end of each line. Counting each of these groups it appeared that there were exactly 8 at the end of each line. At this point I presumed that these characters likely represented a bit in an 8 bit byte. I quickly scripted up the following code to extract the characters and convert them to 1’s and 0’s, and then to their equivalent ascii character.

import binascii
import sys

f = open('OFICIO.txt', 'rb')

lines = f.readlines()
i = 0
out = ''

for line in lines:
 sub_str = line[-10:-2]
 sys.stdout.write(str(i) + ': ' )
 print binascii.hexlify(sub_str)

 sub_str = sub_str.replace('\x20', '0')
 sub_str = sub_str.replace('\x09', '1')
 out += chr(int(int(sub_str,2)))

 print sub_str
 i += 1
 if i > 75:

print out

Running the script gave us the flag:

Bob, I’ll METH you there, bring me this key: EKO{this_is_my_secret_message}

For 400

This challenge started with a pcap with what appeared to be data transactions to and from a USB storage device. My thought was that we needed to reconstruct the files on the drive by parsing these transactions. Looking at a few of the packets I could see that each read and write transaction was composed of two packets. The first packet identified the size of the transaction, the logical block address, and whether it was a read or write. The second packet was the data packet. Each logical block address identified 512 byte chunks. I put together a script that parsed out the read and write data packets and wrote it to a file based on the logical block addresses.

#!/usr/bin/env python

import dpkt
import struct
import binascii

f_out = open('outfile.bin', 'wb')
f = open('data.pcap')
pcap = dpkt.pcap.Reader(f)

usbms_start = 0x1b
flag = False

for ts, buf in pcap:
    #Get the id
    if( len( buf ) > usbms_start + 4 ):
      #Write data
      if flag: * 512)
	 flag = False
	 offset = 0;
      id_str = buf[usbms_start:usbms_start+4]
      #Get the length
      if( len( buf ) > usbms_start + 12 ):
	 len_str = buf[usbms_start+8:usbms_start+12]
	 packlen = struct.unpack('I', len_str)[0]
	 if( len( buf ) > usbms_start + 0x15 ):
	    #Get the offset
	    lba = buf[usbms_start+0x11:usbms_start+0x15]
	    offset =  struct.unpack('>I', lba)[0]
	    operation = ord(buf[usbms_start + 0xf])
	    if id_str == 'USBC' and (operation == 0x2a or operation== 0x28):
	       print '[+] Preparing to write %i bytes to offset %s' % (packlen, hex(offset))
	       flag = True

After running the script the file was identified as an NTFS filesystem. I mounted the filesystem with the following command:

mount outfile.bin /mnt1

The filesystem had several dozen rar files and a text file that said “not the answer”. Copying out the rar files and unrar-ing, we were presented with four image files. At this point I was once again stuck and was unable to complete this challenge before the end of the CTF. Revisiting this challenge later I read that RAR archives can preserve alternate data streams in NTFS file systems. Using powershell I ran

Get-Item -stream *

on each file to identify any alternate data streams. When I got to null.png I got the following output:

When I extracted the ADS with the following command, we got the flag.

Get-Content .\null.png -Raw -Stream NULL | Out-File -Encoding Default flag.jpg


This is the second year I’ve played EKOPARTY CTF and overall I thought it was quite entertaining. Team Samurai took 5th thanks to some talented teammates of mine.