9

照弹不误:出站端口受限环境下反弹Shell的思考

 4 years ago
source link: https://www.freebuf.com/vuls/232544.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

“是否允许出站”这件事我一直以为无需过多思考,无非限制出站协议,或者限制出站端口,对于限制端口的目标十有八九也会保留 80、443,向这两个端口反弹基本能拿到 shell,直到遇到这个目标,引发我对出站端口受限的环境下, 如何 成功反弹 shell 的思考。

这次遇到的目标,通过 nday 轻松拿到 webshell,打点的过程毫无波澜,甚至有一丝无聊: bYBrmyE.jpg!web

webshell 虽然赋予我执行命令、管理文件的能力,但毕竟不是真正的 shell,无法执行交互式命令、无法控制进程状态、无法补全命令等等,非常不利于提权操作以及横向移动,所以,必须反弹 shell。

肌肉记忆让我没有过多思考,攻击端监听端口 12321: UFVRvmv.jpg!web

目标上执行反弹命令: 2iY7z2a.jpg!web

等待许久,攻击端就是不见回连信息,显然,一定是目标上的某种防御在作祟。导致反弹失败的因素很多,反弹命令不存在、禁止出站 IP、流量审查等等都有可能,于是,我从积累的知识库中搜索所有可能,再逐一验证。比如,我在目标上确认 bash 命令的确存在: fEjY7jF.jpg!web

在攻击端能收到目标发起的 ping 探测,攻击端能收到 icmp 记录:

EJjiUnU.jpg!web

用 SSL 加密回连流量仍然反弹失败: 3EFNBz3.jpg!web

我陷入沉思,这台内网机器,既然公网可见,说明它的边界处有个防火墙作公网 IP 映射: EZNrEnf.jpg!web

一般来说,防火墙可能干两件事,一是限制出站协议、一是限制出站端口。协议方面,前面已经验证过 ICMP 可以出网而 HTTP 不行,所以,我可以尝试用 ICMP 隧道反弹,即便可行,效率肯定也不高;端口方面,目标通过防火墙配置出站端口策略,若限制端口黑名单较大或允许端口白名单较小,则攻击者很难猜测出哪些端口有效。从以往的经验判断,80、443 是两个可能性较大的允许出站的端口,于是我又依次尝试向 80、443 反弹,失败!

捋一捋,通过漏洞获取了 webshell,该主机只开放了 HTTP 服务的 80 端口,要获得 shell 有两种方式,正向连接、反向连接。正向连接,目标上用 nc 监听的端口对攻击端不可见,只得利用类似 reGeorg 建立 HTTP 隧道;反向连接,目标上用 nc 回连攻击端 IP 及端口。前者效率低下,聚焦后者,目标允许向外访问任意 IP,但严格限制只能访问外部某几个特定端口,攻击者必须找出有效端口,否则反弹 shell 的流量无法通过防火墙到达攻击端。所以,我需要猜解出站端口。

猜解出站端口,思路与 SQLi 或 CMDi 盲注的带外信道手法差不多,即,攻击端监听某个端口,目标访问攻击端的该端口,若攻击端出现端口访问记录则说明该端口是目标允许的出站端口。思路简单,但实现不一定容易,要成功猜解出有效端口,需要关注三方面事宜:目标的端口探测命令、猜测的端口范围、攻击端的端口请求记录。

目标的端口探测命令

说到端口状态探测,第一时间想到的肯定是 nmap、masscan、nc 这类专用工具。看下目标上有没有: 6rUney6.jpg!web

毫无悬念地没有;自己装个 masscan 试试: AzQjy2a.jpg!web

权限又不够,即便有权限,估计也连接内网的镜像源;那我上传个与目标架构、发行套件一致的 nmap 呢: jIv2YnU.jpg!web

缺失依赖出现段错误而无法运行。

没事,除了以上那些专用工具外,linux 中还有其他挺多可选的方式,可以利用 python、perl各类脚本语言探测端口,也能借助 curl、wget 这类 HTTP 客户端实现,只要能请求服务的命令均可,但是,环境就是那么苛刻,我要啥它没啥: iyEFZfU.jpg!web

