目录

  1. 1. 前言
  2. 2. Waf
    1. 2.1. PHP
      1. 2.1.1. 命令执行
      2. 2.1.2. sql注入
      3. 2.1.3. XSS
      4. 2.1.4. 目录穿越
      5. 2.1.5. 文件上传
    2. 2.2. Python
    3. 2.3. Node
    4. 2.4. Java
      1. 2.4.1. thymeleaf ssti
      2. 2.4.2. 任意方法调用
      3. 2.4.3. 任意文件读取
      4. 2.4.4. 重写resolveClass
    5. 2.5. Golang
  3. 3. Update

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

AWDP个人知识库

2024/6/21 AWDP
  |     |   总文章阅读量:

前言

参考:

https://fushuling.com/index.php/2023/06/01/awd/

https://5ime.cn/awdp.html#nexmoe-content

https://hackerpoet.com/index.php/archives/988/


Waf

PHP

命令执行

function wafrce($str){
	return !preg_match("/openlog|syslog|readlink|symlink|popepassthru|stream_socket_server|scandir|assert|pcntl_exec|fwrite|curl|system|eval|assert|flag|passthru|exec|chroot|chgrp|chown|shell_exec|proc_open|proc_get_status|popen|ini_alter|ini_restore/i", $str);
}
if (preg_match('/passthru|system|tail|flag|exec|base64/i', $_SERVER['REQUEST_URI'])) {
    die('no!');
}

sql注入

function wafsqli($str){
	return !preg_match("/select|and|\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleexml|extractvalue|+|regex|copy|read|file|create|grand|dir|insert|link|server|drop|=|>|<|;|\"|\'|\^|\|/i", $str);
}
$username = addslashes($username);
$password = addslashes($password);

XSS

function wafxss($str){
	return !preg_match("/\'|http|\"|\`|cookie|<|>|script/i", $str);
}

目录穿越

$action = (isset($_GET['action']) ? $_GET['action'] : 'home.php');
$deny_ext = array("..","../");
if (in_array($action, $deny_ext)){
    if (file_exists($action)) {
        include $action;
            } else {
                echo "File not found!";
            }
		}

文件上传

<?php
$allowed_extensions = array("jpg", "png", "gif"); // 允许上传的文件后缀名

    $filename = $_FILES["file"]["name"];
    $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); // 获取文件后缀名并转换为小写
    
    if (!in_array($extension, $allowed_extensions)) { // 检查文件后缀名是否在允许上传的列表中
     echo "不允许上传该类型的文件!";
        
    } else {
        $target_file = "uploads/" . basename($filename); // 为上传文件指定目标路径
        move_uploaded_file($_FILES["file"]["tmp_name"], $target_file); // 将文件从临时目录移动到目标路径
        echo "文件上传成功!";
    }
} 

Python

filter_list = ["apple", "banana", "cherry"]
strings = "ana"  # 匹配包含"ana"的字符串
for i in filter_list:
    if i in strings:
		raise ValueError("Hacker!")
    else:
        ...

Node

过滤后缀:如果依赖里有上传中间件

// 配置 multer 上传中间件
const upload = multer({
    storage: storage, // 使用自定义存储选项
    fileFilter: (req, file, cb) => {
        const fileExt = path.extname(file.originalname).toLowerCase();
        if (fileExt === '.ejs') {
            // 如果文件后缀为 .ejs,则拒绝上传该文件
            return cb(new Error('Upload of .ejs files is not allowed'), false);
        }
        if (fileExt === '.js') {
            // 如果文件后缀为 .js,则拒绝上传该文件
            return cb(new Error('Upload of .js files is not allowed'), false);
        }
        cb(null, true); // 允许上传其他类型的文件
    }
});

过滤目录穿越的后缀:

if (newPath && /app\.js|\\|\.ejs|node_modules/i.test(newPath)) {
    return res.status(400).send('Invalid file name');
}
if (oldPath && /\.\.|flag|node_modules/i.test(oldPath)) {
    return res.status(400).send('Invalid file name');
}

过滤关键词:

