CSEC3616Cybersecurity Engineering

    Problem bank

    Every tutorial problem in the unit, with a worked solution behind each one where the unit has released one.

    29 of 29 shown

    • t01-q1

      Tutorial1_questions.pdf

      recallconceptual

      Programming Satan’s Computer

      Ross Anderson and Roger Needham infamously described security engineering as “Programming Satan’s computer”. Explain the metaphor: why, in particular, is it “Satan’s computer”? 1-2 sentences are sufficient.

      • security-engineering
      Show worked solution

      The metaphor refers to a computer that acts intentionally maliciously, even to the degree that it reacts to the programmers’ behaviour and tries to find the best moment to thwart them. As a result, Security Engineering must tolerate the presence of an intelligent and hostile opponent.

    • t01-q2

      Tutorial1_questions.pdf

      analyseconceptual

      Safety/Reliability Engineering vs. Security Engineering

      Explain the difference between Safety and Reliability Engineerings and Security Engineering.

      • security-engineering
      Show worked solution

      Reliability Engineering or Safety Engineering try to address problems that we know at some point are going to happen, e.g. equipment failures, disk failures, power failures. For some of these we can even calculate failure probabilities: what is the probability of a disk failure after a certain number of years of operation? Many products release this information. Due to the predictable nature of these problems and the fact that we are expecting them, planning for such issues is much easier.

      On the other hand, it is difficult to predict and mitigate the threats coming from an active opponent, which is handled by Security Engineering.

    • t01-q3

      Tutorial1_questions.pdf

      analyseconceptual

      Security Goals - A

      You may wish to revisit the lecture recording to answer the following questions.

      a) True or false? Explain your answer in 1-2 sentences.

      • If you have the choice between secrecy and availability, and your system can only meet one of these goals simultaneously, always go for secrecy.
      • Authentication is the process of obtaining permission to access a file.
      • Privacy (in the context of internet) is my ability to conduct my online activities whilst being anonymous.

      b) Explain in 1-2 sentences.

      • Why does non-repudiation require secure timestamping in many cases?
      • Why is integrity of logfiles important to achieve accountability?
      • Why is it very hard to estimate the complete attack surface of a given system?
      • In the context of the last question, why is it important to remind yourself that security is a process?

      c) Break a protocol

      Danaerys tells Jon how he can enter the city. To enter the city, the guards will question him “Who goes there?” and Jon must reply with the secret passphrase (“The Lord of Winterfell”). Give a reason why this does not meet the definition of a cryptographic protocol and show how it can be broken.

      • security-goals
      Show worked solution

      a) True or false?

      • False. There are situations where availability is more important than secrecy. An example can be the military in a particular combat situation: it may matter more that commanders can communicate at all than the communication being accessible to eavesdroppers.
      • False. Authentication is about establishing the correctness of an identity. The process of obtaining permission to access a resource is authorisation.
      • False. Privacy is my ability to control what information I might share about me with other parties. What is described is anonymity, i.e. I want others to see and know what I am doing, I just don’t want them to know it was me.

      b) Explain in 1-2 sentences.

      • Why does non-repudiation require secure timestamping in many cases? Because it is possible for someone to claim that their computer system had been compromised at some point and it was the attacker who carried out some activity later. Secure timestamping by a third party (e.g. a TSA — Time Stamping Authority) provides a record of what happened at which time, and hence makes such claims much harder.
      • Why is integrity of logfiles important to achieve accountability? An attacker with access to a system can modify the log files to hide their traces. Hence, log files must be protected to ensure they provide a record of attacker activity (it is not always possible, however).
      • Why is it very hard to estimate the complete attack surface of a given system? This would require correctly enumerating every possible way a system can be breached. In practice that is usually not possible: today’s systems are too complex and one may easily overlook an attack vector.
      • In the context of the last question, why is it important to remind yourself that security is a process? What is not possible today may be possible tomorrow: the environment changes, and hence security is not static, but a process.

      c) Break a protocol

      This method provides no freshness. Someone could overhear the password being spoken and use it to enter (i.e. a replay attack).

    • t01-q4

      Tutorial1_questions.pdf

      analyseconceptual

      Security Goals - B

      Someone tells you that you can “just encrypt data” and you can be sure that the following security goals are achieved: confidentiality, integrity, authenticity, and access control. For each goal, explain if the person is right or wrong.

      • security-goals
      Show worked solution

      The easiest way to solve this is to start with the last three goals.

      • Confidentiality — right. Once the data is encrypted, confidentiality is guaranteed: without the key, the plaintext is not accessible.
      • Integrity — wrong. An attacker can make changes to the encrypted data, and it may still seem “plausible enough” when decrypted.
      • Authenticity — wrong. An attacker can make changes to the data, and there is no way to tell if it is authentic — see integrity. Authenticity is a special case of integrity, indicating that the data is exactly as it was when it was under the control of the originator. If a solution does not guarantee integrity, it does not guarantee authenticity. Solutions that do guarantee integrity may still not provide authenticity.
      • Access control — can be argued either way. This security goal overlaps with the others (or, alternatively, is more high-level). We tend to treat it as a security goal in its own right because it is all-important, even though it has many facets.
        • Right: we could argue that encryption controls access to the plain-text data.
        • Wrong: we can equally say that there is nothing about encryption that checks an attacker’s authorisation to modify or delete the data. So encryption provides only one aspect of access control.
    • t01-q5

      Tutorial1_questions.pdf

      analysedesign

      Sydney Opal Card

      You are likely familiar with the Sydney Opal Card system: a swipe card is used to “pay” and allow you to enter and leave the platforms via gates. Let us assume you were in charge when the system was planned. Using our reference framework of Policy, Incentives, Mechanism, and Assurance, please:

      • State two policies to achieve. One sentence per policy is fine.
      • State two incentives that work against those policies.
      • State one attack based on each incentive (see note below). It’s fine if the attack is described fairly generically (no need to refer to concrete technology that it attacks).
      • Give two mechanisms you can think of that will work against the attack based on the incentive.
      • State what assurance you place on each mechanism you have chosen.

      This question is intentionally open: you are free to choose policy and incentives, and you are free to make assumptions about how the transport system works. You are free to choose the attack based on the incentive (the one that your mechanism protects against). Policies, incentives, mechanism, and assurance do not have to be based on the electronic part of the system — it can also be entirely physical.

      • security-engineering
      • security-goals
      Show worked solution

      There are many possible answers. The following is an example.

      Policies

      • A high-level policy could be: customers must be billed correctly for their travel. If desired, that can be broken down into security goals relating to determining the correct amount to pay (integrity, accountability, authenticity etc.), but that was not necessary here as an answer — though a good exercise if you did it.
      • Another policy could be: the billing system must be available whenever a customer wants to travel.
      • Or: users’ travel data must be confidential and must be accessible only to authorised NSW Transport staff.

      Incentives

      • Attacker wants to free-ride.
      • Attacker wants to disturb the train system in Sydney.
      • Attacker wants to access some target users’ travel information.

      Attacks

      • Attacker forges an Opal card in someone else’s name, which is also linked to that person’s credit card. Alternatively: attacker breaks into an online account and resets a customer’s debt to 0.
      • Attacker stages a massive denial-of-service attack against the data centre that houses the accounting.
      • Attacker runs a phishing campaign targeting NSW Transport employees, hoping to harvest credentials for the customer database.

      Mechanisms

      • Forgery is difficult to defend against in an absolute sense. One needs Opal cards with chips that are hard to tamper with and, ideally, have a cryptographic mechanism that makes forgery detectable. History has shown that such systems are still hackable: it is a matter of effort. If the effort outweighs the benefit, the attacker is unlikely to try it — note how easily this balance is shaken if, e.g., the attacker finds a way to forge many cards and sell them. Defending against an attacker trying to break in means having intrusion detection in place, logging suspicious events, good passwords etc. — almost everything covered throughout the course.
      • Denial-of-service attacks are extremely hard to defend against: the natural asymmetry of internet-based services works against anything one can come up with. It is possible to make them less effective, however — for example, by having massive redundancy in place that can be spun up when needed (companies exist that provide exactly this security service). One can alternatively disconnect the internet interface from the internal Opal accounting, e.g. by having the internal accounting run autonomously for a few days without synchronising with the internet-facing systems or checking whether Opal cards are charged. That gives defenders time while keeping the system available. The drawback may be that, since it is not possible to check whether Opal cards are still charged, one may induce a certain potential loss from people who take the opportunity to run up a debt and then toss the Opal card. It may still be preferable to a wider outage, however.
      • Phishing is a common attack vector, discussed more in the usability lecture. Mechanisms to defend against it include deploying an email security solution that can flag suspicious emails, or conducting user training and awareness programs so employees can identify phishing emails. Again and again, various security incidents show that these two approaches are not perfect. Many high-profile network breaches — the iCloud celebrity-photo leak, the Sony hack, the Bangladesh Bank heist, and the eBay data breach — have been traced back to attackers infiltrating the network using well-researched, crafted phishing attacks that misled employees into entering their credentials.
    • t01-q6

      Tutorial1_questions.pdf

      analysedesign

      Designing a New Student Experience

      You have recently become the Head of the Higher Education Portfolio at your home university. Your first move is to cancel the subscription to the old “Whiteboard” system used to support lectures, enrolments, and grading. You replace it with something of your own design, called CAMPUS. In the following, refer to the framework for discussing security aspects that we introduced in the lecture.

      a) Incentives

      For each of the following actors, give an example incentive to attack or protect the system. Each incentive must be different.

      • The Vice Chancellor of the University
      • A student enrolled in a course
      • A Tutor of the same course
      • A Security guard in the CAMPUS data centre

      b) Policies

      For each of the following actors, give a possible security policy they might want to design and a security goal that relates to it.

      • The Vice Chancellor of the University
      • A student enrolled in a course
      • A tutor of the same course
      • security-engineering
      • security-goals
      Show worked solution

      a) Incentives

      There are multiple correct answers. Some examples are listed below.

      • The Vice Chancellor is concerned about the university remaining operational and its reputation (and hence all data must be protected), and therefore has an incentive to protect the system.
      • Students may be motivated to attack the system because they may wish to change their grades (or those of other students), or make the site unavailable so that they don’t have to do the quiz etc.
      • Tutors need to enter marks in order to do their job and get paid (so the site must be available). As such, they have an incentive to protect the system.
      • Similarly, the guard wants to be paid and keep their job.

      b) Policies

      Again several answers are possible.

      • The Vice Chancellor may ask that all data remain correct and secret, and that the privacy of all users and staff is guaranteed. The related security goals would be secrecy, integrity, and privacy.
      • The student may want the grading to be trackable, to understand whether someone manipulated their marks. This relates to accountability.
      • The tutor would like to work online and get paid in a timely manner. The security goal is availability.
    • t01-q7

      Tutorial1_questions.pdf

      recallconceptual

      Terminology

      a) Person-in-the-middle attack. What is a Person-in-the-middle, aka Man-in-the-middle, attack?

      b) Distributed Denial-of-Service. What is a Denial-of-Service attack? And a distributed one?

      c) Supply Chain Attack. What is a Supply Chain Attack?

      d) Security Goals. Tick all that are not classic security goals. Explain your choice.

      • Authenticity
      • Integrity
      • Deterrence
      • Authorisation
      • Confidentiality
      • Denial-of-service prevention
      • terminology
      • security-goals
      Show worked solution

      a) Person-in-the-middle attack

      An attack on the network (or some transmission bus) where the attacker manages to be between two communicating parties, usually carried out to eavesdrop on, or tamper with, the data. Examples are attacks on TLS (with broken authentication) or sniffing devices clipped into a data bus.

      b) Distributed Denial-of-Service

      An attack where someone over the network attempts to exhaust the resources of the victim. DoS attacks can be carried out by a form of malicious crowdsourcing (as demonstrated by LOIC), or by botnets. What usually happens is bots (compromised computers), or a group of people, start sending large volumes of requests towards a web service, and when the service can’t handle such a large load it becomes unavailable even for legitimate users. The term “distributed” applies when the attack is coordinated from different geo-locations, networks, and devices, so that it is extremely difficult to separate attack traffic from benign traffic. “Amplification” is a common tactic often used with DDoS attacks — e.g. DNS amplification, where the response is larger than the query and may even propagate to other DNS servers.

      c) Supply Chain Attack

      A supply chain attack is an attack in which the attacker inserts malicious code, or even a malicious component, into a trusted piece of software or hardware. By doing so, the attacker can hijack the victim’s distribution systems, turning any application they sell, any software update they push out, or even the physical equipment they ship to customers, into Trojan horses. With one well-placed intrusion, attackers can create a springboard into the networks of a supplier’s customers — sometimes numbering hundreds or even thousands of victims. Supply chain attacks are increasing because of their economies of scale. Recent examples include the SolarWinds and Kaseya attacks.

      d) Security Goals

      • Authenticity
      • Integrity
      • Deterrence
      • Authorisation
      • Confidentiality
      • Denial-of-service prevention

      The security goals discussed during the lecture are: confidentiality, integrity, authenticity, authorisation, accountability, non-repudiation, deniability, availability, and privacy. “Denial-of-service prevention” is only one aspect of the goal “availability”. “Deterrence” — the idea that the threat of punishment will deter people from committing crime and reduce the probability and/or level of offending in society — is not a security goal.

    • t01-q8

      Tutorial1_questions.pdf

      analyseconceptual

      Homework — ACSC Essential Eight

      Essential Eight by the Australian Cyber Security Centre (ACSC) is a series of baseline mitigation strategies recommended for organisations to improve their security posture. Implementing these strategies as a minimum makes it much harder for adversaries to compromise systems.

      • Find information about the Australian Cyber Security Centre (ACSC) Essential Eight maturity model.
      • Discuss how each strategy is applicable to a new superannuation provider.
      • essential-eight
      Show worked solution

      Find information about the ACSC Essential Eight maturity model

      The ACSC Essential Eight defines eight aspects of security with respect to Windows-based systems. They are:

      • Application Control
      • Application Patching
      • Restrict Administrative Privileges
      • Patch Operating Systems
      • Configure Microsoft Office Macro Settings
      • Using Application Hardening
      • Multi-Factor Authentication
      • Regular Backups

      When implementing the Essential Eight, organisations should first identify a target maturity level that is suitable for their environment. Organisations should then progressively implement each maturity level until that target is achieved.

      Discuss how each strategy is applicable to a new superannuation provider

      Being a high-risk company, the company must aim to reach maturity level three as quickly as possible.

      Aside

      The released solution stops there — it names the target maturity level but does not walk through each of the eight strategies individually for the superannuation provider. If you want the per-strategy reasoning, work through it yourself: a superannuation provider holds long-lived financial and personal records for members who may not check their balance for years, so integrity and availability of that data matter as much as keeping attackers out in the first place, which is why the tutorial’s answer jumps straight to the highest maturity level rather than treating the eight strategies as optional.

    • t02-q1a

      Tutorial_2_Questions.pdf

      applycommand

      Building a Password Cracker

      Passwords are intuitively understood by humans as a way to protect access to a resource. Just think of the fairy tale of Ali Baba! However, memorising them while maintaining unpredictability is the hard part — which is why “Open Sesame” is an exceptionally bad password.

      In this task, we will create a password cracker to get a feel for how passwords are constructed — and hacked. This will also serve as preparation for the next lecture. We’ll work from very naive approaches to more sophisticated ones.

      How Passwords Are Stored

      Passwords are usually stored as hashes (with salt), not as plain text. Password matching happens by comparing hashes, rather than plain-text passwords.

      Your tutor will explain:

      • What is hashing?
      • Why are passwords stored as hashes?
      • Why is salt important?
      • What is MD5 hashing? (hashing will be covered in detail later)

      Your Task

      You are given a set of MD5 hashes in hashes.txt for you to break. These are encoded versions of correct passwords.

      We provide skeleton code that allows you to compare a password against its corresponding MD5 hash.

      a) Brute-force

      The naive approach to cracking a password is to use brute force, trying all possible combinations of letters, digits, and special characters. For this task, we will limit ourselves to:

      • All letters in the English alphabet (both lowercase and uppercase)
      • Digits 09
      • Special characters #, !, and \$

      Complete the provided code template to brute force passwords up to a length of 4. Also, count the number of guesses required. Since the enumeration can take some time, we have included the MD5 hash of the password aA0# in the hashes_test.txt file. If your iteration loops are implemented correctly, you should find a match quite quickly. Once your code is working with this file, proceed to the hashes.txt file.

      Expected outcome: you should successfully crack a password of length 4. The number of guesses may vary depending on your implementation. If you follow the given code template, the brute-force process should take approximately 140–180 seconds on the provided Azure VM.

      from Crypto.Hash import MD5
      import string
      import sys
      import time
      
      ### Start timing - Do Not Change
      start_time = time.time()
      
      ### TODO: Read the hashes from the file into a list
      hash_file = ## TODO
      hashes = ##
      
      ### TODO: Remove the newline characters
      hashes = ## TODO
      
      ### Function to obtain the MD5 hash of a given string - Do Not Change
      def break_password(password_attempt, md5_hash):
      
             return md5_hash == MD5.new(password_attempt.encode()).hexdigest()
      
      ### Building the alphabet
      alphabet_list_1 = ## TODO Get the ASCII lowercase letters as a list
      alphabet_list_2 = ## TODO Get the ASCII uppercase letters as a list
      alphabet_list_3 = ## TODO Get the digits as a list
      symbols = ## TODO Get the symbols as a list
      
      ### TODO: Build the final alphabet of all allowed characters
      alphabet = ## TODO Build the final alphabet list
      counter = 0
      
      ### Nested for loops for enumeration
      for a in alphabet:
      
             for b in alphabet:
                    for c in alphabet:
                           for d in alphabet:
                                  ## TODO: Increase the counter
                                  ## TODO: Build the password
                                  ## TODO: Obtain the hash
                                  if target_hash in hashes:
                                         print(
                                                "Match:", password, MD5.new(password.encode()).he
                                                "Attempt:", str(counter),
                                                "Time (Seconds):", (time.time() - start_time))

      Now try passwords of length 5.

      • Do you still get a hit?
      • How many guesses?
      • How much time did this take?

      Repeat the experiment for passwords of length 8. Don’t let the program run for more than a few minutes or so (stop the code using the STOP button above).

      • passwords
      • hashing
      Show worked solution
      from Crypto.Hash import MD5
      import string
      import sys
      import time
      
      ### Start timing - Do Not Change
      start_time = time.time()
      
      ### TODO: Read the hashes from the file into a list
      hash_file = open("hashes.txt", "r")
      
      ### TODO: Read all lines from the file
      hashes = hash_file.readlines()
      
      ### TODO: Remove the newline characters
      hashes = [hashe.rstrip() for hashe in hashes]
      
      ### Function to obtain the MD5 hash of a given string - DO NOT Change
      def break_password(password_attempt, md5_hash):
      
             return md5_hash == MD5.new(password_attempt.encode()).hexdigest()
      
      ### Building the alphabet
      alphabet_list_1 = list(string.ascii_lowercase)   # Get the ASCII lowercase letters as a list
      alphabet_list_2 = list(string.ascii_uppercase)   # Get the ASCII uppercase letters as a list
      alphabet_list_3 = list(string.digits)            # Get the digits as a list
      symbols = ['#', '!', '$']                        # Get the symbols as a list
      
      ### TODO: Build the final alphabet of all allowed characters
      alphabet = alphabet_list_1 + alphabet_list_2 + alphabet_list_3 + symbols
      
      counter = 0
      
      ### Nested for loops for enumeration
      for a in alphabet:
      
             for b in alphabet:
                    for c in alphabet:
                           for d in alphabet:
                                  counter = counter + 1
                                  password = str(a + b + c + d)
                                  target_hash = MD5.new(password.encode()).hexdigest()
                                  if target_hash in hashes:
                                         print(
                                                    "Match:", password, MD5.new(password.encode()).hexdigest(),
                                                    "Attempt:", str(counter),
                                                    "Time (Seconds):", (time.time() - start_time))

      Match: Uq4C 6295d31334a99b0cc63c67e6bf15cedb — Attempt: 12704019 — Time: 44.182260274887085 seconds.

      Aside

      The released solution only records this length-4 result. It does not give fixed numbers for the length-5 or length-8 follow-up — the question’s own text says the guess count “may vary depending on your implementation,” and the actual time depends on the machine running it. Treat those two as open-ended: run your own completed loop and note what you observe, rather than looking for a specific figure here.

    • t02-q1b

      Tutorial_2_Questions.pdf

      applycommand

      Continuing the Tutorial 2 password-cracking exercise from t02-q1a: now cracking using a dictionary instead of brute force.

      b) Using a Dictionary — 1

      The above exercise should make you realise that brute force becomes very impractical very quickly. This is why almost all password cracking attempts try to leverage the weaknesses of the human mind: difficulty memorising random strings. Most users therefore tend to rely on words or common patterns they can remember.

      • Write code to load the dictionary file (located in the Tutorial 2 folder) into a suitable Python data structure.
      • What data structure should you use? Discuss this with your tutor if you are unsure!
      • What are the trade-offs?
      • Add code to try words from the dictionary as passwords. Do you get a hit?
      ### Start timing - Do Not Change
      start_time = time.time()
      
      #### TODO Open the dictionary file
      password_file = ## TODO
      
      #### TODO Read the password file into a list
      passwords = ## TODO
      
      #### TODO Remove the newline characters
      passwords = ## TODO
      
      #### TODO Enumerate through each password, generate the hash, and check w
      for password in passwords:
      
             ## TODO

      Now modify your code to be case-insensitive when checking passwords from the dictionary. Do you get more hits?

      ### Start timing - Do Not Change
      start_time = time.time()
      
      ### TODO Get all uppercase versions of the above passwords
      pl_list = ## TODO
      
      ### TODO Get title case versions of the above passwords
      pt_list = ## TODO
      
      ### TODO Combine the three lists of passwords
      passwords_case = ## TODO
      
      #### Ignore case
      for password in passwords_case:
      
             ## TODO
      • passwords
      • hashing
      Show worked solution

      Lists or arrays are fine here, as in the context of this question we have to loop over all entries anyway. An optimisation would be to use a dictionary, with the keys being the first letters of each word — for example, all words starting with a could be stored as a list/array under the key a in the dictionary.

      There should be one more hit:

      Match: tutorial 0575c8d592fb7b088226750aceec2b4e

      from Crypto.Hash import MD5
      import string
      import sys
      import time
      
      ### Start timing - Do Not Change
      start_time = time.time()
      
      #### TODO Open the dictionary file
      password_file = open("dictionary.txt", "r")
      
      #### TODO Read the password file into a list
      passwords = password_file.readlines()
      
      #### TODO Remove the newline characters
      passwords = [password.rstrip() for password in passwords]
      
      #### TODO Enumerate through each password, generate the hash, and check whether it matches
      for password in passwords:
      
             target_hash = MD5.new(password.encode()).hexdigest()
             if target_hash in hashes:
      
                    print("Match:", password, MD5.new(password.encode()).hexdigest(),
                          "Time (Seconds):", (time.time() - start_time))

      Match: tutorial 0575c8d592fb7b088226750aceec2b4e — Time: 0.05276179313659668 seconds.

      Now modify your code to be case-insensitive when checking passwords from the dictionary. Do you get more hits?

      One more hit:

      Match: Tutorial 368fe771261fcb18f7988833c9294a20

      ### Start timing - Do Not Change
      start_time = time.time()
      
      ### TODO Get all uppercase versions of the above passwords
      pl_list = [pl.upper() for pl in passwords]
      
      ### TODO Get title case versions of the above passwords
      pt_list = [pt.title() for pt in passwords]
      
      ### TODO Combine the three lists of passwords
      passwords_case = passwords + pl_list + pt_list
      
      #### Ignore case
      for password in passwords_case:
      
             target_hash = MD5.new(password.encode()).hexdigest()
             if target_hash in hashes:
      
                    print(
                            "Match:", password,
                            MD5.new(password.encode()).hexdigest(),
                            "Time (Seconds):", (time.time() - start_time)
                    )

      Match: tutorial 0575c8d592fb7b088226750aceec2b4e — Time: 0.050745248794555664 seconds. Match: Tutorial 368fe771261fcb18f7988833c9294a20 — Time: 0.10760664939880371 seconds.

    • t02-q1c

      Tutorial_2_Questions.pdf

      applycommand

      Continuing the Tutorial 2 password-cracking exercise from t02-q1b: trying combinations of two dictionary words.

      c) Using a Dictionary — 2

      You hopefully had a hit above, but you still haven’t found all the passwords yet. Try a further approach!

      • Add code to try combinations of words from the dictionary.
      • You may assume there are no spaces between them.
      • Do you get more hits? How much longer does it take?
      ### Start timing - Do Not Change
      start_time = time.time()
      
      ### We need to use a loop break because the passwords list is really larg
      loop_break = False
      
      ### TODO Write two nested loops to go through the passwords twice
      for password1 in passwords:
      
             ## TODO
      • passwords
      • hashing
      Show worked solution

      There are hits. Students can either generate the combinations on the fly or write them out to a new dictionary — do not exceed available RAM; if you do, write the results out to disk. Also, write the code so that it exits after the first hit.

      Match: actress granny 173e06cdeafc20ff5704f595dd7650d8

      ### Start timing - Do Not Change
      start_time = time.time()
      
      ### We need to use a loop break because the passwords list is really large
      loop_break = False
      
      ### TODO Write two nested loops to go through the passwords twice
      for password1 in passwords:
      
             for password2 in passwords:
                    password = password1 + password2
                    target_hash = MD5.new(password.encode()).hexdigest()
                    if target_hash in hashes:
                            print(
                                   "Match:", password1, password2,
                                   MD5.new(password.encode()).hexdigest(),
                                   "Time (Seconds):", (time.time() - start_time)
                            )
                            loop_break = True
                            break
      
             if loop_break:
                    break

      Match: actress granny 173e06cdeafc20ff5704f595dd7650d8 — Time: 1.4428608417510986 seconds.

    • t02-q1d

      Tutorial_2_Questions.pdf

      applycommand

      Continuing the Tutorial 2 password-cracking exercise from t02-q1b: trying letter-substitution and suffix variants of dictionary words.

      d) Using a Dictionary — 3

      Some people replace letters with digits or special characters.

      • Add code to try words from the dictionary, but this time allow for letter substitutions (e.g., 4 for A or a).
      • Do you get more hits? How much more time does it take?
      • Now add code to also consider combinations of words and substitutions.
      • Do you get more hits?
      • How much time does it take?
      ### Start timing - Do Not Change
      start_time = time.time()
      
      for password in passwords:
             password = # TODO Replace A with 4
             password = # TODO Replace a with 4
             target_hash = # TODO
             if target_hash in hashes:
                    # TODO

      Some users append symbols such as ”#” and ”!” to common words.

      • Extend your code to check for added special characters, but limit yourself to two (’#’ and ’!’) and the special characters from the brute-forcing section above.
      • Do you get more hits? How much time does it take?
      for password in passwords:
             password = #TODO Add the suffix !#
             target_hash = #TODO
             if target_hash in hashes:
                    #TODO
      • passwords
      • hashing
      Show worked solution

      A good solution here is to perform the substitutions on the fly, i.e., have an extra method that is called to make substitutions.

      Match: fin4lly 68709d844aed1b2936a9fae66bc8b697

      ### Start timing - Do Not Change
      start_time = time.time()
      
      for password in passwords:
             password = password.replace('a', '4')
             password = password.replace('A', '4')
             target_hash = MD5.new(password.encode()).hexdigest()
             if target_hash in hashes:
                    print(
                            "Match:", password,
                            MD5.new(password.encode()).hexdigest(),
                            "Time (Seconds):", (time.time() - start_time)
                    )

      Match: fin4lly 68709d844aed1b2936a9fae66bc8b697 — Time: 0.02870035171508789 seconds.

      Some users append symbols such as ”#” and ”!” to common words. Extend your code to check for added special characters, but limit yourself to two (’#’ and ’!’) and the special characters from the brute-forcing section above. Do you get more hits? How much time does it take?

      There should be a hit.

      Match: hacking!# e9e8144285eca2c050ab0643409e79c2

      for password in passwords:
             password = password+"!#"
             target_hash = MD5.new(password.encode()).hexdigest()
             if target_hash in hashes:
                    print("Match: ", password, MD5.new(password.encode()).hexdigest())

      Match: hacking!# e9e8144285eca2c050ab0643409e79c2

    • t02-q1e

      Tutorial_2_Questions.pdf

      recallconceptual

      Continuing the Tutorial 2 password-cracking exercise from t02-q1a.

      e) Know the password strengths

      By now, you should have some idea of various password cracking methods and how long they take. The following table shows the times taken to crack different types of passwords. Note the following:

      • These calculations are based on a specific hardware setup. Advances in hardware and CPUs will speed things up.
      • These times are not guaranteed. Usually, there are defenses such as limits on the number of incorrect password attempts, restrictions on the number of requests from a single IP address, etc.
      • Also, the attacker’s options depend on the scenario. An attacker will have more time if they are trying to break into a hard disk to which they have physical access. Much longer and more complex passwords are required in such cases.

      Password cracking — source: hivesystems.com

      Aside

      The table itself did not survive extraction from the source PDF — only its caption (“Password cracking”) and the source link remain. Check the linked Hive Systems article, or the original Tutorial 2 handout, for the actual crack-time-by-length-and-character-set figures.

      • passwords
      • hashing
      Show worked solution

      Aside

      The released solution set has no separate entry for this part — it moves directly from part (d) to part (f). Part (e) is a reading checkpoint rather than a task with its own computed answer: the three notes above are what it is testing. A cracking-time table like this one describes one specific hardware setup with no defensive controls in place, so it is an upper bound on attacker effort, not a guarantee — and an offline attacker who has stolen a hash (say, from a disk image) has far more time available than one facing rate-limiting on a live login form. Part (g) puts these caveats to work in an actual calculation.

    • t02-q1f

      Tutorial_2_Questions.pdf

      recallconceptual

      Homework companion to the Tutorial 2 password-cracking exercise (t02-q1a).

      f) Homework — More on storing passwords

      Read more about salting, peppering, rainbow tables, and selecting password hashes by following this link.

      Aside

      The tutorial links out here; the link target did not survive text extraction from the source PDF.

      • passwords
      • hashing
      Show worked solution

      Salting

      Salting is the process of adding a random string (called a salt) to a password before hashing it.

      • Ensures that even if two users have the same password, their hashes will be different.
      • Makes pre-computed attacks (like rainbow tables) ineffective.
      • Storage: salts are typically stored in plaintext alongside the password hash, because they are not meant to be secret — their job is to make each hash unique.

      Peppering

      Peppering is similar to salting, but the pepper is kept secret (at the application or hardware level) instead of being stored with the password.

      • Adds an extra layer of security if the database is compromised.
      • Unlike salts, the pepper is common across all passwords and not stored in the database.

      Rainbow Tables

      Rainbow tables are large pre-computed lists that map possible passwords to their hash values.

      • Attackers use them to reverse hashes quickly.
      • Salting defeats rainbow tables, because each unique salt requires a new table.

      Selecting Password Hashes

      Use strong, slow password hashing functions such as:

      • bcrypt
      • scrypt
      • Argon2

      These are designed to be computationally expensive and resistant to brute-force attacks. Avoid using fast hash functions like MD5 or SHA-1 for password storage.

    • t02-q1g

      Tutorial_2_Questions.pdf

      applynumeric

      Homework companion to the Tutorial 2 password-cracking exercise (t02-q1a).

      g) Homework — Calculation of password cracking times

      A company uses 8-character passwords composed only of lowercase letters (az). This means there are 26 possible characters per position and 8 positions.

      1. Total password combinations. Calculate the total number of possible passwords.

      2. Cracking rates. Assume the following cracking speeds:

        • A CPU can attempt 100 million (1×1081 \times 10^8) hashes per second
        • A single GPU can attempt 5 billion (5×1095 \times 10^9) hashes per second
        • A multi-GPU rig with 8 GPUs can attempt 40 billion (4×10104 \times 10^{10}) hashes per second

        (a) Calculate how long it would take (in seconds, minutes, hours, and days) to try every possible password in the worst-case scenario for each of the three setups.

      3. Impact of salting. Suppose the company adds a unique 32-bit salt (4 bytes) to each user’s password. There are 1,000 users. (a) Explain how salting affects the attacker’s work if they want to crack all user passwords. (b) Recalculate the total number of combinations the attacker must try if they have to crack all users’ hashes individually. (c) How does this change the cracking time for each setup (CPU, GPU, multi-GPU)?

      Hint

      • Total combinations for unsalted passwords = 26826^8
      • When salting, the attacker must attack each user’s hash individually, because the salt is unique to each user.
      • Convert large numbers of seconds into days to interpret your answers.
      • passwords
      • hashing
      • entropy
      Show worked solution

      1. Total password combinations (unsalted)

      Passwords use 8 lowercase letters (az), so:

      268=208,827,064,57626^8 = 208,827,064,576

      or approximately 2.09×10112.09 \times 10^{11} possible passwords.

      2. Cracking times without salting

      CPU (100 million = 1×1081 \times 10^8 hashes/sec)

      Time = 2.09×1011÷1×108=2,0882.09 \times 10^{11} \div 1 \times 10^8 = 2,088 seconds

      • Seconds: 2,088 s
      • Minutes: 2,088÷60=34.82,088 \div 60 = 34.8 min
      • Hours: 34.8÷600.5834.8 \div 60 \approx 0.58 hours

      Single GPU (5×1095 \times 10^9 hashes/sec)

      Time = 2.09×1011÷5×109=41.82.09 \times 10^{11} \div 5 \times 10^9 = 41.8 seconds

      • Seconds: 41.8 s
      • Minutes: 41.8÷600.7041.8 \div 60 \approx 0.70 min

      Multi-GPU rig (8 GPUs = 4×10104 \times 10^{10} hashes/sec)

      Time = 2.09×1011÷4×1010=5.222.09 \times 10^{11} \div 4 \times 10^{10} = 5.22 seconds

      • Seconds: 5.2 s

      Observation: a multi-GPU rig is about 400x faster than a CPU in this example.

      3. Impact of salting (unique 32-bit salt per user)

      With unique salts:

      • Each user’s hash must be cracked individually.
      • For 1,000 users, total combinations:

      1000×268=1000×2.09×1011=2.09×10141000 \times 26^8 = 1000 \times 2.09 \times 10^{11} = 2.09 \times 10^{14}

      Recalculated times:

      CPU

      Time = 2.09×1014÷1×108=2.09×1062.09 \times 10^{14} \div 1 \times 10^8 = 2.09 \times 10^6 seconds

      • Seconds: 2.09×1062.09 \times 10^6 s
      • Days: 2.09×106÷86,40024.22.09 \times 10^6 \div 86,400 \approx 24.2 days

      Single GPU

      Time = 2.09×1014÷5×109=41,7652.09 \times 10^{14} \div 5 \times 10^9 = 41,765 seconds

      • Seconds: 41,765 s
      • Hours: 41,765÷3,60011.641,765 \div 3,600 \approx 11.6 hours

      Multi-GPU rig

      Time = 2.09×1014÷4×1010=5,2212.09 \times 10^{14} \div 4 \times 10^{10} = 5,221 seconds

      • Seconds: 5,221 s
      • Hours: 5,221÷3,6001.455,221 \div 3,600 \approx 1.45 hours

      Key takeaways

      1. Without salting:
        • Multi-GPU: 5.2 seconds
        • Single GPU: 42 seconds
        • CPU: 35 minutes
      2. With unique salts:
        • Multi-GPU: 1.45 hours
        • Single GPU: 11.6 hours
        • CPU: 24 days
      3. Salting dramatically increases the attacker’s workload, because each user’s hash must be attacked separately and precomputed tables (like rainbow tables) become useless.
    • t03-q1a

      Tutorial_3_Questions.pdf

      applycommand

      Q1: Access Control on Linux

      In the lecture, we learned that Linux implements access control via file permissions — this is the primary mechanism that users interact with. This concept is powerful because Linux (and UNIX) treats every object as a type of file.

      How file permissions work. File permissions are grouped and applied in a structured way:

      • A file always has an owner.
      • A file always has an associated group; this group may have the same name as the owner (for every user, there is also a corresponding group), but it can also be any group defined on the system.

      Permissions are then set for three categories:

      1. Owner — the user who owns the file
      2. Group — the group associated with the file
      3. Other — everyone else on the system

      Types of permissions. There are three fundamental file permissions in Linux:

      1. Read (r) — allows viewing or reading the contents of a file
      2. Write (w) — allows modifying or deleting the contents of a file
      3. Execute (x) — allows running a file (if it is a script or program)

      These permissions form the core of Linux access control and are critical for securing files and resources.

      a) Inspecting the list of users

      i) Take a look at the file /etc/passwd. Try to understand its contents. You can use commands like cat or more (e.g., cat /etc/passwd) to view its contents, or open it in an editor such as vi or nano. You can do this in a separate terminal. Alternatively, you can also use Jupyter Notebook. (In Jupyter, prefix command-line commands with !.)

      Example: !cat /etc/passwd

      ii) Try to log in as the tutorialuser1 user. What happens? i.e., use su tutorial1

      iii) Use sudo su to become root and then use the su command to switch to the tutorialuser1 user.

      i.e., run sudo su followed by su tutorialuser1. Use the command whoami to verify the current username. Use the command cd to navigate to the home directory of tutorialuser1. Use the command pwd to print the home directory path.

      iv) Try to log in (using the su command) as some of the entries that don’t appear to have human names (e.g., www-data, mail). What happens?

      v) Find out what the /usr/sbin/nologin entry in the /etc/passwd file does.

      • access-control
      • unix-permissions
      Show worked solution

      i) Take a look at the file /etc/passwd. What do you find in there?

      Each user has a single line entry. Each entry contains:

      • Username
      • An x indicating the password is stored in an encrypted form (in a separate file)
      • User ID (UID) and Group ID (GID)
      • Home directory
      • Default shell

      ii) Try to log in as the tutorialuser1 user.

      When you run su tutorialuser1, it will prompt you for the password of the tutorialuser1 account. Since you do not know the password, you will not be able to log in.

      iii) Use sudo su to become root and then use su to switch to tutorialuser1.

      1. Run sudo su to become the root user.
      2. Then run su tutorialuser1 to switch to the tutorialuser1 account.
      3. Use whoami and confirm that the logged-in user is tutorialuser1.
      4. Run cd to navigate to the home directory of tutorialuser1.
      5. Finally, use pwd to verify that you are in /home/tutorialuser1.

      iv) Try to log in as www-data or mail.

      Example: su mail (this will ask for a password, and you again don’t have the password). Become root and try again: sudo su, then run su mail or su www-data. You will see that you still can’t log in.

      This is because these users are not meant to log in from a normal shell. If you inspect the /etc/passwd file, you’ll notice that these accounts are configured with shells like /usr/sbin/nologin or /bin/false, which prevents interactive logins.

      v) What does the /usr/sbin/nologin entry do?

      It indicates that the user is not allowed to log in.

      Cross-reference: Unix and Linux permissions.

    • t03-q1b

      Tutorial_3_Questions.pdf

      analyseconceptual

      Q1 b) More on users

      i) While logged in as the ubuntu user (or any other normal user), try to view the /etc/shadow file. What happens?

      ii) How can you solve this issue?

      iii) What is the content of this file? Read about it here: https://www.cyberciti.biz/faq/understanding-etcshadow-file/

      iv) What are the file permissions for /etc/shadow, compared to /etc/passwd? If you don’t know how to find them, type man ls to learn about the command and the necessary options.

      • access-control
      • unix-permissions
      Show worked solution

      i) While logged in as ubuntu, try to view /etc/shadow.

      Command: cat /etc/shadow. You will get a permission denied error when trying to view /etc/shadow as a normal user.

      ii) How can you solve this issue?

      To view the contents of /etc/shadow, you need elevated privileges. Use the following command: sudo cat /etc/shadow

      iii) What is the content of this file?

      The /etc/shadow file stores encrypted password information and related security settings for user accounts. It is accessible only by the root user for security reasons.

      iv) What are the file permissions for /etc/shadow, compared to /etc/passwd?

      Commands to use:

      ls -ltr /etc/passwd
      ls -ltr /etc/shadow

      The outputs will look like this:

      -rw-r--r-- 1 root root   2026 Jul 29 05:51 /etc/passwd
      -rw-r----- 1 root shadow 1369 Jul 29 05:51 /etc/shadow

      The /etc/passwd file is owned by root. Root can read and write to it, while all others have read-only access.

      The /etc/shadow file is owned by root and belongs to the shadow group. Root can read and write to it, members of the shadow group can read it, and all others have no access.

      Explanation. The /etc/passwd file contains basic user account information that needs to be readable by all users and system processes, which is why it has read permissions for everyone. However, only the root user can modify it to protect against unauthorized changes.

      The /etc/shadow file stores sensitive password hashes and security-related information. Because of its sensitive nature, access is restricted to the root user and the shadow group only. This restriction protects user credentials from being exposed to normal users or processes, enhancing system security.

      Cross-reference: Unix and Linux permissions.

    • t03-q1c

      Tutorial_3_Questions.pdf

      applycommand

      Q1 c) Executing files

      • List the permissions of the yes program. Hint: you can find the location of this file by typing which yes into your terminal.
      • Why are you able to execute it?
      • unix-permissions
      Show worked solution
      tutorialuser1@lab22YQ9P:$ which yes
      /usr/bin/yes
      tutorialuser1@lab22YQ9P:$ ls -ltr /usr/bin/yes
      -rwxr-xr-x 1 root root 39256 Sep 5 2019 /usr/bin/yes

      Since there is x permission for the owner, group, and others, all users can execute it.

    • t03-q1d

      Tutorial_3_Questions.pdf

      applycommand

      Q1 d) Setting permissions for files

      An owner can set permissions for a file using the chmod command. Interestingly, there are several ways to specify permissions. For example, one can use the syntax g+r / g-r to add or remove read permission for the group (similarly, u+r and o+r apply to the user and “others”).

      Another approach is to write the permissions in octal notation, where the first digit refers to the user, the second to the group, and the third to “others”. Each digit encodes three bits:

      A value of 7 in octal is 111 in binary, meaning read, write, and execute permissions are all granted. For example, chmod 777 would give full permissions to the user, group, and others.

      Values of each permission setting:

      Value (Octal)Value (Binary)Permission granted
      1001Execute
      2010Write
      4100Read

      Question: What would be the command to set the permissions of a file to match those of /etc/passwd?

      • unix-permissions
      Show worked solution
      ubuntu@lab22YQ9P:$ ls -ltr /etc/passwd
      -rw-r--r-- 1 root root 2026 Jul 29 05:51 /etc/passwd

      The permission string -rw-r--r-- means:

      • Owner (user): read and write (rw-)
      • Group: read-only (r--)
      • Others: read-only (r--)

      This translates to octal 644 (User = 6, Group = 4, Others = 4).

      touch test_file.txt        # Create a new file named test_file.txt
      chmod 644 test_file.txt    # Change the permissions to 644
      ls -ltr test_file.txt      # Check the set permissions

      The output will show test_file.txt now has the same permissions as -rw-r--r-- /etc/passwd.

      Cross-reference: Unix and Linux permissions.

    • t03-q1e

      Tutorial_3_Questions.pdf

      applycommand

      Q1 e) Owners and groups

      Now let’s explore file ownership in more detail.

      • How can you change the owner or group of a file?
      • Try changing the owner of a file that belongs to root. Were you successful?
      • Now try the same operation as the root user. Does it work this time?
      • access-control
      • unix-permissions
      Show worked solution
      ubuntu@lab22YQ9P:$ ls -ltr test_file.txt
      -rw-r--r-- 1 ubuntu ubuntu 0 Jul 29 06:20 test_file.txt
      
      ubuntu@lab22YQ9P:$ sudo chown root:root test_file.txt
      ubuntu@lab22YQ9P:$ ls -ltr test_file.txt
      -rw-r--r-- 1 root root 0 Jul 29 06:20 test_file.txt

      As shown above, running chown with sudo (as root) successfully changes the owner and group of the file to root. Once you do the change, you will not be able to change the ownership of a file owned by root as a normal user.

      Cross-reference: Unix and Linux permissions.

    • t03-q2a

      Tutorial_3_Questions.pdf

      applycommand

      Q2: Potential problems with setuid/setgid

      Back to file permissions again: setting permissions for a file doesn’t just involve 3 octal digits (e.g., 444, 761, …); it actually involves 4 digits! An extra digit can be placed at the start of the command, which is used to set the following special bits:

      • setuid bit (4): when this bit is set on an executable file, the program runs with the privileges of the file’s owner rather than the user who runs it. This is often used for programs that need elevated privileges (e.g., passwd).
      • setgid bit (2): similar to setuid, but it applies the file’s group privileges instead of the user’s. For directories, it ensures that files created inside inherit the directory’s group.
      • sticky bit (1): on directories, this bit ensures that only the file’s owner (or root) can delete or rename the files inside, even if others have write permissions.

      Note: misuse of the setuid or setgid bits can lead to security vulnerabilities because they can grant elevated privileges.

      a) Setting the bits

      • How can you set a file to have both the setuid and setgid bits enabled?
      • Provide the command(s) to set a file’s owner to be bertie and to enable its setuid bit. (Hint: you may need to create the user bertie first using man adduser.)
      • setuid
      • unix-permissions
      Show worked solution
      1. Create a file named my_program:
      touch my_program
      1. Set both setuid and setgid bits on a file:
      chmod 6755 my_program

      6 at the start sets both setuid (4) and setgid (2) bits. 755 sets rwxr-xr-x (owner: read/write/execute; group and others: read/execute).

      ls -ltr my_program
      -rwsr-sr-x 1 ubuntu ubuntu 12345 Jul 29 06:30 my_program

      Notice the s in the owner and group execute positions, indicating the setuid and setgid bits are set.

      1. Set a file’s owner to bertie and enable the setuid bit:
      sudo adduser bertie   # if bertie does not already exist
      sudo chown bertie my_program
      sudo chmod 4755 my_program
      ls -ltr my_program
      -rwsr-xr-x 1 bertie ubuntu 12345 Jul 29 06:35 my_program

      Here, 4 at the start sets only the setuid bit, and 755 applies normal file permissions. The lowercase s (or uppercase S if execute is not set) in the owner’s execute position shows the setuid bit is enabled.

      Cross-reference: Unix and Linux permissions.

    • t03-q2b

      Tutorial_3_Questions.pdf

      analyseconceptual

      Q2 b) Special bits on a directory

      When applied to directories, these special bits have a slightly different meaning.

      • Create a directory named bertiesworld. Change its group ownership to bertie. Then set the appropriate directory bits so that any subdirectory or file created inside bertiesworld automatically inherits the group bertie.
      • In what scenarios could this behavior be useful?
      • Could this introduce any potential security risks?
      • setuid
      • unix-permissions
      Show worked solution
      1. Create the directory and set the group to bertie:
      mkdir bertiesworld
      ls -ltr
      drwxrwxr-x 2 ubuntu ubuntu 4096 Jul 30 12:02 bertiesworld
      
      sudo chown ubuntu:bertie bertiesworld/
      ls -ltr
      drwxrwxr-x 2 ubuntu bertie 4096 Jul 30 12:02 bertiesworld
      1. Create a test file inside the directory:
      cd bertiesworld/
      touch test_file.txt
      ls -ltr
      -rw-rw-r-- 1 ubuntu ubuntu 0 Jul 30 12:04 test_file.txt

      Note: the group is still ubuntu, not bertie.

      1. Set the setgid bit on the directory:
      cd ../
      sudo chmod g+s bertiesworld/
      ls -ltr
      drwxrwsr-x 2 ubuntu bertie 4096 Jul 30 12:06 bertiesworld

      Note: the s in the group execute position shows that the setgid bit is now set.

      1. Create another test file and check the group:
      cd bertiesworld/
      touch test_file_bertie_2.txt
      ls -ltr
      -rw-rw-r-- 1 ubuntu bertie 0 Jul 30 12:13 test_file_bertie_2.txt

      Now, all new files and subdirectories created inside bertiesworld automatically inherit the group bertie.

      When is this useful? This approach is useful when multiple users are collaborating and need shared group access to files and directories.

      Potential risk: it can become harder to track who has access if group memberships are not updated regularly. For example, a user who was removed from the project might still belong to the group and retain access.

      Cross-reference: Unix and Linux permissions.

    • t03-q2c

      Tutorial_3_Questions.pdf

      analyseconceptual

      Q2 c) Executables with setuid

      • Why do some programs require the setuid bit?
      • Are there any programs in your /usr/bin directory that have the setuid bit set and are owned by root? (Hint: you can use the find command with the -perm flag.)
      • setuid
      • privilege-escalation
      Show worked solution

      Some programs need to run with elevated (superuser) privileges even when the user running them does not have superuser access. The setuid bit allows these executables to run with the permissions of the file’s owner (often root), which is required for tasks such as changing passwords or mounting filesystems.

      To find all executables in /usr/bin that have the setuid bit set and are owned by root, you can run:

      find /usr/bin -user root -perm -4000 -exec ls -ldb {} \; > /tmp/filename

      Example output:

      -rwsr-xr-x 1 root root  39144 Mar  7  2020 /usr/bin/fusermount
      -rwsr-xr-x 1 root root  85064 Feb  6 2024 /usr/bin/chfn
      -rwsr-xr-x 1 root root  31032 Feb 21 2022 /usr/bin/pkexec
      -rwsr-xr-x 1 root root  68208 Feb  6 2024 /usr/bin/passwd
      -rwsr-xr-x 1 root root  88464 Feb  6 2024 /usr/bin/gpasswd
      -rwsr-xr-x 1 root root  44784 Feb  6 2024 /usr/bin/newgrp
      -rwsr-xr-x 1 root root  39144 Apr  9 2024 /usr/bin/umount
      -rwsr-xr-x 1 root root  67816 Apr  9 2024 /usr/bin/su
      -rwsr-xr-x 1 root root  53040 Feb  6 2024 /usr/bin/chsh
      -rwsr-xr-x 1 root root  55528 Apr  9 2024 /usr/bin/mount
      -rwsr-xr-x 1 root root 166056 Apr  4 2023 /usr/bin/sudo

      Cross-reference: Unix and Linux permissions.

    • t03-q3

      Tutorial_3_Questions.pdf

      analyseconceptual

      Q3: Real, effective, and saved UIDs in practice

      In the Tutorial 3 folder, you will find a C program named whoami3.c.

      • Inspect the code. Open whoami3.c and read through it. What does it do?
      • As user ubuntu, compile the program: gcc whoami3.c -o whoami3
      • As the user ubuntu, make it executable: chmod +x whoami3
      • Run the program: ./whoami3
      • What do you see? Explain your observation (look closely at the Real, Effective, and Saved UID values).
      • Now change the program’s ownership to root and set the SUID bit:
      sudo chown root:root whoami3
      sudo chmod u+s whoami3
      • Run ls -l whoami3 and note what has changed in the permission bits.
      • Now run the program again, as user ubuntu: ./whoami3
      • What do you see? Explain your observation.
        • How do the Real, Effective, and Saved UIDs compare to the previous run?
        • Why did some values change and others didn’t?
        • What does this tell you about how SUID affects process privileges?
      • uids
      • setuid
      Show worked solution

      What does the code do? The program calls getresuid() to retrieve the calling process’s Real, Effective, and Saved UIDs, then prints all three. It doesn’t modify anything — it’s purely observational, letting us see how the OS tracks these three separate identities for every process.

      Worked example

      AnswerReal: 1000 Effective: 1000 Saved: 1000

      1. Before the SUID bit is set, whoami3 is an ordinary executable owned by ubuntu (UID 1000).
      2. The kernel launches it directly on behalf of ubuntu, so it sets Real = Effective = Saved = the launching user’s UID.
      3. All three UIDs come out identical and equal to 1000. There is no privilege distinction: the process can only do what ubuntu is allowed to do.

      After chown root:root and chmod u+s, the owner execute bit x becomes s (lowercase, since owner execute was already set). This is the SUID bit — it tells the kernel: “whenever this file is executed, set the process’s effective UID to the file owner’s UID (root), regardless of who runs it.”

      Worked example

      AnswerReal: 1000 Effective: 0 Saved: 0

      1. Real UID stays 1000 (ubuntu) — the kernel always preserves who actually launched the process. This never changes just because SUID is set; it’s used for accounting, permission checks like “can this process signal that process,” and audit logging.
      2. Effective UID becomes 0 (root) — this is the direct effect of the SUID bit. The kernel sets the effective UID to match the file owner (root) at exec() time. All permission checks the kernel does from this point on (file access, etc.) use the effective UID, not the real one, so the process now has root-level access even though ubuntu launched it.
      3. Saved UID also becomes 0 (root) — the kernel copies the new effective UID into the saved UID at exec() time. This exists so that if the program later temporarily drops privilege (e.g., seteuid(ruid) to act as ubuntu for safety), it can restore root privilege afterward via seteuid(suid) without needing to re-authenticate. This is exactly how programs like passwd briefly de-escalate and re-escalate privilege while writing to /etc/shadow.

      Key takeaway. SUID doesn’t change who ran the program (Real UID) — it changes what the program is allowed to do (Effective UID), with the Saved UID acting as a “memory” of the elevated privilege so it can be restored after a temporary drop. This is the mechanism that lets an unprivileged user run a small, trusted, root-owned program (like passwd) to perform a specific privileged action, without giving that user root access to everything else.

      Cross-reference: Unix and Linux permissions.

    • t03-q4

      Tutorial_3_Questions.pdf

      analyseconceptual

      Q4: Privilege escalation and fallback with saved UID

      In the Tutorial 3 folder, you will find a C program named whoami4.c. This program builds on whoami3.c — instead of just reading the UIDs once, it prints them, temporarily drops root privilege, prints them again, then restores root privilege using the saved UID and prints them a third time.

      • Inspect the code. Open whoami4.c and read through it. What does it do? What functions does it call, and what do you expect each one to change?
      • As user ubuntu, compile the program: gcc whoami4.c -o whoami4
      • Change the program’s ownership to root and set the SUID bit:
      sudo chown root:root whoami4
      sudo chmod u+s whoami4
      • As user ubuntu, run the program: ./whoami4
      • You should see three lines of output, labelled Start, Dropped, and Restored. For each line, record the Real, Effective, and Saved UID.
      • Explain your observations:
        • At Start, why is the Effective UID 0 even though ubuntu launched the program?
        • At Dropped, which UID changed, and which stayed the same? Why did we lose root privilege here?
        • At Restored, how did the program get root privilege back without asking for a password or re-running as root?
        • If the Saved UID did not exist, would this “drop then restore” pattern still be possible? Why or why not?
        • Real-world connection: the passwd command uses exactly this pattern — briefly dropping to your real privilege level, then restoring root privilege only when it needs to write to /etc/shadow. Why might a program want to minimise the time it spends running with full root privilege, rather than just keeping it for the whole execution?
      • uids
      • privilege-escalation
      • setuid
      Show worked solution

      What does the code do? The program calls getresuid() three times, at different points in execution, to show how the Real, Effective, and Saved UIDs change as the process deliberately drops and then restores its privilege. First it prints the UIDs as soon as the program starts (Start). Then it calls seteuid(ruid) to temporarily drop its effective privilege down to the real (unprivileged) user and prints the UIDs again (Dropped). Finally it calls seteuid(suid) to restore its effective privilege back to root using the saved UID, and prints the UIDs a third time (Restored).

      Worked example

      AnswerStart -> Real: 1000 Effective: 0 Saved: 0

      1. At Start, the Effective UID is already 0 (root), even though ubuntu (UID 1000) launched the program.
      2. This is the same SUID mechanism from the previous task — the kernel sets the effective UID to the file owner’s UID (root) at exec() time, regardless of who ran the file.
      3. The Saved UID is also 0, copied from the effective UID at the same moment.

      Worked example

      AnswerDropped -> Real: 1000 Effective: 1000 Saved: 0

      1. At Dropped, only the Effective UID changes, from 0 back to 1000.
      2. The Real UID was never root to begin with, so it’s unaffected.
      3. Crucially, the Saved UID stays 0seteuid() only changes the effective UID, it does not touch the saved UID.
      4. At this point the process is voluntarily running with the same restricted privilege as ubuntu, even though it’s still technically the same SUID-root process. This is a deliberate safety measure: while performing routine, non-privileged work, the program avoids holding root power it doesn’t currently need.

      Worked example

      AnswerRestored -> Real: 1000 Effective: 0 Saved: 0

      1. At Restored, the Effective UID jumps back to 0.
      2. This works because seteuid(suid) is allowed to set the effective UID to any value that matches the real, effective, or saved UID — and the saved UID was still 0 from the very first exec().
      3. No password, no re-authentication, and no call back into sudo was needed. The kernel already “remembered” that this process was entitled to root privilege, via the saved UID.

      If the Saved UID did not exist, this pattern would be impossible. Once the process dropped its effective UID to 1000, it would have no way to prove it was ever allowed to be root again — it would need to be re-executed from a SUID-root binary from scratch. The saved UID is what makes a reversible drop possible, as opposed to a permanent one.

      Real-world connection. passwd follows exactly this drop-then-restore pattern. It runs as SUID root, drops privilege while doing routine work like reading input or checking your current password, and only restores root privilege for the brief window where it actually needs to write to /etc/shadow. Minimising the time spent at full privilege reduces the “attack window” — if the program crashes, is exploited via a bug, or handles malicious input, it does so with the least privilege necessary for as much of its execution as possible. This is the Principle of Least Privilege applied at the level of a single process’s lifetime, not just at the level of user accounts.

      Key takeaway. seteuid() moves privilege between the Real, Effective, and Saved UIDs without discarding any of them — unlike setuid(), which collapses all three to the same value and permanently gives up the ability to restore a higher privilege. This distinction is exactly why security-sensitive SUID programs use seteuid() for temporary privilege changes, and reserve setuid() only for the point where they want to drop privilege for good.

      Cross-reference: Unix and Linux permissions.

    • t04-q1

      Tutorial_4_Questions.pdf

      analysedesign

      Q1: Revisiting simple encryption

      You are given a text file named enc.txt by a friend. They say they have no idea what it means — after all, they’ve never taken a class in cryptography (and they didn’t enjoy history much in school either).

      They watch you in wonder and awe as you begin decrypting it using Python, without relying on any cryptography libraries.

      Your task is to decrypt the given file enc.txt.

      Recall: this is a ciphertext-only attack scenario, where you are given only the encrypted message (ciphertext) and no information about the plaintext or the key. Your job is to analyze the ciphertext and deduce the plaintext through cryptanalysis techniques.

      a) Observe the contents of the given file

      Write code that prints the contents of the file.

      file =     # TODO: Open the enc.txt file
      
      content =  # TODO: Read the contents
      
      # TODO: Print the contents
      
      # Close the file

      b) Check the properties of the ciphertext

      That doesn’t reveal much, does it? Let’s examine some properties of the ciphertext:

      • Check the length of the ciphertext.
      • Find the factors of its length (use the factorint function in sympy).
      • Which numbers divide the length?
      from sympy.ntheory import factorint
      
      ## TODO print the total length of the content
      ## TODO use the factorint function to find the prime factors of content length

      Still not much insight. Let’s perform some alphabet/symbol analysis:

      Generate frequency plots assuming symbol lengths from 1 to 5. Write the code in a generic way:

      • Allow input of the symbol length to consider.
      • The code should progressively identify symbols, store them in a dictionary, and track each symbol’s frequency.
      • Once the dictionary is fully built, plot the frequency histogram of the symbols.
      • Based on the plots, which symbol length do you think is the correct one?
      ## TODO: Write a function to build the symbol frequency dictionary
      ## TODO: It will take the filename and the symbol length as inputs
      
      def build_symbol_dictionary(filename, symbol_length):
      
          file = ## TODO: Open the given file
      
          count = ## TODO: Build an empty dictionary
      
          symbol = ""                              ## Placeholder for the symbols - no need
      
          while True:
                 # TODO: Read the file by symbol_length
                 symbol =
      
          if not symbol:                           ## TODO: If there is no symbol, it means
      
          else:
                 if symbol in count: ## TODO: If the current symbol is already
      
          else:                                    ## TODO: Else, add the symbol to the dictionary
      
      symbol_frequency = build_symbol_dictionary("enc.txt", 3)
      
      print(symbol_frequency.keys())
      print(sum(symbol_frequency.values()))
      
      import matplotlib.pyplot as plt
      
      plt.bar(list(symbol_frequency.keys()), symbol_frequency.values(), color='b')
      plt.xticks(rotation = 45, size =6)
      plt.xlabel("Symbol")
      plt.ylabel("Frequency")
      plt.show()

      c) Decide on the symbol length

      From the above analysis decide on the symbol length. Now we need to try some substitutions.

      • What symbol can be the whitespace?
      • What symbol can be the letter e?
      • Write code to try out a few substitutions and see what makes sense. Try several runs with different guesses.
      file = open("enc.txt", 'r')
      
      content=file.read()
      sub_content=""
      
      for i in range(0,len(content),3):
             symbol = content[i:i+3]
             if symbol== ##TODO Experiment with guessing some symbols. You can add
                    symbol=
             if symbol== ##TODO Experiment with guessing some symbols. You can add
                    symbol=
             sub_content = sub_content+symbol
      
      print(sub_content)
      
      file.close()

      Hopefully by now you have identified some correct mappings. You can continue this process to find all the mappings, but it can be a bit tedious. Let’s see if we can identify a pattern:

      • Build a new sorted dictionary.
      • Replot the symbol histogram (this time sorting the keys).
      keys = list(symbol_frequency.keys())
      keys.sort()
      sorted_dict = {i: symbol_frequency[i] for i in keys}
      
      import matplotlib.pyplot as plt
      
      plt.bar(list(sorted_dict.keys()), sorted_dict.values(), color='b')
      plt.xticks(rotation = 45, size =6)
      plt.xlabel("Symbol")
      plt.ylabel("Frequency")
      plt.show()

      Based on this graph and the substitutions you found earlier, you can now express the mapping as a formula.

      • What is the equation for the mapping?

      d) Write code to decipher the full text

      import string
      
      chars = list(string.ascii_lowercase)
      
      file = open("enc.txt", 'r')
      
      content = file.read()
      file.close()
      
      plain_content = ""
      
      for i in range(0, len(content), 3):
             symbol = content[i:i+3]
             if symbol != # TODO: Write the program logic for non-whitespace char
      
             else:
                    plain_content += " "
      
      print(plain_content)
      
      with open("plain.txt", "w") as text_file:
             n = text_file.write(plain_content)
      • symmetric-crypto

      No solution released. The unit has not published a worked solution for this question. Nothing here substitutes for one — use the tags above to find the topic to revise instead.

      Revise: Substitution, transposition and frequency analysis.

    • t04-q2

      Tutorial_4_Questions.pdf

      applydesign

      Q2: Doing it right — encryption

      Once you have decrypted enc.txt, you decide to do it properly this time.

      • Write code to read the decoded text file and encrypt it using AES-CBC.
      • Use the pycrypto library (docs).
      • You might need to pad your data. Why?
      • Choose a 128-bit key length.
      • Generate an IV using Crypto.Random.
      • Print the output as a hex-encoded string.
      • Write the output as binary to text.aes.
      # from os import lseek # (Unused import)
      from Crypto.Cipher import AES
      from Crypto.Random import get_random_bytes
      from Crypto.Util.Padding import pad
      
      def AES_Encrypt(inFileName, outFileName):
             # TODO: Generate random 16-byte key and IV
             key =
             iv =
      
             cipher = AES.new(key, AES.MODE_CBC, iv)
      
             # TODO: Load the input file
             with open(inFileName, 'rb') as text_file:
      
                    plaintext =
      
             # TODO: Perform the encryption
             ciphertext =
      
             # TODO: Store the encrypted file
             with open(outFileName, "wb") as cipher_file:
      
                    cipher_file.write(ciphertext)
      
             # TODO: Return the key and initialization vector (needed for decrypti
             return key, iv
      
      key, iv = AES_Encrypt("plain.txt", "testing.enc")
      • symmetric-crypto
      • cbc

      No solution released. The unit has not published a worked solution for this question. Nothing here substitutes for one — use the tags above to find the topic to revise instead.

      Revise: Modes of operation.

    • t04-q3

      Tutorial_4_Questions.pdf

      applydesign

      Q3: Doing it right — decryption

      Write a code snippet to check whether you can decrypt the file you just encrypted.

      from Crypto.Util.Padding import unpad
      
      def AES_Decrypt(key, iv, inFileName):
      
             # TODO: Load the encrypted file
             # TODO: Perform the decryption
      
                 print(plaintext)
      
          AES_Decrypt(key, iv, "testing.enc")
      • symmetric-crypto
      • cbc

      No solution released. The unit has not published a worked solution for this question. Nothing here substitutes for one — use the tags above to find the topic to revise instead.

      Revise: Modes of operation.

    • t04-q4

      Tutorial_4_Questions.pdf

      analyseconceptual

      Q4: Ciphertext tampering in CBC mode

      AES in CBC (Cipher Block Chaining) mode links each ciphertext block to the previous one. See the given figure below.

      The figure did not survive extraction from the source PDF.

      Now consider the following:

      You have a ciphertext encrypted using AES-CBC. One byte of a ciphertext block is corrupted (e.g., flipped or modified).

      Questions:

      1. What will happen to the decryption of the corrupted block?
      2. What will happen to the next block during decryption?
      3. Will the remaining blocks (after the next one) decrypt correctly or incorrectly?

      Why?

      • block-modes
      • cbc

      No solution released. The unit has not published a worked solution for this question. Nothing here substitutes for one — use the tags above to find the topic to revise instead.

      Revise: Modes of operation.