Skip to content

Commit

Permalink
fixup! fixup! ⚰️ refactor: Remove commented-out code, unneeded files
Browse files Browse the repository at this point in the history
  • Loading branch information
eliasgierlinger committed Nov 8, 2023
1 parent 6f44b5b commit 8d10088
Show file tree
Hide file tree
Showing 18 changed files with 129 additions and 205 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Unguard is composed of eight microservices written in different languages that t
| [proxy-service](./src/proxy-service) | Java Spring | unguard-proxy | Serves REST API for proxying requests from frontend (vulnerable to SSRF; no sanitization on the entered URL). |
| [profile-service](./src/profile-service) | Java Spring | default | Serves REST API for updating biography information in a H2 database; vulnerable to SQL injection attacks |
| [membership-service](./src/membership-service) | .NET 7 | default | Serves REST API for updating user memberships in a MariaDB; vulnerable to SQL injection attacks |
| [like-service](./src/like-service) | PHP | default | Serves REST API for liking and unliking posts using MariaDB; vulnerable to an SQL injection attack for removing other users' likes |
| [like-service](./src/like-service) | PHP | default | Serves REST API for adding likes to posts using MariaDB; vulnerable to SQL injection attacks |
| [user-auth-service](./src/user-auth-service) | Node.js Express | default | Serves REST API for authenticating users with JWT tokens (vulnerable to JWT key confusion). |
| [status-service](./src/status-service) | Go | unguard-status | Serves REST API for Kubernetes deployments health, as well as a user and user role list (vulnerable to SQL injection) |
| jaeger | | default | The [Jaeger](https://www.jaegertracing.io/) stack for distributed tracing. |
Expand Down
7 changes: 0 additions & 7 deletions chart/templates/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,4 @@ spec:
name: unguard-envoy-proxy
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: unguard-envoy-proxy
port:
number: 8000
{{end}}
4 changes: 2 additions & 2 deletions docs/TRACING.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ This document explains how to install Jaeger tracing using Helm to the cluster.
1. For local development
1. Install Jaeger (takes a couple of minutes)
```sh
helm install jaeger jaegertracing/jaeger --version 0.71.14 --wait --namespace unguard --create-namespace --values ./chart/jaeger-otlp-values.yaml
helm install jaeger jaegertracing/jaeger --version 0.71.14 --wait --namespace unguard --create-namespace --values ./docs/jaeger/jaeger-otlp-values.yaml
```
2. Install the Jaeger-Operator
```sh
helm install jaeger-operator jaegertracing/jaeger-operator --version 2.22.0 --wait --namespace unguard --create-namespace
```
2. Deploy the AllInOne image for local development
3. Deploy the AllInOne image for local development
```sh
kubectl apply -f ./k8s-manifests/jaeger/jaeger.yaml
```
Expand Down
Binary file modified docs/images/unguard-timeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/unguard-user-profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
2 changes: 1 addition & 1 deletion exploit-toolkit/exploit.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ def sql_inject_unlike_post(post, user, target):
click.echo("Not logged in. Run login command first.")
return

r = session.get(f'http://{target + frontend_base_path}/post', params={'postId': [post, user], 'unlike': ''}, allow_redirects=False)
r = session.get(f'http://{target + frontend_base_path}/unlike', params={'postId': [post, user]}, allow_redirects=False)

# should always be status code 404
click.echo('Request returned status code %s.' % str(r.status_code))
Expand Down
2 changes: 1 addition & 1 deletion exploit-toolkit/exploits/sql-injection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
Unguard has three SQL injection vulnerabilities:
* [One in the Java `profile-service`](./SQLI-PROFILE-SERVICE-H2.md), which is exploitable through the user biography and allows you to access the h2 database.
* [One in the Golang `status-service`](./SQLI-STATUS-SERVICE-MARIADB.md), which is exploitable through the search bar on the Users page and allows you to access the MariaDB database.
* [One in the PHP `like-service`](./SQLI-LIKE-SERVICE-REMOVE-LIKE.md), which allows you to remove another user's like on a given post if you send the right parameters.
* [One in the PHP `like-service`](./SQLI-LIKE-SERVICE-REMOVE-LIKE.md), which allows you to remove another user's like on a given post.
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ This ID is exposed indirectly through the Users page. The admanager user always
The user shown below the admanager has the ID 2, the one below that has the ID 3 etc.

### w/o Toolkit CLI
Once you have the ID of the user whose like on a particular post you want to remove, head over to the frontend page for that post, e.g. http://unguard.kube/ui/post?postId=1.
You can get to that page by liking the post yourself. Then, in the search bar, modify the parameters thusly:
`http://unguard.kube/ui/post?postId=[POST_ID]&postId=[USER_ID]&unlike`.
Once you have the ID of the user whose like on a particular post you want to remove, head over to the frontend page for that post, e.g. http://unguard.kube/ui/post/1.
You can get to that page by liking the post yourself. From the address bar, you can now see the post id (1 in the example). Then open the following in your browser:
`http://unguard.kube/ui/unlike?postId=[POST_ID]&postId=[USER_ID]`.
The second `postId` parameter is misinterpreted by Laravel as the user ID, and the like for that user will be deleted. After you load the site with these parameters,
you should see a 404 error.

Expand Down
12 changes: 0 additions & 12 deletions src/envoy-proxy/config/envoy-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,3 @@ static_resources:
socket_address:
address: unguard-ad-service
port_value: 80
- name: like_service_cluster
dns_lookup_family: V4_ONLY
type: STRICT_DNS
load_assignment:
cluster_name: like_service_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: unguard-like-service
port_value: 80
202 changes: 94 additions & 108 deletions src/frontend/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ router.post('/user/:username/follow', followUser);
// Create post
router.post('/post', createPost);
// get single post
router.get('/post', getPost);
router.get('/post/:postId', getPost);
// Like post
router.get('/like', likePost);
// Unlike post
router.get('/unlike', unlikePost);
// Logout
router.post('/logout', doLogout);
// Login
Expand All @@ -53,29 +57,26 @@ router.post('/membership/:username', postMembership);

router.use('/ad-manager', adManagerRouter);

async function showGlobalTimeline(req, res) {
try {
let [timeline, membership] = await fetchUsingDeploymentBase(req, () =>
Promise.all([
req.MICROBLOG_API.get('/timeline'),
getMembershipOfLoggedInUser(req)
]))
let postArray = timeline.data;
postArray = await insertLikeCountIntoPostArray(req, postArray);

let data = extendRenderData({
data: postArray,
title: 'Timeline',
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
baseData: baseRequestFactory.baseData,
membership: membership.data.membership

}, req);
res.render('index.njk', data)
} catch (err) {
displayError(err, res)
}
function showGlobalTimeline(req, res) {
fetchUsingDeploymentBase(req, () =>
Promise.all([
req.MICROBLOG_API.get('/timeline'),
getMembershipOfLoggedInUser(req)
])).
then(([timeline, membership]) => {
insertLikeCountIntoPostArray(req, timeline.data).then(postArray => {
let data = extendRenderData({
data: postArray,
title: 'Timeline',
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
baseData: baseRequestFactory.baseData,
membership: membership.data.membership

}, req);
res.render('index.njk', data)
}, (err) => displayError(err, res))
}, (err) => displayError(err, res))
}

function showUsers(req, res) {
Expand Down Expand Up @@ -115,29 +116,25 @@ function showUsers(req, res) {
}, (err) => displayError(err, res));
}

async function showPersonalTimeline(req, res) {
try {
let [myTimeline, membership] = await fetchUsingDeploymentBase(req, () =>
Promise.all([
req.MICROBLOG_API.get('/mytimeline'),
getMembershipOfLoggedInUser(req)
]))

let postArray = myTimeline.data;
postArray = await insertLikeCountIntoPostArray(req, postArray);

let data = extendRenderData({
data: postArray,
title: 'My Timeline',
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
baseData: baseRequestFactory.baseData,
membership: membership.data.membership
}, req);
res.render('index.njk', data);
} catch (err) {
displayError(err, res)
}
function showPersonalTimeline(req, res) {
fetchUsingDeploymentBase(req, () =>
Promise.all([
req.MICROBLOG_API.get('/mytimeline'),
getMembershipOfLoggedInUser(req)
]))
.then(([myTimeline, membership]) => {
insertLikeCountIntoPostArray(req, myTimeline.data).then(postArray => {
let data = extendRenderData({
data: postArray,
title: 'My Timeline',
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
baseData: baseRequestFactory.baseData,
membership: membership.data.membership
}, req);
res.render('index.njk', data);
}, (err) => displayError(err, res))
}, (err) => displayError(err, res))
}

function showUserProfile(req, res) {
Expand All @@ -148,21 +145,20 @@ function showUserProfile(req, res) {
req.MICROBLOG_API.get(`/users/${username}/posts`),
getMembership(req, username)
])
).then(async ([bioText, microblogServiceResponse, membership]) => {
let postArray = microblogServiceResponse.data;
postArray = await insertLikeCountIntoPostArray(req, postArray);

let data = extendRenderData({
data: postArray,
profileName: username,
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
bio: bioText,
baseData: baseRequestFactory.baseData,
membership: membership.data.membership
}, req);

res.render('profile.njk', data);
).then(([bioText, microblogServiceResponse, membership]) => {
insertLikeCountIntoPostArray(req, microblogServiceResponse.data).then(postArray => {
let data = extendRenderData({
data: postArray,
profileName: username,
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
bio: bioText,
baseData: baseRequestFactory.baseData,
membership: membership.data.membership
}, req);

res.render('profile.njk', data);
}, (err) => displayError(err, res))
}, (err) => displayError(err, res));
}

Expand Down Expand Up @@ -318,7 +314,7 @@ function createPost(req, res) {
imageUrl: metaImgSrc
}))
}, (err) => displayError(err, res))
.then((postResponse) => res.redirect(extendURL(`/post?postId=${postResponse.data.postId}`)), (err) => displayError(err, res));
.then((postResponse) => res.redirect(extendURL(`/post/${postResponse.data.postId}`)), (err) => displayError(err, res));
} else if (req.body.imgurl) {
// the image post calls a different endpoint that has a different ssrf vulnerability
fetchUsingDeploymentBase(req, () => req.PROXY.get("/image", {
Expand All @@ -331,48 +327,49 @@ function createPost(req, res) {
imageUrl: response.data
}));
}, (err) => displayError(err, res))
.then((postResponse) => res.redirect(extendURL(`/post?postId=${postResponse.data.postId}`)), (err) => displayError(err, res));
.then((postResponse) => res.redirect(extendURL(`/post/${postResponse.data.postId}`)), (err) => displayError(err, res));
} else if (req.body.message) {
// this is a normal message
fetchUsingDeploymentBase(req, () => req.MICROBLOG_API.post('/post', {
content: req.body.message
})).then((postResponse) => {
res.redirect(extendURL(`/post?postId=${postResponse.data.postId}`));
res.redirect(extendURL(`/post/${postResponse.data.postId}`));
}, (err) => displayError(err, res));
} else {
// when nothing is set, just redirect back
res.redirect(extendURL('/'));
}
}

