当前位置: 首页 > news >正文

ctfshow 菜狗杯

ctfshow 小舔田?

<?php
include "flag.php";
highlight_file(__FILE__);class Moon{public $name="月亮";public function __toString(){return $this->name;}public function __wakeup(){echo "我是".$this->name."快来赏我";}
}class Ion_Fan_Princess{public $nickname="牛夫人";public function call(){global $flag;if ($this->nickname=="小甜甜"){echo $flag;}else{echo "以前陪我看月亮的时候,叫人家小甜甜!现在新人胜旧人,叫人家".$this->nickname."。\n";echo "你以为我这么辛苦来这里真的是为了这条臭牛吗?是为了你这个没良心的臭猴子啊!\n";}}public function __toString(){$this->call();return "\t\t\t\t\t\t\t\t\t\t----".$this->nickname;}
}if (isset($_GET['code'])){unserialize($_GET['code']);}else{$a=new Ion_Fan_Princess();echo $a;
}

一个经典的php反序列化,有点小复杂比较绕,总得来说就是要调用call()函数输出flag,输出flag的前提是 Ion_Fan_Princess类中的nickname为"小甜甜",然而 Ion_Fan_Princess类中的_toString()函数调用了call()方法,

然而我们是可以通过一个类的_toStrng方法去调用另外一个类中的_toString方法,那么我们就可以将创建的Moon类中的name属性设置为 Ion_Fan_Princess对象(在PHP中完全可以将一个对象的属性设置为另一个对象

那么可以得出思路

  • 创建一个<font style="color:rgb(64, 64, 64);">Moon</font>对象,将其<font style="color:rgb(64, 64, 64);">name</font>属性设置为<font style="color:rgb(64, 64, 64);">Ion_Fan_Princess</font>对象
  • 设置<font style="color:rgb(64, 64, 64);">Ion_Fan_Princess</font>对象的<font style="color:rgb(64, 64, 64);">nickname</font>为"小甜甜"
  • 序列化这个<font style="color:rgb(64, 64, 64);">Moon</font>对象并通过<font style="color:rgb(64, 64, 64);">code</font>参数传递
  • 得到序列化后的pop链
  • 当反序列化这个pop链时:
  1. 会先调用<font style="color:rgb(64, 64, 64);">Moon</font><font style="color:rgb(64, 64, 64);">__wakeup</font>方法
  2. <font style="color:rgb(64, 64, 64);">__wakeup</font>方法中<font style="color:rgb(64, 64, 64);">echo "我是".$this->name."快来赏我";</font>会尝试将<font style="color:rgb(64, 64, 64);">$this->name</font>(即<font style="color:rgb(64, 64, 64);">Ion_Fan_Princess</font>对象)当作字符串使用
  3. 这会触发<font style="color:rgb(64, 64, 64);">Ion_Fan_Princess</font><font style="color:rgb(64, 64, 64);">__toString</font>方法
  4. <font style="color:rgb(64, 64, 64);">__toString</font>方法调用<font style="color:rgb(64, 64, 64);">call()</font>方法
  5. 因为<font style="color:rgb(64, 64, 64);">nickname</font>是"小甜甜",所以会输出flag

ctfshow 一言既出

<?php
highlight_file(__FILE__); 
include "flag.php";  
if (isset($_GET['num'])){if ($_GET['num'] == 114514){assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");echo $flag;} 
} 

这个代码会检查是否存在GET传参num,传入的num值是否为114514(好啊,来啊)(松散判断,可以加入非数字字符绕过),assert函数会判断为真还是为假,当如果参数是字符串,PHP 会将其作为 PHP 代码动态执行。如果参数是表达式,PHP 会直接计算其值。

如果get传参为1919810(要去了,野兽!)即可,但是前面又要求num值为114514,逻辑上是不成立的,但是php在动态解析数字表达式的时候,遇到非数字字符就会直接停止解析, 而且assert函数会直接计算数字表达式,那我们只要直接构造payload

:::info
?num=114514%2b1805296

:::

%2b是+的url编码,上面的payload在表达式"if ($_GET['num'] == 114514)"中,会直接输出114514(遇到非数字字符直接停止解析),在表达式"assert("intval($_GET[num])==1919810")"中,由于assert函数会将字符串作为代码动态执行,所以会直接计算num的值,得到num为1919810,综上即可得到答案

ctfshow 抽老婆

进入题目,AUV,这不是蔚蓝档案的小春吗,扫了目录但是没什么线索,看看源码喵

可以利用get参数访问文件,感觉可能是文件包含,看看能不能用伪协议直接看flag.php

看来这是被过滤了,那不看flag.php了,我随便看看什么图片吧

可以看到直接报错了,报错信息中可以得知这个网站使用flask框架写的,,看看能不能下载app.py来看题目源码

看来不能直接下载,使用相对路径看看

成功了,打开看看

# !/usr/bin/env python
# -*-coding:utf-8 -*-"""
# File       : app.py
# Time       :2022/11/07 09:16
# Author     :g4_simon
# version    :python 3.9.7
# Description:抽老婆,哇偶~
"""from flask import *
import os
import random
from flag import flag#初始化全局变量
app = Flask(__name__)
app.config['SECRET_KEY'] = 'tanji_is_A_boy_Yooooooooooooooooooooo!'@app.route('/', methods=['GET'])
def index():  return render_template('index.html')@app.route('/getwifi', methods=['GET'])
def getwifi():session['isadmin']=Falsewifi=random.choice(os.listdir('static/img'))session['current_wifi']=wifireturn render_template('getwifi.html',wifi=wifi)@app.route('/download', methods=['GET'])
def source(): filename=request.args.get('file')if 'flag' in filename:return jsonify({"msg":"你想干什么?"})else:return send_file('static/img/'+filename,as_attachment=True)@app.route('/secret_path_U_never_know',methods=['GET'])
def getflag():if session['isadmin']:return jsonify({"msg":flag})else:return jsonify({"msg":"你怎么知道这个路径的?不过还好我有身份验证"})if __name__ == '__main__':app.run(host='0.0.0.0',port=80,debug=True)

这段代码中使用了多个引用,可以看到'/secret_path_U_never_know'路径中,存在一个session验证,如果session中的isadmin为true的话就会输出flag,那我们就可以尝试一下flask的session伪造,构造一个isadmin为true的session上传即可,密钥在源码中也给出来了

利用burp抓包看看本来的session长什么样(非常奇怪,不知道为什么这个页面抓包只有老版本的burp抓包会显示cookie,其他抓包软件比如yakit,新版本的burp都不会显示)

使用flask-session-cookie-manager-master工具伪造session

这样就可以得到伪造的session了

成功解出题目

ctfshow 传说之下(雾)

这边直接修改前段源码看会不会给flag

不行,一吃新豆子就会刷新非常难受兄弟

继续看源码也没看出来什么名堂

控制台告诉我达到2077就可以直接给我flag,但是不能修改前段代码,那我能不能尝试修改游戏的源码呢

猜测这个函数的意义就是每吃到一颗豆子就可以加一分,那我直接修改一个大的

欧克啊,直接保存一下,再玩会游戏

有点小阴险,不是页面直接弹窗给你而是写在控制台里面

ctfshow 化零为整

<?phphighlight_file(__FILE__);
include "flag.php";$result='';for ($i=1;$i<=count($_GET);$i++){if (strlen($_GET[$i])>1){die("你太长了!!");}else{$result=$result.$_GET[$i];}
}if ($result ==="大牛"){echo $flag;
}

代码审计,设置一个result变量为空,for循环遍历GET数组里面每一个参数,如果某一个参数长度大于一,输出“你太长了!!”,结束代码。如果每个参数都满足长度小于等于一的情况,则将参数的值拼接到result变量(应该就是字符串)后,最后检查result变量是否强等于"大牛",既检查变量类型,又检查变量的值。

特别注意的一个点

这里其实规定了get数组里面的参数名称。$i规定了为1到get数组的长度,那里面的键就只能为1,2,3,4.....这种,这里特别注意,我写题的时候随便设置一些字母作为get参数导致报错

还要注意的一个点是

  • 在 PHP 中,<font style="color:rgb(64, 64, 64);">strlen()</font> 函数计算的是字节数,而不是字符数。
  • 对于 UTF-8 编码的中文字符,每个中文字符占用 3 个字节。因此,<font style="color:rgb(64, 64, 64);">strlen("大")</font><font style="color:rgb(64, 64, 64);">strlen("牛")</font> 的结果是 3,而不是 1。

所以这里我们传参就需要提前把字符串转化为url编码

:::info
?1=%e5&2=%a4&3=%a7&4=%e7&5=%89&6=%9b

:::

ctfshow 我的眼里只有$

php extract函数解释

php中的extract()函数,创造一个符号表,允许将传进来的数组键名作为变量名,使用数组键值作为变量值,而且如果变量已经存在,会覆盖原来的变量

php****

<?php
extract($_POST);
echo $a; // 输出用户输入的a字段的值
echo $b; // 输出用户输入的b字段的值
?>

如果用户输入 a=1b=2 并提交表单,输出将是:

复制

1
2

这是因为 extract($_POST);$_POST 数组中提取 ab,并将它们作为变量 $a$b 导入到当前符号表中。

题解

学习完这个函数后我们看这个源码,可以看到我们每传入一个变量比如a,它在符号表中就会被加上一个$的前缀,例如,我们传入_=a,在符号表中就会变为$=a,如果我们再传入一个a=b,那么就会变为$$=b,然而这里的eval函数中存在36个$前缀,如果嫌麻烦可以自行编写脚本,难度不高。在这里我直接给出payload

_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w
&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('ls /');

_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s
&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('cat /f1agaaa');

即可得出答案

不得不说这些题确实花里胡哨的

ctfshow TapTapTap

又是这种游戏,那么大概率在页面源码里面能找到什么信息

每个可以点的连接都点了一遍,发现这个玩意给我下载了一个js文件,打开一看

看了一遍就觉得这两行代码可疑,看着像base64编码,解码一下看看

成功找到flag,当然如果你厉害的话直接闯过20关也会直接给你flag

ctfshow web2 c0me_t0_s1gn

打开源码看看

给了提示,让我看看控制台

让我运行g1ve_flag()来获得flag那就试试

到此,flag就出来了

ctfshow webshell

 <?php error_reporting(0);   // 关闭错误报告class Webshell {      // Webshell类public $cmd = 'echo "Hello World!"';  // 公有变量cmd为字符串echo "Hello World!"public function __construct() {       // 公有构造函数,创建新对象前调用$this->init();                    // 变量this调用init函数}public function init() {              // 公有函数init// 如果在变量this调用的cmd中未匹配到由大写或小写字母组成的子串“flag”if (!preg_match('/flag/i', $this->cmd)) {$this->exec($this->cmd);      // 变量this调用exec函数 传入this调用cmd的返回值}}public function exec($cmd) {          // 公有函数exec$result = shell_exec($cmd);       // 变量result接受cmd执行shell命令返回的字符串形式echo $result;                     // 输出result}}if(isset($_GET['cmd'])) {                 // 如果get请求传入的cmd值存在且非空$serializecmd = $_GET['cmd'];         // 变量serializecmd接收get请求传入的cmd值// 变量unserializecmd接收变量serializecmd反序列化的结果$unserializecmd = unserialize($serializecmd);$unserializecmd->init();              // 变量unserializecmd调用init函数}else {                                    // 否则highlight_file(__FILE__);             // 高亮显示当前文件}?> 

首先我们尝试向 cmd 传入 ls 命令查询当前目录下的文件,既然是由 shell_exec 函数处理命令就不需要使用 system() 了。

因为文件名含有字符串 flag,按 init 函数的过滤规则不能直接 <font style="color:rgb(21, 167, 167);">cat flag.php</font>,这里采用模糊匹配 <font style="color:rgb(21, 167, 167);">cat f*</font> 打开唯一符合条件的文件 flag.php,序列化值为 <font style="color:rgb(21, 167, 167);">O:8:"Webshell":1:{s:3:"cmd";s:6:"cat fl*";}</font>


这里我卡了好久,理应是出flag了的,后面问了大佬才知道使用 cat 命令打开文件无法直接输出文件内容的原因是:浏览器只获取服务器生成的 html 文件,而 flag 文件是 php 文件,浏览器无法直接识别 php 文件,只能将其转换为注释。所以我们得查看html文件才能得到flag

但是不知道为什么cat逆序输出就可以直接显示在界面上

ctfshow 茶歇区

首先试试看改前段代码能不能直接修改分数获得flag

这么看来不行,只给fp余额1024不可能达到114514分,那就试试整数溢出漏洞,先尝试一个非常大的数字

可以看到余额一下变得特别多,但问题是分数也变得奇小无比

当我尝试19位9罐咖啡的时候积分为零,这里看到"9223372036854775807"这个数字,貌似是int64位的最大数字,果然还是存在溢出的,但是溢出过多貌似也没用,而且一罐咖啡能加十分,那就试一下传18位9罐咖啡,正好大于9223372036854775807这个数字,flag就直接出来了

多次尝试后发现,只要是第一次传入的数字大于9223372036854775807多一点,如果是咖啡的话就得是18位,其他的随意(不过矿泉水和大火腿对数据有限制建议不要用)那么获得的余额会非常大,再尝试一次相同的数字就可以满足分数大于114514的要求爆flag了

ctfshow LSB探姬

# !/usr/bin/env python
# -*-coding:utf-8 -*-
"""
# File       : app.py
# Time       :2022/10/20 15:16
# Author     :g4_simon
# version    :python 3.9.7
# Description:TSTEG-WEB
# flag is in /app/flag.py
"""
from flask import *
import os
#初始化全局变量
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():    return render_template('upload.html')
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():if request.method == 'POST':try:f = request.files['file']f.save('upload/'+f.filename)cmd="python3 tsteg.py upload/"+f.filenameresult=os.popen(cmd).read()data={"code":0,"cmd":cmd,"result":result,"message":"file uploaded!"}return jsonify(data)except:data={"code":1,"message":"file upload error!"}return jsonify(data)else:return render_template('upload.html')
@app.route('/source', methods=['GET'])
def show_source():return render_template('source.html')
if __name__ == '__main__':app.run(host='0.0.0.0',port=80,debug=False)

查看题目源码,发现是直接将上传的文件名拼接到路径后面,这样我们就可以进行命令注入,在文件名后加上;"命令"

原理

在 Unix/Linux shell 中:

  • 分号 <font style="color:rgb(64, 64, 64);">;</font> 是命令分隔符,允许在一行中执行多个命令
  • 管道 <font style="color:rgb(64, 64, 64);">|</font>、重定向 <font style="color:rgb(64, 64, 64);">></font><font style="color:rgb(64, 64, 64);"><</font> 等符号也可以被利用
  • 反引号 <font style="color:rgb(64, 64, 64);"><font style="color:rgb(64, 64, 64);"> 或 </font>$()` 可以用来执行子命令

:::info
cmd="python3 tsteg.py upload/"+f.filename

        result=os.popen(cmd).read()

:::

可以知道flag就在flag.py里面

ctfshow-菜狗杯-web签到

image-20250202220352642

主要就是最后这个一句话木马的利用,嵌套的有点多,来理一下:

首先最里面是‘CTFshow-QQ群’,前面是$_COOKIE,也就是取的是cookie中‘CTFshow-QQ群’的值;那如果我们在cookie中传入CTFshow-QQ群=a,那么一句话木马就变成了:

eval($_REQUEST[$_GET[$_POST[a]]][6][0][7][5][8][0][9][4][4]);

那么$_POST[a]就是要以POST方式传入的a参数的值,我们将传入的a=b,那么就变为了:

eval($_REQUEST[$_GET[b]][6][0][7][5][8][0][9][4][4]);

$_GET[b]也就是要以GET方式来传入b参数的值,我们再给b赋值b=c,就得到:

eval($_REQUEST[c][6][0][7][5][8][0][9][4][4]);

$_REQUEST[c][6][0][7][5][8][0][9][4][4],其中$_REQUEST是以任何一种方式请求都可以,c为数组,$_REQUEST请求中传入的值是取的C数组中ID键为[6][0][7][5][8][0][9][4][4]的值。因为PHP数组是可以指定ID键分配值的,那么我们就可以给C数组中的这些键直接赋值:c[6][0][7][5][8][0][9][4][4]= system('ls /');

于是我们用POST形式发包,同时注意“群”要用url编码,否则burp不识别(给c赋值时可以放在请求头也可以放在请求实体中,因为request请求方式无论是用get还是post形式都可以接受,这里放在了请求实体中):

Cookie: CTFshow-QQ%E7%BE%A4:=a

image-20250202220509540

可以看到根目录下的文件,但是可以得知flag就在f1agaaa里面

image-20250202220524718

http://www.hskmm.com/?act=detail&tid=9976

相关文章:

  • 国际服务器(VPS):泰国、印尼、菲律宾、马来西亚、香港、台湾、新加坡、日本、美国、英国等。
  • 缓存常见问题
  • ctfshow 电子取证
  • Hello,World!
  • 最新IDEA 2025 专业版破解永久破解教程(附资源)intellij IDEA
  • AtCoder ABC423F - Loud Cicada 题解 容斥原理
  • 1756:八皇后
  • 矩阵置零-leetcode
  • 嘉立创常用快捷键
  • 02020402 EF Core基础02-EF Core数据的增删改查
  • conda 无法安装依赖 CondaHTTPError: HTTP 000 CONNECTION FAILED for url: tsinghua tencentaliyun
  • 牛客刷题-Day2
  • 图解支付系统账务系统核心设计 - 智慧园区
  • vulnhub(持续更新)
  • 小爱同学连接电脑进行交互 教程
  • 网络流初步浅谈:EK与Dinic
  • 解码C语言结构体
  • 已完成今日求所有满足长为 $a$ 的和为 $b$ 的按位或为 $c$ 的非负整数序列的异或和的异或和大学习
  • Hello Yqc!
  • 2025.9.19——卷9-10选择
  • 软件工程学习日志2025.9.19
  • ECT-OS-JiuHuaShan 框架元推理,是人类良医与福音
  • upload-labs全通关
  • SAPO去中心化训练:多节点协作让LLM训练效率提升94%
  • mybatis-plus学习笔记
  • 区间问题
  • 操作系统,知识体系一共包含哪些部分? - 实践
  • vscode 下载 VS Code Server 卡住(无需手动下载)
  • 查询本地IPV6 地址
  • 解决 Ubuntu 25.04 下 make menuconfig 报 ncurses 错误的问题 - 指南