SUDO MAKE ME A SANDWICH picoCTF 2026 Solution

Published: March 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.

  1. Step 1Check sudo permissions
    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
    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
    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
    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)))'
    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
    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
    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.

Flag

picoCTF{g0tt4_l0v3_s4ndw1ch3s_...}

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.

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=.

Want more picoCTF 2026 writeups?

Useful tools for General Skills

Related reading

What to try next