|
| 1 | ++++ |
| 2 | +date = '2025-04-17T13:01:57+02:00' |
| 3 | +draft = false |
| 4 | +title = 'AnnoyingLinux: Make Sudo Remember Authentication Across Terminals' |
| 5 | +summary = "Why does `sudo` in a new terminal need re-auth?" |
| 6 | +tags = ["#AnnoyingLinux"] |
| 7 | ++++ |
| 8 | + |
| 9 | +{{< alert >}} |
| 10 | +This started out as another [#AnnoyingLinux](/tags/%23AnnoyingLinux/) post, but it ended with me |
| 11 | +realizing how unsafe the default timeout setting for `sudo` is. Read about it |
| 12 | +[further down](#is-this-safe). |
| 13 | +{{< /alert >}} |
| 14 | + |
| 15 | +When you run a `sudo` command, you need to authenticate. And as you've probably noticed, running |
| 16 | +another `sudo` command shortly thereafter doesn't required you to enter your password again. |
| 17 | +However, if you open a new terminal window, the same rule does not apply. |
| 18 | + |
| 19 | +The reason why we don't have to enter the password again when we run another `sudo` command in the |
| 20 | +same terminal, is because the default `timestamp_timeout` setting for `sudoers` is set to 15 |
| 21 | +minutes. Then there's another setting called `timestamp_type`, which is `tty` by default. This means |
| 22 | +that each unique `tty` session is given it's own timestamp. Luckily, there's another type, namely |
| 23 | +`global` that makes the timestamp shared across all sessions. |
| 24 | + |
| 25 | +Open the `/etc/sudoers` file using `sudoedit`: |
| 26 | + |
| 27 | +```bash |
| 28 | +sudoedit /etc/sudoers |
| 29 | +``` |
| 30 | + |
| 31 | +Somewhere in the file, add: |
| 32 | + |
| 33 | +```bash |
| 34 | +Defaults timestamp_type=global |
| 35 | +``` |
| 36 | + |
| 37 | +## Is this safe? |
| 38 | + |
| 39 | +This is less secure than the default setting, but I also discovered that the default setting is |
| 40 | +quite dangerous. |
| 41 | + |
| 42 | +### How is the default unsafe? |
| 43 | + |
| 44 | +If you run a `sudo` command, and then right after run a regular command. Would you think that |
| 45 | +the second command could get root access? No? Me neither. |
| 46 | + |
| 47 | +Let's assume found some useful script online: `some_script_you_downloaded_from_the_internet.py`. |
| 48 | +Normally, it just does whatever it's supposed to do, but if it detects that it has root access, it |
| 49 | +could do anything. |
| 50 | + |
| 51 | +Here's a toy example of such a script: |
| 52 | + |
| 53 | +```python |
| 54 | +import subprocess |
| 55 | +ret = subprocess.run(("sudo", "-n", "echo", "Hello, World"), capture_output=True) |
| 56 | +if ret.returncode == 0: |
| 57 | + print("Hacked!") |
| 58 | + # Here it could install a reverse shell on your system |
| 59 | +else: |
| 60 | + print("I'm a normal program, I promise!") |
| 61 | + # Download images of cats or whatever |
| 62 | +``` |
| 63 | + |
| 64 | +Here's an example of how this script could gain root access without being run with `sudo`. |
| 65 | +And you would have no clue that it was happning! |
| 66 | + |
| 67 | +```text |
| 68 | +> # First we run it in a fresh terminal. |
| 69 | +> python some_script_you_downloaded_from_the_internet.py |
| 70 | +I'm a normal program, I promise! |
| 71 | +> sudo echo "hello" |
| 72 | +[sudo] password for user: |
| 73 | +hello |
| 74 | +> # How we run the script again. Note: We're not calling it with sudo! |
| 75 | +> python some_script_you_downloaded_from_the_internet.py |
| 76 | +Hacked! |
| 77 | +``` |
| 78 | + |
| 79 | +How have I never heard of this before? |
| 80 | + |
| 81 | +Conclusion: Never run programs in a terminal that you have previoiusly called `sudo` in, or |
| 82 | +make sure to run `sudo -k` before you do to reset the timestamp. |
| 83 | + |
| 84 | +### How is using `global` unsafe? |
| 85 | + |
| 86 | +We modify the script to periodically call `sudo` in non-interactive mode to check if it has |
| 87 | +root access. |
| 88 | + |
| 89 | +```python |
| 90 | +from time import sleep |
| 91 | +import subprocess |
| 92 | + |
| 93 | +while True: |
| 94 | + r = subprocess.run(("sudo", "-n", "echo", "Hello, World"), capture_output=True) |
| 95 | + if r.returncode == 0: |
| 96 | + print(f"Hacked!") |
| 97 | + # Here it could install a reverse shell on your system |
| 98 | + break |
| 99 | + else: |
| 100 | + print("I'm a normal program, I promise!") |
| 101 | + # Download images of cats or whatever |
| 102 | + sleep(2) |
| 103 | +``` |
| 104 | + |
| 105 | +If we start this program in a unauthenticated terminal, it will just print |
| 106 | +`I'm a normal program, I promise!` every two seconds. If we then open a seperate terminal and run |
| 107 | +and `sudo` command, then all of a sudden the script will also gain root access and print `Hacked!`. |
| 108 | + |
| 109 | +Example run: |
| 110 | + |
| 111 | +```bash |
| 112 | +> python asdf.py |
| 113 | +No root access. |
| 114 | +No root access. # Here I ran a sudo command in a different terminal |
| 115 | +Root access granted! |
| 116 | +``` |
| 117 | + |
| 118 | +So yes, this is less secure than the default setting. |
| 119 | + |
| 120 | +### |
| 121 | + |
| 122 | +The convenience of not having to write the password all the time is probably more important |
| 123 | +to most people. But could we make it at least slightly less dangerous? |
| 124 | + |
| 125 | +What if it still prompted you, but just for confirmation? |
| 126 | + |
| 127 | +```text |
| 128 | +> sudo echo hello |
| 129 | +[sudo] run as root? [y/n] |
| 130 | +``` |
| 131 | + |
| 132 | +If you ran the program from above, you would at least notice that something funky was going on. |
| 133 | + |
| 134 | +## Further reading |
| 135 | + |
| 136 | +* `man 5 sudoers` |
0 commit comments