Down The Rabbit Hole
- Category: Forensics
- 150 Points
- Solved by the JCTF Team
Description
An Excel file was attached.
Solution
Let's start by inspecting the Excel Workbook. If we open it with a dedicated program, we get one sheet named "Sheet1" saying "Find the hidden sheet".
We can right-click and select "Show sheet" to view hidden sheets:
Sheet2 says:
And Sheet3:
Excel files are actually just zip archives. We can extract them and look inside.
┌──(user@kali)-[/media/…/Down_The_Rabbit_Hole/out/xl/worksheets]
└─$ ls -al
total 37
drwxrwx--- 1 root vboxsf 0 Nov 10 23:19 .
drwxrwx--- 1 root vboxsf 0 Nov 10 23:21 ..
-rwxrwx--- 1 root vboxsf 9309 Nov 10 22:52 sheet1.xml
-rwxrwx--- 1 root vboxsf 9378 Nov 10 23:09 sheet2.xml
-rwxrwx--- 1 root vboxsf 9458 Nov 10 23:19 sheet3.xml
-rwxrwx--- 1 root vboxsf 48 Nov 10 22:41 sheet4.xml
Sheet4 says:
┌──(user@kali)-[/media/…/Down_The_Rabbit_Hole/out/xl/worksheets]
└─$ cat sheet4.xml
Now go back and find the super DUPer hidden rows
If we check inside the XMLs, we can see that some rows have duplicate entries, e.g.:
<row r="106" spans="1:1" x14ac:dyDescent="0.25">
<c r="A106">
<v>73</v>
</c>
</row>
<row r="107" spans="1:1" x14ac:dyDescent="0.25">
<c r="A107">
<v>97</v>
</c>
</row>
<row r="108" spans="1:1" x14ac:dyDescent="0.25">
<c r="A108">
<v>65</v>
</c>
</row>
<row r="109" spans="1:1" x14ac:dyDescent="0.25">
<c r="A109">
<v>73</v>
</c>
</row>
<row r="109" spans="1:1" x14ac:dyDescent="0.25">
<c r="A109">
<v>86</v>
</c>
</row>
<row r="110" spans="1:1" x14ac:dyDescent="0.25">
<c r="A110">
<v>60</v>
</c>
</row>
<row r="111" spans="1:1" x14ac:dyDescent="0.25">
<c r="A111">
<v>108</v>
</c>
</row>
Row 109
is duplicate, the first value is 73
which is ASCII for I
. We locate the duplicates to get the flag.
This can be done programmatically:
import xml.etree.ElementTree as ET
from pathlib import Path
namespaces = {
"t": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
}
flag = ""
for sheet in Path('out/xl/worksheets/').glob('sheet*.xml'):
try:
tree = ET.parse(sheet)
dups = {}
for row in tree.findall('.//t:row', namespaces):
row_num = row.attrib["r"]
row_val = row.find("t:c/t:v", namespaces).text
if row_num in dups:
flag += chr(int(dups[row_num]))
else:
dups[row_num] = row_val
except ET.ParseError:
pass
print(flag)
Output:
┌──(user@kali)-[/media/sf_CTFs/intent/Down_The_Rabbit_Hole]
└─$ python3 solve.py
INTENT{u_f0und_w0nd3rl2nd}