惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
Blog — PlanetScale
Blog — PlanetScale
J
Java Code Geeks
Y
Y Combinator Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
GbyAI
GbyAI
Vercel News
Vercel News
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
Jina AI
Jina AI
B
Blog
Recorded Future
Recorded Future
MyScale Blog
MyScale Blog
I
InfoQ
aimingoo的专栏
aimingoo的专栏
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
爱范儿
爱范儿
Last Week in AI
Last Week in AI
博客园 - 三生石上(FineUI控件)
博客园 - Franky
Schneier on Security
Schneier on Security
V
V2EX
TaoSecurity Blog
TaoSecurity Blog
H
Hacker News: Front Page
Cloudbric
Cloudbric
D
DataBreaches.Net
B
Blog RSS Feed
P
Palo Alto Networks Blog
云风的 BLOG
云风的 BLOG
NISL@THU
NISL@THU
I
Intezer
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Cyberwarzone
Cyberwarzone
F
Fortinet All Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
K
Kaspersky official blog
Forbes - Security
Forbes - Security

Hi, I Am I

[I Am I 年度简报] — 不知终日梦为鱼 初探 ESP32-CAM QQ 聊天记录 MHT 文件转 HTML [I Am I 年度简报] - 草木本无意,荣枯自有时。 Hexo 中实现 Live Photos 支持 写在当下 NKCTF 2024 1z_F0r3ns1c5 Writeup 春秋杯冬季赛 2023 Writeup [I Am I 年度简报] - 2023 某内网渗透内部赛 Writeup 强网拟态 2023 Writeup Github Actions 自动化部署 Hexo 浅析CobaltStrike流量解密 陇剑杯 2023 Writeup ISCC 2023 Writeup ISCC 2023 实战题 Writeup CISCN 2023 Writeup 福建闽盾杯网络空间安全大赛 2023 Writeup 天一永安杯宁波市网络安全大赛 2023 Writeup 贵阳大数据及网络安全精英对抗赛 2023 Writeup 红明谷杯 2023 Writeup Confetti 带来有仪式感的鼓励 记一次 JS 逆向密码加密 [I Am I 年度简报] – 2022 PHP 读取 Excel 文件内容并写入数据库 从0开始的 MoeCTF 开发之路 观安杯 2022 Writeup 利用微信服务号实现早安自动化 Cloudflare批量拉黑IP脚本 蓝帽杯 2022 Writeup 为你的网站添加 Do you like me 小组件 ISCC 2022 Writeup CISCN 2022 Writeup ISCC 2022 实战题 Writeup CTF线下赛AWD攻防总结 [I Am I 年度简报] – 2021 记一次 CNVD 通用型漏洞证书挖掘 Google Adsense 收款流程 使用 Digispark 开发板制作 BadUSB 在 Vue 中使用 Axios 获取钉钉群直播回放的 M3U8 地址 Flask 框架学习记录 关于 Ten·API 防火墙的配置 关于最近 关于 Burp Suite 调教这档事 ISCC 2021 Writeup 记一次 CTF 环境和动态独立靶机部署 各大平台图集解析思路 情话总雷同,恨意千万种 HackThisSite Basic Writeup [I Am I 年度简报] - 2020 Kali 设置中文语言和更换镜像源 使用 Python 下载哔哩哔哩视频 PHP使用 CURL 发送网络请求 PHP蓝奏云直链解析源码 从0开始写一个短视频去水印接口 Windows+Ubuntu 双系统之美化 GRUB Windows+Ubuntu 双系统安装 树莓派安装 Aria2 实现24小时不间断的下载机 抖音无水印解析最新PHP源码 API-Admin Ten·API管理后台 树莓派安装 DLNA 实现流媒体服务器 [I Am I 年度简报] - 2019 Hello Hexo Goindex 将 Google Drive 打造成网盘 自用博客评论邮件通知美化模板 使用 IFTTT 长久保留 Google Voice 号码 Telegram MTProxy 代理一键安装脚本 三分钟学会搭建我的世界基岩版服务器 PHP跳转QQ聊天窗口源码 推荐几款开源HTML5(Web)框架 谷歌新出浏览器 Chromium 可以直接翻墙 Live2D!为你的网站添加看板娘 为你的网站添加Gittalk评论 网站数据离奇丢失...... Lsky Pro(兰空图床)—又一款单纯的图床程序 LOL明天解封ヾ(◍°∇°◍)ノ゙ 唔~本站受到DDOS攻击 [I Am I 年度简报] – 2018 死肥宅也要谈恋爱之早安晚安自动化 1024,Hello,world! OneIndex 将 OneDrive 打造成网盘 宝塔面板 Bt.cn 专业版破解教程 Uptime >>16 years 震惊!坐在家里竟然可以日入百万 免费获取一年的 .ooo 域名 PHP 调用 新浪API 生成短网址 思杰马克丁成 Adobe 中国授权经销商 畅言商业广告上线-去除畅言评论广告 使用网易云音乐官方接口解析VIP音乐 PHP获取QQ昵称和头像API 坦白说查发送人QQ新方法(已失效) 日常水一波 通过微博图片地址溯源上传者 WordPress 评论夜间自动改为必须审核 十步叫你如何无损修复硬盘锁(mbr病毒) [I Am I 年度简报] – 2017 密码破解与心理学 网页屏蔽各种按键的代码分享 网络安全技术专业术语
CTF线下赛AWDP总结
2023-06-29 · via Hi, I Am I

