Differences

This shows you the differences between two versions of the page.

Link to this comparison view

isc:labs:03 [2024/10/19 19:07]
florin.stancu removed
isc:labs:03 [2025/01/23 12:36] (current)
radu.mantu [03. [15p] Password Hashing]
Line 1: Line 1:
-/* ~~SHOWSOLUTION~~ */ +===== Lab 03 - Authentication in Linux =====
- +
-====== Lab 03  Hardware Security ======+
  
 ===== Objectives ===== ===== Objectives =====
-  * Side Channel Attacks + 
-  * Hardware Security Basics +  * Hashing 
-  * OpenSSL / PKCS#11 & #15 tools +  * Linux PAM 
-  * HSMs: Java Card & Simulator+  * Multi-Factor Authentication
  
 ===== Preparation ===== ===== Preparation =====
  
-You may use the UPB's [[https://​cloud.grid.pub.ro|OpenStack cloud to instantiate a Virtual Machine]] to be used for this lab! +You may use the UPB's [[https://​cloud.grid.pub.ro|OpenStack]] cloud to instantiate a Virtual Machine. 
-[[:​isc:​info:​virtualmachine|Read these instructions if you wanna know how!]].+Read these [[:​isc:​info:​virtualmachine|instructions]] if you wanna know how!
  
 ===== Overview ===== ===== Overview =====
  
-==== Basics ====+Hashing is the process of converting data — text, numbers, files, or anything, really — into a fixed-length string of letters and numbers. Data is converted into these fixed-length strings, or hash values, by using a special algorithm called a hash function.
  
-For a long time, hardware had a central role in computer security. Take, for example, the CPU's protection rings model (on x86): they realize ​privilege separation between ​hypervisor / Operating System kernel and the user applications and is enforced at hardware-level for efficiency.+Linux Pluggable Authentication Modules ​(PAMis suite of libraries that allow Linux system administrator to configure methods to authenticate users.
  
-Nowadays, the security requirements of certain applications has led to the implementation of additional access control or cryptographic functions directly into the hardware, e.g., AES-NI / SHA SSE-based instructions,​ the Trusted Platform Module cryptoprocessor,​ Smart Cards or Trusted Execution Environments (ARM TrustZone, Intel SGX, memory encryption etc.).+"​pam_exec"​ is a PAM module that can be used to run an external command.
  
-On different note, hardware is also susceptible ​to security bugs: side channel attacks, cryptographic vulnerabilities (e.g., cache or timing attacks or the much recent Spectre / Meltdown speculative execution bugs)hardcoded credentials ​or even manufacturer-introduced backdoors. +Multi-factor authentication (MFA) is multi-step account login process that requires users to enter more information than just a passwordFor examplealong with the password, users might be asked to enter a code sent to their email, answer a secret question, or scan a fingerprintA second form of authentication can help prevent unauthorized account access if a system password has been compromised.
-These are very difficult (or even impossible) to fix without re-designing the chip and replacing the faulty products.+
  
-==== Side Channel Attacks ​====+===== Tasks =====
  
-A side-channel attack is a type of cyber-attack that targets ​the unintended //side effects// of a software application / hardware component in a computer systemrather than attacking it directly. These side effects may include signals or data that are generated by the system'​s physical components, such as its power consumption,​ electromagnetic emissions, or even sound.+In the current security labwe'll explore ​the fundamentals of Linux authentication.
  
-By analyzing these side effects, an attacker can gain information about the system's operations, such as encryption keys or other sensitive data, without directly accessing ​the systemSide-channel attacks can be executed remotely or locally ​and are often used to target cryptographic systems that use secret keys.+We'll start by examining how the OS performs hashingFirst, we'll crack it and then we'll replicate it using Python.
  
-==== Java Smart Cards ====+Then, we'll utilize the "​pam_exec"​ module to set up a custom Python script for user authentication. By exploiting a weak password vulnerability,​ we will be able to perform a successful login.
  
-Java smart cards are small electronic devices that have an embedded microprocessor and memorywhich can be programmed ​with Java Card technology to perform secure transactions and store sensitive information. Java Card technology is a subset of Java that has been designed specifically for smart card environments.+Finallywe'll enhance security measures by implementing Multi-Factor Authentication (MFA) with Google Authenticator.
  
-Java smart cards can be found in various forms, such as SIM cards in mobile phones, banking cards, e-passports,​ and employee ID cards. They can be purchased from smart card manufacturers or vendors, or provided by organizations to their customers or employees.+==== 01[5p] Setup ====
  
-To program and manage Java smart cards, specialized software development kits and tools are needed, such as Java Card Development Kit (JCDK), Global Platform Card Specification,​ and Smart Card Integrated Development Environment (IDE). Developers can use these tools to create and test Java Card applets that run on the smart cards and perform various secure operations.+We use Docker:
  
-===== Tasks =====+<code bash>
  
-==== [40p] 1Python timing side channel attack ==== +docker pull ghcr.io/​cs-pub-ro/​isc-auth-pam:​latest 
- +mkdir ~/auth-lab 
-Download the {{isc:labs:lab03-sidechannel.zip|side channel demo}} archive here+docker run --rm --name auth-lab -v $(pwd)/​auth-lab/​:/​home/​hacker/​auth-lab -it ghcr.io/​cs-pub-ro/​isc-auth-pam 
- +</​code>​
-Implement the TODOs and crack the password via a timing attack ;)+
  
 <​note>​ <​note>​
-Hint: you have LunarVim installed on the VM, use ''​lvim ${file}'' ​to start it!+The ''​~/auth-lab'' ​folder is used as persistent volume so you won't lose + sync your work inside the container!
 </​note>​ </​note>​
 +
 +Download the {{:​isc:​labs:​lab03-pam.zip|lab archive}} inside the ''​~/​auth-lab''​ directory and unzip it.
 +
 +Analyse the users and groups on the system. What user are we interested in?
  
 <​solution -hidden> <​solution -hidden>
 <​code>​ <​code>​
 +
 +User "​dani.mocanu",​ member of group "​top_10_manelisti"​.
 +
 +</​code>​
 +</​solution>​
 +
 +
 +==== 02. [15p] Password Cracking ====
 +
 +Use John the Ripper to crack our user's password. You need to run it as super user on the file that contains the passwords.
 +
 +We want to use the default mode "​wordlist"​ with the default location ''/​usr/​share/​john/​password.lst''​.
 +
 +If you are getting an error "No password hashes loaded (see FAQ)", you need to specify the format of the hash. It is named the same as the Linux password hashing function ;)
 +
 +**References:​**
 +  * https://​www.openwall.com/​john/​doc/​OPTIONS.shtml
 +
 +<​solution -hidden>
 +<​code>​
 +
 +$ sudo john /etc/shadow --format=crypt
 +dani.mocanu:​snoopdog
 +
 +</​code>​
 +</​solution>​
 +
 +==== 03. [15p] Password Hashing ====
 +
 +Fill in the TODOs in ''​gen_hash.py''​ to generate a new password hash. Using your super user privileges, manually overwrite the old password of our user.
 +
 +Test if you were successful by trying to log in (''​su''​) using the new password.
 +
 +<note warning>
 +**crypt** was deprecated in PEP 594. See [[https://​docs.python.org/​3/​library/​crypt.html|this]] for alternatives. **legacycrypt** works.</​note>​
 +
 +<​solution -hidden>
 +<​code>​
 +
 TODO(1): TODO(1):
-CHARACTERS ​ascii_letters + digits + '{}'+password ​= 'parolanoua'
  
 TODO(2): TODO(2):
-CHECK_PASS_LEN_VULN ​check_password1 +salt crypt.mksalt(crypt.METHOD_SHA512)
-CHECK_PASS_CHARS_VULN = check_password2+
  
 TODO(3): TODO(3):
-for length in range(20) +hash = crypt.crypt(password, salt)
- +
-TODO(4): +
-if CHECK_PASS_LEN_VULN(np):​ +
-    return np+
  
-TODO(5): 
-duration = timed_check_pass(CHECK_PASS_CHARS_VULN,​ pad(np, length)) 
 </​code>​ </​code>​
 </​solution>​ </​solution>​
  
-==== [30p2. Using OpenSSL ​====+==== 04. [5pAccount Locking ​====
  
-You are tasked with encrypting ​large file using RSA. +As we want to use Python script to log in to the user account we worked on so far, you need to delete ​and lock its password so that password-based authentication is disabled for this user account.
-For this, you will need to use both symmetric ​and asymmetric crypto, and all of this can be done using one swiss-army-knife-like tool ''​openssl''​! ​+
  
-  ​For starters, we need a file to encrypt; let's make a backup of ''/​etc/''​<​code>​ +**Reference:** 
-sudo tar czf etc-backup-2023.tar.gz -C /etc . +  * https://​linux.die.net/man/1/passwd
-</code>+
  
-  * We will use a symmetric cipher to encrypt the file using an ephemeral (random) key (which we will then encrypt using RSA): <code> +<solution -hidden
-# generate the random passphrase +<​code>​ 
-dd if=/​dev/​urandom of=.mysecretpass bs=20 count=1 + 
-</code> +sudo passwd ​-dl dani.mocanu
-  * Encrypt the file using ''​openssl enc -aes-256-cbc -e''​ and the password generated earlier from file; +
-  * **Hint**: ''​openssl enc %%--%%help''​ (you can specify password from file using ''​-pass file:<​path-to-passphrase-file>''​). +
-<note info> +
-Almost all ''​openssl''​ operations take their options using a single dash (''​-''​);​ all subcommand may take input from a file specified by ''​-in <​file-path>''​ and may output their results to a file specified by ''​-out <​file-path>''​! +
-</​note>​ +
-<​note>​ +
-OpenSSL'​s symmetric encryption can use PBKDF2 to derive a key from an arbitrary password (salted, by default). +
-It also chooses a secure IV for the first block automatically,​ so we don't have to do anything else (but we could: using the ''​-K''​ and ''​-iv''​ arguments). +
-</​note>​+
  
-  * Generate a RSA key pair: <​code>​ 
-openssl genrsa -out <specify your filename>​ 
 </​code>​ </​code>​
-  * Export the public key to a separate file (**hint:** use ''​openssl rsa -pubout''​ -- ofc, ''​%%--%%help''​ to see the syntax!); +</solution
-  * Encrypt the AES passphrase file (then delete the original!): ​<code+ 
-openssl pkeyutl -in <​path-to-passphrase-file>​ -out backup-secret.enc -pubin -inkey <​your-public-key>​ -encrypt +==== 05[10p] Linux PAM ==== 
-rm -f <path-to-passphrase-file>​ + 
-</​code>​ +We wish to write our own authentication module in Python. 
-  * __**some time later...**__ + 
-  * Oh noesour system crashed and we lost all configuration! Fortunately,​ we have the encrypted backup file! First, you need to decrypt the AES passphrase using your private RSA key! Use ''​openssl pkeyutl''​ with ''​-decrypt''​ (note: ''​-inkey'' ​must point to your private key generated above!)+For this, we must modify ​the ''​common-auth'' ​Linux PAM configuration file (look in ''​/etc/pam.d''​) ​and add this as the **first** rule: <​code>​ 
-  ​Once you obtain the AES passphrase, decrypt the backup file and test it: <​code>​ +auth sufficient pam_exec.so expose_authtok stdout quiet <path-to-auth.py>
-openssl enc -aes-256-cbc -d -pbkdf2 -in etc-backup-encrypted.bin -pass file:<decrypted-passfile> ​-out backup-decrypted.tar.gz +
-# test whether the resulting .tar.gz archive is valid? +
-file backup-decrypted.tar.gz+
 </​code>​ </​code>​
  
-<​solution -hidden><​code>​ +As we want to use the script to authenticate only our special user, and because it will be called for all users, it should be //sufficient// to authenticate using it, but not //​required//​.
-# generate ​the random passphrase +
-dd if=/dev/urandom of=.mysecretpass bs=20 count=1 +
-# use openssl enc: +
-openssl enc -aes-256-cbc -e -pass file:​.mysecretpass -pbkdf2 -in etc-backup-2023.tar.gz -out etc-backup-encrypted.bin +
-# generate RSA key pair +
-openssl genrsa -out key.pem +
-openssl rsa -in key.pem -pubout -out key.pub +
-# encrypt the AES password file: +
-openssl pkeyutl -in .mysecretpass -out backup-secret.enc -pubin -inkey key.pub -encrypt +
-# decrypt the AES password file +
-openssl pkeyutl -in backup-secret.enc -out .decrypted-passfile -inkey key.pem -decrypt +
-# decrypt the archive +
-openssl enc -aes-256-cbc -d -pbkdf2 -in etc-backup-encrypted.bin -pass file:​.decrypted-passfile -out backup-decrypted.tar.gz +
-# test whether the resulting .tar.gz archive is valid? +
-file backup-decrypted.tar.gz +
-</code><​/solution>​+
  
-==== [30p] 3. Java Card Simulator ====+In order for authentication to be successfully performed using Python, we need features, which can be achieved by passing the proper options to the ''​pam_exec.so''​ module: 
 +  * perform reads in the Python script, 
 +  * perform prints from the Python script, 
 +  * do not print anything else.
  
-{{ :​isc:​labs:​lab03.png?​direct&​600 |}}+<note tip> 
 +If you need to debug your //auth.py// script, delete the ''​quiet''​ parameter from the PAM config. Now, your stdout and stderr are no longer suppressed.
  
-In order to simulate a Java Card, we must install all required Java components (Oracle JavaCard SDKs, jCardSim, IsoApplet, VSmartCard).+----
  
-**Note**: you don't need to do this on the ISC VM 2023, it's already been setup!+The PAM module may return different error codes depending on the nature of the error. Use the **errno** tool to check their meaning.
  
-<​spoiler>​ 
-  * Download the [[https://​github.com/​martinpaljak/​oracle_javacard_sdks|Oracle JavaCard SDKs]]: 
-<code bash> 
-git clone https://​github.com/​martinpaljak/​oracle_javacard_sdks.git "​$HOME/​oracle_javacard_sdks"​ 
-export JC_HOME="​$HOME/​oracle_javacard_sdks/​jc222_kit"​ 
-export JC_CLASSIC_HOME="​$HOME/​oracle_javacard_sdks/​jc305u3_kit"​ 
-</​code>​ 
-  * Since we don't have a physical smart card, we need a simulator: download & install [[https://​github.com/​arekinath/​jcardsim|JCardSim]]:​ 
-<code bash> 
-git clone https://​github.com/​arekinath/​jcardsim.git "​$HOME/​jcardsim"​ 
-cd "​$HOME/​jcardsim";​ mvn initialize && mvn clean install 
-</​code>​ 
-  * We now need an applet to install on our (emulated) Smart Card. [[https://​github.com/​philipWendland/​IsoApplet|IsoApplet]] is an open source applet implementing Public Key cryptography operations (with OpenSC integration):​ 
 <code bash> <code bash>
-git clone -b main-javacard-v2.2.2 https://​github.com/​philipWendland/​IsoApplet.git "$HOME/​IsoApplet"​ +apt install ​errno 
-cd "​$HOME/​IsoApplet"​ +errno 13 
-install ​dependencies (fortunately,​ this project uses git submodules) +    ​EACCES 13 Permission denied
-git submodule init && git submodule update +
-# unfortunately,​ jCardSim cannot simulate CAPs (compiled applet firmwares)... +
-# so we directly compile the Java files (make sure to have the card simulator'​s SDK in Java Path): +
-javac -classpath "$HOME/​jcardsim/​target/​jcardsim-3.0.5-SNAPSHOT.jar"​ "​$HOME/​IsoApplet/​src/​xyz/​wendland/​javacard/​pki/​isoapplet/"​*.java +
-</​code>​ +
-  * Finally, note that we need a Card Reader to interface with the smart cards. So, with our simulated card, we will have to use a virtual card reader software (''​vpcd''​ from [[https://​frankmorgner.github.io/​vsmartcard/​virtualsmartcard/​README.html|vsmartcard]]):​ +
-<code bash> +
-git clone https://​github.com/​frankmorgner/​vsmartcard.git "​$HOME/​vsmartcard"​ +
-cd "​$HOME/​vsmartcard/​virtualsmartcard"​ +
-autoreconf -vis && ./configure && sudo make install +
-# Restart PCSC daemon to load our new vcard driver +
-sudo systemctl restart pcscd +
-cd ~  # go back to home+
 </​code>​ </​code>​
  
-Finally, we must create a configuration file for ''​jcardsim''':​+----
  
 +Remember that ''​^?''​ sequences (where ''?''​ certain upper-case letters) represents a control character. If you get an error that contains such a sequence, take a look at the hexdump.
 +</​note>​
 +
 +**References:​**
 +  * https://​linux.die.net/​man/​5/​pam.d
 +  * https://​man7.org/​linux/​man-pages/​man8/​pam_exec.8.html
 +
 +<​solution -hidden>
 <​code>​ <​code>​
-$ cat $HOME/jcardsim.cfg  # <-yes, create this file ! +$ cat /etc/pam.d/common-auth 
-com.licel.jcardsim.card.applet.0.AID=F276A288BCFBA69D34F31001 +... 
-com.licel.jcardsim.card.applet.0.Class=xyz.wendland.javacard.pki.isoapplet.IsoApplet +# here are the local modules 
-com.licel.jcardsim.card.ATR=3B80800101 +auth sufficient pam_exec.so expose_authtok stdout quiet /​home/​hacker/​auth-lab/​auth.py 
-com.licel.jcardsim.vsmartcard.host=localhost +...
-com.licel.jcardsim.vsmartcard.port=35963+
 </​code>​ </​code>​
-</spoiler>​ +</solution>
-<​html><​br></​html>+
  
-**[Re]Start the PCSC service:​** +==== 06. [20pPython Script ====
-<code bash> +
-# the VM does not automatically start this, so do it manually +
-sudo systemctl restart pcscd +
-</​code>​+
  
-**Start ​the simulator:​**+Edit ''​auth.py''​ and fill in TODOs(6.*). 
 + 
 +When you're done, [[http://​crackstation.net|crack ​the new password hash]] and try to log in ;) 
  
-<code bash> 
-java -classpath "​$HOME/​jcardsim/​target/​jcardsim-3.0.5-SNAPSHOT.jar:​$HOME/​IsoApplet/​src"​ com.licel.jcardsim.remote.VSmartCard "​$HOME/​jcardsim.cfg"​ 
-</​code>​ 
 <note hint> <note hint>
-Hint: use separate terminals or ''​tmux''​, since the command is blocking and prints lots of debugging info, so it will not be a good idea to run it in background with ''​&''​ and reuse that terminal!+Note that the password hash is hardcoded in Python (you will also find the algorithm by cracking it!) without any salt. 
 +Once you figure it out, use Python'''​hashlib''​ to compute the input'''​user_hash''​ and compare them!
 </​note>​ </​note>​
  
-**Loading the Smart Card applet:**+<​solution -hidden>​ 
 +<​code>​ 
 +TODO(6.1): 
 +if "​top_10_manelisti"​ not in user_groups:​ 
 +    return False
  
-<​note>​ +TODO(6.2): 
-The APDU [[https://en.wikipedia.org/​wiki/​Smart_card_application_protocol_data_unit|Smart Card Application Protocol Data Unit]] is a communication protocol used for interfacing with smart cards, standardized in ISO/IEC 7816-4.+hash_object = hashlib.sha256() 
 +hash_object.update(user_password.encode("​utf-8")) 
 +user_hash = hash_object.hexdigest()
  
-We use an [[https://​jcardsim.org/​docs/​quick-start-guide-using-in-cli-mode|initial APDU script]] to install & execute the IsoApplet into the emulated smart card. +13412ffd6149204f40e546ffa9fbd7124b410198a6ba3924f788622b929c8eb2 ​---crackstation.net---> poochiedontsurf 
-</note>+</​code>​ 
 +</solution>
  
-<code bash> +==== 07. [30p] Multi-Factor Authentication ====
-# install IsoApplet usign a APDU script +
-opensc-tool --card-driver default --send-apdu 80b800001a0cf276a288bcfba69d34f310010cf276a288bcfba69d34f3100100 +
-opensc-tool -n+
  
-# create PKCS#15 structure on our smart card (also set a PIN and a PUKfor security purposes) +Finallylet's add two-factor authentication ​to our script!
-pkcs15-init --create-pkcs15 --so-pin 123456 --so-puk 0123456789abcdef +
-# generate an RSA key pair to use for signing (note: auth-id is a PIN slot) +
-pkcs15-init --generate-key rsa/​2048 ​ --id 1 --key-usage decrypt,​sign --label MyRSAKey --auth-id FF --pin 123456 +
-# download the generated public key to your machine +
-pkcs15-tool --read-public-key "​1"​ --output "​smartcard-pubkey.pem"​+
  
-echo "Sunt de acord să cedez toată averea mea asistenților de ISCAdevăraaat\!"​ > textToSign.txt +Edit ''​setup_mfa.py'',​ input a 32-byte secret and print its QR enrollment code to the console
-openssl dgst -engine pkcs11 -sign "​pkcs11:object=MyRSAKey;​type=private;​pin-value=123456"​ -keyform ENGINE -sha256 -out textSignature.sig textToSign.txt+After this, run the script and enroll the key inside a [[https://www.rfc-editor.org/​rfc/​rfc6238|RFC 6238 (TOTP)]] compatible (e.g.: Google Authenticator,​ TOTP Authenticator,​ KeePassXC etc.).
  
-# now everyone can check whether ​the document is correctly signed using the public ​key+Solve the remaining TODOs(7.*) in ''​auth.py''​ to integrate your MFA (note: ​the TOTP's secret ​key should be the same!).
-openssl dgst -sha256 -verify smartcard-pubkey.pem -keyform PEM -signature textSignature.sig textToSign.txt +
-# modificați fișierul textToSign.txt și re-verificați semnătura digitală..ce se întâmplă?​ +
-</​code>​+
  
-<​note ​important+<​note>​ 
-Unfortunately,​ jCardSim is unstable and will crash after some short timeout ​(approx. several minutes), so make sure you run these commands ​with little pause between them (make a script). +**Hint / workaround:​** PAM only gives you one ''​input()'' ​with the user-typed password ​(then closes stdin, any following reads will get EOF). 
-If it crashedyou must restart ​both the ''​pcscd''​ service ​and the simulator: <code+Soin order to read both a password ​and a numeric TOTP code, you must read them all at once (use whatever convention you desire, e.g.: password then 6-digit code either concatenated or split by space, then parse/extract it in Python). 
-killall java + 
-sudo systemctl restart pcscd +Custom ''​.so''​ PAM plugins have no such limitations (but you must then write them in a system programming language).
-java -classpath "​$HOME/​jcardsim/​target/​jcardsim-3.0.5-SNAPSHOT.jar:$HOME/IsoApplet/​src"​ com.licel.jcardsim.remote.VSmartCard "​$HOME/​jcardsim.cfg"​ +
-</​code>​+
 </​note>​ </​note>​
  
-Finallyhere's one last challenge: use ''​openssl'' to encrypt / decrypt a file using this key!+<note important>​If you're getting an error (e.g., invalid padding) while scanning QR / verifying the TOTPmake sure ''​TOTP_SECRET'' ​is 32 bytes in length!</​note>​ 
 + 
 +**References:​** 
 +  * https://​pyauth.github.io/​pyotp/#​module-pyotp 
 +  * https://​pyqrcode.readthedocs.io
  
 <​solution -hidden> <​solution -hidden>
-<​code ​bash> +<​code>​
-openssl pkeyutl -encrypt -in textToSign.txt -pubin -inkey smartcard-pubkey.pem -out encrypted.enc +
-openssl pkeyutl -decrypt -in encrypted.enc -engine pkcs11 -keyform ENGINE -inkey "​pkcs11:​object=MyRSAKey;​type=private;​pin-value=123456"​ -out decrypted.txt +
-</​code>​ +
-</​solution>+
  
-Bonus[[https://​access.redhat.com/​articles/​1523343|You can configure OpenSSH to use a private key stored inside a smart card for authentication]]!+setup_mfa.py:
  
-==== 3Feedback ​====+TODO(1): 
 +TOTP_SECRET ​"​NeverGonnaGiveYouUpNeverGonnaLet"​ 
 + 
 +TODO(2): 
 +qr_code ​pyqrcode.create(totp_auth) 
 +print(qr_code.terminal(quiet_zone=1)) 
 + 
 +auth.py: 
 + 
 +TODO(7.1):​ 
 +TOTP_SECRET ​"​NeverGonnaGiveYouUpNeverGonnaLet"​ 
 + 
 +TODO(7.2): 
 +user_password ​user_secret[:​-6] 
 +user_totp ​user_secret[-6:​] 
 + 
 +TODO(7.3):​ 
 +totp pyotp.TOTP(TOTP_SECRET) 
 +totp_correct ​totp.verify(user_totp) 
 + 
 +</​code>​ 
 +</​solution>​
  
-Please take a minute to fill in the [[https://​forms.gle/​5Lu1mFa63zptk2ox9|feedback form]] for this lab.+===== Feedback =====
  
 +Please take a minute to fill in the [[https://​docs.google.com/​forms/​d/​e/​1FAIpQLSeMrKoWY6UKe1N_BASUARA-HixTuvSfrEnx_FKstT-RW464NQ/​viewform |feedback form]] for this lab.
  
isc/labs/03.1729354032.txt.gz · Last modified: 2024/10/19 19:07 by florin.stancu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0