function waf(query) {
  if (query.includes("flag") || query.includes("nc")) {
    throw new Error("禁止使用 flag 和 nc 关键字!");
  } else {
    // 执行正常操作
  }
}

Java

检测解码后的payload是否含有恶意类

String sea = new String(decodemsg);
if(!sea.contains("com.sea.")){
    return "filtered";
}

thymeleaf ssti

@Controller
public class AboutController {
  @GetMapping({"/about"})
  public String about(HttpSession session, @RequestParam(defaultValue = "") String type) {
    String username = (String)session.getAttribute("name");
    if (StringUtils.isEmpty(username))
      return "about/tourist/about"; 
    if (!type.equals(""))
      return "about/" + type + "/about"; 
    return "about/user/about";
  }
}

fix版本:

@Controller
public class AboutController {
  @GetMapping({"/about"})
  public String about(HttpSession session, @RequestParam(defaultValue = "") String type) {
    String username = (String)session.getAttribute("name");
    if (StringUtils.isEmpty(username))
      return "about/tourist/about"; 
    if (!type.equals("")) {
      if (type.equals("system"))
        return "about/system/about"; 
      if (type.equals("tourist"))
        return "about/tourist/about"; 
      if (type.equals("user"))
        return "about/user/about"; 
      return "index";
    } 
    return "about/user/about";
  }
}

任意方法调用

@Controller
public class LogOutController {
  @GetMapping({"/logout"})
  public String logout(HttpServletRequest request, HttpSession session, @RequestParam(defaultValue = "logout") String method, @RequestParam(defaultValue = "com.mengda.awd.Utils.SessionUtils") String targetclass) throws Exception {
    Class<?> ObjectClass = Class.forName(targetclass);
    Constructor<?> constructor = ObjectClass.getDeclaredConstructor(new Class[0]);
    constructor.setAccessible(true);
    Object CLassInstance = constructor.newInstance(new Object[0]);
    try {
      if (method.equals("logout")) {
        Method targetMethod = ObjectClass.getMethod(method, new Class[] { HttpSession.class });
        targetMethod.invoke(CLassInstance, new Object[] { session });
      } else {
        Method targetMethod = ObjectClass.getMethod(method, new Class[] { String.class });
        targetMethod.invoke(CLassInstance, new Object[] { request.getHeader("X-Forwarded-For") });
      } 
    } catch (Exception e) {
      return "redirect:/";
    } 
    return "redirect:/";
  }
}

fix:

@Controller
public class LogOutController {
  @GetMapping({"/logout"})
  public String logout(HttpServletRequest request, HttpSession session, @RequestParam(defaultValue = "logout") String method, @RequestParam(defaultValue = "com.mengda.awd.Utils.SessionUtils") String targetclass) throws Exception {
    String[] blackList = { 
        "cat", "flag", "exec", "tac", "/", "*", "sh", "bash", "Runtime", "ProcessBuilder", 
        "ProcessImpl", "UNIXProcess", "File", "Read", "run", "build", "start" };
    for (String s : blackList) {
      if ("X-Forwarded-For".contains(s))
        return "index"; 
    } 
    for (String s : blackList) {
      if (targetclass.contains(s))
        return "index"; 
    } 
    for (String s : blackList) {
      if (method.contains(s))
        return "index"; 
    } 
    Class<?> ObjectClass = Class.forName(targetclass);
    Constructor<?> constructor = ObjectClass.getDeclaredConstructor(new Class[0]);
    constructor.setAccessible(true);
    Object CLassInstance = constructor.newInstance(new Object[0]);
    try {
      if (method.equals("logout")) {
        Method targetMethod = ObjectClass.getMethod(method, new Class[] { HttpSession.class });
        targetMethod.invoke(CLassInstance, new Object[] { session });
      } else {
        Method targetMethod2 = ObjectClass.getMethod(method, new Class[] { String.class });
        targetMethod2.invoke(CLassInstance, new Object[] { request.getHeader("X-Forwarded-For") });
      } 
      return "redirect:/";
    } catch (Exception e) {
      return "redirect:/";
    } 
  }
}

任意文件读取

