【CTF】命令执行RCE

【CTF】命令执行RCE

一、命令执行几种payload写法

image-20230402152352392

*方法一*

查看靶场内容,其中过滤了flag字样,那么也就是说可以执行phpinfo()、system()等的命令。一般目标的敏感文件位于tmp目录下,使用命令c=system(“ls /tmp”)查看tmp目录下的文件,由结果可看出其中有flag.php文件。然后使用命令system(“cat /tmp/flag.php”)即理论上可以查看flag文件,由于该页面过滤了flag字样,那么即可使用通配符的方式也就可以得到题目的flag值。

*方法二*

可以在页面代码内部的c执行时,在c中再执行一次eval函数,语句如下:

img

可以看到,命令内部是有flag的,但是为何没有被过滤呢,这是由于页面代码对c中的flag进行了过滤,但是c的整体仅是一个内部是GET语句的eval语句,而flag并不属于c中的内容,自然也不会被过滤。也就是说传输到命令内,格式为eval(“eval($_GET[w])”),也就是说外层的eval执行的是内部字符串表达式的结果,eval会将内部的整体作为代码执行。

eval功能:返回传入字符串的表达式的结果。

所以,如果将c=eval(…)内部的eval除去后,无法正常显示flag文件,因为没有eval函数后,格式为eval(“($_GET[w])”),eval内部没有任何命令可执行,只是普普通通的接受了一个GET传输入的w的值,而对他没有任何处理。

同理还可以在c后使用system命令。

也还可以使用P7-4节学习的php伪协议,通过语句来获得:

c=include($_GET[w]);&w=php://filter/convert.base64-encode/resource=/tmp/flag.php

通过该语句得到的是flag文件内容的base64编码形式的结果。

*方法三*
还可使用命令echo ls先查看当前目录下的文件,``反引号是一个操作系统层面的命令,与system函数同级,但是他执行的结果不打印,结果不会回显出来,所以使用了echo命令。

打开靶场后,查看靶场所显示的源代码:

img

其中if语句内表示,当执行一个GET传入的内容c中没有flag/system/php时,才会执行刚刚输入的c命令。其中preg_match表示做正则匹配的函数。一般目标的敏感文件存放于tmp目录下,tmp目录是linux的临时文件,一般是可读可写的。

那么直接可以使用c=eval($_GET[w]);&w=system(“cat /tmp/flag.php”);即可得到flag。

二、payload命令执行

首先可以查看,页面,可以发现被过滤的字符:

img

最简单的一种方式,img,使用该语句即可得到flag。或者使用

c=include($_GET[w]);&w=php://filter/convert.base64-encode/resource=/tmp/flag.php

也可以得到flag

除了这两种方法,还有什么高阶的方式呢。

由于本题过滤了flag和php,首先使用echo ls,并且注意,此处的代码内是过滤了空格的,对于空格的跳过,此处不可以使用$IFS 9 的形式来绕过空格,因为 9的形式来绕过空格,因为 9的形式来绕过空格,因为IFS$9是在操作系统中使用的,但是本题是代码层面的命令,就只能使用%09来绕过空格。首先,为了查看以下本题的tmp目录下的文件,想要使用echo ls /tmp命令,即echo%09ls%09/tmp,即可发现tmp目录下的flag文件:

img

但是能够发现,本题的过滤时十分严格的,cat等命令都无法使用。既然cat无法使用,那么尝试命令more/tac等,得到flag:

c=echo%09tac%09/tmp/f*;

等价于c=echo tac /tmp/flag.php

img

三、命令执行和通配符的绕过

从本题来看,此处过滤的命令更多了,在之前学习的命令都无法使用

img

查看过滤了的字符,其中%、数字9和空格都被过滤,那么还可以使用${IFS}来绕过空格,然后还可以使用通配符‘?’来绕过对flag的限制。发现cat也被过滤了,那么可以使用单引号绕过的方式:

c=c'a't${IFS}/tmp/fla?.php 

即可得到:img

注:此处为什么不能够写为f*代表flag.php文件呢,因为通配符中,*可以表示一个或多个字符,但是‘?’只能代表一个字符,并且本题的*被过滤,无法使用。

还可以使用另一种命令来绕过对cat的过滤,即c=/bin/c?t${IFS}/tmp/fla?.php