好嘛,这时,我觉得基本无解,不借助第三方命令不可能达到端口探测的目的。休息一会,是喝口水去打乒乓球哇,还是去打乒乓球喝口水呢。回过头,检查之前的反弹命令是否有误: j6reUji.jpg!web

突然,我注意到 /dev/tcp 后面的 12321 端口,( ⊙ o ⊙ )#,这不就是在探测端口么。一切皆文件!linux 下探测端口状态竟如此简单,比如,探测 baidu.com 的 443 端口,一定存活: rAni6z2.jpg!web

但由于这不是专门的端口探测工具,所以存在三个问题,一是如何批量输入端口,二是如何控制任务超时,三是如何查看探测结果。于是,我开始在本地探索解决思路。

批量端口问题。这个不难,使用 bash 的 for 语句即可: j2iqayi.jpg!web

若目标过滤大括号,可考虑: iEf2auB.jpg!web

若目标过滤小于号,可考虑: BNRJfei.jpg!web

甚至无需任何特殊字符: a6zqqm7.jpg!web

解决了第一个问题。

超时控制问题。当我用 /dev/tcp 去探测某个存在的端口时,命令将立即返回,而探测某个不存活端口时,命令将挂起,直到强制退出: naqiQju.jpg!web

所以,必须得想个法子让它超时时自动结束。超时,timeout,哇,这么简单啊。比如,本来要执行 8s 的命令: JBfQ7rY.jpg!web

借助 timeout命令可以实现超时控制: aqQVFrj.jpg!web

第二个问题搞定。

查看探测结果。你知道,在 bash 中命令执行结果可以通过环境变量 $? 查看,成功为 0、是否非 0,换言之,我需要找个方式判断 $? 是否为 0,常规方式用 if 语句,或者,更优的方式,短路运算符 && 和 ||: AvI3uyb.jpg!web

第三个问题也不是问题了。

解决了以上三个问题,我在不借助任何外部命令情况下,实现了批量探测端口: uyYjIbr.jpg!web

虽然这次关于出站端口探测的思考是由 linux 的目标引起的,但从知识体系完整性考虑,我应该把 windows 系统也考虑进去。windows 命令行环境条件苛刻,几乎没有专门用于端口探测的内部命令,只能借助那些访问服务的命令间接实现,另外,我仍然需要解决批量端口、超时控制等问题。

批量输入端口,cmd 中的 for 语句可以实现: N7JFBrU.jpg!web

控制任务超时,linux 有 timeout 命令,本质上,一旦超时则向目标进程发送 KILL 信号,达到控制进程运行时长的目的。win 也有 timeout,但语义完全不同,类似 linux 的 sleep 命令,没关系,参考 linux 版 timeout 原理,搭配 taskkill 命令也能间接实现: Ifuuque.jpg!web 其中,ping -n 32 127.0.0.1 模拟长时间运行的进程,timeout /t 1 为等待时长,一旦等待时长到期则用 taskkill /im ping.exe 杀死进程。虽然不及 linux 优雅,聊胜于无。

具体到端口探测,大概有 tnc 模块和 telnet 命令两种方式。

tnc 模块方式。tnc 也就是 powershell 的 Test-NetConnection 模块,专门用于网络连接性测试: ZJnaIvA.jpg!web

其中,tnc 默认先用 ICMP 探测 IP 存活性,通过指定 -InformationLevel 选项为 Quiet 参数,可忽略 IP 探测,只关注端口是否存活,类似 nmap 的 -Pn 选项,以提高端口探测效率。

telnet 命令方式。服务器上基本安装了 telnet,也可用于探测端口状态: zAbeUjV.jpg!web

用 telnet 命令访问攻击端的 [440, 445] 的端口,每次访问限时 1s。

猜测的端口范围

目标既然允许访问外部的几个端口,一定是那些端口提供目标所需服务,那么,我没必要一开始就到 [1, 65535] 中去找有效端口,而是根据服务的常见程度,将所有端口划为几个层次,依次逐层猜解。