@WebFilter(urlPatterns = {"/*"})
public class myFilter implements Filter {
  public void init(FilterConfig filterConfig) throws ServletException {
    super.init(filterConfig);
  }
  
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)servletRequest;
    String request_uri = URLDecoder.decode(request.getRequestURI(), "utf-8");
    if (Check.check(request_uri).booleanValue()) {
      String static_resources_path = "/usr/local/tomcat/webapps/app/WEB-INF/classes/static/" + request_uri;
      static_resources_path = URLDecoder.decode(static_resources_path, "utf-8");
      try {
        servletResponse.getWriter().write(File.readFile(static_resources_path));
      } catch (Exception e) {
        servletResponse.getWriter().write("error~");
      } 
    } else {
      filterChain.doFilter(servletRequest, servletResponse);
    } 
  }
  
  public void destroy() {
    super.destroy();
  }
}

fix:直接对File类下手

public class File {
  public static String readFile(String filePath) throws Exception {
    String[] blackList = { "flag", ".." };
    for (String s : blackList) {
      if (filePath.contains(s))
        return "hacker"; 
    } 
    String file_content = "";
    FileInputStream fileInputStream = null;
    try {
      try {
        fileInputStream = new FileInputStream(filePath);
        byte[] bytes = new byte[4];
        while (true) {
          int readCount = fileInputStream.read(bytes);
          if (readCount == -1)
            break; 
          file_content = file_content + new String(bytes, 0, readCount);
        } 
        fileInputStream.close();
      } catch (FileNotFoundException e) {
        e.printStackTrace();
        fileInputStream.close();
      } 
      return file_content;
    } catch (Throwable th) {
      fileInputStream.close();
      throw th;
    } 
  }
}

重写resolveClass

参考:https://flowerwind.github.io/2021/05/01/%E4%B8%BA%E4%BB%80%E4%B9%88%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E9%87%8D%E5%86%99resolveClass%E6%9D%A5%E7%99%BD%E5%90%8D%E5%8D%95%E9%98%B2%E5%BE%A1java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/

在将字节码恢复为类的过程中,调用了resolveClass方法,而这个resolveClass方法如果被重写的话,调用的就是重写类的resolvceClass

我们如果在重写的resolvceClass中写入代码,即可控制需要恢复类的名称

protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    Iterator var2 = BLACKLIST.iterator();

    String s;
    do {
        if (!var2.hasNext()) {
            return super.resolveClass(desc);
        }

        s = (String)var2.next();
    } while(!desc.getName().startsWith(s));

    throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}

static {
    BLACKLIST.add("com.sun.jndi");
    BLACKLIST.add("com.fasterxml.jackson");
    BLACKLIST.add("org.springframework");
    BLACKLIST.add("com.sun.rowset.JdbcRowSetImpl");
    BLACKLIST.add("java.security.SignedObject");
    BLACKLIST.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    BLACKLIST.add("java.lang.Runtime");
    BLACKLIST.add("java.lang.ProcessBuilder");
    BLACKLIST.add("java.util.PriorityQueue");
}

Golang

package main

import (
	"fmt"
	"strings"
)

func main() {
	filterList := []string{"apple", "banana", "cherry"}
	str := "ana" // 匹配包含"ana"的字符串
	for _, s := range filterList {
		if strings.Contains(s, str) {
			panic("Hacker!")
		}
	}
}

Update

update.sh

常规php文件:

cp 要替换的文件 /var/www/html/待替换的文件

python:

#!/bin/sh

cp /app.py /app/app.py
ps -ef | grep python | grep -v grep | awk '{print $2}' | xargs kill -9 
cd /app && nohup python app.py  >> /opt/app.log 2>&1 &

nodejs:

#!/bin/sh

cp server.js /app/server.js
ps -ef | grep node | grep -v grep | awk '{print $2}' | xargs kill -9 
cd /app && nohup node server.js  >> /opt/aa.log 2>&1 &

golang:

cp ./修改后编译好的文件 /编译文件的目录
ps -ef |grep go编译的名字| awk '{print $2}' | xargs kill -9
cd /编译文件目录 &&./执行编译文件 > /dev/null 2>&1 

java:

直接覆盖jar包重启环境