show me she shell

这是一道tomato师傅出的不完整的java题,java…,java…我恨java┑( ̄Д  ̄)┍

这是一个题目一是列目录+任意文件读取,

二是垂直越权+CLRF配SSRF打redis+反序列化命令执行

题目的难度在于代码本身的不完整和java,没办法实际测试,所以只能强行阅读源码,幸运的是代码结构是spring完成的,和python的flask/django结构很强,这为我们阅读源码提供了可能。

1

整个代码中,控制器只有5个,其中

1

2

3

4

5

index 首页

login 登陆、注册

manager 管理员管理

post 用户发送post

user 用户功能,包括上传头像和删除自己发送的post

entity是python中类似于model的定义,其中包括了User、Post

interceptor主要负责路由以及权限设置,核心代码如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@Override

public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception {

String requestUri = request.getRequestURI();

for (String s : excludedUrls) {

if (requestUri.endsWith(s)) {

return true;

}

}

User user = (User) request.getSession().getAttribute("user");

if(user == null){

request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response);

return false;

}else{

return true;

}

}

通过request.getRequestURL获取连接,其中后缀在excludedUrls的不需要登陆,其他都需要登陆才能访问。

关于excludedUrls的设置在配置文件中

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/register.do

/login.do

/doregister.do

mapper其中包含了部分核心函数,但只有函数定义,没有代码

service中包含了关于user操作和post操作的核心函数

utiles是一些其余的核心函数

第一个漏洞点其实比较容易发现,在user的控制器中我们可以看到关于更换头像的函数

1

2

3

4

5

6

7

8

@RequestMapping(value = "/headimg.do",method = RequestMethod.GET)

public void UpdateHead(@RequestParam("url")String url){

String downloadPath = request.getSession().getServletContext().getRealPath("/")+"/headimg/";

String headurl = "/headimg/"+ HttpReq.Download(url,downloadPath);

User user = (User) session.getAttribute("user");

Integer uid = user.getId();

userMapper.UpdateHeadurl(headurl,uid);

}

关于获取头像的地方调用了HttpReq.Download函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

