Code Monkey home page Code Monkey logo

maybe's Introduction

Package
Tests

rm -rf pic*

Are you sure? Are you one hundred percent sure?

maybe...

... allows you to run a command and see what it does to your files without actually doing it! After reviewing the operations listed, you can then decide whether you really want these things to happen or not.

Screenshot

What is this sorcery?!?

maybe runs processes under the control of ptrace (with the help of the excellent python-ptrace library). When it intercepts a system call that is about to make changes to the file system, it logs that call, and then modifies CPU registers to both redirect the call to an invalid syscall ID (effectively turning it into a no-op) and set the return value of that no-op call to one indicating success of the original call.

As a result, the process believes that everything it is trying to do is actually happening, when in reality nothing is.

That being said, maybe should ⚠️ NEVER ⚠️ be used to run untrusted code on a system you care about! A process running under maybe can still do serious damage to your system because only a handful of syscalls are blocked. It can also check whether an operation such as deleting a file succeeded using read-only syscalls, and alter its behavior accordingly. Therefore, a rerun without restrictions is not guaranteed to always produce the displayed operations.

Currently, maybe is best thought of as an (alpha-quality) "what exactly will this command I typed myself do?" tool.

Installation

maybe runs on Linux 🐧 and requires Python 2.7+/3.3+ 🐍. If you have the pip package manager, all you need to do is run

pip install maybe

either as a superuser or from a virtualenv environment. To develop maybe, clone the repository and run

pip install -e .

in its main directory to install the package in editable mode.

Usage

maybe [options] command [argument ...]

Positional arguments

Argument Description
command the command to run under maybe's control
argument ... argument(s) to pass to command

Optional arguments

Argument Description
-a OPERATION ...,
--allow OPERATION ...
                                             
allow the command to perform the specified operation(s). all other operations will be denied. possible values for OPERATION are: change_owner, change_permissions, create_directory, create_link, create_write_file, delete, move; as well as any filter scopes defined by loaded plugins
-d OPERATION ...,
--deny OPERATION ...
deny the command the specified operation(s). all other operations will be allowed. see --allow for a list of possible values for OPERATION. --allow and --deny cannot be combined
-p FILE ...,
--plugin FILE ...
load the specified plugin script(s)
-l, --list-only list operations without header, indentation and rerun prompt
--style-output {yes,no,auto} colorize output using ANSI escape sequences (yes/no) or automatically decide based on whether stdout is a terminal (auto, default)
-v, --verbose if specified once, print every filtered syscall. if specified twice, print every syscall, highlighting filtered syscalls
--version show program's version number and exit
-h, --help show a help message and exit

Plugin API

By default, maybe intercepts and blocks all syscalls that can make permanent modifications to the system. For more specialized syscall filtering needs, maybe provides a simple yet powerful plugin API. Filter plugins are written in pure Python and use the same interfaces as maybe's built-in filters.

The public API is composed of the following two members:

maybe.T

A Blessings Terminal object that can be used to format console output (such as operation as documented below). Output formatted with this object automatically complies with the --style-output command line argument.

maybe.register_filter(syscall, filter_function, filter_scope=None)

Add the filter filter_function to the filter registry. If the filter is enabled (which is the default, but can be altered with the --allow and --deny command line arguments), it intercepts all calls to syscall made by the controlled process. filter_scope determines the key to be used in conjunction with --allow and --deny to enable/disable the filter (multiple filters can share the same key). If filter_scope is omitted or None, the last part of the plugin's module name is used.

filter_function itself must conform to the signature filter_function(process, args). process is a Process control object that can be used to inspect and manipulate the process, while args is the list of arguments passed to the syscall in the order in which they appear in the syscall's signature. If an argument represents a (pointer to a) filename, the argument will be of type str and contain the filename, otherwise it will be of type int and contain the numerical value of the argument.

When called, filter_function must return a tuple (operation, return_value). operation can either be a string description of the operation that was prevented by the filter, to be printed after the process terminates, or None, in which case nothing will be printed. return_value can either be a numerical value, in which case the syscall invocation will be prevented and the return value received by the caller will be set to that value, or None, in which case the invocation will be allowed to proceed as normal.

Example

Here, maybe's plugin API is used to implement an exotic type of access control: Restricting read access based on the content of the file in question. If a file being opened for reading contains the word SECRET, the plugin blocks the open/openat syscall and returns an error.

read_secret_file.py

from os import O_WRONLY
from os.path import isfile
from maybe import T, register_filter

