mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-02-25 08:43:36 +00:00
I'm sure there are still some exploits in the code, but there are certainly fewer now. Instead of just executing whatever arguments are passed into privilegedactions.privilegedaction_run, we now limit the actions that can be run in the following ways: - Only actions that exist in the actions directory can be executed. Attempting to run the action "echo; rm -rf /" will look for a file named "actions/echo; rm -rf /", of which there are none. - Shell literals are escaped: attempting to run the "echo" action with options like "'hi'; rm -rf /") will echo "'hi'; rm -rf /". - It is difficult to interact with the spawned process through this interface. We can't control whether the spawned process allows interaction. The details of the contract are included in privilegedactions.py, and this contract is tested in privilegedactions_test.py.
102 lines
2.8 KiB
Python
102 lines
2.8 KiB
Python
#! /usr/bin/env python
|
|
# -*- mode: python; mode: auto-fill; fill-column: 80 -*-
|
|
|
|
import sys
|
|
from actions.privilegedactions import privilegedaction_run
|
|
import unittest
|
|
|
|
class TestPrivileged(unittest.TestCase):
|
|
"""Verify that privileged actions perform as expected:
|
|
|
|
1. Privileged actions run as root.
|
|
|
|
2. Only whitelisted privileged actions can run.
|
|
|
|
A. Actions can't be used to run other actions:
|
|
|
|
$ action="echo 'hi'; rm -rf /"
|
|
$ $action
|
|
|
|
B. Options can't be used to run other actions:
|
|
|
|
$ options="hi'; rm -rf /;'"
|
|
$ "echo " + "'$options'"
|
|
|
|
C. Scripts in a directory above the actions directory can't be run.
|
|
|
|
D. Scripts in a directory beneath the actions directory can't be run.
|
|
|
|
3. The actions directory can't be changed at run time.
|
|
|
|
"""
|
|
def test_run_as_root(self):
|
|
"""1. Privileged actions run as root.
|
|
|
|
"""
|
|
self.assertEqual(
|
|
"0", # user 0 is root
|
|
privilegedaction_run("id", "-ur")[0].strip())
|
|
|
|
def test_breakout_actions_dir(self):
|
|
"""2. The actions directory can't be changed at run time.
|
|
|
|
Can't currently be tested, as the actions directory is hardcoded.
|
|
|
|
"""
|
|
pass
|
|
|
|
def test_breakout_up(self):
|
|
"""3A. Users can't call actions above the actions directory.
|
|
|
|
Tests both a relative and a literal path.
|
|
|
|
"""
|
|
options="hi"
|
|
|
|
for arg in ("../echo", "/bin/echo"):
|
|
with self.assertRaises(ValueError):
|
|
privilegedaction_run(arg, options)
|
|
|
|
def test_breakout_down(self):
|
|
"""3B. Users can't call actions beneath the actions directory."""
|
|
action="directory/echo"
|
|
|
|
self.assertRaises(ValueError, privilegedaction_run, action)
|
|
|
|
def test_breakout_actions(self):
|
|
"""3C. Actions can't be used to run other actions.
|
|
|
|
If multiple actions are specified, bail out.
|
|
|
|
"""
|
|
# counting is safer than actual badness.
|
|
actions = ("echo ''; echo $((1+1))",
|
|
"echo '' && echo $((1+1))",
|
|
"echo '' || echo $((1+1))")
|
|
options = ("good", "")
|
|
|
|
for action in actions:
|
|
for option in options:
|
|
with self.assertRaises(ValueError):
|
|
output = privilegedaction_run(action, option)
|
|
|
|
print(output)
|
|
|
|
# if it doesn't error, we'd better not evaluate the data.
|
|
self.assertFalse("2" in output[0])
|
|
|
|
def test_breakout_options(self):
|
|
"""3D. Options can't be used to run other actions."""
|
|
|
|
action = "echo"
|
|
# counting is safer than actual badness.
|
|
options = "good; echo $((1+1))"
|
|
|
|
output, error = privilegedaction_run(action, options)
|
|
|
|
self.assertFalse("2" in output)
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|
|
|