返回

中转cloudflare的安全隐患与隐患利用

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

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

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

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

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

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

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

  2. 对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
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
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

ip段加端口段扫描

防止滥用,暂时不公开 [ppblock ex=“输入密码查看代码”]

#!/bin/bash
#扫描使用iptables反代cloudflare的脚本
#该脚本用于对指定的ip段的端口段进行扫描,查看其是否反代/中转了cloudflare
TURL='https://v2ex.com'
HOST='v2ex.com'
SNI='v2ex.com'
FILENAME="fcfscan.txt"
#获取ip段
read -p "请输入需要扫描的服务器ip:" server_ip
read -p "请输入掩码长度:" netmask
read -p "请输入扫描起始端口:" port_start
read -p "请输入扫描的结束端口:" port_end
#检测输入合法性
#declare -i port_start
#declare -i port_end
touch $FILENAME

if [ ${port_start} -gt ${port_end} ]; then
  echo "输入格式出错,起始端口应小于结束端口"
  exit 1
elif [ $port_end -gt 65535 ]; then
  echo "输入格式出错,端口过大"
  exit 1
fi
#获取ip段信息
ipc_result=$(ipcalc -nb ${server_ip}/${netmask})
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
port_num=$(($port_end - $port_start + 1))
total_port_num=$(($host_num * $port_num))
continue_portnum=$port_start
scanned_port_num=0
#记录扫描耗时
start_time=$(date +%s)
echo "-------------------------------------------------------" >> $FILENAME
echo "开始进行cf中转扫描 ip范围:${host_min}~${host_max} 掩码长度:${netmask} 共有:${host_num}个ip" >> $FILENAME
echo "扫描端口范围:${port_start}~${port_end} 范围内:${port_num}个端口 总共:${total_port_num}个端口" >> $FILENAME
#暂时不考虑服务器禁止icmp
function pingCheck() {
  echo "检测${1}的在线情况"
  ping $1 -q -c2  > /dev/null
  if [ $? -eq 0 ]; then
    echo "${1}在线"
    return 0
  else
    echo "${1}不在线"
    #端口进度要更新一下
    scanned_port_num=$continue_portnum
    return 1
  fi  
}
# echo "扫描端口范围:${port_start}~${port_end} 范围内:${port_num}个端口 总共:${total_port_num}个端口" | tee -a $FILENAME
trap 'onCtrlC' INT
function onCtrlC () {
  #之后可以考虑做成可以断点继续扫描的形式
  #现场保护 如果没扫描完就退出就创建一个临时文件
  if [ $scanned_port_num -ne $total_port_num ]; then
    touch ".${FILENAME}"
    #保存状态...其实有点麻烦..涉及到的数据有点多,暂时不做了
    #考虑到下面写入了服务器列表,所以这里不保存数组了,只保存当前扫描到的ip 最大的ip(或者剩下的ip数) 端口范围即可
    echo ${scan_ip} > ".${FILENAME}"
    echo ${host_max} >> ".${FILENAME}"
    echo ${port_start} >> ".${FILENAME}"
    echo ${port_end} >> ".${FILENAME}"
  fi
  # touch ".${FILENAME}_stat"
  end_time=$(date +%s)
  echo "服务器列表 ${find_servers[*]}" | tee -a $FILENAME
  echo -e "手动中止扫描,耗时$(($end_time - $start_time))秒,对${host_min}~${host_max}${port_start}~${port_end}端口扫描,共找到${find_count}个可用端口${find_server_count}个可用服务器" | tee -a $FILENAME
  exit 0
}
#二重循环.扫描每个ip的指定端口范围
#循环扫描端口
find_count=0
find_server_count=0
find_servers=()
#要将字符串列表转变为数组,只需要在前面加(),所以关键是将分隔符转变为空格分隔,这个数组用于递增切换地址
scanip_arr=($(echo $host_min | tr '.' ' '))
sip=1
for ((j = 1;j <= $host_num;j++))
do
#当前服务器不是中转服务器
find_server_flag=false
#拼接当前要扫的ip
scan_ip=$(echo ${scanip_arr[*]} | tr ' ' '.')
#为了加快扫描速度,先检查服务器是否可以ping通,暂时不考虑禁Iicmp情况
continue_portnum=$(($continue_portnum+$port_num))
pingCheck ${scan_ip}
pc=$?
#如果ping得通才扫描,不然跳过
if [ $pc -eq 0 ]; then
  echo "开始对ip:${scan_ip} 进行扫描 当前进度(${j}/${host_num})"
  sport=0
    for ((i = $port_start;i <= $port_end;i++))
    do
      #暂时只看https,为了节约时间,不检测对80端口的转发
      let scanned_port_num++
      let sport++
      res=$(curl --connect-timeout 1 --max-time 2 -w %{http_code} -o /dev/null -s -I --resolve ${SNI}:${i}:${scan_ip} https://${SNI}:${i}/cdn-cgi/trace)
      #echo "$res  已经找到:$find_count个可用"
      if [ $res == 200 ]; then
        let find_count++
        #如果一个ip找到了中转端口,说明这个服务器可能有更多的端口用作中转,记录该服务器
        if [ $find_server_flag == false ]; then
          echo "发现新的中转服务器:${scan_ip}" >> $FILENAME
          find_server_flag=true
          let find_server_count++
          find_servers[${#find_servers[@]}]=$scan_ip
        fi
        #写入文件
        #echo "ip:${scan_ip} 端口:${i}可用"  | tee -a $FILENAME
        echo "ip:${scan_ip} 端口:${i}可用" >> $FILENAME
      fi
      percentage=$(echo "scale=2; $scanned_port_num / $total_port_num" | bc)
      clear
      echo -e "ip进度:(${sip}/${host_num}) 端口进度:(${sport}/${port_num})正在扫描端口$i 已经找到:$find_count个可用端口,$find_server_count个中转服务器 总进度:($scanned_port_num/$total_port_num) $percentage%"
      #curl -w %{http_code} -s -I --resolve ${SNI}:${i}:${server_ip} https://${SNI}:${i}/cdn-cgi/trace
    done
  fi
#之后要对iparr进行增加
let sip++
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
end_time=$(date +%s)
echo "服务器列表 ${find_servers[*]}" | tee -a $FILENAME
echo "耗时$(($end_time - $start_time))秒,完成对${host_min}~${host_max}${port_start}~${port_end}端口扫描,共找到${find_count}个可用端口${find_server_count}个可用服务器" | tee -a $FILENAME
exit 0

[/ppblock]

利用

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

[ppblock ex=“还是不要做坏事了”] 想到的利用方法如下:

  1. 用来科学上网

    注 利用cloudflare来上网是对cloudflare资源的滥用,我个人是不建议这样做的。

    有很多人(尤其是nat上扫出来的),使用iptables反代cloudflare的目的就是用作科学上网,尤其是广东的部分移动可以连接到cloudflare的香港节点,速度相当的快,所以有些人出于保护ip,或者是加快速度(某些情况,尤其是连接香港节点的时候,cf真的能够改善线路不好的机器的速度),在扫描出反代端口之后我们也可以。

    我先用curl看了看连接的是哪个节点。

    可以看见是hkg节点
    可以看见是hkg节点

    我用了这个nat的端口来中转我的ruvds测试了一下,本来最近ruvds30卢布机器都绕美国了,速度实在不行,但是套上cloudflare后,再经过广州移动这一中转…速度快了很多啊。

    image-20201105220000319
    image-20201105220000319

  2. 用来反代网站

    不过反代网站建议另外弄一个脚本按ip段扫描443/80端口。不过也可以尝试一下。我就不改dns了,直接修改本机host来展示。

    可以发现,网站也是可以正常反代的,之后我还会编写一个脚本来对指定的ip段进行80/443端口扫描,如果有人用iptables来反代cloudflare加速网站的话…那么他的反代节点就会被我们利用,而且iptables也不会记录什么信息…可能最多只能知道有很多ip连接了这个端口,但是无法区分。于是我们可以借助他人的反代节点来加速我们的网站 不过感觉很不道德呀…

    反代网站测试
    反代网站测试
    [/ppblock]

后记

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

参考

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

关于curl的几种时间参数

comments powered by Disqus
本站访客数:
Built with Hugo
Theme Stack designed by Jimmy