forked from 0xddaa/bashfuck
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbashfuck.sh
executable file
·129 lines (104 loc) · 2.83 KB
/
bashfuck.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#!/bin/bash
usage(){
echo "usage: $0 [-h] [-v] [-t] [-b] cmd" >&2
}
_help() {
echo "encode a bash command with charset \$()#!{}<\'," >&2
echo "" >&2
echo "positional arguments:" >&2
echo " cmd" >&2
echo "" >&2
echo "optional arguments:" >&2
echo " -h, --help show this help message and exit" >&2
echo " -v, --verbose print input command and bashfuck length too" >&2
echo " -t, --test test bashfuck and output result" >&2
echo " -b, --bash leaves the default bash string using [bash] chars, but avoids" >&2
echo " the usage of \"!\" and uses one byte less. always works." >&2
}
join_by() { local IFS="$1"; shift; echo "$*"; }
n=( )
n[0]="\$#"
n[1]="\${##}"
n[2]="\$((${n[1]}<<${n[1]}))"
n[3]="\$((${n[2]}#${n[1]}${n[1]}))"
n[4]="\$((${n[1]}<<${n[2]}))"
n[5]="\$((${n[2]}#${n[1]}${n[0]}${n[1]}))"
n[6]="\$((${n[2]}#${n[1]}${n[1]}${n[0]}))"
n[7]="\$((${n[2]}#${n[1]}${n[1]}${n[1]}))"
str_to_oct() {
# Converts a string to its octal representation, enclosed in: $\'STR\'
s="\$\\'"
for ((i=0; i<${#1}; i++))
do
char="${1:$i:1}"
oct=$(printf "%03o" \'"${char}")
e="\\\\"
for ((j=0; j<${#oct}; j++))
do
e+="${n[${oct:$j:1}]}"
done
s+="${e}"
done
s+="\\'"
echo "${s}"
}
arg_to_cmd() {
# Given an array of strings returns the octal representation of every single string,
# wrapped in '{}' and separated by ','
for str in "$@"
do
OCTAL_STRING_ARR+=( $(str_to_oct "${str}") )
done
echo "{$(join_by , "${OCTAL_STRING_ARR[@]}")}"
}
encode(){
# Given a command returns the bashfuck'd version of it
CMD=$1
USE_DEFAULT_BASH_STR=$2
if [[ ${USE_DEFAULT_BASH_STR} -eq 1 ]]
then
SHELL_STR='bash'
else
SHELL_STR="\${!#}"
fi
ARG=('bash' '-c' "${CMD}")
ENCODED_CMD=$(arg_to_cmd "${ARG[@]}" )
PAYLOAD="${SHELL_STR}<<<${ENCODED_CMD}"
echo "${PAYLOAD}"
}
main(){
TEST_MODE=0
USE_DEFAULT_BASH_STR=0
VERBOSE=0
while [[ "$#" -gt 0 ]]
do
case "$1" in
-v|--verbose) VERBOSE=1; shift 1;;
-b|--bash) USE_DEFAULT_BASH_STR=1; shift 1;;
-t|--test) TEST_MODE=1; shift 1;;
-h|--help) usage; echo >&2; _help; exit 1;;
-*) usage; echo "Unknown option: $1" >&2; exit 1;;
*) CMD="$*"; break;;
esac
done
if [[ -z "${CMD}" ]]
then
usage
echo "Missing required parameter: cmd" >&2
exit 1
fi
PAYLOAD=$(encode "${CMD}" ${USE_DEFAULT_BASH_STR})
if [[ ${VERBOSE} -eq 1 ]]
then
echo "cmd: \`${CMD}\`"
echo "result (${#PAYLOAD} byte)"
fi
echo "${PAYLOAD}"
if [[ ${TEST_MODE} -eq 1 ]]
then
# Runs a bashfuck'd command
/bin/bash -c "${PAYLOAD}"
fi
exit 0
}
main "$@"