Browse Source


Jonathan Hodgson 1 month ago
  1. 2
  2. 36
  3. 175
  4. 140
  5. 30


@ -1,4 +1,6 @@


@ -12,7 +12,11 @@ blog(){
find content/ -type f -name '*.md' $newer | while read file; do
newFileName=$(getBlogSlug "$file")
mkdir -p "public_html/${newFileName%/*}"
mkdir -p "public_gemini/${newFileName%/*}"
pandoc --template=templates/blog.html -f markdown -t html5 -M comments "$file" > "public_html/$newFileName"
local info="$(sed -n '/---/,/---/p' "$file" | sed '/^$/,$d' | sed -n '1,/---/p' | sed '/^---$/d')"
local title="$(echo "$info" | yq -r .title)"
echo "# $title" | cat - "$file" | sed '/^---/,/^---/d' | md2gemini --link at-end > "public_gemini/${newFileName%.*}.gmi"
touch tmp/lastBlogUpdate
@ -42,6 +46,10 @@ makeIntro(){
<p><a href='$slug'>Read More</a></p>
</article>" > "$output"
echo "=> ${slug%.*}.gmi $title
" > "${output%.*}.gmi"
echo "<item>
@ -59,7 +67,11 @@ getIntro(){
if [ "$2" = "rss" ]; then
local output="tmp/rss/$file"
makeIntro "$file"
if [ "$2" = "gmi" ]; then
local output="tmp/intros/${file%.*}.gmi"
#makeIntro "$file"
cat "$output"
@ -70,9 +82,9 @@ index(){
# First loop makes all the intros needed and stores them in tmp/intros
find content/blog/ -type f -name '*.md' | grep -v 'xxx' | sort -r | head -n 20 | while read file; do
makeIntro "$file" &
makeIntro "$file"
# Once they are created, we will put them in, in order
@ -82,6 +94,22 @@ index(){
echo "$template" | sed -n '/#CONTENT#/,$p' | sed '1d'
) > public_html/index.html
# Once they are created, we will put them in, in order
echo "# Jonathan Hodgson"
echo ""
echo "## Blogs"
echo ""
find content/blog/ -type f -name '*.md' | grep -v 'xxx' | sort -r | head -n 20 | while read file; do
grep -Eq '^draft: true' "$file" || getIntro "$file" gmi
echo ""
echo "## Other Links"
echo "=> /help-me-out.gmi Help Me Out"
echo "=> /other-stuff-you-might-like.gmi Other Stuff You Might Like"
) > public_gemini/index.gmi
@ -182,11 +210,13 @@ clean(){
rm -rf tmp > /dev/null 2> /dev/null
# Don't remove the public_html folder or docker won't re-attach it
rm -rf public_html/*
rm -rf public_gemini/*
# Make sure the folders we will need exist
mkdir -p public_html/{blog,tag}
mkdir -p public_gemini/{blog,tag}
mkdir -p tmp/{tag,templates,intros}


@ -0,0 +1,175 @@
title: Writing Prose in Vim
- Vim
description: Vim is a popular text editor among programmers, it is not limited to programming though. In this blog I will discuss how I write just about everything in vim
date: 2021-10-06
If you know me at all, you probably know I am a fan of vim. I have written [more
than a few](/tag/vim.html) blog posts on it already.
Most vim users use vim for programming. That is, after all, what most people use
plain text editors for. However, since learning vim, I have tried to use it for
as much as I can. Everything from emails to blog posts to letters. Of course,
programming as well, but certainly not only that.
"Why?", you might ask. Because it makes my life easier. I have said before that
Vim is not as difficult as many vim users would like you to think it is.
## Spellcheck
I need a spell check. My spelling is atrocious. This is not helped by the fact
that I am dyslexic. Having a decent spell checker is essential if I am writing
anything that is going to be read by another human. Incidently, I count future
me as another human in this regard.
Vim has a built-in spell check.
set spell
set spelllang=en_gb
How it will look will depend on your colour scheme but in my case it is an
Basic usage consists of the following keyboard shortcuts:
* `[s` or `]s` to move between misspelled words
* `z=` to get a list of suggestions
* `zg` to add the current word to your custom dictionary
As with anything in vim, these shortcuts can be changed, although I have never
felt the need to.
Vim's insert completion can be used to look up words in the internal dictionary
as well. To use this, start writing a word and complete it with `ctrl-x ctrl-k`.
This will present you with a list of words that start with what you have typed.
You can select one, as normal, with `ctrl-n` and `ctrl-p`.
Finally, on the topic of spell checking, I have added an insert mode mapping
that is used to correct the previous misspelled word.
The mapping is as follows:
inoremap <C-l> <c-g>u<Esc>[s1z=`]a<c-g>u
1. `<c-g>u` - break undo sequence (new change)
1. `<Esc>` - go into normal mode
1. `[s` - go to previous spelling mistake
1. `1z=` - change to the top spelling suggestion
1. ```]`` - go to the end of the last changed word
1. `a` - enter insert mode
1. `<c-g>u` - break undo sequence (new change)
This was shamelessly stolen from [Gilles Castel's excellent blog post on using
vim for LaTeX](
## Undo Points
When programming in vim, a lot of time is spent in both normal mode and insert
mode. However, when writing prose, much more time is spent in insert mode. As a
result, each undo point tends to be much larger. This can result in the rather
annoying behaviour that undoing anything can remove a huge amount of writing.
To minimize this annoyance, I add undo points whenever I type punctuation
inoremap ! !<C-g>u
inoremap , ,<C-g>u
inoremap . .<C-g>u
inoremap : :<C-g>u
inoremap ; ;<C-g>u
inoremap ? ?<C-g>u
inoremap ( <C-g>u(
inoremap ) )<C-g>u
The only point I really need to elaborate on here is that for the opening
bracket: I add the undo-point before inserting the character. This results in
what I consider preferable behaviour whereby a whole bracket group is removed at
once when undoing, rather than leaving the opening bracket.
## Distraction Free Writing
Distraction free writing seems to be quite a fashionable / desirable feature in
a lot of editors at the moment. I have tried and used [Goyo](
and [limelight]( which go some way to
reproducing the distraction free environment of many other editors. However, I
no longer use them. Both worked well, but vim doesn't have that many
distractions without them. When I am writing, I open vim in a full screen
terminal. I don't even have a clock visible. Being able to highlight the
paragraph I am currently working on or putting the text in the middle of the
screen wasn't worth it for me.
## Proselint
[Proselint](, as the name suggests, is a
linter for prose. It points out areas where my writing could be better. To use
it with vim, I have the following autofunction:
function! mine#functions#proselint() abort
let oldmakeprg = &l:makeprg
" set new value of makeprg and call the function
set makeprg=proselint\ %
" set makeprg back to old value
let &l:makeprg = oldmakeprg
and a mapping:
nnoremap <leader>p :call mine#functions#proselint()<CR>
This allows me to fill the quickfix list with a list of suggestions from
## LanguageTool
LanguageTool is similar in concept to Proselint although seems to have a lot
more checks under its hood. It can be intergrated into vim in a similar way to
Proselint, with an autofunction.
function! mine#functions#languagetool() abort
let oldmakeprg = &l:makeprg
let olderrformat = &l:errorformat
" set new value of makeprg and call the function
set makeprg=languagetool\ -l\ en-GB\ %
let &l:errorformat =
\ '%-GPicked up _JAVA_OPTIONS: %.%#,' .
\ '%-GExpected text language: %.%#,' .
\ '%-PWorking on %f...,' .
\ '%-I%.%# [main] DEBUG %.%#,' .
\ '%+IUsing %.%# for file %.%#,' .
\ '%I%\d%\+.) Line %l\, column %c\, Rule ID: %m,' .
\ '%-CMessage%m,' .
\ '%-CSuggestion%m,' .
\ '%-CMore info%m,' .
\ '%-C%\s%#^%\+%\s%#,' .
\ '%-C%.%#,' .
\ '%-Z%\s%#,' .
\ '%-Q,' .
\ '%-GTime: %.%#'
" set makeprg back to old value
let &l:makeprg = oldmakeprg
let &l:makeprg = olderrformat
You will notice that this is slightly longer because the error format used by
Proselint is compatible with Vim's default. LanguageTool's is not.


@ -0,0 +1,140 @@
title: XSS Through the Referer Header
- Security
description: In this blog post, I document how I achieved reflected XSS though a malicious http header.
date: 2021-10-14
XSS is a vulnerability in which a malicious actor is able to run JavaScript in
an unsuspecting clients browser session. Normally, this is done via input fields
whose values are reflected back to the user without proper sanitisation.
In this blog, I demonstrate a method I recently used that injected the payload
via the Referer [sip] header.
Only do this on websites you own or have permission to do so no. It is illegal
in most places to do this without permission.
## Initial foothold
I found myself looking at a website whose input fields were all well sanitised,
getting ready to write a pretty boring report. However, I spotted a snippet of
JavaScript in an analytics script that looked something like this:
"referer": ""
That sparked my interest so I opened pappy and changed the referer header to `"
+ alert(1),"":"`.
BRILLIANT! That worked. The referer header was not being sanitised.
## Crafting the referer header
So, that is nice, but it would take some serious social engineering to convince
someone to intercept a request in burp / pappy / some other proxy tool, change
their referer header to our payload and then submit the request. Really we
needed a way to control that header ourselves. For anyone that doesn't know, the
[referer header](
holds the address of the page that makes the request. This basically means the
previous page if a link was clicked.
There is also a related header called [referrer-policy](
that controls how much information is sent. I want to send as much as possible
so I manually set this to `unsafe-url` for my proof of concept.
header('Referrer-Policy: unsafe-url');
I am using php here because I know it. I'm not saying php is the best choice.
Use what you know and can use quickly for proof of concepts.
My initial thought was to simply use HTTP redirects to first redirect the user
to a page whose url contained my payload, and then from there to the vulnerable
The code for that was:
header('Referrer-Policy: unsafe-url');
if ( $_SERVER['REQUEST_URI'] == "/index.php" ) {
header('Location: //localhost:8081/index.php/' . urlencode($payload));
That didn't work. After some searching I realised that the referer header is not
changed when an http redirect is followed. It does however if the url is changed
with JS.
header('Referrer-Policy: unsafe-url');
if ( $_SERVER['REQUEST_URI'] == "/index.php" ) {
$location='//localhost:8081/index.php/' . urlencode($payload);
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
document.location="<?php echo $location; ?>";
So, after hosting this at localhost:8081, I was able to visit it in my browser.
If doing this in the real world, I would host it on a public server somewhere
and try and convince a victim to click a link.
In my case, the initial link would be to `//localhost:8081/index.php`. This
would then use JS to redirect the victim to to
`//localhost:8081/index.php/%22%2Balert%281%29%2C%22%22%3A%22`. We would then
use JS again to redirct the user to the vulnerable site. With the
referrer-policy header set to unsafe-url, the browser will set the referer
header to the url including our payload and trigger our payload. In this
example, we are doing `alert(1)`. That's pretty boring and obvious to the user.
However, in the real world, we could send another request back to our server
with the contents of document.cookie to steal the session, or prompt the user to
re-enter their credentials and send that to ourselves. Once you have
unrestricted XSS, account compromise is normally possible.
## Improvements
Many of you may have noticed ways to improve the payload. For example, I hard
coded localhost:8081, the payload and various other information. If you want,
feel free to improve it but this is supposed to be a proof of concept, not a
well build program. When you're making POCs, it's the one time you really don't
need to worry about coding well - it's about getting something that works in a
short time frame.
## Solution
This issue came about because http headers were trusted. If the site had
validated the http header, or encoded it in some way, this would not have been
possible. Always sanitise. Never assume that anything that comes in the request
is safe.


@ -7,11 +7,19 @@ I consume a lot more content than I produce. If you are interested in similar st
## Linux
### HexDSL
Does a lot of videos on Linux gaming (not of much interest to me) but also does
great videos on non-gaming linux content. *Side note:* He seems like a great guy
on his discord server.
* [Website](
* [Youtube](
### Brodie Robertson
Videos about Linux and surrounding areas
* [Lbry](
* [Youtube](
### Greg Hurrell
@ -20,20 +28,6 @@ Mostly videos on Vim and some tools he uses with Vim.
* [Youtube](
### Hex DSL
Does a lot of videos on Linux gaming (not of much interest to me) but also does videos on interesting Linux tools
* [Lbry](
* [Youtube](
### Luke Smith
Videos on Linux, Vim, LaTeX and surrounding areas.
* [Lbry](
* [Youtube](
* [Blog](
### Self Hosted
@ -53,7 +47,6 @@ Interviews with hackers (white and black hat) as well as discussing famous hacks
Hacking tutorials
* [Lbry](
* [Youtube](
### Ippsec
@ -89,7 +82,6 @@ Hardware reviews, weekly news roundup, tutorial videos
Mostly mathematical videos. Includes a brilliant playlist on basic calculus
* [Lbry](
* [Youtube](
### CGP Grey
@ -102,14 +94,13 @@ Informative, well researched videos on lots of topics (although often opinionate
Informative, well researched videos on lots of topics
* [Lbry](
* [Youtube](
### Minute Physics
Science videos. Mostly physics and applied maths.
* [Lbry](
* [Youtube](
### Numberphile
@ -127,7 +118,6 @@ Podcasts about all sorts of interesting things. From disappearing aeroplanes to
Science videos, mostly physics and applied maths.
* [Lbry](
* [Youtube](
### Yuval Noah Harari