0%

[CISCN2024 线下赛]备战AWDP

备战6月23日的CISCN线下赛 AWDP

前言

本次CISCN华东南赛区我们学校是承办方,当然晋级名额变成三队,不过由于本人实力有限加上最后黑灯的时候差一点做出来easycms-revenge,本来都打算退役了。在看到主办方发的晋级名单后,发现我们以倒一晋级线下赛,实在是续了下香火。既然都进了,总不能玩几小时pvz吧,所以打算从今天开始备战AWDP

AWDP

首先了解一下什么是 AWDP ,AWDP模式(Attack,Defense,WebandPwn),分为 Break 与 Fix 环节。根据英文全称也可以看出来,只有 Web 和 Pwn 这两个方向的题目。

每个战队拥有相同的起始分数及相同配置的虚拟靶机,参赛队员需对平台中的GameBox发起攻击,向平台提交正确的flag(证明自己具备对该题的攻击能力);在此期间,由平台以轮次制的方式向参赛战队的靶机发起攻击,检查其他选手的漏洞是否修补成功,若修补成功则认为参赛战队具备该漏洞的防御能力。

简单来说,AWDP 和传统 CTF 并无任何区别,仅仅是多了一个 Fix 功能,也就是你提交 flag 后拿到的是攻击分,而 Fix 成功后才会拿到防御分。

文件传输

这里以xshell和xftp为例

我们先使用xshell连接服务器,进入到test文件夹

然后新建一个shell,点击新建文件传输

然后自动弹出来xftp,添加会话连接

连接好后,右键对应文件点击传输即可

防守WAF

参考文章

PHP

RCE

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

SQL注入

1
2
3
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);
}

XSS

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

文件上传

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
<?php

// 允许上传的文件类型
$allowedExtensions = array("jpg", "jpeg", "png", "gif");

// 上传文件的目录
$uploadDir = "uploads/";

// 检查文件是否上传成功
if (isset($_FILES["file"])) {
$file = $_FILES["file"];

// 获取文件的扩展名
$extension = pathinfo($file["name"], PATHINFO_EXTENSION);

// 检查文件类型是否允许上传
if (!in_array(strtolower($extension), $allowedExtensions)) {
echo "只允许上传以下类型的文件: " . implode(", ", $allowedExtensions);
exit;
}

// 检查上传文件是否为真实的图像文件
if (getimagesize($file["tmp_name"]) === false) {
echo "上传文件不是有效的图像文件。";
exit;
}

// 生成一个唯一的文件名以避免重复
$uniqueFileName = uniqid() . "." . $extension;

// 移动上传的文件到目标目录
if (move_uploaded_file($file["tmp_name"], $uploadDir . $uniqueFileName)) {
echo "文件上传成功!";
} else {
echo "文件上传失败。";
}
}

XXE

1
2
3
4
5
<?php
$xmlData = file_get_contents('php://input');
libxml_disable_entity_loader(true);
$xml = simplexml_load_string($xmlData);
echo $xml->name;

Python

自定义过滤关键词

1
2
3
4
5
filter_list = ["apple", "banana", "cherry"]
strings = "ana" # 匹配包含"ana"的字符串
for i in filter_list:
if i in strings:
print("Hacker!" )

Nodejs

自定义过滤关键词

1
2
3
4
5
6
7
const keywords = ["apple", "banana", "cherry"];

for (const i of keywords) {
if (code.includes(i)) {
console.log("Hacker!")
}
}

Java

自定义过滤关键词

1
2
3
4
5
6
7
8
String[] filterList = {"apple", "banana", "cherry"};
String str = "ana"; // 匹配包含"ana"的字符串

for (String s : filterList) {
if (s.contains(str)) {
System.out.println("Hacker!");
}
}

SSRF

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
68
69
70
/**
@ author: Drunkbaby
@ usages: 用于 SSRF 的自定义防护
去到对应的接口下面修改即可
*/

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;


/**
@ author: Drunkbaby
@ usages: 用于 SSRF 的自定义防护
@ ban 了几个协议,如果单纯全部阻挡什么 127.0.0.1,业务不一定过得去。
*/

@Component
@WebFilter(urlPatterns = "/system/role/list", filterName = "sqlInjectFilter")
public class sqlFilter implements Filter {
public void destroy() {
}

public void init(FilterConfig arg0) throws ServletException {
}

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 获得所有请求参数名
Enumeration params = request.getParameterNames();
String sql = "";
while (params.hasMoreElements()) {
// 得到参数名
String name = params.nextElement().toString();
// 得到参数对应值
String[] value = request.getParameterValues(name);
for (int i = 0; i < value.length; i++) {
sql = sql + value[i];
}
}
if (sqlValidate(sql)) {
throw new IOException("您发送请求中的参数中含有非法字符");
} else {
chain.doFilter(request, response);
}
}

/**
* 参数校验
* @param str
*/
public static boolean sqlValidate(String str) {
str = str.toLowerCase();//统一转为小写
String badStr = "file|http|jar|gopher|tar|war";
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
//循环检测,判断在请求参数当中是否包含SQL关键字
if (str.indexOf(badStrs[i]) >= 0) {
return true;
}
}
return false;
}
}

log4j2

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
package com.ruoyi.common.xss;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

