ins'hack ctf – industrial process
(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}
.