def filter_open(path, flags):
    if path.startswith("/home/") and isfile(path) and not (flags & O_WRONLY):
        with open(path, "r") as f:
            if "SECRET" in f.read():
                return "%s %s" % (T.red("read secret file"), T.underline(path)), -1
            else:
                return None, None
    else:
        return None, None

register_filter("open", lambda process, args:
                filter_open(process.full_path(args[0]), args[1]))
register_filter("openat", lambda process, args:
                filter_open(process.full_path(args[1], args[0]), args[2]))

Indeed, the plugin works as expected:

[user@localhost]$ maybe --plugin read_secret_file.py --deny read_secret_file -- bash
$ echo "This is a normal file." > file_1
$ echo "This is a SECRET file." > file_2
$ cat file_1
This is a normal file.
$ cat file_2
cat: file_2: Operation not permitted

License

Copyright © 2016-2017 Philipp Emanuel Weidmann ([email protected])

Released under the terms of the GNU General Public License, version 3

maybe's People

Contributors

p-e-w avatar sanketplus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maybe's Issues

Vague error with `maybe rm ./*`

Getting a vague error upon maybe rm ./*:

Warning: Running without traceFork support. Syscalls from subprocesses can not be intercepted.
Traceback (most recent call last):
  File "/usr/local/bin/maybe", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/site-packages/maybe/maybe.py", line 154, in main
    prepareProcess(process)
  File "/usr/local/lib/python2.7/site-packages/maybe/maybe.py", line 53, in prepareProcess
    process.syscall()
  File "/usr/local/lib/python2.7/site-packages/ptrace/debugger/process.py", line 469, in syscall
    ptrace_syscall(self.pid, signum)
  File "/usr/local/lib/python2.7/site-packages/ptrace/binding/func.py", line 254, in ptrace_syscall
    ptrace(PTRACE_SYSCALL, pid, 1, signum)
  File "/usr/local/lib/python2.7/site-packages/ptrace/binding/func.py", line 148, in ptrace
    raise PtraceError(message, errno=errno, pid=pid)
ptrace.error.PtraceError: ptrace(cmd=22, pid=35717, 1, 0) error #22: Invalid argument

Installed using sudo pip install maybe. On OS X El Capitan.

Feature: more permissive licensing

Hello,

maybe is amazing! It would be great if maybe switched from GPLv3 to something less restrictive. That way it can be included in projects without requiring the project to have a restricted license as well.

Thanks for considering!

Error tracing process: ptrace(cmd=1, pid=31271, 140728292737024L, 0) error #5: Input/output error

See log:


root@i-770a93ad # uname -a
Linux i-770a93ad 3.19.0-47-generic #53~14.04.1-Ubuntu SMP Mon Jan 18 16:09:14 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

root@i-770a93ad # maybe service nginx reload
Error tracing process: ptrace(cmd=1, pid=31271, 140728292737024L, 0) error #5: Input/output error.
WARNING:root:Terminate <PtraceProcess #31271>

root@i-770a93ad # maybe apt-get update
FATAL -> Could not set close on exec Bad file descriptor
maybe has not detected any file system operations from apt-get update.

terminfo database problem

Hi, your project sounds quite interesting. So I pip-ed it and tried with the simplest command possible:

maybe rm filename

However, what I received, was an error message:

Traceback (most recent call last):
  File "/home/username/anaconda3/bin/maybe", line 7, in <module>
    from maybe.maybe import main
  File "/home/username/anaconda3/lib/python3.4/site-packages/maybe/maybe.py", line 23, in <module>
    from .syscall_filters import SYSCALL_FILTERS
  File "/home/username/anaconda3/lib/python3.4/site-packages/maybe/syscall_filters.py", line 17, in <module>
    from .utilities import T, format_permissions
  File "/home/username/anaconda3/lib/python3.4/site-packages/maybe/utilities.py", line 16, in <module>
    T = Terminal()
  File "/home/username/anaconda3/lib/python3.4/site-packages/blessings/__init__.py", line 106, in __init__
    self._init_descriptor)
_curses.error: setupterm: could not find terminfo database

Do you know what could be source of the problem?
Thanks,
Jan

P.S. Anaconda distribution, Python 3.4.3, Linux (Fedora 22)

tracing errors in various programs

Hello!

I tried to maybe a few programs and had trouble with some. Here are some logs:

 $ maybe /usr/bin/tor
 Feb 07 05:12:58.285 [notice] Tor v0.2.5.12 (git-3731dd5c3071dcba) running on Linux with Libevent 2.0.21-stable, OpenSSL 1.0.1k and Zlib 1.2.8.
 Error tracing process: invalid literal for int() with base 0: '"Feb 07 05:12:58.286 [notice] Tor can\'t help you if you use it wrong! Learn how to be safe at https://www.torproject.org/download/download#warning\\n"'.
 WARNING:root:Terminate <PtraceProcess #20412>

 $ maybe /usr/bin/wicd-client 
 Error tracing process: ptrace(cmd=1, pid=20469, 140724290867200L, 0) error #5: Input/output error.
 WARNING:root:Terminate <PtraceProcess #20469>

Any ideas what's wrong?

Error tracing process: invalid literal for int() with base 0 ....

When running maybe foo.sh with foo.sh

#!/bin/bash

rm -rvf ~/*

I get

Error tracing process: invalid literal for int() with base 0: 'b"removed \'/home/justin/Test.png\'\\n"'.
WARNING:root:Terminate <PtraceProcess #2060>
WARNING:root:Terminate <PtraceProcess #2059>

This happens with

python-2.7/3.4
python-ptrace-0.8.1
blessings-1.6

Be able to call Aliases as well

While I was trying the software I immediately tried a command which is an alias in my environment, but maybe was not able to find it. Is it possible to support aliased commands?

The software is awesome, thanks!

Path output errors due to chdir

It seems that there's problems with chdir at the moment. If a command chdir()s, path output will not be correct:

~ % maybe rm node_modules -rf
  [...]
  delete /home/dv/.travis.yml
  delete /home/dv/readable-stream
  delete /home/dv/node_modules
  delete /home/dv/package.json
  delete /home/dv/readme.md
  delete /home/dv/concat-stream
  delete /home/dv/node_modules

Note that most of the files that seem to be scheduled for deletion in my home dir are actually somewhere in a subdir of node_modules. But since rm chdir()s into the subfolders, the output will not correctly contain that subfolder.

npm does not run under maybe (TTY related issue)

npm (node package manager) seems to require some TTY features not supported by maybe:

$ maybe npm help                                                                                                               
tty.js:73
    handle: new TTY(fd, false),
            ^

Error: EBADF: bad file descriptor, uv_tty_init
    at new WriteStream (tty.js:73:13)
    at createWritableStdioStream (internal/process/stdio.js:151:16)
    at process.getStdout [as stdout] (internal/process/stdio.js:14:14)
    at console.js:248:38
    at NativeModule.compile (bootstrap_node.js:601:7)
    at Function.NativeModule.require (bootstrap_node.js:546:18)
    at setupGlobalConsole (bootstrap_node.js:318:41)
    at startup (bootstrap_node.js:74:7)
    at bootstrap_node.js:613:3
maybe has not detected any file system operations from npm help.

Doesn't work on macOS

Warning: Running without traceFork support. Syscalls from subprocesses can not be intercepted.
Traceback (most recent call last):
  File "/Users/evar/anaconda/bin/maybe", line 11, in <module>
    sys.exit(main())
  File "/Users/evar/anaconda/lib/python3.6/site-packages/maybe/maybe.py", line 159, in main
    prepareProcess(process)
  File "/Users/evar/anaconda/lib/python3.6/site-packages/maybe/maybe.py", line 58, in prepareProcess
    process.syscall()
  File "/Users/evar/anaconda/lib/python3.6/site-packages/ptrace/debugger/process.py", line 469, in syscall
    ptrace_syscall(self.pid, signum)
  File "/Users/evar/anaconda/lib/python3.6/site-packages/ptrace/binding/func.py", line 254, in ptrace_syscall
    ptrace(PTRACE_SYSCALL, pid, 1, signum)
  File "/Users/evar/anaconda/lib/python3.6/site-packages/ptrace/binding/func.py", line 148, in ptrace
    raise PtraceError(message, errno=errno, pid=pid)
ptrace.error.PtraceError: ptrace(cmd=22, pid=4326, 1, 0) error #22: Invalid argument

logic problem with syscalls which should fail

maybe always returns success for intercepted syscalls, if they should fail, e.g. a not-existing file, thus changing the logic of the program.
maybe should check per syscall if the syscall might succeed.

Issue with CMDer on W10

Get this when I try to use any command with "maybe".

Traceback (most recent call last):
  File "C:\Python27\Scripts\maybe-script.py", line 9, in <module>
    load_entry_point('maybe==0.3.0', 'console_scripts', 'maybe')()
  File "c:\python27\lib\site-packages\pkg_resources\__init__.py", line 558, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "c:\python27\lib\site-packages\pkg_resources\__init__.py", line 2682, in load_entry_point
    return ep.load()
  File "c:\python27\lib\site-packages\pkg_resources\__init__.py", line 2355, in load
    return self.resolve()
  File "c:\python27\lib\site-packages\pkg_resources\__init__.py", line 2361, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "c:\python27\lib\site-packages\maybe\maybe.py", line 15, in <module>
    from ptrace.debugger import ProcessSignal, NewProcessEvent, ProcessExecution, ProcessExit
  File "c:\python27\lib\site-packages\ptrace\debugger\__init__.py", line 6, in <module>
    from ptrace.debugger.child import ChildError
  File "c:\python27\lib\site-packages\ptrace\debugger\child.py", line 4, in <module>
    from os import (
ImportError: cannot import name fork

Seems *maybe* don't support OSX 10.12

When I run maybe in OSX 10.12, I always receive errors below:

Warning: Running without traceFork support. Syscalls from subprocesses can not be intercepted.
Traceback (most recent call last):
  File "/usr/local/bin/maybe", line 9, in <module>
    load_entry_point('maybe==0.4.0', 'console_scripts', 'maybe')()
  File "/Library/Python/2.7/site-packages/maybe/maybe.py", line 159, in main
    prepareProcess(process)
  File "/Library/Python/2.7/site-packages/maybe/maybe.py", line 58, in prepareProcess
    process.syscall()
  File "/Library/Python/2.7/site-packages/ptrace/debugger/process.py", line 469, in syscall
    ptrace_syscall(self.pid, signum)
  File "/Library/Python/2.7/site-packages/ptrace/binding/func.py", line 254, in ptrace_syscall
    ptrace(PTRACE_SYSCALL, pid, 1, signum)
  File "/Library/Python/2.7/site-packages/ptrace/binding/func.py", line 148, in ptrace
    raise PtraceError(message, errno=errno, pid=pid)
ptrace.error.PtraceError: ptrace(cmd=22, pid=88532, 1, 0) error #22: Invalid argument

Modularize syscall filters

It should be possible to selectively enable/disable groups of syscall filters, e.g. to permit a program to move files, but not to delete them.

bash: program: No such file or directory

user@user-desktop:~/Desktop$ maybe mkdir test
bash: program: No such file or directory

Not sure if it's the program or it's just an issue with my Ubuntu set up. Installed via pip.

Add tests and CI

Tests could be as simple as coreutils covering all file system operations and comparing maybe's output with a sample file.

Thank you!

Just wanted to drop by and say thanks for a wonderful piece of software.
Keep up the good work ;)

easy to bypass in linux 64 bit

using int 0x80 in a elf-64 binary, its easy to bypass, example:

test.asm

section .text
    global _start
section .rodata
    x db '/tmp/abcd',0x0

_start:
    mov eax, 5
    mov ebx, x
    mov ecx, 64
    mov edx, 0644
    int 0x80

    xor eax, eax
    inc eax
    int 0x80
$ nasm -f elf64 test.asm
$ ld -o test test.o
$ rm -f abcd
$ [ -e "abcd" ] && echo file found
$ maybe ./test
maybe has not detected any file system operations from ./test.
$ [ -e "abcd" ] && echo file found
file found

Error with dig

maybe dig google.com

produces "Error 514L". This is an upstream (python-ptrace) bug.

Check if script is executable in first place

To test maybe, I wrote a simple script

$ cat demo.sh 
rm -rf temp123
mkdir temp123
$ maybe ./demo.sh
- Error executing ./demo.sh: [Errno 13] Permission denied.
$ sudo maybe ./demo.sh
- Error executing ./demo.sh: [Errno 13] Permission denied.

I tried giving it the appropriate rights

$ sudo chmod 777 demo.sh 
$ sudo maybe ./demo.sh
- Error executing ./demo.sh: [Errno 8] Exec format error.

I added the line #!/bin/sh to the top, and this gave the desired output.

$ sudo maybe ./demo.sh
maybe has prevented ./demo.sh from performing 2 file system operations:

  delete pwd/temp123
  create directory pwd/temp123

Do you want to rerun ./demo.sh and permit these operations? [y/N] n

Is it possible to detect, if the script could have been run in the first place, instead of showing these errors ?

bash script stuck when run with maybe

I have tried runnning the Nodejs install script with maybe 0.4.0-1 on Ubuntu Yakkety, and with maybe, the script simply stops after "echo 'Installing the NodeSource Node.js 6.x repo...'".

$ curl -sL https://rpm.nodesource.com/setup_6.x | sudo maybe bash -x -
+ print_status 'Installing the NodeSource Node.js 6.x repo...'
++ echo 'Installing the NodeSource Node.js 6.x repo...'
^Cmaybe has not detected any file system operations from bash -x -.

I had to run it without maybe to get the script to run!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.