December 3, 2020

反代/中转cloudflare的安全隐患与隐患利用

转载来自:https://notesail.com/posts/danger-of-cloudflare-transit.html

本文核心:不要直接中转或反代cloudflare!存在着被他人利用的风险,还有就是不要利用文章内容做坏事!

我发现出于各种目的,很多人喜欢用iptables或者是brook、socat等工具对cloudflare的端口进行中转或者是反代,但是这些工具都有一个共同的特点,不会检查传入的主机名(域名)就直接将流量转发给了cloudflare,这里存在着一个巨大的风险——他人只要知道了你反代/中转服务器的相应端口,那他只需要指定host与sni,就可以利用你的反代/中转服务器通过cloudflare与他自己的服务器进行数据交换。

补充 除了使用iptables,像是socat、brook等端口转发工具同样有被利用的风险,因为他们都没有检查传入的域名,如果真的有反代cloudflare的需求,请使用nginx并指定主机名。另外这篇文章仅出于折腾目的而写,无论是利用cloudflare来科学上网还是利用别人的服务器,都是不道德的。

出于学习的目的,我想对这个方案进行一下测试与利用,看看能否扫描出反代或是中转cloudflare的机器的相应端口,顺便练习一下shell脚本的编写,大概会实现下面两个功能:

  1. 对指定ip的端口段进行扫描 (已经简单实现)

适合用来找出一台机器上反代了cloudflare的端口(如商家将机器切分成nat出来销售,某些用户会用iptables反代cloudflare)

  1. 对ip段的80/443端口进行扫描 (还没有实现)

适合大范围查找反代了cloudflare进行建站的机器。

可行性测试

假设我们的域名

example.com使用了cloudflare,并点亮了橙色云朵(开启了cdn),那么我们可以访问,example.com/cdn-cgi/trace,如果可以正常访问,那么就意味着成功通过cloudflare访问到了这个机器。

我再找一台机器使用iptables端口转发脚本对cloudflare进行中转。

wget --no-check-certificate -qO natcfg.sh https://raw.githubusercontent.com/arloor/iptablesUtils/master/natcfg.sh && bash natcfg.sh

并添加如下中转

设置中转

之后我们尝试使用

https://中转机器ip:10086,发现如果没指明host是没办法访问的。

报错

之后我们使用curl进行测试

如果是80端口,那么我们需要设置HEADER中的host

curl -H 'Host: 开启了CDN的域名' http://中转机ip:10087

curl检测http

但是对于https,因为host记录在header中,而https需要先进行tls握手,tls握手信息中并没有包含目标网站的主机名,所以需要再通过SNI协议来指明访问的主机名是什么。

如果我们不设置sni直接尝试通过 https访问10086端口,会返回错误 curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number

对于开启了https的端口(比如iptables转发的是cloudflare的ip的443端口),可以使用这种方式,--resolve参数介绍如下

--resolve <host:port:address[,address]...> Resolve the host+port to this address

作用类似于修改了dns,将example.com:10086的请求导向了后面的中转机的ip:相同端口

curl --resolve 开启了cdn的域名:10086:中转机ip https://开启了cdn的域名:10086/cdn-cgi/trace

正确返回后的请求

另外补充几条之后需要用到的curl参数

  • curl -I 可以显示响应头,如果对于/cdn-cgi/trace的请求响应头是404,说明这机器反代的并不是cloudflare,200则代表是cloudflare。
  • --connect-timeout SECONDS 设置连接超时时间(连接超时指的是连不上服务器,比如域名无法解析的情况)
  • -m, --max-time SECONDS 设置请求超时的时间,如果连接上了,但是半天没有回复的时间
  • -s 不显示进度条信息
  • -w %{http_code}

-w可以指定curl的输出,而我们只需要查看http状态码即可

  • -o /dev/null 如果我们只要状态码,可以把原来的输出丢弃

shell脚本编写

单ip端口段扫描

当前脚本功能还是比较简单,只扫描某一指定ip的指定ip段,之后我考虑编写扫描ip段指定端口的脚本。或者加入多线程扫描。

#!/bin/bash
#扫描使用iptables反代cloudflare的脚本
#该脚本用于对指定的一个ip的指定范围的端口进行扫描,寻找使用了iptables反代cloudflare的端口
TURL=''
HOST=''
SNI=''

read -p "请输入需要扫描的服务器ip:" server_ip
read -p "请输入扫描起始端口:" port_start
read -p "请输入扫描的结束端口:" port_end
#检测输入合法性
#declare -i port_start
#declare -i port_end
touch find_result.txt
if [ ${port_start} -gt ${port_end} ]; then
  echo "输入格式出错,起始端口应小于结束端口"
  exit 1
elif [ $port_end -gt 65535 ]; then
  echo "输入格式出错,端口过大"
  exit 1
