parent
e17a042f52
commit
9d8c010bad
25 changed files with 2069 additions and 18 deletions
@ -0,0 +1,15 @@ |
||||
-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Version: OpenPGP.js v4.10.4 |
||||
Comment: https://openpgpjs.org |
||||
|
||||
xjMEXMLhpBYJKwYBBAHaRw8BAQdAdP4j18qDM+5einbcOoMmbygC7blelM2D |
||||
1WjQ/QK4CzXNNSJqb25hdGhhbkBqb25hdGhhbmguY28udWsiIDxqb25hdGhh |
||||
bkBqb25hdGhhbmguY28udWs+wncEEBYKAB8FAlzC4aQGCwkHCAMCBBUICgID |
||||
FgIBAhkBAhsDAh4BAAoJEESBz3saoNE4wecA/R94tVukEZISL8dPYN17+JLl |
||||
QYo5U9NpSlYM1bjbKdqPAQDophGzo6KaCwNkm3/05xJvf+ZnsOBTG4PkvPFJ |
||||
5XZZAM44BFzC4aQSCisGAQQBl1UBBQEBB0BgX9KpJi1EvI74EU5jb4PVjo+H |
||||
0DuuN58sodzsIpQQDgMBCAfCYQQYFggACQUCXMLhpAIbDAAKCRBEgc97GqDR |
||||
OGapAPwNdh8veidfCqZuXoyBfdRGPRv9zb7pA3JvP85hMsTxXgEAld6Rqey2 |
||||
dw17AxOy5uHs/nj/3Zwl2SvGNR4htgPv1go= |
||||
=qcW+ |
||||
-----END PGP PUBLIC KEY BLOCK----- |
@ -0,0 +1,56 @@ |
||||
-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Version: OpenPGP.js v4.10.4 |
||||
Comment: https://openpgpjs.org |
||||
|
||||
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 |
||||
YDlctaOrI9VzScwGG3H7z+kzCKU= |
||||
=h4NF |
||||
-----END PGP PUBLIC KEY BLOCK----- |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 7.6 KiB |
@ -0,0 +1,149 @@ |
||||
--- |
||||
title: Current Word Completion |
||||
date: 2020-05-26 |
||||
description: ZSH's tab completion is excellent, although limited to completing based on your command. With this, I can also make completions based on my current word. |
||||
tags: |
||||
- ZSH |
||||
- FZF |
||||
--- |
||||
|
||||
Tab completion is one of the main reasons I decided to make the switch to ZSH. Being able to see descriptions and cycle between options makes finding options a breeze: |
||||
|
||||
![ZSH Tab Completion](/assets/switch-to-zsh/zsh-tab-complete.gif) |
||||
|
||||
However, there are times that I would prefer to have completion options based on the current word I have typed, regardless of the command I am running. I suppose this is more like an extension of ZSH's global aliases, but with the ability to choose from multiple options. An example of this is for word lists. There are a lot of commands I use that require word lists and it is a pain to write out the full path before I am able to tab-complete. My solution is what I have been calling Current Word Completion. My current iteration of it relies on [FZF](https://github.com/junegunn/fzf) for the completions, although in the long term, I would like to make it work with ZSH's built in completion system. Here is a demonstration using `wl` for wordlists and `myip` for my local machines ip addresses. |
||||
|
||||
![Current Word Completion](/assets/word-complete/currentWordComplete.gif) |
||||
|
||||
You can see that it doesn't stop ZSH's normal completion from working and it can work multiple times on any command. |
||||
|
||||
As with tab-completion, I have it bound to my tab key. |
||||
|
||||
If all you want to do is implement it yourself, you can find the source code [here](https://git.jonathanh.co.uk/jab2870/Dotfiles/src/branch/master/shells/zsh/includes/currentwordcompletion.zsh). Simply source the file in your `.zshrc` and you should be good to go. You can add new options to the `word_replace` function. |
||||
|
||||
If you are interested in how it works, keep reading. |
||||
|
||||
## Overriding tab key |
||||
|
||||
The tab key is interpreted by the terminal as ctrl+i for [legacy reasons](https://bestasciitable.com/). That is why I am binding to `^I`. |
||||
|
||||
The code below will check what ctrl+i is currently bound to and store it. This means we can override the tab key in such a way that if we are not on a word that should be expanded, we can fall back to whatever tab used to do. |
||||
|
||||
We then bind ctrl+i to a new function that will spawn a notification and then run whatever ctrl+i used to be bound to. |
||||
|
||||
```zsh |
||||
currentwordcomplete(){ |
||||
notify-send "Yay, it works" |
||||
zle ${currentword_default_completion:-expand-or-complete} |
||||
} |
||||
|
||||
# Record what ctrl+i is currently set to |
||||
# That way we can call it if currentword_default_completion doesn't result in anything |
||||
[ -z "$currentword_default_completion" ] && { |
||||
binding=$(bindkey '^I') |
||||
[[ $binding =~ 'undefined-key' ]] || currentword_default_completion=$binding[(s: :w)2] |
||||
unset binding |
||||
} |
||||
zle -N currentwordcomplete |
||||
bindkey '^I' currentwordcomplete |
||||
``` |
||||
|
||||
You can check this works by sourcing the file and pushing tab. You should get a notification and then normal tab completion should work. |
||||
|
||||
## Splitting up the current line |
||||
|
||||
The `LBUFFER` variable in a ZSH widget contains a string equal to everything before your cursor in the buffer. We can use expansion built into ZSH to turn that into an array of words. |
||||
|
||||
```zsh |
||||
tokens=(${(z)LBUFFER}) |
||||
``` |
||||
|
||||
The `z` flag here will expand the string using shell parsing to split the string into arguments. This takes into account quotes and escaped spaces. Full details can be found in [the documentation](http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags). |
||||
|
||||
This means that we have an array of words. The word we are currently on will be the last: |
||||
|
||||
```zsh |
||||
lastWord="${tokens[-1]}" |
||||
``` |
||||
|
||||
I also get the first argument which is normally going to be the command currently being run. Although I don't use this at the moment, I thought it might be useful to be able to exclude certain commands from completion. |
||||
|
||||
```zsh |
||||
cmd="${tokens[1]}" |
||||
``` |
||||
|
||||
## Word Replace |
||||
|
||||
I have a function called `word_replace` that takes the word that should be replaced and the command, and prints what it should be replaced with. It also returns 0 on success (there was a replacement found). |
||||
|
||||
```zsh |
||||
word_replace(){ |
||||
local ret=1 |
||||
local word="$1" |
||||
local cmd="$2" |
||||
case "$word" in |
||||
wl) wordlistSelect; return 0 ;; |
||||
myip) ip route | grep -oE '(dev|src) [^ ]+' | sed 'N;s/\n/,/;s/src //;s/dev //' | awk -F',' '{print $2 " " $1}' | sort -u | fzf -1 --no-preview | cut -d' ' -f1; return 0 ;; |
||||
esac |
||||
return "$ret" |
||||
} |
||||
``` |
||||
|
||||
In this case, it is a simple switch statement. An interesting side note is the use of `-1` in the FZF command for `myip`. This will prevent FZF from running if there is only 1 option fed to it. So, if I am only connected on one interface, it will simply fill my ip address rather than prompting me to choose one. |
||||
|
||||
Obviously, the logic used here could be as simple or complex as you wish. |
||||
|
||||
## Getting the completion |
||||
|
||||
Inside the `currentwordcomplete` function, I get the output of the `word_replace` function which is passed the current word. If that doesn't result in a completion, I will run the `word_replace` function again, using only the part of the word that comes after an `=` sign (if there is one). |
||||
|
||||
In either case, a variable called swap will contain what the current word should be replaced with. |
||||
|
||||
There will also be a variable called `ret` that will be equal to 0 if the replacement should be made. |
||||
|
||||
```zsh |
||||
currentwordcomplete(){ |
||||
... |
||||
# Check we haven't pushed space |
||||
if [ "${LBUFFER[-1]}" != " " ]; then |
||||
swap="$(word_replace "$lastWord" "$cmd")" |
||||
ret="$?" |
||||
|
||||
# This part checks if the part after an = is completable |
||||
if [ "$ret" -ne "0" ]; then |
||||
local afterEqual="${lastWord##*=}" |
||||
local beforeEqual="${lastWord%=*}" |
||||
# If they are different, there is an equals in the word |
||||
if [ "$afterEqual" != "$lastWord" ]; then |
||||
swap="${beforeEqual}=$(word_replace "$afterEqual" "$cmd")" |
||||
ret="$?" |
||||
fi |
||||
fi |
||||
fi |
||||
... |
||||
} |
||||
``` |
||||
|
||||
## Making the change |
||||
|
||||
Finally, I check if the completion should be made. If it shouldn't, I call whatever function the tab key used to be bound to. If it should, I change the last item of the tokens array that we created earlier. I then set the LBUFFER variable to the changed string. |
||||
|
||||
```zsh |
||||
if [ "$ret" -eq "0" ]; then |
||||
if [ -n "$swap" ]; then |
||||
tokens[-1]="$swap" |
||||
LBUFFER="${tokens[@]}" |
||||
fi |
||||
zle reset-prompt |
||||
return 0 |
||||
else |
||||
zle ${currentword_default_completion:-expand-or-complete} |
||||
return |
||||
fi |
||||
``` |
||||
|
||||
## Conclusion |
||||
|
||||
This is a relatively un-intrusive addition to ZSH that I use most days. I don't use a huge number of these but the two I mentioned here, word lists and my ip, I use a lot. |
||||
|
||||
You can find the [full source here](https://git.jonathanh.co.uk/jab2870/Dotfiles/src/branch/master/shells/zsh/includes/currentwordcompletion.zsh). If you are interested in my full [ZSH config is here](https://git.jonathanh.co.uk/jab2870/Dotfiles/src/branch/master/shells/zsh/). |
@ -0,0 +1,230 @@ |
||||
--- |
||||
title: Upgrading reverse shells |
||||
date: 2020-06-01 |
||||
description: Once you've got a reverse shell, there are a number of things you can do to make it nicer to work with. I go through those and at the end, automate the annoying bit with zsh. |
||||
tags: |
||||
- ZSH |
||||
- Pentesting |
||||
- Linux |
||||
--- |
||||
|
||||
So, you've managed to get a reverse shell through one means or another. Unfortunately, it's pretty basic. You can't use your arrow keys, the line gets overwritten if your command is too long and you can't backspace properly. All in all, it's not a nice experience. |
||||
|
||||
## Pseudo terminal |
||||
|
||||
The first thing I like to do is spawn a TTY. This tells terminal tools that they can be run interactively. For example, if you run `ls` in a normal terminal, you are likely to see it output the files in columns: |
||||
|
||||
```bash |
||||
localhost$ ls |
||||
burppro Desktop GitRepos Pictures |
||||
'Calibre Library' Documents go Templates |
||||
Contacts Downloads Music Videos |
||||
``` |
||||
|
||||
However, in your reverse shell, you won't see the output in columns |
||||
|
||||
```bash |
||||
reverseshell$ ls |
||||
burppro |
||||
'Calibre Library' |
||||
Contacts |
||||
Desktop |
||||
Documents |
||||
Downloads |
||||
GitRepos |
||||
go |
||||
Music |
||||
Pictures |
||||
Templates |
||||
Videos |
||||
``` |
||||
|
||||
This is because in the first instance, ls "knows" that it is being used in an interactive session. However, in the reverse shell, ls doesn't know that it is being used interactively, so it gives the most simple output possible. If you were to pipe ls to another program, it would do the same. |
||||
|
||||
|
||||
```bash |
||||
localhost$ ls | cat |
||||
burppro |
||||
'Calibre Library' |
||||
Contacts |
||||
Desktop |
||||
Documents |
||||
Downloads |
||||
GitRepos |
||||
go |
||||
Music |
||||
Pictures |
||||
Templates |
||||
Videos |
||||
``` |
||||
|
||||
This is why you can use ls with tools like `grep` without worrying about joining all the columns. |
||||
|
||||
It is possible to spawn a tty (or at least a pseudo tty) with python2 or 3 with the same command: |
||||
|
||||
```bash |
||||
python 'import pty; pty.spawn("/bin/bash")' |
||||
python2 'import pty; pty.spawn("/bin/bash")' |
||||
python3 'import pty; pty.spawn("/bin/bash")' |
||||
``` |
||||
|
||||
On most Linux boxes you come across, one of these should work. If `/bin/bash` isn't available, you could try `/bin/sh`, although this is more limited than bash. |
||||
|
||||
## Setting the window size |
||||
|
||||
On our reverse shell, the width and height are not set and, as a result, default to 80x24. This is usable, but not particularly nice. To change it, we will background the reverse shell and take a note of the actual terminal size. |
||||
|
||||
To background, simply press ctrl+z. |
||||
|
||||
```bash |
||||
reverseshell$ ^Z |
||||
zsh: suspended nc -lvnp 4444 |
||||
localhost$ |
||||
``` |
||||
|
||||
To get the current size of your terminal window (in rows and columns), you can run the command: |
||||
|
||||
```bash |
||||
localhost$ stty -a |
||||
speed 38400 baud; rows 59; columns 136; line = 0; |
||||
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; |
||||
rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0; |
||||
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts |
||||
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8 |
||||
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 |
||||
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc |
||||
``` |
||||
|
||||
The important part here is on the first line, in this example, I have 59 rows and 136 columns. Your values are likely to be different. |
||||
|
||||
We could foreground the session by typing `fg<enter><enter>` and running the command |
||||
|
||||
```bash |
||||
stty rows 59 columns 136 |
||||
``` |
||||
|
||||
However, while we have the session backgrounded, there is some more stuff we can do. |
||||
|
||||
## Special keys and correct size |
||||
|
||||
Special key codes, such as escape and key cords that begin with ctrl won't work. This makes using programs like vim almost impossible. |
||||
|
||||
In order to rectify this, we need to make our local shell send the raw characters through netcat. Hopefully, you still have the netcat session backgrounded, if not, you can just push ctrl+z again. |
||||
|
||||
We can then run the following command in order to make our shell pass the raw characters and prevent it from echoing those characters. |
||||
|
||||
```bash |
||||
stty raw -echo |
||||
``` |
||||
|
||||
In order to get the reverse shell back, type `fg<enter><enter>`, although you won't see this on the screen as we have asked the terminal not echo commands (that is what the `-echo` does). |
||||
|
||||
:::{.tip} |
||||
If your local shell is zsh, you will have to run the fg command before the prompt is re-drawn, otherwise the `-echo` option will be reset. If you are interested in why, read about it [here](https://www.zsh.org/mla/workers/2014/msg00870.html). |
||||
|
||||
The remediation is simply this: |
||||
|
||||
```zsh |
||||
stty raw -echo; fg<enter><enter> |
||||
``` |
||||
::: |
||||
|
||||
We now simply need to tell the shell running on the remote server what the window size is. This is done with another stty command: |
||||
|
||||
```bash |
||||
stty rows 59 columns 136 |
||||
``` |
||||
|
||||
By this point, you should have a pretty nice terminal. You can use programs such as vim or tmux that take over the whole terminal and rely on being able to hijack specific keys. |
||||
|
||||
## Terminfo |
||||
|
||||
Teaminfo is a database that describes the capabilities of terminals. This covers things like the ability to display colour or italics. |
||||
|
||||
It is unlikely that your specific terminal has entries in the servers terminfo database, so a good choice is to set your `TERM` variable to `xterm-256color`. |
||||
|
||||
This is a common setting that is very likely to be on servers, it also enables a lot of the capabilities that most people want out of a terminal, including colour support. |
||||
|
||||
To do so, run: |
||||
|
||||
```bash |
||||
EXPORT TERM="xterm-256color" |
||||
``` |
||||
|
||||
## Making it easier |
||||
|
||||
By this point you should have quite a nice terminal to use. You can use your arrow keys, vim, tmux (if it's installed) and just about anything else. Importantly, you will also be able to use sudo or su which can ask for user input whilst running. |
||||
|
||||
However, I would like to share with you my method that automates a significant part of this. You will need to be using zsh on your host machine for this to work. It may be possible in bash, I have just never spent the time trying to implement it. |
||||
|
||||
Firstly, I took and adapted the [widget used by Greg Hurrell](https://github.com/wincent/wincent/blob/d6c52ed5528f353e652799e50d0e9250f1bec6ce/aspects/dotfiles/files/.zshrc#L280-L289) uses to foreground tasks with `ctrl+z`. |
||||
|
||||
```zsh |
||||
function fg-bg() { |
||||
if [[ $#BUFFER -eq 0 ]]; then |
||||
fg |
||||
fi |
||||
} |
||||
zle -N fg-bg |
||||
bindkey '^Z' fg-bg |
||||
``` |
||||
|
||||
This means that after pushing ctrl+z to background a task, you can then push ctrl+z again to foreground it. |
||||
|
||||
However, if we have backgrounded netcat, I would like to perform some extra steps. In the code below I check if the program is `nc`, `ncat` or `netcat`. |
||||
|
||||
```zsh |
||||
local backgroundProgram="$(jobs | tail -n 1 | awk '{print $4}')" |
||||
case "$backgroundProgram" in |
||||
"nc"|"ncat"|"netcat") |
||||
# Our NC logic needs to go here |
||||
;; |
||||
*) |
||||
fg |
||||
;; |
||||
esac |
||||
``` |
||||
|
||||
Inside the netcat case, I get the rows and columns, put the commands that need running on the server on my clipboard and run the `stty raw -echo` command on my host. That looks like this: |
||||
|
||||
```zsh |
||||
# Make sure that /dev/tty is given to the stty command by doing </dev/tty |
||||
local columns=$(stty -a < /dev/tty | grep -oE 'columns [0-9]+' | cut -d' ' -f2) |
||||
local rows=$(stty -a < /dev/tty | grep -oE 'rows [0-9]+' | cut -d' ' -f2) |
||||
notify-send "Terminal dimensions" "Rows: $rows\nColumns: $columns\nstty command on clipboard" |
||||
echo "stty rows $rows cols $columns |
||||
export TERM=\"xterm-256color\"" | xclip -i -selection clipboard |
||||
stty raw -echo < /dev/tty; fg |
||||
``` |
||||
|
||||
Notice that we need to provide the stty command `/dev/tty` as we are not running it interactively. |
||||
|
||||
Put that all together: |
||||
|
||||
```zsh |
||||
function fg-bg() { |
||||
if [[ $#BUFFER -eq 0 ]]; then |
||||
local backgroundProgram="$(jobs | tail -n 1 | awk '{print $4}')" |
||||
case "$backgroundProgram" in |
||||
"nc"|"ncat"|"netcat") |
||||
# Make sure that /dev/tty is given to the stty command by doing </dev/tty |
||||
local columns=$(stty -a < /dev/tty | grep -oE 'columns [0-9]+' | cut -d' ' -f2) |
||||
local rows=$(stty -a < /dev/tty | grep -oE 'rows [0-9]+' | cut -d' ' -f2) |
||||
notify-send "Terminal dimensions" "Rows: $rows\nColumns: $columns\nstty command on clipboard" |
||||
echo "stty rows $rows cols $columns |
||||
export TERM=\"xterm-256color\"" | xclip -i -selection clipboard |
||||
stty raw -echo < /dev/tty; fg |
||||
;; |
||||
*) |
||||
fg |
||||
;; |
||||
esac |
||||
fi |
||||
} |
||||
zle -N fg-bg |
||||
bindkey '^Z' fg-bg |
||||
``` |
||||
|
||||
There we go. Once you have a reverse shell, you still have to manually type the python command to get a tty. I suggest looking into your terminal emulator's settings to see if you can map the command to a key binding. Once that is done, simply press `ctrl+z` twice, then `enter`. You will be able to paste the stty command in order to set the correct width and height. |
||||
|
||||
![Reverse shell upgraded quickly](/assets/rev-shell/reverse-shell.gif) |
@ -0,0 +1,207 @@ |
||||
--- |
||||
title: Enumerating Users on WordPress |
||||
description: | |
||||
Finding valid usernames can significantly improve your chances of breaking into a WordPress |
||||
account. In this blog post I cover some of the methods I use to find valid users and how you |
||||
can protect your own site against them. |
||||
date: 2020-09-05 |
||||
tags: |
||||
- Security Advice |
||||
- Websites |
||||
--- |
||||
|
||||
WordPress is an extremely popular Content Management System (CMS) and as a result receives a lot of |
||||
interest from hackers. WordPress has a bad reputation in some circles for being insecure, however if |
||||
you are selective about the themes / plugins you install and keep it up to date, it is my belief |
||||
that it is a nice system for both developers and users. |
||||
|
||||
However, assuming you keep everything up to date, in many cases the biggest security weakness is |
||||
your credentials. If a malicious actor guesses your username and password, it doesn't matter how |
||||
recently you did your updates, they are probably going to get in. |
||||
|
||||
When I am tasked with testing the security of a WordPress site, one of the first things I do is |
||||
attempt to find usernames. In this blog post, I document some of the ways I do that. |
||||
|
||||
:::note |
||||
For the purposes of this blog post, I have created a local WordPress site I can use for testing. Do |
||||
not attempt these tactics unless you own the site you are testing or have explicit permission from |
||||
the site owner to do so. |
||||
::: |
||||
|
||||
## Trial and error |
||||
|
||||
![Login form showing valid user](../../assets/wordpress/login-username-enumeration.png) |
||||
|
||||
The most simple way is to attempt to login in with common usernames, `admin` being one of the most |
||||
common. You can see from the screenshots above that if you enter a correct username, the error |
||||
message tells you that you have the password wrong; if you enter an incorrect username, the message |
||||
tells you that there is an unknown username. |
||||
|
||||
This makes it trivial to tell if a username exists: try it and if you get the "incorrect password" |
||||
error, you know you have a valid username. |
||||
|
||||
To fix this, you simply need to make wordpress return generic error messages: |
||||
|
||||
```php |
||||
<?php |
||||
function no_wordpress_errors(){ |
||||
return 'Something is wrong!'; |
||||
} |
||||
add_filter( 'login_errors', 'no_wordpress_errors' ); |
||||
``` |
||||
|
||||
|
||||
## User ID Cycling |
||||
|
||||
Wordpress dynamically assigns users with IDs and creates pages for each user. Normally these can be |
||||
accessed by going to a url like `<domain>/author/admin/`. However, you can also access them by |
||||
manually specifying the users ID. For example `<domain>/?author=1` will redirect the visitor to |
||||
`<domain>/author/admin/`, this gives the attacker an easy way to get usernames. |
||||
|
||||
```bash |
||||
for i in {1..5}; do |
||||
curl -s -o /dev/null -w "%{redirect_url}\n" "example-wordpress.local/?author=$i" |
||||
done |
||||
``` |
||||
|
||||
In a default WordPress installation, you will get something like this: |
||||
|
||||
``` |
||||
http://example-wordpress.local/author/admin/ |
||||
http://example-wordpress.local/author/user1/ |
||||
http://example-wordpress.local/author/user2/ |
||||
http://example-wordpress.local/author/user3/ |
||||
``` |
||||
|
||||
We have just found 3 new usernames. |
||||
|
||||
One solution to this is to simply prevent WordPress queries from being able to look up a user by ID. |
||||
|
||||
```php |
||||
<?php |
||||
function do_404_author_query($query_vars) { |
||||
if ( !empty($query_vars['author'])) { |
||||
global $wp_query; |
||||
$wp_query->set_404(); |
||||
status_header(404); |
||||
nocache_headers(); |
||||
|
||||
$template = get_404_template(); |
||||
if ($template && file_exists($template)) { |
||||
include($template); |
||||
} |
||||
exit; |
||||
} |
||||
return $query_vars; |
||||
} |
||||
add_action('request', 'do_404_author_query'); |
||||
``` |
||||
|
||||
## Rest API |
||||
|
||||
Out of the box, WordPress gives out a lot of information out about its users through the Rest API. |
||||
|
||||
I can pull out a list of all usernames with the following: |
||||
|
||||
```bash |
||||
curl -s example-wordpress.local/wp-json/wp/v2/users | jq '.[].name' |
||||
``` |
||||
|
||||
The easiest way to mitigate this is simply to remove the user endpoints. |
||||
|
||||
```php |
||||
function remove_users_endpoints( $endpoints ) { |
||||
return array_filter( $endpoints, function($endpoint){ |
||||
return (0 === preg_match( '/^\/wp\/v2\/users/', $endpoint )); |
||||
} , ARRAY_FILTER_USE_KEY); |
||||
} |
||||
add_filter( 'rest_endpoints', 'remove_users_endpoints' ); |
||||
``` |
||||
|
||||
## oEmbed |
||||
|
||||
Oembed is a protocol that allows websites to embed content from other sites. WordPress supports both |
||||
embedding and being embedded. When a site requests to embed a page, it makes a request that looks |
||||
like the following: `<domain>/wp-json/oembed/1.0/embed?url=http%3A%2F%2F<domain>/a-post-by-user-3/`. |
||||
|
||||
The WordPress server then returns something like this: |
||||
|
||||
```json |
||||
{ |
||||
"version": "1.0", |
||||
"provider_name": "Example Wordpress", |
||||
"provider_url": "http://example-wordpress.local", |
||||
"author_name": "user3", |
||||
"author_url": "http://example-wordpress.local/author/user3/", |
||||
"title": "a post by user 3", |
||||
"type": "rich", |
||||
"width": 600, |
||||
"height": 338, |
||||
"html": "<embed code>" |
||||
} |
||||
``` |
||||
|
||||
This includes the author's name and a url for the author archive. In order to prevent wordpress from |
||||
including this information in the oembed response, add the following to a plugin or to your themes |
||||
functions.php file: |
||||
|
||||
```php |
||||
<?php |
||||
function remove_author_from_oembed($data) { |
||||
unset($data['author_url']); |
||||
unset($data['author_name']); |
||||
return $data; |
||||
} |
||||
add_filter( 'oembed_response_data', 'remove_author_from_oembed' ); |
||||
``` |
||||
|
||||
## In-Page Info |
||||
|
||||
Perhaps the least interesting is simply looking at pages. Many pages, particularly news or blog |
||||
pages, include the author. This is often linked to the author archive page which will disclose their |
||||
username. It is normally possible to use simple tools like grep or hq to get the usernames from |
||||
these pages. |
||||
|
||||
For example, to get the author of the page <http://example-wordpress.local/a-post-by-user-2/>, I |
||||
could do the following: |
||||
|
||||
```bash |
||||
$ curl http://example-wordpress.local/a-post-by-user-2/ | hq '.post-author a' attr href' |
||||
http://example-wordpress.local/author/user2/ |
||||
``` |
||||
|
||||
Here we see the author is `user2`. |
||||
|
||||
However, sites often have hundreds or thousands of pages so doing this manually would be tedious. |
||||
Once again, we can turn to the rest api. |
||||
|
||||
The following will go through the first 100 posts on the site and attempt to get the author's link |
||||
from it. |
||||
|
||||
```bash |
||||
$ curl http://example-wordpress.local/wp-json/wp/v2/posts?per_page=100 | jq ‘.[].id’ | while read i; do |
||||
curl -L …/?p=$i | hq '.post-author a' attr href |
||||
done |
||||
``` |
||||
|
||||
It is worth noting here that the wordpress rest api limits requests to 100 results per request. This |
||||
means that if a site has more posts / pages than that, you might need to use the `page=n` parameter |
||||
which will give you the nth page of results. |
||||
|
||||
I don't normally find this necessary since it is normally the earliest pages that are created by |
||||
high privileged accounts. |
||||
|
||||
The easiest way to rectify this is simply not to include the author's details in the page, although |
||||
instructions on how to do this will vary depending on the theme in use. However, you may wish to be |
||||
able to group blog posts by author. In this case, I would suggest only publishing content using low |
||||
privileged accounts (author or contributor). This will mean that in the event that one of these |
||||
accounts is compromised, the damage an attacker can do is limited. |
||||
|
||||
--- |
||||
|
||||
These steps should prevent most attempts at user enumeration although remember that security is not |
||||
a plugin or a few lines of code copied from a blog on the internet. There are many layers that |
||||
should be implemented and this is but one. For details on how best to secure your website, you |
||||
should consult with an expert. |
||||
|
||||
|
@ -0,0 +1,140 @@ |
||||
--- |
||||
title: My Setup at the beginning of 2021 |
||||
tags: |
||||
- FZF |
||||
- Linux |
||||
- ZSH |
||||
description: How I work at the beginning of 2021 |
||||
date: 2021-01-08 |
||||
--- |
||||
|
||||
Inspired by [The Orange One's State of the |
||||
Apps](https://theorangeone.net/posts/state-of-the-apps-2021/), I thought I would |
||||
document the tools I use to get my job done. If for no other reason than to look |
||||
back next year and see what has changed. |
||||
|
||||
## OS |
||||
|
||||
I'm running [Arch Linux](https://archlinux.org/) on all my computers. This is |
||||
because it works well for me. All the packages I could ever need are available |
||||
and there is no reason for me to change. |
||||
|
||||
For other people's computers that I maintain ( parents, wife etc. ), I normally |
||||
put Manjaro on there. It is quick to set up and if I ever need to ssh in to fix |
||||
or install something, I can use the same commands I do on my personal machines. |
||||
|
||||
## Desktop Environment |
||||
|
||||
[DWM](https://git.jonathanh.co.uk/jab2870/dwm). I don't understand why tiling |
||||
window managers aren't the default. I remember when Windows (Vista?) introduced |
||||
their window snapping and thinking at the time that this was how computers |
||||
should work. |
||||
|
||||
[i3](https://i3wm.org/) is another great tiling window manager that I used to |
||||
use and I normally still recommend to people who want to try one. |
||||
|
||||
I use [Rofi](https://github.com/davatorium/rofi) as my program launcher and for |
||||
various other scripts. In the coming year, I may look to switch this to dmenu or |
||||
fzf. I am happy with rofi and have no real reason to change other than |
||||
curiosity. |
||||
|
||||
## Editor |
||||
|
||||
[Neovim](https://github.com/neovim/neovim). This year saw me make the switch |
||||
from Vim to Neovim as my primary editor. Initially, the thing that convinced me |
||||
to make the switch was [Deoplete](https://github.com/Shougo/deoplete.nvim) |
||||
although since moving I have also taken advantage of neovim's excellent language |
||||
server support. |
||||
|
||||
In terms of plugins for vim / neovim, I try to keep it to a minimum. As I |
||||
mentioned, I have deoplete with a couple of completion sources. I have a couple |
||||
of language definitions and a colour scheme. |
||||
|
||||
One plugin I do use and rely on heavily is |
||||
[FZF](https://github.com/junegunn/fzf.vim) which provides fuzzy searching of |
||||
files, help pages, buffers or anything else I give it. |
||||
|
||||
## Shell |
||||
|
||||
I use ZSH as my shell. During this last year I have moved away from oh-my-zsh. |
||||
In doing this, I have noticed a significant speed increase in my shell. |
||||
|
||||
If you're interested, you can read about [why I choose |
||||
ZSH](https://jonathanh.co.uk/blog/the-switch-to-zsh.html). |
||||
|
||||
## Browser |
||||
|
||||
I use a [customized version of |
||||
Firefox](https://jonathanh.co.uk/blog/keyboard-driven-firefox.html). The most |
||||
significant changes I have made to it are the hiding of the top bar and the use |
||||
of [Tridactyl](https://github.com/tridactyl/tridactyl) which adds vim-like |
||||
bindings to Firefox. A recent addition I made was |
||||
[Firevim](https://github.com/glacambre/firenvim) which embeds a neovim client |
||||
into text areas. |
||||
|
||||
## Email |
||||
|
||||
My personal email provider is [Protonmail](https://protonmail.com/). I am very |
||||
happy with it. My [email setup](https://jonathanh.co.uk/blog/mutt-setup.html) |
||||
consists of neomutt, vim, mbsync, notmuch and imapfilter. |
||||
|
||||
## Calender |
||||
|
||||
I have recently come across [Remind](https://dianne.skoll.ca/projects/remind/) |
||||
which has replaced [Calcurse](https://www.calcurse.org/) as my calender |
||||
application of choice. It is configured, and reminders are added using a text |
||||
file (or a collection of text files). These can then be version controlled. |
||||
|
||||
It allows for complex reminder logic to be specified. I may well write a blog |
||||
post about it in the future but, for now, just know that you can use many |
||||
programming constructs like if statements when defining events. |
||||
|
||||
For a more detailed introduction, I strongly recommend [Tim Chase's post on |
||||
Remind](https://blog.thechases.com/posts/remind/). |
||||
|
||||
## RSS |
||||
|
||||
This is pretty simple. I use [Newsboat](https://newsboat.org/). It does |
||||
everything I need it to, it's configurable and has vim-like key bindings. |
||||
|
||||
Incidentally, this is also how I manage my YouTube subscriptions. Any new videos |
||||
come into my RSS reader, I watch them using MPV and then I stop watching |
||||
youtube. This has been one of the biggest increases to my productivity. I no |
||||
longer get youtube suggestions so I don't find myself getting lost in the |
||||
endless rabbit holes of youtube. I watch what I want to watch and then I get on |
||||
with something else. |
||||
|
||||
## Notes |
||||
|
||||
I just use Vim. I am currently working on a shell script to help me better index |
||||
my folder of notes although I'll write a blog post about that when it is nearer |
||||
a first stable release. |
||||
|
||||
## Online Hosting / File Sync |
||||
|
||||
I host in instance of [Nextcloud](https://nextcloud.com/) that provides me with |
||||
file sync, online editing and sync for contacts, calender etc. |
||||
|
||||
## Keyboard |
||||
|
||||
I still use the [Colemak](https://colemak.com/) keyboard layout which has been |
||||
designed for ergonomic typing. I do the majority of my typing on an [Ergodox |
||||
EZ](https://ergodox-ez.com/). |
||||
|
||||
## Passwords |
||||
|
||||
I use [Pass](https://www.passwordstore.org/) which is a thin wrapper around Git |
||||
and gnupg. Passwords are stored in an encrypted text file and version controlled |
||||
with Git. On my phone I use [Password Store](https://f-droid.org/en/packages/dev.msfjarvis.aps/) which works in almost the same way. |
||||
|
||||
## Phone |
||||
|
||||
I currently use a rooted, de-googled Samsung Note 8. There is not much to say |
||||
really except almost all my apps come from F-droid. I have been unhappy for a |
||||
while with the current state of phones, although that is starting to change with |
||||
the advent of new GNU/Linux phones. I have a Pinephone being delivered soon and |
||||
have ordered a [FX Tech Pro1 |
||||
X](https://www.indiegogo.com/projects/pro1-x-smartphone-functionality-choice-control#/). |
||||
Both of these ship Linux and I'm hoping that I'll be able to replace my android |
||||
handset as my daily driver. |
||||
|
@ -0,0 +1,110 @@ |
||||
--- |
||||
title: Before you start with Home Assistant |
||||
tags: |
||||
- Home Assistant |
||||
description: I use Home Assistant for my home automation setup. I plan do write some more blogs about it in the future. This is supposed to help those out who are considering starting out. It is not supposed to be a setup guide, rather some things to consider before starting out. |
||||
date: 2021-03-19 |
||||
--- |
||||
|
||||
If you are considering getting into home automation, [Home |
||||
Assistant](https://www.home-assistant.io/) is a great way to start. My current |
||||
setup consists mostly of lighting, sensors and buttons, and a few small |
||||
appliances. I Before you start, there are a few things that you might want |
||||
to consider. |
||||
|
||||
## Server |
||||
|
||||
So, currently, I have home assistant installed on an old laptop that works as a |
||||
server. Alternatively, you could use a Raspberry PI or any similar single board |
||||
computer. |
||||
|
||||
Home Assistant sells a single board computers which are a bit more powerful than |
||||
a Raspberry PIs and all the needed software is pre installed: |
||||
|
||||
<https://www.home-assistant.io/blue> |
||||
|
||||
What you end up doing will probably depend on your budget, what you have lying |
||||
around and how DIY you want the solution to be. |
||||
|
||||
## Celling Lighting |
||||
|
||||
One of the easiest / cheapest things to start with is lighting. It also brings |
||||
some of the (IMHO) biggest advantages. To do lighting, you have 2 options. Smart |
||||
switches or smart bulbs (or both, I guess that makes three). |
||||
|
||||
### Smart Switches |
||||
|
||||
This is (probably) the easiest and cheapest starting point, however comes with a |
||||
pretty big caveat. You'll need to check that your wall socket has a ground or |
||||
neutral wire (probably green and yellow sleeve in the UK). If not, this option |
||||
probably won't work for you. I am not an electrician so if you are in doubt, |
||||
find someone who knows what they are taking about. *SERIOUSLY*. If you electrocute |
||||
yourself, that is generally bad and not on me. |
||||
|
||||
I use these [switches from |
||||
Sonoff](https://www.banggood.com/SONOFF-T1-EU-or-UK-AC-100-240V-1-or-2-or-3-Gang-TX-Series-WIFI-Wall-Switch-433Mhz-RF-Remote-Controlled-Wifi-Switch-Smart-Home-Switch-Works-With-Alexa-Google-Home-p-1470883.html?rmmds=myorder&cur_warehouse=UK&ID=6278514). |
||||
They can be flashed with open firmware so you can be sure they are not talking |
||||
back to their mother ship. |
||||
|
||||
It can be useful to get a switch panel with extra switches. That way you can use |
||||
the extra buttons for triggering automations or turning on other things other |
||||
than the celling lights. |
||||
|
||||
### Smart Bulbs |
||||
|
||||
Smart bulbs are a generally a bit more expensive than smart switches. However, |
||||
they often come with the advantage that you can change the brightness / colour |
||||
etc. |
||||
|
||||
Unfortunately, you'll need to leave the switch on all the time if you want the |
||||
advantage of being able to turn them on with your phone or on a timer. |
||||
|
||||
I use these [bulbs from BlitzWolf](https://www.banggood.com/BlitzWolf-BW-LT21-RGBWW-10W-E27-APP-Smart-LED-Light-Bulb-Work-With-Amazon-Alexa-Google-Assistant-AC100-240V-p-1551059.html?rmmds=myorder&cur_warehouse=CN). |
||||
These can also be flashed with with open firmware. |
||||
|
||||
### What I do |
||||
|
||||
Anywhere I have a smart bulb, I also have smart switches. This is so I can leave |
||||
the power to the bulb on at all times. You don't want (and your wife definitely |
||||
won't want) to have to get your phones out every time you want to turn the light |
||||
on or off. If you have a smart switch, it can "ask" the bulb to turn off but |
||||
without cutting its power. This means you can turn the bulb on with your phone |
||||
or a switch or an automation. |
||||
|
||||
In some places, I only have a smart switch and dumb bulbs. This is basically |
||||
just the bathroom and kitchen where we have loads of spot lights and it's not |
||||
worth the cost to make each bulb smart |
||||
|
||||
I can still turn the lights on / off with the switch or our phones or with |
||||
automations. |
||||
|
||||
## Other small appliances |
||||
|
||||
I also have things like lamps attached these to [Sonoff |
||||
Basics](https://www.banggood.com/SONOFF-BASICR2-10A-2200W-WIFI-Wireless-Smart-Switch-Remote-Control-Socket-APP-Timer-AC90-250V-50-or-60Hz-Works-with-Amazon-Alexa-Google-Home-Assistant-IFTTT-p-1019971.html?cur_warehouse=CN&rmmds=search). |
||||
They work in basically the same way as a smart switch with a dumb bulb. It means |
||||
that I can turn them off with smart buttons, my phone or any automations. |
||||
|
||||
Lastly, I have some [Sonoff |
||||
S26](https://www.banggood.com/SONOFF-S26-10A-AC90V-250V-Smart-WIFI-Socket-CN-or-US-or-UK-or-AU-or-DE-or-FR-or-BR-or-CH-or-IL-or-IT-Wireless-Plug-Power-Sockets-Smart-Home-Switch-Work-With-Alexa-Google-Assistant-IFTTT-p-1308841.html?cur_warehouse=CN&ID=3426&rmmds=search)s. |
||||
I use these for appliances such as my kettle that are used around water and I |
||||
want to play it safe. You could probably use a Sonoff basic for this, but they |
||||
don't come with any water certification. At least these are kept above surfaces |
||||
that might have water on them. |
||||
|
||||
I'm happy to pay a bit extra and just not risk electrocuting myself. |
||||
|
||||
## Firmware |
||||
|
||||
Everything I have linked above use a chip called an ESP8266 (or variations of |
||||
it) which mean it can be flashed with open source firmware called |
||||
[esphome](https://esphome.io/). This way, I am happy to buy cheep Chinese bulbs |
||||
and I am confident that the bulbs are not talking back to China. |
||||
|
||||
If you are interested I can talk to you about flashing it at a later date. |
||||
Different devices require it to be done in different ways. |
||||
|
||||
----- |
||||
|
||||
Hopefully that should be enough to get you started, I will be writing more posts |
||||
on home automation in the future. |
Loading…
Reference in new issue