HTB Attended

10.10.10.221 | 50 pts

PART 1 : INITIAL RECON

1.1 NMAP SCAN

$ nmap --min-rate 3000 -oN nmap-tcp.initial -p- -Pn -T4 -v 10.10.10.221

  PORT   STATE SERVICE
  22/tcp open  ssh
  25/tcp open  smtp

$ nmap -oN nmap-tcp -p 22,25 -Pn -sC -sV -v 10.10.10.221

  PORT   STATE SERVICE VERSION
  22/tcp open  ssh     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
  | 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
  |     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 [email protected]
  |     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 [10.10.14.28], pleased to meet you, 8BITMIME, ENHANCEDSTATUSCODES, SIZE 36700160, DSN, HELP,
  |_ This is OpenSMTPD To report bugs in the implementation, please contact [email protected] with full details 2.0.0: End of HELP info

PART 2 : PORT ENUMERATION

2.1 TCP PORT 22 : OpenSSH

Failing to login three times lets us see what authentication methods are accepted when attempting to SSH into the box:

It seems like nothing unusual is needed to authenticate.

2.2 TCP PORT 25 : SMTP

Looking at the banner when trying to access the OpenSMTPD service via telnet, a user (guly) and domain (attended.htb) are seen. And, when you try to verify if it is an existing email address, the server responds with 250 Recipient Ok:

Now starting a local SMTP server to check if guly replies to emails:

Now to begin sending an email:

We get the following reply saying that guly is currently working on an inssue with someone named, freshness and will only entertain emails coming from him:

Trying to impersonate freshness using swaks:

Now, the response says that now, guly needs to receive an attachment. And also, python2 is installed in the gateway which only allows RFC compliant connections (e.g. SMTP, ICMP, HTTP, etc.):

Trying to create an attachment then sending it to guly as freshness:

I get notified that that attachment I sent as going to be opened in vim and that freshness' configuration file should be in /home/shared/:

The following are everything we’ve gotten from the email exchange above:

  • Only emails from freshness will be entertained.

  • Only RPC compliant protocols for outbound traffic will be allowed which means simple socket connections (for reverse shells) will not work.

  • Python2 could be used and the sent attachment will be opened using vim.

  • A config file should be in /home/shared/.

PART 3 : EXPLOITATION

3.1 VIM MODELINES

While searching for code execution exploits in vim, I came across CVE-2016-1248 which is Arbitrary Command Execution using vim modelines. However, it was discovered around 2016 and a newer exploit (CVE-2019-12735) was found on 2019.

What happens when modelines is enabled in vim is that when the file contains the following line:

The command will execute before proceeding to the actual file contents.

3.2 PAYLOAD CREATION

There are a few challenges to overcome since only RPC compliant protocols are allowed for outbound connections so first, I created an automated vim modeline payload generator using bash:

This incorporates a python2 one-liner which when expanded looks like the following:

What it essentially does is to chunk the response to a maximum of 3000 bytes per request since there seems to be a limit to how much data I could send back to my server.

After which, I created an HTTP Server that will be used to exfiltrate the responses from the machine and since I’m chunking the requests, the following code will manage with rebuilding the response:

3.3 REMOTE CODE EXECUTION

Start the created HTTP Server:

Then, begin sending commands then wait until the HTTP server reflects the output:

PART 4 : USER SHELL (freshness)

4.1 SYSTEM ENUMERATION

Checking for all the users that have a directory in /home:

The config file mentionedd earlier should be in /home/shared but the directory doesn’t seem to have global read permissions, however exploring guly’s home directory:

There is a .config.swp file (.swp files are usually created when a file is opened in vim and will serve as a backup until the opened file is closed). Since this is a binary file, it can’t be exfiltrated by simply outputting the file:

4.2 .config.swp RESTORATION

Saving the hex output into a file, .config.swp.hex, then decoding it into the original file:

To fully recover the file, just open a file called config in vim and then press r:

It seems to be an ssh_config file for the user, freshness. Maybe this could be abused by using ProxyCommand like so:

Based on the directory listing of /home, /home/shared is world writable:

Now, attempting to write a config file with ProxyCommand into /home/shared/config using the vim modelines exploit:

After waiting for guly to reply to the sent email, we can SSH into the box as freshness:

PART 5 : EXPLOITING authkeys

5.1 ENUMERATING freshness