也可以:img,也能够执行,因为tmp目录下只有flag一个文件

四、php中读文件、命令执行的函数

参考博客:

https://blog.csdn.net/weixin_39687736/article/details/123862218

1. *system*

system( c o m m a n d , command, command,return)

执行系统命令/php自定义命令,并将相应的执行结果输出,同步进程,执行完后进行后续代码执行。

2. *exec*
exec( c o m m a n d , command, command,outpub,$return)
exec输出的是命令执行结果的最后一行内容。如果你需要获取未经处理的全部输出数据,请使用passthru()函数。如果想要使用exec显示当前目录下的所有文件的话,可以使用语句:exec(command:“ls >> 1.txt”);来将exec读取到的当前目录下的所有文件名导入到一个新创建的1.txt文件中。
<< : 叠加; < : 覆盖

3. *passthru*

passthru( c o m m a n d , command, command,return_var)

与system函数同级,若今后题目中system函数被过滤了,即可使用它。

4. *shell_exec*
shell_exec($command)
与exec一样,无回显,需要使用echo显示结果。

5. 反引号`
echo `command`

反引号和shell_exec意思相同
在php中称之为执行运算符,PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回

6. …(见上方连接)

*php中读文件函数*

可使用echo fiel_get_contents(“flag.php”)该语句是以流的形式读取文件,需要在查看页面源代码中查看读取到的接果。

hightlight_file(“flag.php”),不需要使用echo

五、intval函数绕过:

img

函数:

① isset():用于检测变量是否已设置并且非NULL。

② preg_match():正则匹配,前一参数放要搜索的内容,后一参数是需要被匹配的内容,测试被匹配内容中是否存在要搜索的内容。例如本题即num中不能有数字。

③ die():函数输出一条消息,并退出当前脚本。本题中即当num中有数字,输出nonono。

④ intval():用于获取变量的整数值。

*intval函数*

*语法*

int intval ( mixed $var [, int $base = 10 ] )

*参数说明*

$var:要转换成 integer 的数量值。

$base:转化所使用的进制。

*如果 base 是 0,通过检测 var 的格式来决定使用的进制*

如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,

如果字符串以 “0” 开始,使用 8 进制(octal);否则,

将使用 10 进制 (decimal)。

*返回值*

成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。

最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。64 位系统上,最大带符号的 integer 值是 9223372036854775807。

字符串有可能返回 0,虽然取决于字符串最左侧的字符。

在对intval函数的返回值处,发现如果传入空的array(数组),则会返回0,非空的array则会返回1,那么只需要传入一个非空的数组,即可实现让本程序输出flag值。

要如何传入一个数组呢,格式:num[]=w,即在需要传入的num后加上方括号,即代表此时传入的是一个数组,并且使传入的数组为非空且无数字,即可得到flag。

img

并且,本题如果直接书写为num[]=或者num[]也可以得到flag,因为如果不定义num的值的话,默认num有一个值为空,也满足条件。

例题:

得到代码后,分析代码:

img

分析本题,当num为4476时,程序会die,并输出nonono,但是想要获得flag的话,又需要num为4476。

那么这里就涉及到intval函数的一个知识点,当int intval ( mixed $var [, int $base = 10 ] )中的base为0时,通过检测 var 的格式来决定使用的进制,
如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,

如果字符串以 “0” 开始,使用 8 进制(octal);否则,

将使用 10 进制 (decimal)。

那么推测,是否可以通过输入16进制或者8进制的num值,来绕过本程序的检验,先进行进制转换

4476的16进制:0x117c,8进制:010574

尝试将上述两种进制以格式传入,

十六进制:num=0x117c

八进制:num=010574

使用以上两种方式,都成功的获取到了本题的flag。

img

除了以上的方法,由于intval会取整,可以使用小数的方式来得到,例如输入4476.1、4476w等方式,都能够得到flag。

六、php弱类型特性

**属于弱类型校验,只校验值是否一样,不校验类型,而=**属于强类型校验,不仅校验值,还校验类型。

img

本题及表示,当a与b同时都有POST输入,而a与b的传输不相同,但是a和b的md5值相同,才能够得到flag,否则输出wrong。

*方法一*

*md5()函数*

*语法:*

md5(string,raw) 

*定义和用法:*

md5() 函数计算字符串的 MD5 散列。

md5() 函数使用 RSA 数据安全,包括 MD5 报文摘要算法。

来自 RFC 1321 的解释 - MD5 报文摘要算法:MD5 报文摘要算法将任意长度的信息作为输入值,并将其换算成一个 128 位长度的"指纹信息"或"报文摘要"值来代表这个输入值,并以换算后的值作为结果。MD5 算法主要是为数字签名应用程序而设计的;在这个数字签名应用程序中,较大的文件将在加密(这里的加密过程是通过在一个密码系统下[如:RSA]的公开密钥下设置私有密钥而完成的)之前以一种安全的方式进行压缩。

如需计算文件的 MD5 散列,请使用 md5_file() 函数。

md5() 函数不能处理数组,数组都返回 null,md5(a[]) 结果为 null。

本题运用到了md5()函数的不能处理数组,数组都返回 null,md5(a[]) 结果为 null的特性,当给a和b传入不同两个数组时,在第一步判断时不会认定a与b相同,但是在第二步判断时,由于md5函数不能处理数组,所以会返回null那么即可传输:a[]=1&b[]=2,即可得到flag:
image-20230402154209038

*方法二*

PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每个以”0E”开头的哈希值都解释为0,因此若是两个不一样的密码通过哈希之后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。

因为php中0e代表0的科学计数法,无论后面的数字跟的是多少,结果都等于0。

http://www.javashuo.com/article/p-adowhkuk-q.html

然后在本题中,利用一些经过md5加密后开头为0e的原始值,来对a和b进行赋值,即可成功绕过:
img

a=s878926199a&b=s155964671a

img

七、变量覆盖

img

*函数*

*定义和用法*

parse_str() 函数把查询字符串解析到变量中。

****注释:****如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。

****注释:****php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么 在 parse_str() 解析之前,变量会被 addslashes() 转换。

*语法*

parse_str(string,array)

*参数**描述*
string必需。规定要解析的字符串。
array可选。规定存储变量的数组名称。该参数指示变量存储到数组中。

*方法一**:*

结合parse_str()函数的性质,本题需要传入两个值,一个是post传输的v1和一个GET传输的v3,也就是说本题需要传入一个v1=‘flag’=(v3的md5加密后的值)。

img

接下来执行后即可获得本题的flag。

img

*方法二*

运用md5()函数对数组进行操作后结果为null的性质,对v3当作数组进行传入值,然后不对v1传入任何值也可获得本题的flag。

$$变量覆盖

img

*foreach有两种语法:*

*第一种*:遍历给定的 数组语句 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。

foreach (array_expression as $value)

*第二种*:同上,同时当前单元的键名也会在每次循环中被赋给变量 $key。

foreach (array_expression as $key => $value)

本题的foreach函数里有GET和POST两个参数,那么也就是证明,可以自定义的通过GET/POST的方式传输进几个变量,并通过键值对的形式赋值。

* k e y = key= key=value*

当传入一个a=1时,此时变为了$a= 1 ,也就是把 a 变换为了变量 1,也就是把a变换为了变量 1,也就是把a变换为了变量a,把1变为了变量$1,并把 1 赋值给 1赋值给 1赋值给a。这与$ k e y = key= key=value有何区别呢,当value传入一个值后,其由于$value本身就是一个变量,他就会表示一个具体的变量的值。

那么对于本题而言,出现了两次 k e y = key= key=value,再结合下方的if语句:

img

只要阻止这一步执行了die命令,就可以获得flag,但是想要使传入的flag等于题目的flag显然是不现实的,也就是说在最后一个if语句处,一定会die,但是可以通过变量相等的传递,来逐步实现。比如对于本题,在第一个foreach处先传入一个suces=flag,然后在第二个foreach处通过error=suces的形式,即可实现对flag和error两个字符串的过滤(因为他们变成变量的形式了)。在最后一个if语句,执行了die( e r r o r ) ,但是此时的 e r r o r 已经等于了 error),但是此时的error已经等于了 error),但是此时的error已经等于了flag,也就是说,输出的会是flag。

那么经过两次传参,也就是说

第一次: s u c e s = suces= suces=flag

第二次: e r r o r = error= error=suces

也就是说,此时的 e r r o r 在值上等于 error在值上等于 error在值上等于flag,那么即可通过die($error)语句,获得flag。

img

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunny-Dog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值