/**
@ author: Drunkbaby
@ usages: 用于 log4j2 的自定义防护
@ 过滤 url:在 WebFilter 当中添加 urlPatterns
*/

@Component
@WebFilter(urlPatterns = "/system/role/list", filterName = "sqlInjectFilter")
public class Log4j2Filter implements Filter {
public void destroy() {
}

public void init(FilterConfig arg0) throws ServletException {
}

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 获得所有请求参数名
Enumeration params = request.getParameterNames();
String sql = "";
while (params.hasMoreElements()) {
// 得到参数名
String name = params.nextElement().toString();
// 得到参数对应值
String[] value = request.getParameterValues(name);
for (int i = 0; i < value.length; i++) {
sql = sql + value[i];
}
}
if (sqlValidate(sql)) {
throw new IOException("您发送请求中的参数中含有非法字符");
} else {
chain.doFilter(request, response);
}
}

/**
* 参数校验
* @param str
*/
public static boolean sqlValidate(String str) {
str = str.toLowerCase();//统一转为小写
String badStr = "$|$$|jndi|rmi|ldap|{|}|";
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
//循环检测,判断在请求参数当中是否包含log4j2关键字
if (str.indexOf(badStrs[i]) >= 0) {
return true;
}
}
return false;
}
}

文件上传

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
/**
@ author: Drunkbaby
@ usages: 用于文件上传的接口修改
@ 需要在对应接口中修改
*/

@RestController
public class securityUpload {
@RequestMapping("/securityUpload")
public String handleFileUpload(@RequestParam("file") MultipartFile file){
if (file.isEmpty()){
return "请上传文件";
}
// 获取文件名
String fileName = file.getOriginalFilename();
String suffix = fileName.substring(fileName.lastIndexOf("."));
String contentType = file.getContentType();

//过滤
String[] picWhite = {".png",".jpg",".gif",".webp",".bmp"};
String[] white_type = {"image/gif","image/jpeg","image/jpg","image/png"};
Boolean SuffixFlag = false;
Boolean TypeFlag = false;
for (String pic_suffix:picWhite){
if (suffix.toLowerCase().equals(pic_suffix)){
SuffixFlag = true;
break;
}
}
for (String white_suffix:white_type){
if (contentType.toLowerCase().equals(white_suffix)){
TypeFlag = true;
break;
}
}
if (!SuffixFlag||!TypeFlag){
return "File Type not allow";
}

String filePath = System.getProperty("user.dir")+"/tmp";

Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
String newfileName = dateFormat.format(date)+Integer.toHexString((int)new Date().getTime())+suffix;

File dest = new File(filePath+File.separator+newfileName);
if (!dest.getParentFile().exists()){
dest.getParentFile().mkdirs();
}
try{
file.transferTo(dest);
return "上传成功";
} catch (IOException e) {
e.printStackTrace();
return "上传失败";
}
}
}

fastjson

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
package FastjsonFilters;


import java.util.Objects;
import java.util.regex.Pattern;

/**
@ author: Drunkbaby
@ usages: 用于 Fastjson 的自定义防护
@ 针对 1.2.24 版本,则只添加过滤即可,若针对其他版本
ParserConfig.getGlobalInstance().setAutoTypeSupport(false); 将 autoTypeSupprt 设置为 false
*/

public class Fastjson1224Filter {
public String Unserjson(@RequestParam String str, @RequestParam String input) throws Exception {
if (str != null && Objects.hashCode(str) == secret.getKey().hashCode() && !secret.getKey().equals(str)) {
String pattern = ".*rmi.*|.*jndi.*|.*ldap.*|.*\\\\x.*";
Pattern p = Pattern.compile(pattern, 2);
boolean StrMatch = p.matcher(input).matches();
if (StrMatch) {
return "Hacker get out!!!";
}

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(input);
}

return "hello";
}
}

Golang

自定义过滤关键词

1
2
3
4
5
6
7
filterList := []string{"apple", "banana", "cherry"}
str := "ana" // 匹配包含"ana"的字符串
for _, s := range filterList {
if strings.Contains(s, str) {
fmt.Println("Hacker!")
}
}

防御修复

打包&解包

1
tar -zcvf update.tar.gz update.sh file1 file2
1
tar -zxvf 文件名.tar.gz

如果要求把要修补的文件上传到服务器里,替换原来服务器中的文件,所以我们要知道文件具体的路径,大概思路就是在根目录下查找该漏洞对应的文件路径,然后把我们修复的上传即可

PHP

php不用重启服务,我们先cp把源码保存下来

1
cp index.php ../index.php

然后把改好的文件移到对应位置

1
mv -f explorer.php index/explorer.php

python

1
2
3
4
#!/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

1
2
3
4
#!/bin/sh
cp ./app.js /app/app.js
ps -ef | grep app.js | grep -v grep | awk '{print $2}' | xargs kill -9
nohup node /app/app.js || tail -f /dev/null &

参考文章

https://5ime.cn/awdp.html

https://blog.csdn.net/weixin_51614272/article/details/125527593

https://fushuling.com/index.php/2024/06/15/ciscn%e8%a5%bf%e5%8d%97%e5%a4%8d%e8%b5%9bawdp-web/