Post

Self-VM DevOops靶机复盘

难度-Easy

Self-VM DevOops靶机复盘

网段扫描

1
2
3
4
5
6
7
8
9
root@LingMj:~/xxoo/jarjar# arp-scan -l
Interface: eth0, type: EN10MB, MAC: 00:0c:29:d1:27:55, IPv4: 192.168.137.190
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.137.1	3e:21:9c:12:bd:a3	(Unknown: locally administered)
192.168.137.135	a0:78:17:62:e5:0a	Apple, Inc.
192.168.137.154	3e:21:9c:12:bd:a3	(Unknown: locally administered)

4 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.020 seconds (126.73 hosts/sec). 3 responded

端口扫描

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
57
58
59
60
61
62
63
64
65
66
67
68
root@LingMj:~/xxoo/jarjar# nmap -p- -sV -sC 192.168.137.154
Starting Nmap 7.95 ( https://nmap.org ) at 2025-04-27 21:21 EDT
Nmap scan report for devoops.hmv.mshome.net (192.168.137.154)
Host is up (0.011s latency).
Not shown: 65534 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
3000/tcp open  ppp?
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, Kerberos, NCP, RPCCheck, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServerCookie, X11Probe: 
|     HTTP/1.1 400 Bad Request
|   FourOhFourRequest: 
|     HTTP/1.1 403 Forbidden
|     Vary: Origin
|     Content-Type: text/plain
|     Date: Mon, 28 Apr 2025 09:21:38 GMT
|     Connection: close
|     Blocked request. This host (undefined) is not allowed.
|     allow this host, add undefined to `server.allowedHosts` in vite.config.js.
|   GetRequest: 
|     HTTP/1.1 403 Forbidden
|     Vary: Origin
|     Content-Type: text/plain
|     Date: Mon, 28 Apr 2025 09:21:37 GMT
|     Connection: close
|     Blocked request. This host (undefined) is not allowed.
|     allow this host, add undefined to `server.allowedHosts` in vite.config.js.
|   HTTPOptions, RTSPRequest: 
|     HTTP/1.1 204 No Content
|     Vary: Origin, Access-Control-Request-Headers
|     Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
|     Content-Length: 0
|     Date: Mon, 28 Apr 2025 09:21:37 GMT
|_    Connection: close
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3000-TCP:V=7.95%I=7%D=4/27%Time=680ED824%P=aarch64-unknown-linux-gn
SF:u%r(GetRequest,FE,"HTTP/1\.1\x20403\x20Forbidden\r\nVary:\x20Origin\r\n
SF:Content-Type:\x20text/plain\r\nDate:\x20Mon,\x2028\x20Apr\x202025\x2009
SF::21:37\x20GMT\r\nConnection:\x20close\r\n\r\nBlocked\x20request\.\x20Th
SF:is\x20host\x20\(undefined\)\x20is\x20not\x20allowed\.\nTo\x20allow\x20t
SF:his\x20host,\x20add\x20undefined\x20to\x20`server\.allowedHosts`\x20in\
SF:x20vite\.config\.js\.")%r(Help,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r
SF:\n\r\n")%r(NCP,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(HTTPO
SF:ptions,D2,"HTTP/1\.1\x20204\x20No\x20Content\r\nVary:\x20Origin,\x20Acc
SF:ess-Control-Request-Headers\r\nAccess-Control-Allow-Methods:\x20GET,HEA
SF:D,PUT,PATCH,POST,DELETE\r\nContent-Length:\x200\r\nDate:\x20Mon,\x2028\
SF:x20Apr\x202025\x2009:21:37\x20GMT\r\nConnection:\x20close\r\n\r\n")%r(R
SF:TSPRequest,D2,"HTTP/1\.1\x20204\x20No\x20Content\r\nVary:\x20Origin,\x2
SF:0Access-Control-Request-Headers\r\nAccess-Control-Allow-Methods:\x20GET
SF:,HEAD,PUT,PATCH,POST,DELETE\r\nContent-Length:\x200\r\nDate:\x20Mon,\x2
SF:028\x20Apr\x202025\x2009:21:37\x20GMT\r\nConnection:\x20close\r\n\r\n")
SF:%r(RPCCheck,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(DNSVersi
SF:onBindReqTCP,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(DNSStat
SF:usRequestTCP,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(SSLSess
SF:ionReq,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(TerminalServe
SF:rCookie,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(TLSSessionRe
SF:q,1C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(Kerberos,1C,"HTTP/
SF:1\.1\x20400\x20Bad\x20Request\r\n\r\n")%r(SMBProgNeg,1C,"HTTP/1\.1\x204
SF:00\x20Bad\x20Request\r\n\r\n")%r(X11Probe,1C,"HTTP/1\.1\x20400\x20Bad\x
SF:20Request\r\n\r\n")%r(FourOhFourRequest,FE,"HTTP/1\.1\x20403\x20Forbidd
SF:en\r\nVary:\x20Origin\r\nContent-Type:\x20text/plain\r\nDate:\x20Mon,\x
SF:2028\x20Apr\x202025\x2009:21:38\x20GMT\r\nConnection:\x20close\r\n\r\nB
SF:locked\x20request\.\x20This\x20host\x20\(undefined\)\x20is\x20not\x20al
SF:lowed\.\nTo\x20allow\x20this\x20host,\x20add\x20undefined\x20to\x20`ser
SF:ver\.allowedHosts`\x20in\x20vite\.config\.js\.");
MAC Address: 3E:21:9C:12:BD:A3 (Unknown)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 23.39 seconds

获取webshell

picture 0

这个靶机对我还是很有难度的,哈哈哈

picture 1
picture 2

没扫出对应的东西,更新gobuster没用

picture 3
picture 4
picture 5

找一下密码

picture 6
picture 7
picture 8
picture 9
picture 10

picture 11

确实可读任意文件

picture 12

还能读什么

picture 13

这个是提示?

picture 14

太多不好看

picture 15

浅浅读一下server.js,一个认证,认证✅可以命令执行

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import express from 'express';
import jwt from 'jsonwebtoken';
import 'dotenv/config'
import { exec } from 'child_process';
import { promisify } from 'util';

const app = express();

const address = 'localhost';
const port = 3001;

const exec_promise = promisify(exec);

const COMMAND_FILTER = process.env.COMMAND_FILTER
    ? process.env.COMMAND_FILTER.split(',')
        .map(cmd => cmd.trim().toLowerCase())
        .filter(cmd => cmd !== '')
    : [];

app.use(express.json());

function is_safe_command(cmd) {
    if (!cmd || typeof cmd !== 'string') {
        return false;
    }
    if (COMMAND_FILTER.length === 0) {
        return false;
    }

    const lower_cmd = cmd.toLowerCase();

    for (const forbidden of COMMAND_FILTER) {
        const regex = new RegExp(`\\\\b${forbidden.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\b|^${forbidden.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}$`, 'i');
        if (regex.test(lower_cmd)) {
            return false;
        }
    }

    if (/[;&|]/.test(cmd)) {
        return false;
    }
    if (/[<>]/.test(cmd)) {
        return false;
    }
    if (/[`$()]/.test(cmd)) {
        return false;
    }

    return true;
}

async function execute_command_sync(command) {
    try {
        const { stdout, stderr } = await exec_promise(command);

        if (stderr) {
            return { status: false, data: { stdout, stderr } };
        }
        return { status: true, data: { stdout, stderr } };
    } catch (error) {
        return { status: true, data: error.message };
    }
}

app.get('/', (req, res) => {
    return res.json({
        'status': 'working',
        'data': `listening on http://${address}:${port}`
    })
})

app.get('/api/sign', (req, res) => {
    return res.json({
        'status': 'signed',
        'data': jwt.sign({
            uid: -1,
            role: 'guest',
        }, process.env.JWT_SECRET, { expiresIn: '1800s' }),
    });
});

app.get('/api/execute', async (req, res) => {
    const authorization_header_raw = req.headers['authorization'];
    if (!authorization_header_raw || !authorization_header_raw.startsWith('Bearer ')) {
        return res.status(401).json({
            'status': 'rejected',
            'data': 'permission denied'
        });
    }

    const jwt_raw = authorization_header_raw.split(' ')[1];

    try {
        const payload = jwt.verify(jwt_raw, process.env.JWT_SECRET);
        if (payload.role !== 'admin') {
            return res.status(403).json({
                'status': 'rejected',
                'data': 'permission denied'
            });
        }
    } catch (err) {
        return res.status(401).json({
            'status': 'rejected',
            'data': `permission denied`
        });
    }

    const command = req.query.cmd;

    const is_command_safe = is_safe_command(command);
    if (!is_command_safe) {
        return res.status(401).json({
            'status': 'rejected',
            'data': `this command is unsafe`
        });
    }

    const result = await execute_command_sync(command);

    return res.json({
        'status': result.status === true ? 'executed' : 'failed',
        'data': result.data
    })
});

app.listen(port, address, () => {
    console.log(`Listening on http://${address}:${port}`)
});

picture 16
picture 17

1
2
JWT_SECRET='2942szKG7Ev83aDviugAa6rFpKixZzZz'
COMMAND_FILTER='nc,python,python3,py,py3,bash,sh,ash,|,&,<,>,ls,cat,pwd,head,tail,grep,xxd'

picture 18

1
jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOi0xLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3NDU4MzQ5NTgsImV4cCI6MTc0NTgzNjc1OH0.FKklJqCYfNPVJFqk43p-715A5T7jVpe0HroQGMua8BA"

伪造一下才行

picture 19
picture 20

得处理一下不然不能提权,没ban wget和curl看看能不能用

picture 21

没成功

picture 22
picture 23
picture 24
picture 25

奇怪为啥都不成功呢

picture 26
picture 27

换nc都ash成功了

picture 28
picture 29

完了这个靶机寄了,哈哈哈,搁置一下

提权

userflag:

rootflag:

This post is licensed under CC BY 4.0 by the author.