There is a note left in ~/authkeys stating that an authkeys command was enabled in the attended gateway but not in the attended machine:

Based on /etc/ssh/sshd_config, the authkeys command takes four arguments and is owned by root:

Based on the OpenBSD sshd_config documentation:

Parameter

Description

%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.

5.2 PERSONAL OpenBSD

I created my own local OpenBSD instance to aid in exploiting the authkeys binary and inside, I edited the sshd_config file:

5.3 BUFFER OVERFLOW

Looking at the disassembly of something is written to 0x6010c0:

And based on gdb that address is part of the .data segment of the memory which is the corresponding virtual address space of a program that contains initialized static variables:

Since I know that the key should be base64 encoded, I created an encoded string of "A"s:

Then running it in gdb and setting a breakpoint to 0x00400361:

The keys argument is decoded and written to 0x6010c0. Now, to create a long enough buffer to induce a segmentation fault:

Checking at what offset in the buffer the return address was overwritten with:

The return address is overwritten after 776 bytes!

5.4 STARTING THE EXPLOIT

The payload offset is adjusted by 22 bytes for the added bytes by the SSH key constructor and another 8 bytes for the consideration of RBP and finally writing the new return address:

Checking in with gdb, the return address is now "CCCCCCCC":

5.5 ROP GADGETS

There is a syscall instruction available so perhaps execve() may be an option:

Checking if there’s a way to control RAX, there are a few gadgets that can help set the desired value for the register:

Finally, gadgets that can help set the values of RDI, RSI, and RDX which requires conversion from a single precision floating point value to an integer and a pointer assignment for RDX when used with movups:

5.6 CONTROLLING RAX

The binary value of "3B" (execve syscall) is "111011". What will happen when mov eax, 0xffffffff; xor rcx, rcx; ret; is that RAX will be set to 0xFFFFFFFF or 11111111111111111111111111111111.

From there we can use right bitshifts and the not operator to get the correct value but take note that the not al operator only converts the last two bytes so if our current value is the one mentioned earlier, 0xFFFFFFFF, we need to shift the bits to the right 24 times to achieve 0x000000FF:

Translating the following into the exploit code:

Now trying the new payload from exploit code:

The RAX register has been controlled!

5.7 RDI, RSI, RDX CONTROL

These three registers will serve as the arguments for the execve function:

Based on the OpenBSD sshd_config documentation:

REGISTER

PARAMETER

VALUE

RDI

const char *pathname

/bin/sh

RSI

char *const argv[]

["/bin/sh", "-c", "<command>"]

RDX

char *const envp[]

NULL

However, controlling the three registers is a bit tricky using the following gadgets:

To set the value of RDI, the process will go as follows:

  1. Set the value of RDX to be the pointer to the address of your intended RDI. The value of RDX should be a floating point number since it will be converted later on to the desired value.

  2. Convert the value of RDX from a single precision floating point number to an integer using the cvtss2si gadget which will set its value to RSI.

  3. Move the value to RDI from RSI.

I created a function to that will convert the address to its single precision floating point number equivalent:

To set the value of RSI, just follow the steps above from 1 until 3. Finally, to set the value of RDX, there is simply a pop rdx; ret gadget available. With all this information, the exploit code has been modified to the following:

The needed registers have been completely controlled and to give an overview to what is inside RDI and RSI:

The pointers inside RSI are all going to the right places so the only thing left to do is to trigger the syscall.

5.8 ADDING SYSCALL

To finalize the exploit, the syscall instruction has been added to the code:

5.9 TESTING LOCALLY

Running the completed exploit code then passing the generated payload as an identity file when logging in via SSH:

Then checking if the file was created:

So the exploit works on my local setup, the only thing to do is to run it on the machine.

PART 6 : PRIVESC (freshness -> root)

6.1 ATTENDED GATEWAY

It was stated earlier in a note that the authkeys was implemented on attendedgw but not on attended. Looking for other machines in the network via ping sweep then scanning for ports remotely:

The Attended Gateway is at 192.168.23.1 and now, to check for open ports, I opened a reverse proxy via SSH to the machine and ran the following script:

It seems like the SSH port is open at TCP port 2222.

6.2 FINAL EXPLOIT

The exploit has been modified to write an SSH public key to the authorized_keys file in /root:

Now passing the generated payload via SSH:

PART 7 : REFERENCES

Last updated