Content-Type: text/plain

(Part of a series of writeups from INS'HACK CTF 2019.)

The challenge text reads:

In company XXX, we have a big expertise in laser cutting and we are well informed about cybersecurity. We have setup a small honeypot to simulate the cut of some pieces. In our fake process, we have manufactured 25 pieces of 1 meter by 1 meter.

We have found this really weird file thanks to a super effective detection tool. There was also a weird string:

893c539a84e6c96acf5f2ceea2ad9ef7be895580

The file provided is tempList.txt.

$ cat tempList.txt | wc -l
65088

$ head tempList.txt
Nice France
Málaga Spain
Fenerbahçe Turkey
Salzburg Austria
Nice France
Lyon France
Málaga Spain
Levadia Tallinn Estonia
Levadia Tallinn Estonia
Levadia Tallinn Estonia

Not particularly useful on its own.

Searching for the hash mentioned in the challenge text yields commit Cloakify@893c539.

CloakifyFactory - Data Exfiltration & Infiltration In Plain Sight; Convert any filetype into list of everyday strings, using Text-Based Steganography; Evade DLP/MLS Devices, Defeat Data Whitelisting Controls, Social Engineering of Analysts, Evade AV Detection

The meaning of the lines in tempList.txt is now clear. Let's download that exact version and decloak our file with it.

$ python cloakifyFactory.py
  ____ _             _    _  __        ______         _
 / __ \ |           | |  |_|/ _|       |  ___|       | |
| /  \/ | ___   __ _| | ___| |_ _   _  | |_ __ _  ___| |_ ___  _ __ _   _
| |   | |/ _ \ / _` | |/ / |  _| | | | |  _/ _` |/ __| __/ _ \| '__| | | |
| \__/\ | |_| | |_| |   <| | | | |_| | | || |_| | |__| || |_| | |  | |_| |
 \____/_|\___/ \__,_|_|\_\_|_|  \__, | \_| \__,_|\___|\__\___/|_|   \__, |
                                 __/ |                               __/ |
                                |___/                               |___/

             "Hide & Exfiltrate Any Filetype in Plain Sight"

                         Written by TryCatchHCF
                     https://github.com/TryCatchHCF
  (\~---.
  /   (\-`-/)
 (      ' '  )         data.xls image.jpg  \     List of emoji, IP addresses,
  \ (  \_Y_/\    ImADolphin.exe backup.zip  -->  sports teams, desserts,
   ""\ \___//         LoadMe.war file.doc  /     beers, anything you imagine
      `w   "

====  Cloakify Factory Main Menu  ====

1) Cloakify a File
2) Decloakify a File
3) Browse Ciphers
4) Browse Noise Generators
5) Help / Basic Usage
6) About Cloakify Factory
7) Exit

Selection: 2

====  Decloakify a Cloaked File  ====

Enter filename to decloakify (e.g. /foo/bar/MyBoringList.txt): tempList.txt

Save decloaked data to filename (default: 'decloaked.file'):

Preview cloaked file? (y/n default=n): n
Was noise added to the cloaked file? (y/n default=n): n

Ciphers:

1 - emoji
2 - geoCoordsWorldCapitals
3 - dessertsHindi
4 - evadeAV
5 - desserts
6 - dessertsPersian
7 - geocache
8 - statusCodes
9 - hashesMD5
10 - dessertsChinese
11 - worldFootballTeams
12 - starTrek
13 - dessertsSwedishChef
14 - dessertsRussian
15 - dessertsArabic
16 - worldBeaches
17 - skiResorts
18 - pokemonGo
19 - belgianBeers
20 - amphibians
21 - dessertsThai
22 - ipAddressesTop100
23 - rickrollYoutube
24 - topWebsites

Enter cipher #:

The only one that makes sense is worldFootballTeams.

Decloaking file using cipher:  worldFootballTeams

Decloaked file tempList.txt , saved to decloaked.file
Press return to continue...

Inspecting the output shows it's a packet capture (pcap) file.

$ file decloaked.file
decloaked.file: pcap-ng capture file - version 1.0

Using tshark we can inspect the packets.

