Skip to main content

SUDO MAKE ME A SANDWICH picoCTF 2026 Solution

Use elevated system permissions on a Linux machine to access a flag restricted to privileged users.

Published: March 20, 2026Updated: September 20, 2026

Description

Can you read the flag? I think you can!

Launch the challenge instance and SSH in.
Check what sudo privileges the current user has.

Solution

Want to try it yourself first?

The guided walkthrough reveals hints one step at a time.

Walk me through it
  1. Step 1Check sudo permissions
    Observation
    The 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.
    Run sudo -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.
    bash
    sudo -l
    What 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/sudoers describing which user can run which command as which target user, with or without a password. sudo -l prints 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 with system()), 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.

  2. Step 2Escape to a root shell from emacs
    Observation
    sudo -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.
    bash
    sudo emacs
    bash
    # Inside emacs:
    bash
    # Press Alt+X (M-x), type 'term', press Enter
    bash
    # Then at the terminal prompt: cat /home/ctf-player/flag.txt
    bash
    # Non-interactive alternative:
    bash
    sudo emacs -Q -nw --eval '(term "/bin/bash")'
    bash
    # Or direct file read:
    bash
    sudo 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 stderr

    with-temp-buffer creates an in-memory scratch buffer; insert-file-contents performs 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.

  3. Step 3Read the flag
    Observation
    That 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.
    bash
    cat /home/ctf-player/flag.txt
    bash
    # or: find / -name flag.txt 2>/dev/null

    Expected 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.txt at the filesystem root. With a root shell, all of these are accessible. The find / -name flag.txt 2>/dev/null command searches the entire filesystem for files named flag.txt, with stderr redirected to /dev/null to 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

Granting sudo on anything that can spawn a subshell, an editor, a pager, an interpreter, a search tool with an exec flag, is the same as granting sudo on everything: GTFOBins carries a tested one-liner for each. Least privilege means granting the specific capability, through Linux capabilities or a purpose-built wrapper, rather than sudo on a general-purpose tool.

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/sudoers against GTFOBins. Any binary with a shell-out command (:!sh in vim, !sh in less, M-x term in 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+ep is 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 via AmbientCapabilities=.

Related reading

Useful tools for General Skills

Where to go next