第一层,最常见端口。经验来看,DNS 的 53、HTTP 服务的默认端口 80、HTTPS 的 443 是三个最常见的出站端口。

win 下执行如下命令行确认出站端口: 77RrMnf.jpg!web linux 下执行: 2yaU3mb.jpg!web

第二层,较常见端口。大概包括如下几类:

web 服务, HTTP 的 80、HTTPS 的 443;

中间件服务,weblogic 的 7001/7002、webshpere 的 9080/9090、jboss 的 8080;

远程管理服务,SSH 的 22、telnet 的 23、SNMP 的 161、RDP 的 3389;

数据传输服务,FTP 的 21、SCP/SFTP 的 22、SMB 的 137/138/139/445;

数据库服务,mssql 的 1433、mysql 的 3306、oracle 的 1521、LDAP 的 389;

缓存服务,redis 的 6379、memcached 的 11211;

邮件服务,SMTP 的 25、POP3 的 110、IMAP 的 143;

其他服务,DNS 的 53、NTP 的 123、kerberos 的 88;

win 下探测: zyAjquE.jpg!web linux 下探测: 32qmAze.jpg!web

第三层,top100 常见端口。nmap 默认扫描 top1000 常见端口,通过 –top-ports 选项可以指定扫描更多常见端口: FBRVN3U.jpg!web 也就是说,nmap 内部梳理了一份已知服务默认端口列表,并且还能按常见程度排序,简直巴适,要获取 top100 常见端口: BvyYfar.jpg!web

win 下探测: iUR3yuN.jpg!web

linux 下探测: fyqmY3u.jpg!web

采用相同思路,我可以持续探测 top200、top400 常见端口。

攻击端的端口请求记录

从目标发起的端口访问请求,攻击端必须得配合记录,否则即便找到有效的出站端口,我也无法获悉。

思路一,单个逐次监听端口。对于少量端口的探测,攻击端很容易记录。比如,要验证 windows 目标的 8088 端口是否为出站端口,我先在攻击端用 nc -n -v -lp 8088 监听 8088,指定 -v 选项观察实时访问记录,再在目标上用 telnet 192.168.56.8 8088 连接攻击端的 8088 端口,最后在攻击端查看端口访问记录,若有则该端口是有效出站端口: yiYNBj2.jpg!web

若无则重复以上步骤继续验证其他端口。

对于 top100 甚至 top1000 这样大规模的端口探测,当前验证目标的 80 端口,那么攻击端也要联动监听 80,验证 81 则联动监听 81,手工执行 nc 不断监听不同端口是不现实的,这就需要一个脚本,控制攻击端的监听动作和目标端的探测动作,倒是可以写个这样的脚本,但通用性不强,我得找寻更加普适性的方法。

思路二,批量捆绑监听端口。试想一下,如果能够把攻击端的多个端口流量转发至单个汇聚端口,就只需监听单个汇聚端口,目标上发起多个端口探测,只要在攻击端转发的多个端口的范围内,那么,一旦找到有效出站端口,攻击端的汇聚端口一定有访问记录。说到端口转发,系统自带的 ssh、iptables,三方的 frp、nps,这些工具都能高效实现,于是,我从这四个工具中找寻具备端口捆绑能力的那位,简单查阅资料,iptables 就是我需要的。只需一条命令行即可实现端口捆绑,如下:

sudo iptables -A PREROUTING -t nat -p tcp --dport 1:65535 -j REDIRECT --to-port 4442

这样,就把 65535 个全量端口捆绑至 4442 端口。

验证下,在攻击端将全量端口捆绑至 4442 端口: AZ7jimA.jpg!web

监听 10086 端口: YFbMjmR.jpg!web

靶机目标取消所有出站限制,访问攻击端的 10086 端口: NZva2eY.jpg!web

显示端口不存活!这正是端口捆绑的必然结果,因为攻击端接收到的 10086 端口访问流量已被转发至 4442 端口,而 nc 并未监听 4442,所以没有访问记录。接下来,nc 监听 4442: FR7Vfmj.jpg!web

