ArunNetworkingPro
🖐️

Linux+ Break Room · Case 07

The invisible hand

The permissions say yes. Something you can't see says no.

A fortune teller program, breakroom-seer, can read one vision but not the prophecy, even though both files are readable by everyone. The church bell service is supposed to ring on port 99, but it isn't root, so the kernel won't let it. Meanwhile an ordinary-looking program can read any file on the system, secrets included. And the oracle only answers over HTTPS with a certificate from someone called Agatha, which expired long ago. Find the invisible hand.

Case 07 of 10SecurityTroubleshootingNeeds sudo

⚠️ This case needs sudo

Get the case

Rule 4: read it before you run it, less breakroom-mission-07.sh. The top of the file lists everything it creates and how --clean removes it.

curl -O https://arunnetworkingpro.com/labs/breakroom-mission-07.sh
sudo bash breakroom-mission-07.sh

At any point, ask the house how many ghosts are left:

sudo bash breakroom-mission-07.sh --check

Part A: the seer

A1

Permission says yes

ls -l /srv/breakroom-07/
breakroom-seer /srv/breakroom-07/vision.txt
breakroom-seer /srv/breakroom-07/prophecy.txt

Same permissions, different answer. Who said no? Find the evidence.

Hint

When permissions aren't the reason, a security layer is. AppArmor writes its refusals to the kernel log: sudo journalctl -k | grep DENIED (nothing there? try sudo journalctl -g DENIED). It names the program (profile=) and the file (name=). sudo aa-status lists the loaded profiles.

A2

Change the rule, keep the guard

Let the seer read the prophecy, while its profile stays in enforce mode. Turning AppArmor off isn't a fix.

Hint

Profiles live in /etc/apparmor.d/, named after the program's path. Look for a deny line: deny always wins over allow. After editing, reload just that profile: sudo apparmor_parser -r /etc/apparmor.d/usr.local.bin.breakroom-seer.

Part B: powers without root

B1

The bell won't ring

breakroom-07-bell keeps failing. Find why, and let it use port 99 without running it as root.

Hint

sudo journalctl -u breakroom-07-bell shows the error. Ports below 1024 need a capability, CAP_NET_BIND_SERVICE, not full root. Give it to the exact program the unit runs: sudo setcap cap_net_bind_service+ep <file>. (Or in the unit: AmbientCapabilities=CAP_NET_BIND_SERVICE.) Then restart and nc 127.0.0.1 99.

B2

A dangerous superpower

Somewhere on the system, an ordinary program can read every file, even /etc/shadow, for any user who runs it. Find it and take the power away.

Hint

sudo getcap -r / 2>/dev/null lists every file with capabilities. ping having cap_net_raw is normal. Something with cap_dac_read_search is not. sudo setcap -r <file> removes them.

Part C: the oracle's certificate

C1

Read the certificate

curl https://oracle.haunted:8443

curl refuses. Who is the certificate for, and when did it expire?

Hint

openssl s_client -connect oracle.haunted:8443 </dev/null | openssl x509 -noout -subject -dates. Or read the file itself: openssl x509 -in /etc/breakroom-07/oracle.crt -noout -subject -dates.

C2

A new certificate

Make a new self-signed certificate for oracle.haunted, valid for a year, put it where the oracle expects it, restart, and make curl trust it:

curl --cacert /etc/breakroom-07/oracle.crt https://oracle.haunted:8443
Hint

sudo openssl req -x509 -newkey rsa:2048 -nodes -days 365 -keyout /etc/breakroom-07/oracle.key -out /etc/breakroom-07/oracle.crt -subj "/CN=oracle.haunted" -addext "subjectAltName=DNS:oracle.haunted". Modern clients check the subjectAltName, not the CN. Then sudo systemctl restart breakroom-07-oracle.

✅ How you know you won

sudo bash breakroom-mission-07.sh --check says Verdict: 5/5. The invisible hand was Linux security all along. Case closed.

Answer key (no peeking until you've tried)

A1: the kernel log shows apparmor="DENIED" ... profile="/usr/local/bin/breakroom-seer" name="/srv/breakroom-07/prophecy.txt". A2: in /etc/apparmor.d/usr.local.bin.breakroom-seer, replace audit deny /srv/breakroom-07/prophecy.txt r, with /srv/breakroom-07/prophecy.txt r,, then apparmor_parser -r. B1: PermissionError: [Errno 13] binding port 99. sudo setcap cap_net_bind_service+ep /usr/local/lib/breakroom-07/python3, restart. B2: /usr/local/bin/breakroom-peek cap_dac_read_search=ep; sudo setcap -r /usr/local/bin/breakroom-peek. C1: subject=CN = agatha.beyond, notAfter=Feb 1 00:00:00 2025 GMT. C2: the openssl req -x509 ... command from the hint, then restart the oracle.

💥 Let it haunt you again

Run the script again. The haunting starts over, fresh:

sudo bash breakroom-mission-07.sh

Now do it without the hints. And when you're done for good: sudo bash breakroom-mission-07.sh --clean.

🧠 The ghost, explained

Verdict: not a ghost. The invisible hand was three real security layers above plain permissions: AppArmor, capabilities and TLS certificates.

Permissions are only the first bouncer. Mandatory access control (AppArmor on Ubuntu and Debian, SELinux on Red Hat and Fedora) adds a second list of rules per program, which even root can't ignore. When ls -l says yes but the program gets "Permission denied", check the kernel log for DENIED (AppArmor) or AVC (SELinux, with ausearch and sealert).

deny wins, and it's quiet. An AppArmor deny rule overrides any allow, and plain deny doesn't even log. That's why this case used audit deny: so there was evidence to find.

Capabilities slice root into small pieces. Binding a low port, sending raw packets, reading any file: each is its own capability. Giving a program exactly the one it needs beats running it as root. But a capability like cap_dac_read_search on a common tool is a backdoor, which is why getcap -r / belongs in every security audit.

A certificate is a signed name with an expiry date. Clients check three things: is it still valid, does the name (subjectAltName) match the address I asked for, and do I trust whoever signed it? Self-signed certs pass the third check only when you tell the client to trust them (--cacert).

← Case 06 · All cases · Case 08 → · Stuck, or found a better way? Email me

🎉 Got it, thank you!

Your comment just landed in my inbox. I read every one, and I'll reply by email.

🤔 That didn't go through

Something in the form looked off. Check your name, email and comment and try again, or just email me.

🐢 Whoa, slow down

That's a lot of comments in a short time, so the box is taking a breather. Try again later, or email me.

😴 The comment box is napping

My server is taking a quick break, so your comment couldn't be sent. Sorry! Please email me instead.

💬 Leave a comment

Stuck, found a better way, or just built it and want to brag? Tell me. It comes straight to my inbox (nothing is posted publicly), and I'll reply by email.

Your email is only used to reply to you. Never shared, never added to any list.