写在前面

最近参加了 CISCN 的分区赛,其赛制为 AWDP,博主也是第一次打 AWDP,博主是 Web 手,同时带了一个茶歇 Ak 手,1v4太痛苦啦。一上午都在二等奖,最后黑灯模式变成了三等奖…这里记录一下博主赛前准备的内容,以及一些总结复盘,同时可以和我之前写的CTF线下赛AWD攻防总结一起食用

AWDP

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

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

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

赛前准备

可以适当的准备一些通防脚本,通防住就是赚到,当然比赛的时候没防住😭,其次就是队伍内部一定要合理分工,不过博主队伍就一个主力,没考虑分工的事情,这里就不多说了。

另外要清楚一点,能防不代表能打,反之亦然,不过博主认为修复比攻击容易~~(也有可能是博主拥有开发经验的缘故)~~,所以真正到比赛的时候不要一直纠结于去攻击,可以看看如何修复。同时 Fix 或者 Break 越早越好,博主是第三轮 Break 成功一个赛题,让我多拿很多轮次的分,所以说前期 Break / Fix 速度越快越好

另外一般比赛前都会让你提前熟悉平台以及提供测试赛题进行测试,一定要测明白…当时博主感觉测试赛题没头没尾,直接没修复成功就跑路啦,导致第二天研究了俩小时如何进行打包出正确的 Fix 包,当时上传 Fix 包一直提示服务异常,后来看到公告才意识到是因为题目描述里给错了单词,patch.sh 写成了 pacth.sh 导致一直修复失败,另外感觉裁判机的 Check 机制也是很迷惑

打包的话,赛后看群友说 bandzip 可以直接对 tar.gz 进行压缩操作,博主是直接起了一个Linux虚拟机用来打包

tar -zcvf fix.tar.gz *

语言环境

最好拥有多个语言的环境(PHPGolangJavaPythonNodeJS),当时比赛前确确实实是都装好了 (其实电脑上一直都有,不过 gojava 没写过) ,这五种语言的赛题确确实实也都遇到了,但当时 Golang 不熟悉语法同时没有扩展,并未打包成功,Java 题当时有思路修复,但不会打包 war 包…结果这两种语言的赛题全部扑街

其次就是像一些常见的库都提前安装一下,比如 flaskspringboot 之类的,最后就是学一下 build 的方法

代码片段

简单来说赛前可以收集一些写好的过滤规则,到时候直接调用,节省时间。由于 PHP 类的防御代码实在是太多啦,所以这里只简单列几个。

其实最主要的还是记录一下各个语言如何循环遍历匹配关键字即可,具体的黑名单内容还是得随机应变,具体可以看下面的赛题复盘

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);
}

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);
}

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

if (preg_match('/system|tail|flag|exec|base64/i', $_SERVER['REQUEST_URI'])) {
    die('no!');
}

Python

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

Node

const keywords = ["apple", "banana", "cherry"];

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

Java

由于博主不会 Java,赛前提前去 Github 找了一个项目,JavaSecFilters JavaSec过滤器 ,给出的代码片段也非常的不错,但是比赛时候不会打包 war 直接扑街…

String[] filterList = {"apple", "banana", "cherry"};
String str = "ana"; // 匹配包含"ana"的字符串

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

Golang

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

通防脚本

不过感觉通防脚本适用场景不太行… 具体可以随机应变,据其他赛区的师傅说,使用 K4l0nG_WAF 防住了一道 ThinkPHP 的题目

https://github.com/leohearts/awd-watchbird
https://github.com/sharpleung/CTF-WAF
https://github.com/NonupleBroken/AWD_PHP_WAF
https://github.com/DasSecurity-HatLab/AoiAWD
https://github.com/dr0op/k4l0ng_WAF

赛题复盘

rceIt

The magical node patch说明: 远程patch路径为:/app/app.js 推荐pacth.sh示例如下:

#!/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 &
const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const randomize = require('randomatic');
const path = require('path');
const { VM } = require('vm2');

const app = express();
const vm = new VM();

function merge(target, source) {
    for (let key in source) {
        if (key === 'escapeFunction' || key === 'outputFunctionName') {
            throw new Error("No RCE")
        }
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

app
    .use(bodyParser.urlencoded({extended: true}))
    .use(bodyParser.json());
app.use(express.static(path.join(__dirname, './static')));
app.set('views', path.join(__dirname, "./views"));
app.set('view engine', 'ejs');
app.use(session({
    name: 'tainted_node_session',
    secret: randomize('aA0', 16),
    resave: false,
    saveUninitialized: false
}))

app.all("/login", (req, res) => {
    if (req.method == 'POST') {
        let userInfo = {}
        try {
            merge(userInfo, req.body)
        } catch (e) {
            return res.render("login", {message: "Login Error"})
        }

        if (userInfo.username == "admin" && userInfo.password === "realpassword") {
            userInfo.logined = true
        }

        req.session.userInfo = userInfo
        if (userInfo.username == "admin" && userInfo.logined == true)
        {
            return res.redirect('/sandbox')
        }
        else {
            return res.render("login", {message: "You are not admin"})
        }
    }else {
        if (req.session.userInfo){
            if (req.session.userInfo.logined == true && req.session.userInfo.username == "admin"){
                return res.redirect('/sandbox')
            }else{
                return res.render("login", {message: "You are not admin"})
            }
        }else {
            return res.render('login', {message: ""});
        }
    }
});

app.all('/sandbox', (req, res) => {
    if (req.session.userInfo.logined != true || req.session.userInfo.username != "admin") {
        return res.redirect("/login")
    }
    
    const code = req.query.code || '';
    result = vm.run((code));
    res.render('sandbox', { result });
})

app.all('/', (req, res) => {
    return res.redirect('/login')
})

app.listen(8888, () => console.log(`listening on port 8888!`))

在我印象中这道题貌似到比赛结束没人攻击成功,不过根据代码逻辑可以很快速的定位到漏洞点 /sandbox

result = vm.run((code));
res.render('sandbox', { result });

我们直接加一个判断 Fix 成功

const keywords = ["flag", "exec", "read", "open", "ls", "cat"];

for (const i of keywords) {
    if (code.includes(i)) {
        result = "Hacker!"  
    }else{
        result = vm.run((code));
    }
}

search_engine

小明写了一个搜索引擎,但是看起来似乎有些问题。 patch说明: 远程patch路径为:/app/app.py 推荐pacth.sh示例如下:

#!/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 &
from flask import *
import os
from waf import waf
import re

app = Flask(__name__)

pattern = r'([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}):([0-9]{2,5})'
content = '''<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta ip="%s">
<meta port="%s">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ciscn Search Engine</title>
</head>
<body>
<div class="htmleaf-container">
	<div class="wrapper">
		<div class="container">
			<h1>Ciscn Search Engine</h1>
			<form class="form" method="post" action="/" id="Form">
				<input name="word" type="text" placeholder="word">
				<button type="submit" id="login-button">Search</button>
			</form>
		</div>
		<ul class="bg-bubbles">
			<li>%s</li>
		</ul>
	</div>
</body>
</html>'''

@app.route("/", methods=["GET", "POST"])
def index():
	ip, port = re.findall(pattern,request.host).pop()
	if request.method == 'POST' and request.form.get("word"):
		word = request.form.get("word")
		if not waf(word):
			word = "Hacker!"
	else:
		word = ""

	return render_template_string(content % (str(ip), str(port), str(word)))


if __name__ == '__main__':
	app.run(host="0.0.0.0", port=int(os.getenv("PORT")))

后来通过 SSTI 读出来了 waf.py 的内容

import re
def waf(data):
    pattern=r'^[\x20-\x7e]+$'
    if len(re.findall(pattern,data))!=1:
        return False
    blackwords=['message','listdir','self','url_for','_','"',"os","read","cat","more","`","[","]","class","config","+","eval","exec","join","import","popen","system","header","arg","form","os","read","write","flag","ls","ll","sort","nl","",";",":","\\"]
    for blackword in  blackwords:
        if blackword in data:
            return False 
    return True

原本修复的话打算直接在 waf.pyblackwords 列表内再加几个函数,但是一直没 Fix 成功,这题卡我一上午(嗯…前面提到的卡我俩小时就是这题),最后直接在 waf(word) 前面再加一个判断 Fix 成功

filter_list = ["{", "(", "lipsum", "attr"]
for i in filter_list:
	if i in word:
		word = "Hacker!"
if not waf(word):
	word = "Hacker!"