$ tshark -r decloaked.file | tail
416 208.593571892     10.0.0.3 → 10.0.0.1     Modbus/TCP 81 Response: Trans:   209; Unit:   0, Func:   3: Read Holding Registers
417 209.595764935     10.0.0.1 → 10.0.0.3     Modbus/TCP 78    Query: Trans:   210; Unit:   0, Func:   3: Read Holding Registers
418 209.596582304     10.0.0.3 → 10.0.0.1     Modbus/TCP 81 Response: Trans:   210; Unit:   0, Func:   3: Read Holding Registers
419 210.598777593     10.0.0.1 → 10.0.0.3     Modbus/TCP 78    Query: Trans:   211; Unit:   0, Func:   3: Read Holding Registers
420 210.599567982     10.0.0.3 → 10.0.0.1     Modbus/TCP 81 Response: Trans:   211; Unit:   0, Func:   3: Read Holding Registers
421 211.601482968     10.0.0.1 → 10.0.0.3     Modbus/TCP 78    Query: Trans:   212; Unit:   0, Func:   3: Read Holding Registers
422 211.602300766     10.0.0.3 → 10.0.0.1     Modbus/TCP 81 Response: Trans:   212; Unit:   0, Func:   3: Read Holding Registers
423 212.604520340     10.0.0.1 → 10.0.0.3     Modbus/TCP 78    Query: Trans:   213; Unit:   0, Func:   3: Read Holding Registers
424 212.605493929     10.0.0.3 → 10.0.0.1     Modbus/TCP 81 Response: Trans:   213; Unit:   0, Func:   3: Read Holding Registers

This is a conversation between a monitor of some kind, and an industrial device (probably a laser cutter, per the challenge text). Every second the monitor requests the contents of the device's registers.

The interesting packets are the responses, which are the even frames.

$ tshark -Y 'frame.number == 2' -r decloaked.file -V | tail
    Register 0 (UINT16): 0
        Register Number: 0
        Register Value (UINT16): 0
    Register 1 (UINT16): 0
        Register Number: 1
        Register Value (UINT16): 0
    Register 2 (UINT16): 0
        Register Number: 2
        Register Value (UINT16): 0

The 3 register values correspond to x, y (the coordinates of a point), and i (the index of the sheet).

After iterating through even numbers and selecting the response packets from the capture file, and applying a little cleanup, the result is a very large object.

{
    "0": [{"x": 0, "y": 0 }, /* ... */ ],
    "1": [{"x": 0, "y": 0 }, /* ... */ ],
    /* ... */
    "24": [{"x": 0, "y": 125 }, /* ... */ ]
}

Plotting these points will reveal the flag, supposedly.

Emmet makes it very easy to generate a lot of canvases (or any element, for that matter). If you have the editor plugin, entering canvas.i$*25 and tab completing it should generate them in one go.

<style>
    canvas {
        width: 100px; /* scaled down by a factor of 10 */
        height: 100px
    }
</style>

<canvas class="i1"></canvas>
<canvas class="i2"></canvas>
<canvas class="i3"></canvas>
<canvas class="i4"></canvas>
<canvas class="i5"></canvas>
<canvas class="i6"></canvas>
<canvas class="i7"></canvas>
<canvas class="i8"></canvas>
<canvas class="i9"></canvas>
<canvas class="i10"></canvas>
<canvas class="i11"></canvas>
<canvas class="i12"></canvas>
<canvas class="i13"></canvas>
<canvas class="i14"></canvas>
<canvas class="i15"></canvas>
<canvas class="i16"></canvas>
<canvas class="i17"></canvas>
<canvas class="i18"></canvas>
<canvas class="i19"></canvas>
<canvas class="i20"></canvas>
<canvas class="i21"></canvas>
<canvas class="i22"></canvas>
<canvas class="i23"></canvas>
<canvas class="i24"></canvas>
<canvas class="i25"></canvas>

<script>
    function getContext(i) {
        return document.querySelector('.i' + (+i + 1)).getContext('2d')
    }

    for (let i of Object.keys(sheets)) {
        const points = sheets[i]
        const context = getContext(i)
        context.beginPath()
        context.moveTo(0, 0)

        for (let j = 0; j < points.length; j++) {
            // scaled down by a factor of 10
            context.lineTo(points[j].y / 10, points[j].x / 10)
        }

        context.stroke()
    }
</script>

Looping through each sheet, a path is drawn between each point. The result is sideways, and barely legible, but readable nonetheless: INSA{HACKINDUSTRIALPROCESS}.

plotted.png

#Code