Security issues documented, and more nice improvements
This commit is contained in:
parent
f3668df846
commit
93934438c7
2
Makefile
2
Makefile
@ -36,5 +36,7 @@ debug:
|
|||||||
$(DEV) --debug
|
$(DEV) --debug
|
||||||
clean: clean_pub
|
clean: clean_pub
|
||||||
rm -fr resources/ hugo_stats.json .hugo_build.lock
|
rm -fr resources/ hugo_stats.json .hugo_build.lock
|
||||||
|
clean_js: clean
|
||||||
|
rm -fr node_modules/ yarn.lock
|
||||||
clean_pub:
|
clean_pub:
|
||||||
rm -fr public/
|
rm -fr public/
|
||||||
|
@ -46,7 +46,6 @@ Handlebars.registerPartial('people', `
|
|||||||
{{#each members}}
|
{{#each members}}
|
||||||
<li>{{name}} age {{age}} from {{home}}</li>
|
<li>{{name}} age {{age}} from {{home}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
balle!
|
|
||||||
</ul>`)
|
</ul>`)
|
||||||
|
|
||||||
// Calling non-existent partials with block syntax uses the block content as fallback
|
// Calling non-existent partials with block syntax uses the block content as fallback
|
||||||
|
BIN
assets/apprentice/skyid/intrauser-key.png
Normal file
BIN
assets/apprentice/skyid/intrauser-key.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
BIN
assets/apprentice/skyid/old-intrauser-key.png
Normal file
BIN
assets/apprentice/skyid/old-intrauser-key.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -1,6 +1,6 @@
|
|||||||
.chroma {
|
.chroma {
|
||||||
padding: .1vh 0 0; margin: 0 0 .7vh; min-height: 3.3vh;
|
min-height: 3.3vh; overflow: auto;
|
||||||
color: #ffffff; background-color: #111111 }
|
color: #ffffff; background-color: #111111; }
|
||||||
.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0 }
|
.chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0 }
|
||||||
.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0 }
|
.chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0 }
|
||||||
.chroma .hl { background-color: #ffffcc }
|
.chroma .hl { background-color: #ffffcc }
|
||||||
|
@ -26,7 +26,7 @@ figure {
|
|||||||
margin: 3% 10%;
|
margin: 3% 10%;
|
||||||
padding: 3%;
|
padding: 3%;
|
||||||
article {
|
article {
|
||||||
max-width: 85%;
|
max-width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a:hover %headings {
|
a:hover %headings {
|
||||||
|
@ -5,7 +5,7 @@ aliases: []
|
|||||||
categories: ['meta']
|
categories: ['meta']
|
||||||
series: []
|
series: []
|
||||||
tags: ['info', 'meta']
|
tags: ['info', 'meta']
|
||||||
toc: false
|
toc: true
|
||||||
title: Welcome!
|
title: Welcome!
|
||||||
description: Landing page for Siverts GitHub Pages blog and portfolio.
|
description: Landing page for Siverts GitHub Pages blog and portfolio.
|
||||||
---
|
---
|
||||||
|
@ -5,7 +5,7 @@ aliases: []
|
|||||||
categories: ['meta']
|
categories: ['meta']
|
||||||
series: []
|
series: []
|
||||||
tags: ['info','meta']
|
tags: ['info','meta']
|
||||||
toc: false
|
toc: true
|
||||||
title: Siverts Tech Adventure Blog
|
title: Siverts Tech Adventure Blog
|
||||||
description: Landing page for Siverts Tech Adventure Blog!
|
description: Landing page for Siverts Tech Adventure Blog!
|
||||||
---
|
---
|
||||||
|
@ -5,7 +5,7 @@ aliases: []
|
|||||||
categories: ['meta']
|
categories: ['meta']
|
||||||
series: []
|
series: []
|
||||||
tags: ['info','meta']
|
tags: ['info','meta']
|
||||||
toc: false
|
toc: true
|
||||||
title: Siverts Technisch Avontuur Blog
|
title: Siverts Technisch Avontuur Blog
|
||||||
description: Bestemmingspagina voor Siverts Siverts Technisch Avontuur Blog!
|
description: Bestemmingspagina voor Siverts Siverts Technisch Avontuur Blog!
|
||||||
---
|
---
|
||||||
|
@ -5,7 +5,7 @@ aliases: []
|
|||||||
categories: ['meta']
|
categories: ['meta']
|
||||||
series: []
|
series: []
|
||||||
tags: ['info','meta']
|
tags: ['info','meta']
|
||||||
toc: false
|
toc: true
|
||||||
title: Siverts Teknologi Evenetyr Blog
|
title: Siverts Teknologi Evenetyr Blog
|
||||||
description: Landingsside for Siverts Teknologi Evenetyr Blog!
|
description: Landingsside for Siverts Teknologi Evenetyr Blog!
|
||||||
---
|
---
|
||||||
|
@ -5,7 +5,7 @@ aliases: []
|
|||||||
categories: ['apprentice', 'indices', 'meta']
|
categories: ['apprentice', 'indices', 'meta']
|
||||||
series: ['apprentice']
|
series: ['apprentice']
|
||||||
tags: ['apprentice']
|
tags: ['apprentice']
|
||||||
toc: false
|
toc: true
|
||||||
title: IT Apprentice
|
title: IT Apprentice
|
||||||
description: This part of my blog is a collection of things I've learned as an apprentice working in IT.
|
description: This part of my blog is a collection of things I've learned as an apprentice working in IT.
|
||||||
---
|
---
|
||||||
@ -16,29 +16,30 @@ It also serves the purpose of the required documentation Apprentices in Norway a
|
|||||||
|
|
||||||
Here are lists of stuff I'll be writing about here;
|
Here are lists of stuff I'll be writing about here;
|
||||||
|
|
||||||
### Programming
|
## Programming
|
||||||
#### Python
|
### Python
|
||||||
- Flask :baby_bottle:
|
- Flask :baby_bottle:
|
||||||
- SQLAlchemy :sake:
|
- SQLAlchemy :sake:
|
||||||
#### Browser JavaScript
|
- MSAL (Microsoft Authentication Library) :banjo:
|
||||||
|
### Browser JavaScript
|
||||||
- jQuery :calling:
|
- jQuery :calling:
|
||||||
- Handlebars.js :wavy_dash:
|
- Handlebars.js :wavy_dash:
|
||||||
|
|
||||||
### Programs
|
## Programs / Tools
|
||||||
- FreeRADIUS :crystal_ball:
|
- FreeRADIUS :crystal_ball:
|
||||||
- Postgres :floppy_disk:
|
- Postgres :floppy_disk:
|
||||||
- Ansible :gun:
|
- Ansible :gun:
|
||||||
|
|
||||||
### Operating Systems/Networking devices
|
## OS / Networking
|
||||||
- Debian :dolls:
|
- Debian :dolls:
|
||||||
- Mikrotik :package:
|
- Mikrotik :package:
|
||||||
- Cisco Meraki :cloud:
|
- Cisco Meraki :cloud:
|
||||||
|
|
||||||
### Azure
|
## Azure
|
||||||
- App Registration :rocket:
|
- App Registration :rocket:
|
||||||
- Active Directory :dizzy:
|
- Active Directory :dizzy:
|
||||||
|
|
||||||
### The WHM saga (shortie)
|
## The WHM saga (shortie)
|
||||||
- Wordpress :eyes:
|
- Wordpress :eyes:
|
||||||
- cPanel :shit:
|
- cPanel :shit:
|
||||||
- WHM :ok_hand:
|
- WHM :ok_hand:
|
||||||
|
@ -67,3 +67,6 @@ If it's a tenant admin that's logging in that person may click the check for gra
|
|||||||
Small and simple powershell script for adding the "service principal" to a tenant;
|
Small and simple powershell script for adding the "service principal" to a tenant;
|
||||||
|
|
||||||
{{< highlight powershell >}}{{% asset "/apprentice/azure/service-principal.ps1" %}}{{< /highlight >}}
|
{{< highlight powershell >}}{{% asset "/apprentice/azure/service-principal.ps1" %}}{{< /highlight >}}
|
||||||
|
|
||||||
|
After the service principal is created in the Azure AD tenant with users that's going to consume the app.
|
||||||
|
Admins may set "App Roles" per user under "Enterprise Applications" in Azure.
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
date: 2022-05-25T09:54:01Z
|
date: 2022-05-25T09:54:01Z
|
||||||
draft: true
|
draft: true
|
||||||
aliases: []
|
aliases: []
|
||||||
categories: ['various']
|
categories: ['apprentice', 'expose']
|
||||||
series: []
|
series: ['apprentice', 'expose']
|
||||||
tags: ['various']
|
tags: ['apprentice', 'expose', 'various']
|
||||||
toc: true
|
toc: true
|
||||||
title: Sircon
|
title: Sircon
|
||||||
description:
|
description:
|
||||||
|
12
content/blog/apprentice/mikrotik.en.md
Normal file
12
content/blog/apprentice/mikrotik.en.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
date: 2022-06-08T10:34:42Z
|
||||||
|
draft: true
|
||||||
|
aliases: []
|
||||||
|
categories: ['various']
|
||||||
|
series: []
|
||||||
|
tags: ['various']
|
||||||
|
chroma: false
|
||||||
|
toc: true
|
||||||
|
title: Mikrotik
|
||||||
|
description:
|
||||||
|
---
|
@ -1,14 +1,119 @@
|
|||||||
---
|
---
|
||||||
date: 2022-06-07T07:15:51Z
|
date: 2022-06-07T07:15:51Z
|
||||||
draft: true
|
draft: false
|
||||||
aliases: []
|
aliases: []
|
||||||
categories: ['various']
|
categories: ['apprentice']
|
||||||
series: []
|
series: ['hacking', 'security']
|
||||||
tags: ['various']
|
tags: ['apprentice', 'tech', 'bug']
|
||||||
|
chroma: true
|
||||||
toc: true
|
toc: true
|
||||||
title: Security
|
title: Security issues
|
||||||
description:
|
description: Various security issues I found, disclosed and got fixed as an apprentice!
|
||||||
---
|
---
|
||||||
|
|
||||||
Apache .htconfig fuckery, WP bug.
|
Here is a collection of security issues I found, disclosed and got fixed as an apprentice!
|
||||||
Ansible and intrauser ssh keys.
|
At SkyLabs I fixed the issues myself, but at Sircon they delegated all the boring support to me and the previous apprentice.
|
||||||
|
So there I just disclosed the issue to the responsible person and both of them fixed their respective issues within an hour.
|
||||||
|
|
||||||
|
## SkyLabs
|
||||||
|
SkyLabs has surprisingly good stability compared to expectations based on the codebase.
|
||||||
|
I'm guessing it's a byproduct of Python that let's us fail pretty safely on almost all endpoints.
|
||||||
|
The in-house development is mainly four services, two JS frontends and two Python Flask backends, all web.
|
||||||
|
|
||||||
|
These services are a captive portal, it's API and an admin interface web app and it's API.
|
||||||
|
|
||||||
|
We support lots of interesting [authentication](https://wiki.skylabs.no/partner:authentication) methods!~
|
||||||
|
|
||||||
|
### Sky ID Admin
|
||||||
|
And so far the only real security related issue I've found in the in-house codebase is that the Access Control was missing a check.
|
||||||
|
The problem is that API keys that are associated with a deleted account is valid.
|
||||||
|
Allowed methods are by x-api-key Authorization header, api-key as an url parameter or by Json Web Token.
|
||||||
|
The JWT is passed in an Authorization Bearer header, which is wrong, [Bearer](https://datatracker.ietf.org/doc/html/rfc6750) Auth header prefix should be reserved for [OAuth 2](https://datatracker.ietf.org/doc/html/rfc6749).
|
||||||
|
|
||||||
|
Anyway when using API keys, not JWT, the server just fetches the API key record from the database.
|
||||||
|
The function calls for this goes from the Access Control controller class through an API key controller.
|
||||||
|
Problem is the API key controller class only checks if the associated user exists, not if it's been marked as deleted.
|
||||||
|
So if you have an API key, you may have your accounts full access after it's supposedly been deleted.
|
||||||
|
|
||||||
|
And the fix for it was super easy!~
|
||||||
|
|
||||||
|
We use a custom endpoint decorator, like flasks own @app.route, but we control what happens.
|
||||||
|
It's here the authentication is implemented, on all requests that go to routes defined with this decorator will run the Access Control controller.
|
||||||
|
Access Control goes through the auth options in it's dunder init function and sets self.current_user to some admin user from the database.
|
||||||
|
To fix the problem I could've made this check specific to the API keys by putting it in the API key controller that also has a user getter function.
|
||||||
|
But I chose to add an if statement to the Access Control user getter, this is called by our endpoint decorator.
|
||||||
|
So I literally just changed this.
|
||||||
|
|
||||||
|
{{< highlight py >}}
|
||||||
|
class AccessControl:
|
||||||
|
current_user = None
|
||||||
|
...
|
||||||
|
def get_current_user(self):
|
||||||
|
return self.current_user
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
To that!
|
||||||
|
|
||||||
|
{{< highlight py >}}
|
||||||
|
class AccessControl:
|
||||||
|
current_user = None
|
||||||
|
...
|
||||||
|
def get_current_user(self):
|
||||||
|
if self.current_user.deleted is None:
|
||||||
|
return self.current_user
|
||||||
|
else:
|
||||||
|
raise Forbidden
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
To fix this issue.
|
||||||
|
|
||||||
|
### Servers
|
||||||
|
On the other side, the cloud servers had some bigger problems.
|
||||||
|
Here I did find a *BIG* security issue. Ancient ssh keys.
|
||||||
|
We use Ansible to manage the servers and deploy code updates.
|
||||||
|
There is an Ansible var file that has a list of user accounts with options for shell and ssh pubkey.
|
||||||
|
That file also has a list of old accounts that should be deleted.
|
||||||
|
These are per employee users.
|
||||||
|
But the intrauser and ansible users have existed since 2016...
|
||||||
|
using the same keys that they were created with.
|
||||||
|
The reason this is such a big threat is the fact that intrauser, ansible and all employee accounts has sudo access without password.
|
||||||
|
|
||||||
|
So check it; these screenshots are from SkyLabs' Ansible git log!~
|
||||||
|
|
||||||
|
{{< img src="apprentice/skyid/old-intrauser-key.png" caption="The old intrauser ssh key" >}}
|
||||||
|
{{< img src="apprentice/skyid/intrauser-key.png" caption="Me finally updating the key" >}}
|
||||||
|
|
||||||
|
## Sircon
|
||||||
|
These guys has a few beefy ass physical servers in their own rack in a supposedly EMP and fire safe room in the basement of their offices.
|
||||||
|
Those physical machines run virtual machines that run WHM/cPanel and whatever PHP app the customer would like.
|
||||||
|
But >90% of their customers are non-technical and just only interact with the default WordPress setup and maybe the e-mail service that comes with their cPanel.
|
||||||
|
Most of the people calling in to their support line has problems using their e-mail, and it's usually the end user who fucked up.
|
||||||
|
|
||||||
|
### Servers
|
||||||
|
Have you ever used Apache httpd? Have you ever used its .htaccess config?
|
||||||
|
Well it's pretty cool, but may pose a threat on shared "web hotels" like WHM servers running a whole lot of cPanel accounts.
|
||||||
|
If using .htaccess is allowed users may set their own configuration options for httpd.
|
||||||
|
|
||||||
|
And the issue I identified here was the fact that cgi configuration options where allowed in .htaccess.
|
||||||
|
So I had some fun making wired cgi scripts in python, perl and bash.
|
||||||
|
|
||||||
|
The last thing I did before disclosing it to Sircons server guy was to make a proof of concept script that gave me a shell on the server.
|
||||||
|
As they don't allow ssh access and PHP is configured to not allow anything dangerous, like the system() function, that would allow you to create shells.
|
||||||
|
|
||||||
|
Example .htaccess to run cgi scripts.
|
||||||
|
|
||||||
|
{{< highlight apache >}}
|
||||||
|
Options +ExecCGI
|
||||||
|
AddHandler cgi-script .cgi .pl .py .sh .rb
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The fix for this is the [AllowOverride](https://httpd.apache.org/docs/current/mod/core.html#allowoverride) directive.
|
||||||
|
Just make sure it does not include "FileInfo".
|
||||||
|
|
||||||
|
### WordPress
|
||||||
|
I found a bug on their homepage [sircon.no](https://sircon.no) where they have a simple WordPress shop with a couple of cool features, like [this](https://sircon.no/sjekk-om-din-nettbutikk-nettsted-driftes-miljovennlig/) where you supposedly can check how green a web page is. As in green energy.
|
||||||
|
|
||||||
|
The bug I found was missing server side validation and a shopping cart that's stored in the browsers local storage with price specified.
|
||||||
|
This is a pretty minor issue however, as they don't do automatic provisioning, and it only gets added to some internal webpage they use to keep track of everything.
|
||||||
|
But whatever you posted as the price would show up in that tool.
|
||||||
|
You could even post negative prices!
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<header>
|
||||||
<h1>{{ .Title | humanize }}</h1>
|
<h1>{{ .Title }}</h1>
|
||||||
</header>
|
</header>
|
||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
{{if gt (len .Pages) 0}}
|
{{if gt (len .Pages) 0}}
|
||||||
@ -10,9 +10,7 @@
|
|||||||
<ul class='contents'>
|
<ul class='contents'>
|
||||||
{{ range .Pages }}
|
{{ range .Pages }}
|
||||||
<br />
|
<br />
|
||||||
<a href='{{ .RelPermalink }}'>
|
<a href='{{ .RelPermalink }}'><li>{{ partial "summary.html" . }}</li></a>
|
||||||
<li>{{ partial "summary.html" . }}</li>
|
|
||||||
</a>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<article>
|
<article>
|
||||||
<header>
|
<header>
|
||||||
<h1>{{ .Title | humanize }}</h1>
|
<h1>{{ .Title }}</h1>
|
||||||
</header>
|
</header>
|
||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
</article>
|
</article>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{{ if .IsTranslated }}<h4>{{ T "translations" }}</h4>
|
{{ if .IsTranslated }}<h6>{{ T "translations" }}:</h6>
|
||||||
<ul>{{ range .Translations }}
|
<ul>{{ range .Translations }}
|
||||||
<li>
|
<li>
|
||||||
<a href='{{ .RelPermalink }}'>{{ .Lang }}: {{ .Title }}{{ if .IsPage }} ({{ i18n "wordCount" . }}){{ end }}</a>
|
<a href='{{ .RelPermalink }}'>{{ .Lang }}: {{ .Title }}{{ if .IsPage }} ({{ i18n "wordCount" . }}){{ end }}</a>
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
{{- $lastmod := .Lastmod.Format "02/01/2006" }}
|
{{- $lastmod := .Lastmod.Format "02/01/2006" }}
|
||||||
<article>
|
<article>
|
||||||
<div>
|
<div>
|
||||||
<!-- <h3><a href='{{ .RelPermalink }}'>{{ .Title }}</a></h3> -->
|
<h3>{{ .Title }}</h3>
|
||||||
<h3>{{ .Title | humanize }}</h3>
|
|
||||||
{{ i18n "published" }} {{ $pubdate }}
|
{{ i18n "published" }} {{ $pubdate }}
|
||||||
{{ if ne $pubdate $lastmod }}
|
{{ if ne $pubdate $lastmod }}
|
||||||
{{ i18n "modified" }} {{ $lastmod }}
|
{{ i18n "modified" }} {{ $lastmod }}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<aside id='meta'>
|
<aside id='meta'>
|
||||||
<header>
|
<header>
|
||||||
<h4>{{ or (.Param "tit") .Title | humanize }}</h4>
|
<h4>{{ or (.Param "tit") .Title }}</h4>
|
||||||
<p>
|
<p>
|
||||||
{{ i18n "readingTime" .ReadingTime }}.
|
|
||||||
{{ i18n "wordCount" .WordCount }}.
|
{{ i18n "wordCount" .WordCount }}.
|
||||||
|
{{ i18n "readingTime" .ReadingTime }}.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
{{ if .Param "docs" }}
|
{{ if .Param "docs" }}
|
||||||
<h6>Documentation:</h6>
|
<h5>Documentation:</h5>
|
||||||
<ul>
|
<ul>
|
||||||
{{ range .Param "docs" }}
|
{{ range .Param "docs" }}
|
||||||
<li><a href='{{ .url }}' target='_blank'>{{ .name }}</a></li>
|
<li><a href='{{ .url }}' target='_blank'>{{ .name }}</a></li>
|
||||||
@ -15,7 +15,18 @@
|
|||||||
{{ with .Param "git" }}
|
{{ with .Param "git" }}
|
||||||
<p>Check out the git repo; <a href='{{ .url }}'>{{ .name }}</a>!</p>
|
<p>Check out the git repo; <a href='{{ .url }}'>{{ .name }}</a>!</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ $toc := .TableOfContents }}
|
||||||
|
{{ if ne $toc "<nav id=\"TableOfContents\"></nav>" }}
|
||||||
<h6>Table of Contents:</h6>
|
<h6>Table of Contents:</h6>
|
||||||
{{ .TableOfContents }}
|
{{ $toc }}
|
||||||
|
{{ end }}
|
||||||
{{ partial "i18n/list.html" . }}
|
{{ partial "i18n/list.html" . }}
|
||||||
|
{{ if and (ne (len .Pages) 0) $.IsNode }}
|
||||||
|
<h5>Posts:</h5>
|
||||||
|
<ul>
|
||||||
|
{{ range .Pages }}
|
||||||
|
<li><a href='{{ .RelPermalink }}' target='_top'>{{ .Title }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ end }}
|
||||||
</aside>
|
</aside>
|
@ -2,4 +2,9 @@
|
|||||||
{{ if .Get 1 }}
|
{{ if .Get 1 }}
|
||||||
{{ $opts = $opts | append (split (.Get 1) ",") }}
|
{{ $opts = $opts | append (split (.Get 1) ",") }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ transform.Highlight (htmlUnescape .Inner) (.Get 0) (delimit $opts ",") }}
|
{{ $content := .Inner }}
|
||||||
|
{{ $in := split .Inner "\n" }}
|
||||||
|
{{ if gt (len $in) 1 }}
|
||||||
|
{{ $content = (delimit (after 1 $in) "\n") }}
|
||||||
|
{{ end }}
|
||||||
|
{{ transform.Highlight (htmlUnescape $content) (.Get 0) (delimit $opts ",") }}
|
Loading…
Reference in New Issue
Block a user