public static String Download(String urlString,String path){

String filename = "default.jpg";

if(endWithImg(urlString)) {

try {

URL url = new URL(urlString);

URLConnection urlConnection = url.openConnection();

urlConnection.setReadTimeout(5*1000);

InputStream is = urlConnection.getInputStream();

byte[] bs = new byte[1024];

int len;

filename = generateRamdonFilename(getFileSufix(urlString));

String outfilename = path + filename;

OutputStream os = new FileOutputStream(outfilename);

while ((len = is.read(bs)) != -1) {

os.write(bs, 0, len);

}

os.close();

is.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return filename;

}

这里调用URL类来获取返回

1

2

URL url = new URL(urlString);

URLConnection urlConnection = url.openConnection();

但这之前我们需要绕过endWithImg的判断

1

2

3

4

5

6

7

8

9

private static boolean endWithImg(String imgUrl){

if(StringUtils.isNotBlank(imgUrl)&&(imgUrl.endsWith(".bmp")||imgUrl.endsWith(".gif")

||imgUrl.endsWith(".jpeg")||imgUrl.endsWith(".jpg")

||imgUrl.endsWith(".png"))){

return true;

}else{

return false;

}

}

函数比较清楚,对图片链接的结尾做了判断,也很好绕过,我们可以用形似

1

http://11111/111.php?a=1.jpg

就可以直接绕过判断了,这里还算比较明白,我们可以直接用file协议去读本地文件,形似file:///etc/passwd?a=1.jpg就可以获取文件内容了。

唯一的问题是,我们如何找到flag位置了,这就涉及到一个小trick了

在java中,我们可以用file:///或netdoc:///来列目录

通过这种方式,我们可以获取到服务器上的第一个flag

2

当然这里的第一题是当时的非预期,因为这种列目录方式只在java中才有,我们回到题目继续分析。

在第一题中我们找到了一个SSRF漏洞,在第二题中,修复了headimg使用file协议读文件的漏洞,但我们可以用CRLF向Redis写入数据。

1

headimg.do?url=http://127.0.0.1%0a%0dSET%20A%20A:6379

–>

1

redis set A A

但是有什么用呢?

让我们再回到题目代码

在managercontroller中,我们可以发现所有关于redis的操作都在这里,但这里有一个限制是要求当前用户的isadmin必须为1,但整个代码中并没有任何关于这部分的操作,所以我们顺着回顾代码中可能接触到设置isadmin的位置。

跟入注册代码controller.LoginController中,关于注册的代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@RequestMapping(value = "/doregister.do",method = RequestMethod.POST)

public String DoRegister(User user, String repassword, Model model){

String result = userService.register(user,repassword);

if(result.equals("ok")){

return "login";

}else{

model.addAttribute("message",result);

return "register";

}

}

@RequestMapping(value = "/register.do",method = RequestMethod.GET)

public String Register(){

return "register";

}

跟入userService.register函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public String register(User user,String repassword) {

String username = user.getUsername();

String password = user.getPassword();

if(StringUtils.isBlank(username.trim())|| StringUtils.isBlank(password.trim())){

return "You need set username and password";

}

int uid = userMapper.SelectIdByUsername(username);

if(uid>0){

return "This username has been registered!";

}

if(!password.equals(repassword)){

return "repassword";

}

userMapper.InsertUser(user);

return "ok";

}

仔细观察我们可以发现,虽然函数中从user中获取了username和password并进入userMapper.SelectIdByUsername验证,但在插入数据的时候仍然直接传入了user类。

这里我们看看user类的定义(这应该是类似于python中model的定义方式)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public class User{

private Integer id;

private String username;

private String password;

private String headurl;

private Boolean isadmin;

public User(Integer id, String username, String password, String headurl, Boolean isadmin) {

this.id = id;

this.username = username;

this.password = password;

this.headurl = headurl;

this.isadmin = isadmin;

}

...

我们可以注意到这个函数在初始化时接受了isadmin,而在控制器中路由接收到这个参数时也没有做任何的处理,所以这里存在AutoBuilding漏洞

当我们在注册的时候,原post参数为

1

username=test&password=test&repassword=test

我们只要加入isadmin即可

1

username=test&password=test&repassword=test&isadmin=1

我们成功给当前用户加入了管理员权限

在获得了manager权限后,我们就可以执行manager控制器下的操作了,让我们来看看代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

@RequestMapping(value = "/audit.do")

public String AuditPost(@RequestParam("pid") Integer pid,HttpSession session) {

User user = (User) session.getAttribute("user");

try {

if (user.getIsadmin()) {

postMapper.AuditPost(pid);

Post post = postMapper.GetOne(pid);

redisClient.set(pid,post);

return "manager";

}

}catch (Exception e){

return "redirect:/";

}

return "redirect:/";

}

@RequestMapping(value = "/check.do")

public String CheckPost(@RequestParam("pid") Integer pid, HttpSession session, Model model){

User user = (User) session.getAttribute("user");

try {

if (user.getIsadmin()) {

Post post = redisClient.getObject(pid);

model.addAttribute("post", post);

return "manager";

}

}catch(Exception e){

return "redirect:/";

}

return "redirect:/";

}

这其中有一个特殊的操作就是对于redis的操作,关于redis的代码在utils.RedisClient中

1

2

3

4

5

6

7

8

9

10

11

12

13

public void set(Integer id, T t) {

byte[] key = getKey(id);

RedisSerializer serializer = redisTemplate.getValueSerializer();

byte[] val = serializer.serialize(t);

getConnection().set(key, val);

}

public T getObject(Integer id) {

byte[] key = getKey(id);

byte[] result = getConnection().get(key);

return (T) redisTemplate.getValueSerializer().deserialize(result);

}

很明显其中的getObject函数有反序列化的操作,如果我们想要通过反序列化来构造RCE的话,我们需要一个gadget.

这里tomato用了SpringAbstractBeanFactoryPointcutAdvisor

https://github.com/mbechler/marshalsec

这下思路就非常清晰了,整个利用链如下

注册->使用AutoBuilding越权登陆->使用headimg的ssrf配合crlf向redis中写入序列化数据->check.do反序列化->RCE

完整exp如下

java 解析sh文件内容_java 文件读取相关推荐

  1. java 读取文件内容_Java如何读取txt文件的内容?

    这个并不困难,大概的步骤是这样的: TXT是一个文本文件,一般采用流的方式读取: java提供了一个FileInputStream,我们可以直接以文件路径构造这个流,也可以以文件对象构造他,如:Fil ...

  2. java 读文件 二进制_JAVA中读取文件(二进制,字符)内容的几种方法总结

    JAVA中读取文件内容的方法有很多,比如按字节读取文件内容,按字符读取文件内容,按行读取文件内容,随机读取文件内容等方法,本文就以上方法的具体实现给出代码,需要的可以直接复制使用 public cla ...

  3. python读文件每一行-Python文件内容按行读取到列表中

    Python文件内容按行读取到列表中 示例文件内容如下: Hello World Python 通常来讲,我们如果只是迭代文件对象每一行,并做一些处理,是不需要将文件对象转成列表的,因为文件对象本身可 ...

  4. Java解析魔兽争霸3录像W3G文件(五):Action和APM计算

    在游戏进行中,玩家会进行各种操作,例如编队.移动.技能.造建筑等,这些操作就是Action.APM(Actions Per Minute),表示每分钟的操作次数,APM可以很好的反映玩家的手速和实力, ...

  5. linux命令看文件内容,Linux文件内容查看相关命令

    1.more命令 在Linux中,more命令是一个基于vi编辑器的文本过滤器,它能以全屏的方式按页显示文本文件的内容,more里面内置了一些快捷键. (1)命令语法 more(选项)(参数) (2) ...

  6. 比Everything更强的文件搜索工具,支持文件名、文件内容和文件图片上的文字搜索,文件内容搜索工具,文件图片内容搜索工具,OCR图片文本识别搜索,文件快速搜索工具,文字识别文件搜索工具

    Windows自带的文件搜索功能想必不需要过多吐槽,搜索速度简直是在龟爬,所以小编很早之前就在用Everything进行文件搜索了,不过,今天的主角不是它,而是比它更更更更更强的一款软件! 这款软件适 ...

  7. 无法确定本地文件类型_如何从文件内容确定文件类型

    无法确定本地文件类型 I. Introduction 一,引言 There's an interesting discussion going on now in an Experts Exchang ...

  8. java ftp读取文件内容_java读取ftp中TXT文件的案例

    最近在开发关于java读取ftp中TXT文件,其中有些坑踩了一下,再次做个记录 1.读取文件时我会根据文件名称去生成数据库表,oracle数据库对于表名的长度是有限制的,最多30个字符 2.对于多个文 ...

  9. java 按行读取大文件文件内容_Java实现按行读取大文件

    Java实现按行读取大文件 String file = "F:" + File.separator + "a.txt"; FileInputStream fis ...

最新文章

  1. 后备干部,究竟应该提拔什么样的员工?
  2. 个人IHttpHandler,IHttpModule认识
  3. php 数组 组成新数组,PHP让数组中相同值的组组成新的数组详解
  4. excel合并两列内容_还在为合并WPS表格(Excel)中两列内容而犯愁?此方法简单高效...
  5. 基于智慧教室|无纸化会议的新选择:RTMP解决方案
  6. java 接口 设计模式吗_JAVA接口设计模式-工厂模式
  7. android excel布局,Androidui布局控件(2)表格布局excelPanel
  8. A3C的算法原理和算法流程
  9. 遍历JSON的三种方法
  10. APP银联支付(微信、支付宝、云闪付)
  11. navicat mysql 数据库备份_怎么用navicat自动备份mysql数据库
  12. 老挑毛u盘一键装系统计算机意外地,u盘装系统 重装Win7系统出现提示计算机意外的重新启动或遇到错误怎么处理 我已经删除了所有分...
  13. *4-2 CCF 2014-12-2 Z字形扫描
  14. HTML+CSS大作业 电影网站设计——电影介绍(11页) 大学生电影网页作品 电影网页设计作业模板 学生网页制作源代码下载
  15. 算法训练营 图的应用(最小生成树)
  16. 输出姓名对应的电话号码C语言,C语言电话本程序(只是简单的姓名和电话号码增删改查),在线等...
  17. 高德坐标与谷歌坐标互相转换
  18. springboot+flyway+oracle11g+mysql8 整合包
  19. 1248. 统计「优美子数组」 前缀和
  20. 要给视频批量添加背景音乐该怎么办?

热门文章

  1. 程序买卖(一舟,改自网络)
  2. MongoDB的使用技巧(转)
  3. 查看java的dump日志并进行分析
  4. JDK源码(8)-Byte
  5. http 几种请求方法的差别
  6. 使用static代码块实现线程安全的单例设计模式
  7. REEM-C的安装与启动
  8. Nginx服务架构初探(七):nginx邮件服务
  9. 为什么成员属性不会被重写
  10. 将中国标准时间转换成标准格式的代码