命令执行漏洞
原理:开发者为了实现网站的一些特殊功能时(比如ping命令)需要使用一些特殊函数,这些函数可以执行系统命令,如果没有进行严格的过滤,那么用户就可以通过这个函数实现一些危险的命令,或者加入一些危险的参数,从而获取信息
危险函数
这些函数如果可以被用户注入命令,就会带来危险性,写了个大概意思,具体参考php文档
system()
- 作用:
执行外部程序,并且显示输出
system()函数用于向操作系统传递控制台命令行,以Windows系统为例,通过system()函数执行命令和在cmd窗口中执行命令的效果是一样的 - 注意
命令必须是字符串的形式
exec()
与system()类似,执行系统命令,返回最后一行数据,但不显示输出
shell_exec()
通过shell执行命令,返回全部数据,但不显示输出,这个函数等同于执行运算符(反引号)
``(反引号)
在php中称之为执行运算符,PHP将尝试将反引号中的内容作为shell命令来执行,并返回数据
passthru()
passthru直接将结果输出到浏览器,不返回数据,且其可以输出二进制,比如图像数据。
popen(command,mode)
打开进程文件指针
函数需要两个参数,执行命令:command
,文件的连接模式:mode
,有r和w代表读和写。
函数会执行command命令,但不会返回执行结果,而是返回一个文件指针,想要去获取命令执行的结果,就类似于操作文件去获取
1 |
|
proc_open()
执行一个命令,并且打开用来输入/输出的文件指针。
类似popen()函数,但是proc_open()提供了更加强大的控制程序执行的能力。
pcntl_exec()
在当前进程空间执行指定程序
常用命令
Linux
- cat
命令用于连接文件并打印到标准输出设备上,可以知道文件内容,但是经常被过滤吧 - cd:切换当前工作目录;
用法:cd 后面加目录 - ls:显示指定工作目录下的内容;
用法:ls 文件或路径 - dir: 显示指定工作目录下的内容
和ls
类似,同于ls -C -b
,在ls
被过滤时是一个不错的选择 - cp:拷贝文件;
用法:cp 要复制的文件 目标路径/新的名字 - echo:输出
用法:输入什么就打印什么,可以配合管道符|
使用,也可以配合反引号内联执行echo > 1.txt
将输出的内容重定向到文件中,表示覆盖后写入echo >> 1.txt
:将输出的内容重定向到文件中,表示追加写入 - **find [path] [expression]**:
查找指定文件;用法:find 目录 -name 文件path
是要查找的目录路径,可以是一个目录或文件名,也可以是多个路径,如果未指定路径,则默认为当前目录expression
是可选参数,用于指定查找的条件,可以是文件名、文件类型、文件大小等等。
例如:find /home -name “*.txt”
查找/home目录下,所有以.txt 结尾的文件或者目录-name
:是按文件名查找,支持使用通配符 * 和 ?。 - pwd:
显示当前所在的目录 - more:
命令类似cat,不过会以一页一页的形式显示 - less:
与more
类似 - head:
查看头几行 - tac:
从最后一行开始显示,可以看出 tac 是 cat 的反向显示(是从最后一行输出,不是每一行逆序输出) - tail:
查看尾几行 - nl:
可以显示出文件内容的,而且输出行号 - od:
od指令会读取所给予的文件的内容,并将其内容以八进制字码呈现出来,八进制三个为一组
使用od -c
可以使内容以原本的形式显示 - vi:
文本编辑器,这个也可以查看 - vim:
Vim是从vi发展出来的一个文本编辑器,这个也可以查看
可以去菜鸟教程详细学习 - sort:
sort可针对文本文件的内容,以行为单位来排序,可以查看文件 - uniq:
uniq 可检查文本文件中重复出现的行列,可以查看文件,但是重复内容只会显示一个 - file -f:
会将文件内容以报错的形式显示出来 - grep:
用于查找文件里符合条件的字符串或正则表达式,并打印匹配的行:
例如:grep a 1.txt
在文件1.txt中查找字符”a”,并打印匹配的行:grep test *txt
表示在当前目录中,包含test
字符串的txt文件,,并打印出该字符串的行。
Windows
cd:切换目录
D::跳转到其他硬盘
ping:测试IP
ipconfig:查看网络详情,类似于linux的ifconfig
dir:显示目录中的文件内容,类似于linux的ls
type:查看文件,类似于linux的cat、less、more;用法:type 文件名
md:创建文件夹,类似于linux的mkdir;用法:md 目录名
tree:查看目录结构
copy:复制文本文件;用法:copy 文件1 文件2
注:
参数/b指定以二进制格式复制、合并文件,用于图像类/声音类文件
参数/a指定以ASCII格式复制、合并文件,用于txt等文档类文件
net start 服务名;net stop 服务名
cls:清空cmd命令行,类似于linux的clear
ctrl+C:结束或退出cmd正在执行的脚本
find:查找
find /c “所要搜索的文件所包含的字符串” 文件的绝对路径
for:对一个或一组文件,字符串或命令结果中的每一个对象执行特定命令
(1)找出C盘下的所有文件,并将所有文件名都输出出来
for /r C: %i in () do @echo %i
(2)找出C盘下所有后缀是.txt的文件,并将其输出
for /r C: %i in (.txt) do @echo %i
(3)找出C盘下所有后缀是.txt和.jpg的文件,并将其输出
for /r C: %i in (.txt,.jpg) do @echo %i
命令分隔符(连接符)
命令分隔符,就是用来分隔命令的,在两句命令中加入不同的符号,效果也不相同,在Windows和Linux两种不同的操作系统中也略有区别
我们主要用分割符来拼接语句,所以,我们更关心后面的语句能否执行
|
在Windows中,会直接执行后面的语句,但如果前段语句为假,则后面的语句也无法执行
在Linux中,是作为管道符,管道符左边命令的输出就会作为管道符右边命令的输入,无论前面真假,显示后面的执行结果&
无论前面的语句的真假,后面的语句都可以执行&&
前面为假,则后面不执行||
前面为真,则后面不执行;
分号
Linux特有的命令连接,前面执行完前面的语句再执行后面的,也就是说,无论前面的真假,后面都会执行
通配符
- 作用
通配符用来模糊搜索文件。当不知道真正字符或者懒得输入完整名字时,常常使用通配符代替一个或多个真正的字符,或者在ctf中,关键字被过滤,也可以用通配符来替代 ?
匹配一个任意字符,如fl?g
可以匹配成flag
,flbg
等*
匹配0个或多个任意字符,也就是可以匹配任意内容,如f*g
,匹配成以f开头,g结尾的所有字符[]
Linux里有这个通配符,而Windows没有
匹配括号中指定的任意一个字符,如fl[abc]g
可以匹配到flag
,flbg
,flcg
扩展:
[-] 匹配括号中范围内的任意一个字符,如a-c
代表范围a到c
[^] 逻辑非,表示匹配不是括号内的一个字符- 注意
反斜杠\
或单引号'
双引号"
都会使通配符失效。
如:\*
,"*"
,'*'
都表示*
本身,不通配任何文件。
代码执行
危险函数
eval()
严格来说,php中eval()不是函数,而是一个语言结构,所以无法变量函数(可变函数)函数的方式调用.
效果:将括号内的作为php代码执行.
变量函数参考
assert()
如果括号内是字符串将会当成php代码执行
注意:在php7.2之前,assert()是一个函数,可以使用变量(可变)函数调用.在此之后assert也同eval,是语言解构而不是函数.
preg_replace(参1,参2,参3)+/e
执行一个正则表达式的搜索和替换,注意,php7.0之后,不再支持/e模式
- 效果:
搜索参数3中是否有参数1,如果有则用参数2进行替换 - /e模式:
/e模式下,可能会导致代码执行漏洞,将参数1写成/参数1/e
的形式,如果发生了替换,参数2就会被当成php代码,最后替换成代码执行后的结果
例如:preg_replace($_GET[a],$_GET[b],$_GET[c])
payload:?a=/hello/e&b=phpinfo()&c=hello world
//替换时会执行phpinfo
create_function(参1,参2)
- 函数作用
根据传递的参数创建匿名函数,并为其返回唯一名称。参1声明函数的变量部分,参2是执行的方法代码部分
简单来说,这个函数就是类似于function一个函数,函数1就是这个函数里面的变量,参数二的部分会被当做function函数内的代码执行 - 利用函数执行代码
其实感觉原理和sql注入有点像,先将前面的语句闭合,再写入想要执行的代码,最后将后面的语句注释
前提是,参数二是用变量拼接的,如$c='echo '.'($s'.'+'."$a)".';';
例如,$a,$b是_GET传参的实际效果:1
2
3
4
5
6
$a=$_GET[a];
$c='echo '.'($s'.'+'."$a)".';';
$New=create_function('$s',$c);
$New(100);那我们构造一个payload:1
2
3function New{
echo (100+$a);
}?a=888);}phpinfo();/*
原函数变成:1
2
3
4
5function new{
echo (100+888);}
phpinfo();
/*);
}
array_map(参1,参2)
为数组的每个元素应用回调函数
参数1为回调函数(可以是自己定义的,也可以是php自带的),参数2是一个数组,每个元素将会被作为命令放在回调函数里执行
例如:
1 |
|
call_user_func(参1,参2)
和array_map()相似,参数1为回调函数,参数2是一个变量,作为回调函数的参数或命令
再给个例子payload=?a=phpinfo()
1 |
|
call_user_func_array(参数1,参数2)
和array_map()相似,参数1是回调函数,参数2为数组
array_filter(参数1,参数2)
使用回调函数过滤数组的元素
参数1是一个数组,参数2是回调函数
usort()
使用用户自定义的比较函数对数组中的值进行排序
先贴一个别人的,有点没看懂
1 | php环境>=5.6才能用 |
uasort()
使用用户定义的比较函数对数组进行排序并保持索引关联
获取信息
如果达成了代码执行的条件的情况下,首先可以试着执行phpinfo();
,并在页面查找是否有flag{
,如果没有,再观察disable_functions
部分有哪些函数被禁用
达成了代码执行的效果之后,一般分为几种情况
1.代码执行的参数过滤函数(如命令执行的危险函数)
2.有些函数被禁用disable_functions
3.过滤了字母,或是一些特殊字符
首先要明确的是,如果命令执行的函数不能用了,还可以考虑文件读取函数
读取文件用的函数
1 | highlight_file() |
需要注意的是,如果函数仅仅是被过滤了,那么我们可以试着”转移矛盾“
但是如果函数是被禁用了,就彻底没办法用了(也许可以试一下蚁剑的插件)
过滤了字母和特殊字符时,先考虑取反绕过和异或绕过等
如果禁用的是~
和^
和一些关键字,可以采用scandir()
数组中找需要的函数
读取目录的函数
1 | scandir() |
数组的控制
1 | next() //将内部指针指向数组中的下一个元素,并返回下一个元素的值。 |
随机读取当前目录下的一个文件show_source(array_rand(array_flip(scandir(current(localeconv())))));