async function getPost(req, res) {
const postId = req.query.postId;
try {
if(req.query.like !== undefined) {
await fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.post(`/like-service/like-post`, {postId: postId}))
}
else if (req.query.unlike !== undefined) {
await fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.post(`/like-service/like-delete`, {postId: postId}));
}
} catch {}


const likeData = await getLikeCount(req, postId)

function getPost(req, res) {
const postId = req.params.postId;
fetchUsingDeploymentBase(req, () => req.MICROBLOG_API.get(`/post/${postId}`)).then((response) => {
insertLikeCountIntoPostArray(req, [response.data]).then(postArray => {
let postData = postArray[0];
let data = extendRenderData({
post: postData,
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
baseData: baseRequestFactory.baseData
}, req);

let postData = response.data;
postData = {...postData, likeCount: likeData.likeCount, userLiked: likeData.userLiked};
res.render('singlepost.njk', data);
}, (err) => displayError(err, res))
}, (err) => displayError(err, res))
}

let data = extendRenderData({
post: postData,
username: getJwtUser(req.cookies),
isAdManager: hasJwtRole(req.cookies, roles.AD_MANAGER),
baseData: baseRequestFactory.baseData
}, req);
function likePost(req, res) {
const postId = req.query.postId;
fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.post(`/like/` + postId)).then((response) => {
res.redirect(extendURL(`/post/${postId}`));
}, (error) => res.status(statusCodeForError(error)).render('error.njk', handleError(error)));
}

