freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

照弹不误:出站端口受限环境下反弹Shell的思考
2020-04-09 09:30:29

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

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

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

肌肉记忆让我没有过多思考,攻击端监听端口 12321:1586066373_5e8973c5ee041.png!small

目标上执行反弹命令:1586066385_5e8973d180ee7.png!small

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

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

微信图片_20200407094734.png

用 SSL 加密回连流量仍然反弹失败:1586066402_5e8973e23be98.png!small

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

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

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

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

目标的端口探测命令

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

毫无悬念地没有;自己装个 masscan 试试:1586066439_5e8974070738f.png!small

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

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

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

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

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

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

批量端口问题。这个不难,使用 bash 的 for 语句即可:1586066467_5e89742369624.png!small

若目标过滤大括号,可考虑:1586066472_5e897428b31e8.png!small

若目标过滤小于号,可考虑:1586066478_5e89742e45978.png!small

甚至无需任何特殊字符:1586066482_5e897432432d0.png!small

解决了第一个问题。

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

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

借助 timeout命令可以实现超时控制:1586066497_5e8974411f8d7.png!small

第二个问题搞定。

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

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

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

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

批量输入端口,cmd 中的 for 语句可以实现:1586066513_5e8974511361e.png!small

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

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

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

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

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

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

猜测的端口范围

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

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

win 下执行如下命令行确认出站端口:1586066555_5e89747b7598f.png!smalllinux 下执行:1586066565_5e8974852207e.png!small

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

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 下探测:1586066571_5e89748bea920.png!smalllinux 下探测:1586066578_5e897492565eb.png!small

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

win 下探测:1586066596_5e8974a44e8db.png!small

linux 下探测:1586066601_5e8974a976b57.png!small

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

攻击端的端口请求记录

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

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

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

对于 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 端口:1586066633_5e8974c9a532e.png!small

监听 10086 端口:1586066637_5e8974cda4598.png!small

靶机目标取消所有出站限制,访问攻击端的 10086 端口:1586066643_5e8974d3203af.png!small

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

目标端再次访问 10086:1586066652_5e8974dc11ea6.png!small

你看,攻击端收到访问记录了:1586066656_5e8974e0a8c3e.png!small

验证完毕,不再需要端口捆绑,恢复先前规则:1586066661_5e8974e51f995.png!small

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

windows 环境的模拟实验

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

第一步,查看环境信息。靶机 IP 为 192.168.56.9,如下:1586066681_5e8974f9707c4.png!small

攻击端 IP 为 192.168.56.8,如下:1586066688_5e897500464cd.png!small

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

攻击端监听 2047 端口:1586066700_5e89750c113fa.png!small

从目标访问攻击端的 2047 端口,结果为不存活:1586066704_5e89751063d04.png!small

攻击端重新监听 2048 端口:1586066709_5e897515b4903.png!small

从目标访问攻击端的 2048 端口,结果为存活:1586066715_5e89751b660e7.png!small

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

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

并在攻击端监听汇聚端口 4442:1586066726_5e897526d8764.png!small

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

第五步,反弹 shell。首先,攻击端取消端口捆绑:1586066742_5e8975362a0b4.png!small

然后,攻击端生成反弹端口为 2048 的 ps1 脚本木马:1586066746_5e89753acec96.png!small

并启动 MSF 监听 2048 端口:1586066759_5e897547968e6.png!small

接着目标上执行 ps1 木马:1586066766_5e89754ea8bc4.png!small

最后顺利反弹 shell:1586066770_5e897552d66d8.png!small

拿下真实目标

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

第一步,攻击端捆绑端口至 12321,并监听该端口:1586066784_5e8975605a298.png!small

第二步,目标上探测 top100 常见端口,执行:1586066788_5e8975648cbb2.png!small

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

写入完整命令行:1586066798_5e89756e3329e.png!small

赋予执行权限后执行:1586066801_5e897571b7a7a.png!small

很快即可查看到结果:1586066805_5e897575f2c13.png!small

无一存活!

第三步,继续尝试 top200、top300 的端口,仍然没找到;尝试 top400:1586066814_5e89757e83950.png!small

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

第四步,反弹 shell。攻击端取消端口捆绑,监听 42510 端口接收 shell:1586066818_5e8975826e424.png!small

目标向攻击端的 42510 端口反弹:1586066822_5e8975868ab73.png!small

攻击端成功接收 shell:1586066827_5e89758b1854a.png!small

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

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

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











# 反弹shell # 端口 # 出站
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者