{"id":13576261,"url":"https://github.com/taviso/scanlimits","last_synced_at":"2025-03-25T23:30:29.216Z","repository":{"id":53330881,"uuid":"217402714","full_name":"taviso/scanlimits","owner":"taviso","description":"Tool to examine the behaviour of setuid binaries under constrained limits.","archived":false,"fork":false,"pushed_at":"2021-03-31T14:53:52.000Z","size":26,"stargazers_count":62,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-20T22:03:59.346Z","etag":null,"topics":["audit","linux","security"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/taviso.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-24T22:01:00.000Z","updated_at":"2023-12-20T14:52:30.000Z","dependencies_parsed_at":"2022-09-02T04:32:50.692Z","dependency_job_id":null,"html_url":"https://github.com/taviso/scanlimits","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taviso%2Fscanlimits","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taviso%2Fscanlimits/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taviso%2Fscanlimits/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taviso%2Fscanlimits/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taviso","download_url":"https://codeload.github.com/taviso/scanlimits/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245560896,"owners_count":20635652,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audit","linux","security"],"created_at":"2024-08-01T15:01:08.546Z","updated_at":"2025-03-25T23:30:26.483Z","avatar_url":"https://github.com/taviso.png","language":"C","readme":"# scanlimits\n\n\u003e A tool to examine the behaviour of setuid binaries when constrained.\n\nIf you set resource limits using `setrlimit()`, `prlimit()` or the `ulimit`\nshell builtin, then those limits apply even across a setuid `execve()`.\n\nTo put it another way, any limits you apply to your current shell also apply to\nany setuid executables you run. Some developers find this surprising, and it\ncan introduce vulnerabilities.\n\nHere is an example, the `pam_nologin(8)` module is used to prevent\nlogin if the file `/etc/nologin` exists, so that the system administrator can\ntemporarily disable login. The code just looks like this:\n\n```c\n\tfd = open(nologin, O_RDONLY, 0);\n\tif (fd \u003c 0) {\n\t\tlogin_close(lc);\n\t\treturn (PAM_SUCCESS);\n\t}\n```\n\nIt doesn't check `errno` though, and an unprivileged user can make this return\nfailure (`EMFILE`, Too many open files) by setting a very low `RLIMIT_NOFILE`\nlimit.\n\nTherefore, this check can be bypassed if it's required for `su` or similar\nauthentication.\n\n# Building\n\nJust type `make`, requires `glib2.0`.\n\n# Usage\n\nThis tool will attempt to scan for all the different errors that a program\nproduces when it's constrained. It does that by simply recording the output and\nwaiting for it to change. That doesn't necessarily mean it's a bug, it's up to\nyou to determine if the error is something security relevant.\n\nHere is an example run, let's see what kind of errors sudo generates when\nconstrained:\n\n```\n$ limits -o output.sh -b filters.txt -- sudo --list --non-interactive\nfile filters.txt contained 7 valid filter patterns.\nsearching RLIMIT_CPU...\n\t@0x000000000000000001...same\nsearching RLIMIT_FSIZE...\n\t@0x000000000000000001...same\nsearching RLIMIT_DATA...\n\t@0x00000000000003ffff...different\nTesting RLIMIT_DATA = 0x000000000000071fff...new\nTesting RLIMIT_NOFILE = 0x00000000000000000a...new\nTesting RLIMIT_NOFILE = 0x000000000000000009...new\nTesting RLIMIT_NOFILE = 0x000000000000000008...new\nTesting RLIMIT_NOFILE = 0x000000000000000007...new\nTesting RLIMIT_NOFILE = 0x000000000000000005...new\nTesting RLIMIT_NOFILE = 0x000000000000000004...new\nTesting RLIMIT_NOFILE = 0x000000000000000003...new\nTesting RLIMIT_NOFILE = 0x000000000000000001...\nsearching RLIMIT_MEMLOCK...\n\t@0x000000000000000001...same\nsearching RLIMIT_AS...\n\t@0x0000000000003fffff...different\nsearching RLIMIT_RTTIME...\n\t@0x000000000000000001...same\n```\n\nNow we should have a shellscript that will print all the different errors\nthat `limits` found:\n\n```\n$ bash ~/output.sh\ncannot allocate TLS data structures for initial thread\nsudo: error while loading shared libraries: /lib64/libselinux.so.1: cannot allocate version reference table: Cannot allocate memory\nsudo: error while loading shared libraries: libpcre2-8.so.0: failed to map segment from shared object\nsudo: error while loading shared libraries: libcap-ng.so.0: failed to map segment from shared object\nsudo: error while loading shared libraries: libc.so.6: cannot map zero-fill pages\nsudo: error while loading shared libraries: libc.so.6: failed to map segment from shared object\nsudo: error while loading shared libraries: libdl.so.2: cannot map zero-fill pages\nsudo: error while loading shared libraries: libdl.so.2: failed to map segment from shared object\nsudo: error while loading shared libraries: libpthread.so.0: cannot map zero-fill pages\nsudo: error while loading shared libraries: libpthread.so.0: cannot create shared object descriptor: Cannot allocate memory\nsudo: error while loading shared libraries: libz.so.1: cannot map zero-fill pages\nsudo: error while loading shared libraries: libz.so.1: failed to map segment from shared object\nsudo: error while loading shared libraries: libcrypto.so.1.1: cannot map zero-fill pages\nsudo: error while loading shared libraries: libcrypto.so.1.1: failed to map segment from shared object\nsudo: error while loading shared libraries: libsudo_util.so.0: failed to map segment from shared object\nsudo: error while loading shared libraries: libutil.so.1: cannot map zero-fill pages\nsudo: error while loading shared libraries: libutil.so.1: failed to map segment from shared object\nsudo: error while loading shared libraries: libselinux.so.1: cannot map zero-fill pages\nsudo: error while loading shared libraries: libselinux.so.1: failed to map segment from shared object\nsudo: error while loading shared libraries: libaudit.so.1: cannot map zero-fill pages\nsudo: error while loading shared libraries: libaudit.so.1: cannot create shared object descriptor: Cannot allocate memory\noutput.sh: line 23:  9932 Segmentation fault      ./runlimit RLIMIT_DATA 0x6fff sudo --list --non-interactive \u003c /dev/null\noutput.sh: line 24:  9933 Segmentation fault      (core dumped) ./runlimit RLIMIT_STACK 0x3001 sudo --list --non-interactive \u003c /dev/null\noutput.sh: line 25:  9941 Aborted                 (core dumped) ./runlimit RLIMIT_STACK 0x1001 sudo --list --non-interactive \u003c /dev/null\nsudo: unable to open audit system: Too many open files\nsudo: a password is required\nsudo: unable to initialize PAM: Critical error - immediate abort\nsudo: /etc/sudoers.d: Too many open files\nsudo: no valid sudoers sources found, quitting\nsudo: error initializing audit plugin sudoers_audit\nsudo: unknown user: root\nsudo: error initializing audit plugin sudoers_audit\nsudo: unable to allocate memory\nsudo: error in /etc/sudo.conf, line 0 while loading plugin \"sudoers_policy\"\nsudo: unable to load /usr/libexec/sudo/sudoers.so: /usr/libexec/sudo/sudoers.so: cannot open shared object file: Too many open files\nsudo: fatal error, unable to load plugins\nsudo: error while loading shared libraries: libaudit.so.1: cannot open shared object file: Error 24\nsudo: error while loading shared libraries: libpthread.so.0: failed to map segment from shared object\nsudo: error while loading shared libraries: libaudit.so.1: failed to map segment from shared object\n```\n\nThere are lots of errors from the loader, unable to load necessary shared\nlibraries, these are probably not very interesting.\n\nHowever, there are a few interesting errors there, `unable to open audit\nsystem`, `unable to initialize PAM`, `no valid sudoers sources found`, `unknown\nuser: root`, these are worth exploring to see if they might fail open anywhere.\n\n## Options\n\n| Option            | Description                                              |\n| ----------------- | -------------------------------------------------------- |\n| `-t TIMEOUT`      | Kill the process if it takes longer than this (seconds). |\n| `-b FILTER`       | Load regex (one per line) to clean output.               |\n| `-o OUTPUT`       | Generate a script to see the different outputs found.    |\n| `-i INFILE`       | Attach this file to stdin of processes.                  |\n\n### Filters\n\nThis tool works by comparing the error messages produced with previous outputs\nseen. That only works if error messages are consistent, but some tools like to\nput timestamps or pids into errors making them unique.\n\nIf the tool you're testing does this, you need to make a filter to explain\nhow to remove the inconsistent data.\n\nHere is an example, this filter will remove timestamps from glib error\nmessages:\n\n```\n# This is a log timestamp, used by some glib programs.\n\\d+:\\d+:\\d+\\.\\d+:\n```\n\n# Examples\n\nImagine a setuid helper program that updates `/etc/passwd` for you.\n\n```c\n    char *buf = read_passwd_file();\n    change_user_shell(buf, username, newshell);\n    f = fopen(\"/etc/passwd\", \"w\");\n    fwrite(buf, 1, strlen(buf), f);\n```\n\nThis isn't safe, the user can make that `fwrite()` stop wherever they\nwant, effectively truncating the file at any boundary they choose. These bugs\ncan really happen, e.g. CVE-2019-14865\n\n\nAnother example might be programs that parse configuration files, for example\nimagine a program that has a configuration file like this:\n\n```\n# All users in group staff are allowed.\nallow %staff\n\n# Except these users\nsource users.deny\n```\n\nThat won't work, because a user can limit the number of files that can be\nopened, making the attempt to open the `users.deny` file return `EMFILE`.\n\n","funding_links":[],"categories":["C"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaviso%2Fscanlimits","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaviso%2Fscanlimits","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaviso%2Fscanlimits/lists"}