res.render('singlepost.njk', data);
}, (err) => displayError(err, res))
function unlikePost(req, res) {
const postId = req.query.postId;
fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.delete(`/like`, {params: {postId: postId}})).then((response) => {
res.redirect(extendURL(`/post/${postId}`));
}, (error) => res.status(statusCodeForError(error)).render('error.njk', handleError(error)));
}

function postMembership(req, res) {
Expand All @@ -398,25 +395,14 @@ function postBio(req, res) {
});
}


async function getLikeCount(req, postId) {
let response = await fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.get(`/like-service/like-count/` + postId))
return response.data
}

async function getMultipleLikeCounts(req, postIds) {
let response = await fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.get(`/like-service/like-count/`, { params: { postIds: postIds } }))
return response.data
}

async function insertLikeCountIntoPostArray(req, data) {
let likeData = await getMultipleLikeCounts(req, data.map(post => post.postId));

return data.map(post => {
let likeCount = likeData.likeCounts.find(likeCount => likeCount.postId == post.postId)?.likeCount ?? 0;
let userLiked = likeData.likedPosts.some(like => like.postId == post.postId);
return {...post, likeCount: likeCount, userLiked: userLiked};
});
function insertLikeCountIntoPostArray(req, posts) {
return fetchUsingDeploymentBase(req, () => req.LIKE_SERVICE_API.get(`/like`, { params: { postId: posts.map(post => post.postId) } }))
.then(likeResponse => likeResponse.data)
.then(likeData => posts.map(post => {
let likeCount = likeData.likeCounts.find(likeCount => likeCount.postId == post.postId)?.likeCount ?? 0;
let userLiked = likeData.likedPosts.some(like => like.postId == post.postId);
return {...post, likeCount: likeCount, userLiked: userLiked};
}));
}


Expand Down
6 changes: 3 additions & 3 deletions src/frontend/views/login.njk
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{#
{#
Copyright 2023 Dynatrace LLC
Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -46,8 +46,8 @@ limitations under the License.
</div>

<div class="row">
<div class="col-md-6"><button class="btn btn-primary" formaction="{{ extendURL('/login') }}" type="submit" style="width: 100%;" name="login">Login</button></div>
<div class="col-md-6"><button class="btn btn-dark" formaction="{{ extendURL('/register') }}" type="submit" style="width: 100%;" name="register">Sign up</button></div>
<div class="col-md-6"><button class="btn btn-primary w-100" formaction="{{ extendURL('/login') }}" type="submit" name="login">Login</button></div>
<div class="col-md-6"><button class="btn btn-dark w-100" formaction="{{ extendURL('/register') }}" type="submit" name="register">Sign up</button></div>
</div>
</form>
</div>
Expand Down
11 changes: 5 additions & 6 deletions src/frontend/views/post.njk
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ limitations under the License.
<div class="row">
{% if (post.imageUrl) %}
<div class="col-auto">
<img src="{{ post.imageUrl }}" height="150px" alt="">
<img class="mw-100 h-100" src="{{ post.imageUrl }}" style="max-height: 150px" alt="">
</div>
{% endif %}
<div class="col">
Expand All @@ -45,14 +45,13 @@ limitations under the License.
</div>
</div>
<div class="card-footer">
<div style="float:right">
<div style="display: inline-block;">
<div class="float-right">
<div class="d-inline-block">
<h6>{{post.likeCount}}</h6>
</div>
<form action="{{extendURL('/post')}}" method="get" style="display: inline-block; height:46px">
<form action="{{extendURL('/unlike' if post.userLiked else '/like')}}" method="get" class="d-inline-block">
<input type="hidden" name="postId" value="{{post.postId}}">
<input type="hidden" name="{{ 'unlike' if post.userLiked else 'like' }}">
<button type="submit" style="background: transparent; border: none;">
<button type="submit" class="bg-transparent border-0">
<i class="bi {{ 'bi-hand-thumbs-up-fill' if post.userLiked else 'bi-hand-thumbs-up' }}" style="font-size: 1.25rem"></i>
</button>
</form>
Expand Down
Loading

0 comments on commit 8d10088

Please sign in to comment.