目标端再次访问 10086: ze63qaY.jpg!web

你看,攻击端收到访问记录了: 7NjIVni.jpg!web

验证完毕,不再需要端口捆绑,恢复先前规则: 7rIN73q.jpg!web

安逸哇,这样我监听单个端口即可获取全量端口的探测记录。如果再结合结合 MSF 的 reverse_tcp_allports 载荷,甚至可以省去找寻有效端口的步骤,直接反弹 shell。

windows 环境的模拟实验

简单模拟实战中反弹 shell 的场景。先看 windows 环境。

第一步,查看环境信息。靶机 IP 为 192.168.56.9,如下: YRzyAnZ.jpg!web

攻击端 IP 为 192.168.56.8,如下: y6rINfb.jpg!web

第二步,配置出站策略。在操作系统自带防火墙中增加出站策略,只允许 2048 端口出站,即禁止 1-2047 及 2049-65535,规则管理界面中配置即可: YBFZR3v.jpg!web

攻击端监听 2047 端口: uq2Yriq.jpg!web

从目标访问攻击端的 2047 端口,结果为不存活: iayuQf2.jpg!web

攻击端重新监听 2048 端口: naaE3uV.jpg!web

从目标访问攻击端的 2048 端口,结果为存活: Ujyuuiu.jpg!web

说明只放行 2048 端口的出站策略已生效。

第三步,攻击端端口捆绑。为了便于接收目标发起的端口探测,将攻击端的全量端口捆绑至 4442 端口: Bn2YBza.jpg!web

并在攻击端监听汇聚端口 4442: BJvAFv2.jpg!web

第四步,目标端猜解出站端口。现在,我的身份恢复为攻击者,假定先前通过漏洞拿到目标 192.168.56.9 的命令执行权限,且执行结果有回显。尝试在 nmap 定义的 top100 常见端口范围内找寻允许出站的端口,很快找到 2048 为有效端口: EFFJJ3Y.jpg!web

第五步,反弹 shell。首先,攻击端取消端口捆绑: VriEveJ.jpg!web

然后,攻击端生成反弹端口为 2048 的 ps1 脚本木马: MbAfqaU.jpg!web

并启动 MSF 监听 2048 端口: I7zMb2N.jpg!web

接着目标上执行 ps1 木马: VzMfuyu.jpg!web

最后顺利反弹 shell: RJbUrqN.jpg!web

拿下真实目标

现在,我把本地推演的思路应用到目标环境中。

第一步,攻击端捆绑端口至 12321,并监听该端口: ze6Vbqy.jpg!web

第二步,目标上探测 top100 常见端口,执行: fya2ymf.jpg!web

啊( ⊙ o ⊙ )啊!居然报错,不应该,难道是 webshell 中无法执行复杂的 shell 语句?不急,把它写入个脚本文件中执行看看,新建脚本文件 x.sh: fyE3MvA.jpg!web

写入完整命令行: RJZJjaR.jpg!web

赋予执行权限后执行: YbeIbiV.jpg!web

很快即可查看到结果: iIRBZrZ.jpg!web

无一存活!

第三步,继续尝试 top200、top300 的端口,仍然没找到;尝试 top400: QfmmmaR.jpg!web

哈哈哈哈,还真找到了它,42510,唯一允许出站的那个端口!

第四步,反弹 shell。攻击端取消端口捆绑,监听 42510 端口接收 shell: vEBnya7.jpg!web

目标向攻击端的 42510 端口反弹: VnIny2n.jpg!web

攻击端成功接收 shell: ZNziUnM.jpg!web

呵呵,这下心情舒畅了,晚上整火锅。

好了,以后遇到能 ping 通外网但无法反弹的目标,得多个心眼考虑是否限制了出站端口,用上述手法尝试猜解,或许,能找到唯一的答案!

*本文原创作者:yangyangwithgnu,本文属于FreeBuf原创奖励计划,未经许可禁止转载


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK