PRIV^:Privilege Escalation

csvtool

Understand csvtool callbacks and why unrestricted sudo access to this parser can execute commands.

intermediateupdated 2026-10-12csvtool · sudo

csvtool

csvtool processes CSV files; its call action invokes a command for rows. If sudo delegates unrestricted execution of the binary as root, that callback inherits root. The report demonstrates this permission mistake.

Establish the delegation

id
sudo -l
csvtool --help

Read the exact executable path, allowed arguments, run-as identity and password requirement. A rule restricted to a safe operation does not imply permission to use every subcommand.

Demonstrate with a bounded command

Create a local one-row fixture:

printf 'alpha,beta\n' > lab.csv
csvtool call '/usr/bin/id' lab.csv
# Only where sudo -l allows this binary and action as root:
sudo /usr/bin/csvtool call '/usr/bin/id' lab.csv

The first call establishes ordinary execution; the second establishes the elevated identity. A shell that prints text successfully is not sufficient evidence: compare the reported UID.

Success: a permitted callback reports UID 0. Prefer this short proof over an interactive shell. Restrict the delegated operation and its arguments or replace it with a purpose-built task. Record the rule and command output, then remove the fixture. This is a sudo configuration problem; csvtool need not have a memory-corruption vulnerability.

Understand the callback boundary

call is an intended command-execution feature. Its callback is interpreted through a shell and receives CSV fields as arguments. Even if the input file is harmless, delegating the callback command to an untrusted user delegates execution as the run-as identity.

printf 'first,second\nthird,fourth\n' > lab-two-rows.csv
csvtool call '/usr/bin/id' lab-two-rows.csv

Multiple rows can invoke the callback repeatedly. Keep an identity proof to one row to avoid making repeated changes if a callback later does more than print its identity.

Read sudo rules precisely

Rule detailConsequence
(root)Permitted command runs as root
(backup)Command runs as the backup identity unless another identity is allowed
NOPASSWDSudo does not require a password for that allowed command
Absolute executable pathUse that path; a same-named binary is not equivalent
Arguments or command regexDetermine whether call is actually permitted
WildcardsMay match more argument strings than the author intended
command -v csvtool
ls -l /usr/bin/csvtool
sudo -l

The executable being readable is expected. The relevant finding is permission to run its command callback under a stronger identity. If the rule grants only a fixed col or head operation, the unrestricted call example does not establish an allowed path.

Minimal reproducible comparison

printf 'alpha,beta\n' > lab.csv
/usr/bin/csvtool call '/usr/bin/id' lab.csv > ordinary-identity.txt
sudo /usr/bin/csvtool call '/usr/bin/id' lab.csv > delegated-identity.txt
cat ordinary-identity.txt delegated-identity.txt

Use the explicit run-as option only if the rule grants that identity, for example sudo -u backup /usr/bin/csvtool .... Record real and effective UID where they differ. A password prompt followed by rejection indicates the delegation was not successfully exercised.

Failure and remediation

SymptomCheck
Unknown call actionWhich csvtool implementation/version is installed
Fixture cannot be openedFile path and read permission under the run-as identity
Sudo rejects argumentsExact rule, executable and argument constraints
Output shows your original UIDWhether sudo ran and which run-as identity was selected

Remove the fixture after recording the result. Replace an unrestricted parser grant with a fixed task that validates its inputs and cannot select a callback command. Changing the CSV file’s permissions alone does not fix a rule that already permits arbitrary callbacks. See Privilege Escalation for other sudo delegates.

How attacks work. Examples use fictional lab values; replace placeholders only with values from the system you are testing.

On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.