Description
Can you read the flag? I think you can!
Setup
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Check sudo permissions
ObservationThe description hints you can read the flag, and gives you SSH access. That is privilege escalation, so start by listing the sudo rules already in place for your user.Runsudo -l. The first move on any sudo box. If a NOPASSWD rule exists for any binary that can shell out (vim, less, find, awk, perl, emacs), GTFOBins has a tested escape sequence.bashsudo -lWhat didn't work first
Tried: Check /etc/sudoers directly with cat /etc/sudoers to read the rules yourself.
/etc/sudoers is root-owned and root-readable, so a non-root user gets permission denied. sudo -l asks sudo itself and prints the rules that apply to you, with no read access to the file needed.
Tried: Run sudo su or sudo bash immediately to get a root shell before checking permissions.
When the rule grants specific binaries rather than ALL or a shell, sudo su and sudo bash both fail outright. Enumerate first and find out exactly which binaries are permitted.
Learn more
sudo reads rules from
/etc/sudoersdescribing which user can run which command as which target user, with or without a password.sudo -lprints the rules that apply to you. Always run it first.The classic misconfigurations: text editors (vim, emacs, nano), interpreters (python, perl, ruby), file utilities that shell out (find with
-exec, awk withsystem()), pagers (less, man), and the NOPASSWD directive which makes the escape silent.Always go to GTFOBins first. It is the canonical, version-tested catalog of Unix binaries that bypass local restrictions when invoked with elevated privileges, and the listed escape sequence almost always works on the first try. Memorizing escapes is a waste of time; reading GTFOBins is not.
Step 2Escape to a root shell from emacs
Observationsudo -l shows a NOPASSWD rule for emacs. GTFOBins has the escape: an interactive terminal inside emacs, or a headless batch eval, either of which gives a root shell with no password.Pick the right escape. Interactive (M-x term) if you want a stable root shell to poke around in - good for learning, good when you control the terminal. Non-interactive (--batch --eval) if the environment is restricted (no PTY, no proper terminal allocation, automation pipeline) - dumps the file in one shot and exits.bashsudo emacsbash# Inside emacs:bash# Press Alt+X (M-x), type 'term', press Enterbash# Then at the terminal prompt: cat /home/ctf-player/flag.txtbash# Non-interactive alternative:bashsudo emacs -Q -nw --eval '(term "/bin/bash")'bash# Or direct file read:bashsudo emacs -Q --batch --eval '(with-temp-buffer (insert-file-contents "/home/ctf-player/flag.txt") (message "%s" (buffer-string)))'What didn't work first
Tried: Use sudo emacs to open the flag file directly as an argument: sudo emacs /home/ctf-player/flag.txt.
That opens the file in an editor buffer and does show the contents, but the session is interactive and many CTF environments give SSH no proper PTY. It also hides the point: the intended escalation is opening a shell inside emacs, not reading one buffer. The batch eval is more reliable headless.
Tried: Try the vim GTFOBins escape (sudo vim -c ':!/bin/bash') since both are text editors with sudo.
The rule names emacs, not vim, and sudo matches the exact binary path. Anything else fails immediately. Use whatever sudo -l actually listed.
Learn more
GNU Emacs is a Lisp-based computing environment that happens to edit text. M-x term (Meta+X, type "term", Enter) spawns an interactive terminal emulator inside Emacs. The shell it launches inherits the process privileges of Emacs itself - which, when Emacs is run via
sudo, means root.Annotated batch escape:
sudo emacs -Q --batch --eval ' (with-temp-buffer ;; create a throwaway buffer (no UI) (insert-file-contents "/home/ctf-player/flag.txt") ;; read as root (message "%s" (buffer-string)))' ;; print to stderrwith-temp-buffercreates an in-memory scratch buffer;insert-file-contentsperforms the privileged read;(message ...)writes to stderr, which the calling shell captures and displays. No PTY, no interactive shell, no flag-text-to-screen indirection - exactly what you want when the box is locked down.The challenge name nods to xkcd #149: "sudo make me a sandwich." The serious lesson behind the joke is the principle this challenge embodies: any program that can shell out, when run via sudo, becomes a full privilege escalation primitive. Editors, pagers, scripting interpreters, file-finding tools - they all collapse the "run X as root" permission into "run anything as root." See the Linux CLI for CTF guide for adjacent enumeration patterns.
Step 3Read the flag
ObservationThat shell runs as root, so the flag file reads with a plain cat: permission checks no longer apply.With root privileges in the spawned shell, read the flag file.bashcat /home/ctf-player/flag.txtbash# or: find / -name flag.txt 2>/dev/nullExpected output
picoCTF{ju57_5ud0_17_...}Learn more
In CTF challenges, flag files are commonly placed in the home directory of a specific user (
/home/ctf-player/), in/root/, or in/flag.txtat the filesystem root. With a root shell, all of these are accessible. Thefind / -name flag.txt 2>/dev/nullcommand searches the entire filesystem for files namedflag.txt, with stderr redirected to/dev/nullto suppress permission errors from directories you cannot read (though with root, that is no longer an issue).In real-world privilege escalation scenarios, post-exploitation tasks are more varied: reading sensitive configuration files (
/etc/shadow, database credentials), accessing other users' home directories, modifying system files to maintain persistence, or pivoting to other networked systems. In a CTF, the target is simply the flag file, making the privilege escalation goal clear and specific.Proper defenses against sudo abuse include: applying the principle of least privilege (grant only the specific capabilities users truly need), auditing sudoers files regularly, using sudo -l output as part of security reviews, and preferring container-based privilege separation over UNIX user permission models where possible.
Interactive tools
- Regex TesterTest regular expressions against a string with live match highlighting, flag toggles, and common CTF pattern shortcuts.
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Reverse Shell GeneratorGenerate reverse shell payloads (bash, nc, python, perl, ruby, php, node, powershell) and matching listeners. Set host and port once, copy any variant.
Flag
Reveal flag
picoCTF{ju57_5ud0_17_...}
The sudo config allows running emacs as root. Emacs includes a full terminal emulator (M-x term) - any shell spawned from within it runs as root, giving direct access to the flag file.
Key takeaway
How to prevent this
How to prevent this
Granting sudo to a feature-rich binary (vim, less, emacs, find, awk, perl) is equivalent to granting sudo ALL. GTFOBins enumerates them.
- Audit your
/etc/sudoersagainst GTFOBins. Any binary with a shell-out command (:!shin vim,!shin less,M-x termin emacs) breaks the principle of least privilege. - If you genuinely need a user to run one task as root, write a tiny purpose-built script and grant sudo on that script only. Validate every argument; reject anything starting with
-. - Replace sudo grants with capabilities. Capabilities split UID 0 into ~40 fine-grained privileges; sudo hands over the whole thing. The canonical case: a Python web server that needs to bind port 80 (a privileged port). Don't do
sudo python server.py- that gives the script root. Instead:# Grant *only* "may bind low ports" to the python interpreter: sudo setcap cap_net_bind_service+ep $(readlink -f $(which python3)) # Now this works without sudo, with no other root powers: python3 -m http.server 80
cap_net_bind_service+epis permitted+effective for that one capability. The script cannot read /etc/shadow, modify /root, or shell out as root - it can only bind low ports. systemd unit files express the same idea viaAmbientCapabilities=.