fi
echo "开始对${server_ip}的${port_start}~${port_end}进行扫描" | tee -a find_result.txt
#循环扫描端口
find_count=0
for ((i = $port_start;i <= $port_end;i++))
do
  echo -e "正在扫描端口$i 状态:\c"
  res=$(curl --connect-timeout 3 --max-time 2 -w %{http_code} -o /dev/null -s -I --resolve ${SNI}:${i}:${server_ip} https://${SNI}:${i}/cdn-cgi/trace)
  echo $res
  if [ $res == 200 ]; then
    let find_count++
    echo "找到可用端口${i}"  | tee -a find_result.txt
    #写入文件
  fi
  #curl -w %{http_code} -s -I --resolve ${SNI}:${i}:${server_ip} https://${SNI}:${i}/cdn-cgi/trace
done
echo "完成对${server_ip}的${port_start}~${port_end}进行扫描,共找到${find_count}个可用端口" | tee -a find_result.txt

ip段扫描

当前实现的脚本效率比较低,考虑到未来的优化方案,可以写成程序,先使用nmap筛选出80/443开放的ip,然后再使用多线程扫描,这样应该能快上很多。

这里首先用到了ipcalc这个程序,用来根据输入ip与掩码长度计算ip范围。效果如下

image-20201106091353955
#!/bin/bash
#扫描使用iptables反代cloudflare的脚本
#该脚本用于对指定的ip段的80/443端口扫描查看是否反代了cloudflare
TURL=''
HOST=''
SNI=''

read -p "请输入需要扫描的ip:" server_ip
read -p "请输入掩码长度:" netmask
ipc_result=$(ipcalc -nb ${server_ip}/${netmask})
#起始ip地址
touch ipsg_scan.txt
# echo $ipc_result
host_min=$(echo "${ipc_result}" | awk '/HostMin/ {print $2}')
host_max=$(echo "${ipc_result}" | awk '/HostMax/ {print $2}')
host_num=$(echo "${ipc_result}" | awk '/Hosts\/Net/ {print $2}')
netmask=$(echo "${ipc_result}" | awk '/Netmask/ {print $2}')
host_address=$(echo "${ipc_result}" | awk '/Address/ {print $2}')
if [ $host_num -eq 1 ]; then
host_min=$host_address
host_max=$host_address
fi
# host_max=$(echo "${ipc_result}" | egrep '^HostMax:' | egrep '[0-9].*' -o)
echo "开始扫描,最小ip地址:${host_min},最大ip地址:${host_max},ip数量:${host_num}" | tee -a ipsg_scan.txt
find_count=0
find_port_count=0
#要将字符串列表转变为数组,只需要在前面加(),所以关键是将分隔符转变为空格分隔
scanip_arr=($(echo $host_min | tr '.' ' '))
# echo "ip arr: ${scanip_arr[*]}"
for ((i = 1;i <= $host_num;i++))
do
scan_ip=$(echo ${scanip_arr[*]} | tr ' ' '.')
percent=$(echo "scale=3; $i / $host_num * 100" | bc)
#443端口扫描
echo -e "正在扫描ip:${scan_ip} 扫描百分比 ${percent}%\t443端口:\c"
res443=$(curl --connect-timeout 2 --max-time 3 -w %{http_code} -o /dev/null -s -I --resolve ${SNI}:443:${scan_ip} https://${SNI}:443/cdn-cgi/trace)
echo -e "$res443 80端口:\c"
#80端口扫描 为了加快扫描速度,可以不扫
res80=$(curl --connect-timeout 2 --max-time 2 -w %{http_code} -o /dev/null -s -I -H "Host: ${HOST}" http://${scan_ip}:80/cdn-cgi/trace)
echo $res80
if [ $res443 == 200 ] || [ $res80 == 200 ]; then
  let find_count++
  echo "找到可用ip:${scan_ip} 80端口状态:${res80} 443端口状态:${res443}"  | tee -a ipsg_scan.txt
  #写入文件
fi

#之后要对iparr进行增加
scanip_arr[3]=$((${scanip_arr[3]}+1))
if [ ${scanip_arr[3]} -gt 255 ]; then
  echo "C+1"
  scanip_arr[3]=1
  scanip_arr[2]=$((${scanip_arr[2]}+1))
fi
if [ ${scanip_arr[2]} -gt 255 ]; then
  echo "B+1"
  scanip_arr[2]=1
  scanip_arr[1]=$((${scanip_arr[1]}+1))
fi
if [ ${scanip_arr[1]} -gt 255 ]; then
  echo "A+1"
  scanip_arr[1]=1
  scanip_arr[0]=$((${scanip_arr[0]}+1))
fi
done
echo "完成扫描 共扫描了${host_num}个ip,找到${find_count}个可用ip" | tee -a ipsg_scan.txt

利用

之后我使用该脚本对国内某商家的nat母鸡ip进行了扫描...不得不说直接使用iptables反代cloudflare的人还不少。。。仅仅扫描了1000个端口就已经出现了好几个反代端口了

后记

通过这一次实践,我再一次体会到了千万不要直接使用不检查主机名的工具直接中转或者反代cloudflare的ip,不然一不小心就会被别有用心之人利用,建议要反代cloudflare的话还是改用nginx,并设置好hostname,这样才不会被利用啊。

参考

https://hacksbrain.com/2018/08/27/testing-sni-enabled-servers-with-curl/