Cyberlium

Python › Module 1 › Lesson 2

BeginnerModule 1Lesson 2/5

Variables, Loops, Functions

Build the syntax you need for scanners and parsers: variables, loops, and reusable functions

15 min+21 XP3 quiz
Module progress2 of 5
Variables · Loops · Functions

Opening

Every parser and every scanner is a loop over stuff. Copy-paste twenty times is how bugs go to production.

Lesson 1 gave you why Python exists in a SOC: readable glue, stdlib batteries, authorized files. This lesson is the grammar those tools are made of. A log hunter is “for each line, if needle in line.” A port check is “for each number in a list, do one action.” A helper is “def name(args): return something.” If you only memorize keywords, you will still paste print("Checking 22") twenty times, miss 443, and ship a script that cannot be reviewed. Variables, loops, and functions are how one idea scales without multiplying mistakes. The example in this lesson is deliberately quiet: a fictional list of ports, printed, not connected. Printing is not a scan. socket.connect comes later, and only against localhost or a lab you own. You will practice in $HOME/cyberlium-lab on YOUR machine. You will not loop a wordlist against anyone’s login form. You will not paste twenty slightly different blocks “just this once.” Functions exist so “just this once” dies in review.

1. Variables and types you will actually store: str, int, list

A variable is a name bound to a value. host = "127.0.0.1" is a str — text, even when it looks like an address. port = 22 is an int — arithmetic and comparisons work; "22" as a string would not sort or increment the way you think. timeout = 1.0 is a float you are not using yet; is_lab = True is a bool for a guard you should keep. Python does not make you declare types up front, which is speed of writing; it also will not stop you from concatenating a string to an int and exploding at runtime. For security scripts, name the type in the name or in a comment until it is muscle: target_host, port_number, line_text. Future you is on-call.

A list is an ordered collection: ports = [22, 80, 443] or lines you will load from a file next lesson. Lists are how “one” becomes “many” without twenty variables named port1, port2, port3. Indexing starts at 0; you rarely need indexes when a for loop gives you the item. Mixing types in one list (22, "http", None) is legal and almost always a future bug in a parser. Keep port lists as ints. Keep log lines as str. Convert on the boundary with int() or str() when you must, and fail loudly if the conversion is nonsense — a lab file you wrote should not contain surprise types, but production logs will.

2. for loops: lines and ports conceptually — print is not a packet

for item in collection: repeats an indented block once per element. for port in ports: print("Lab list only:", port) is the entire mechanism of a later checker, minus the dangerous part. The colon opens a block; indentation is the block. Forget the indent and Python SyntaxError’s you; mix tabs and spaces and you get a fight. The loop variable is just a name: port, line, url. Each pass rebinds it. After the loop, it still holds the last value — do not rely on that as a feature. while condition: exists for “until a flag”; for security beginners, for-each over a list you already have is the safer default because it cannot spin forever unless the list is huge.

Conceptually you will loop log lines the same way: for line in f: if needle in line. The file object is iterable; you do not load a gigabyte into RAM to hunt a substring. That pattern is the next lesson. This lesson’s port list is fiction you typed: common service numbers as data, not a promise that those services are running, not a SYN to a stranger. If you add socket.create_connection inside the loop “to see,” you changed the lesson into a scan. Do not. Printing teaches iteration. Connecting needs permission, a timeout, and a target you own — Module 2.

3. def functions: one job, a name, a return — why twenty copies fail

def banner(name): starts a function. The indented body runs only when you call banner("lab-target"). return hands a value back; without return, the function yields None. Parameters are local names: the caller’s variable is not rewritten unless you mutate a list in place (avoid that until you mean it). A function is a contract: inputs in, result or side effect out, name that tells the truth. label_host(host: str) -> str that returns f"[lab] host={host}" is reviewable. x(a) that sometimes prints and sometimes connects is how accidental scans happen when someone reuses a helper.

Copy-paste fails on a delay. You duplicate a five-line check for port 22, then 80, then 443. Next week you add a timeout to the first copy and forget the others. A reviewer cannot see the difference in a diff full of clones. A bug in the pasted block is now N bugs. The fix is not “be more careful.” The fix is one function and one loop: for port in ports: print(label_port(port)). When the label format changes, you change one return. When the list grows, you append a number — you do not clone a function. That is the same reason Lesson 1 preferred readable scripts: the unit of review is a named behavior, not a wallpaper of near-duplicates.

4. Names, main, and keeping the dangerous verb out of the loop until you mean it

target_host beats x. failed_logins beats data2. ports_lab_only beats stuff. Honest names are how a teammate sees that this list is not a live inventory. A small main() that only prints, behind if __name__ == "__main__":, keeps import from running the loop as a side effect later. You do not need that guard to pass this lesson, but you should know it exists so a future import of your helper does not spray stdout — or worse, connect. Comments that say “NO SOCKET YET” are not cute; they are a scope comment a tired you will obey. Put the script under $HOME/cyberlium-lab. chmod 600 if the file might later grow hostnames or findings you do not want a shared Windows account to read.

5. Wrong vs right: twenty pasted prints vs one loop, and print vs a live scan

Worked failure — “I will just duplicate the block.” Right is types, for-each, def, print-only on a fictional list.

  • Wrong

    print("Checking 22"); print("Checking 80"); … twenty times, then add socket.create_connection to a company IP “because it is a loop lesson.” Store the target as x. Mix "22" and 22 in one list. Loop a password wordlist against a login you do not own. Skip the lab folder. Treat a printed port as proof the port is open. Copy-paste the function body instead of calling it. That is how scope and bugs explode.

  • Right

    host as str, port as int, ports as a list of ints. for port in ports: print a lab-only line. def a tiny label function and call it from the loop. Run only on YOUR machine under $HOME/cyberlium-lab. chmod 600 the script if it may hold sensitive notes later. No connects. Next lesson is File Handling — with open() on files you created, still not /etc/shadow.

6. Practical: one list, one loop, one function — print fictional ports only

Write ports_lab.py in the lab directory. The list is teaching data: 22, 80, 443 as numbers people recognize, not a scan plan. The function returns a string; the loop prints it. Confirm python/python3 still runs. If you want a second loop, iterate a tiny list of fictional log-line strings you typed in the same file — still print, still your data. Do not import socket in this file. Do not read /etc/passwd to “make a user list.”

Command guide

ports_lab.py — variables, for, def; print only, no connect

DEFENSIVE syntax lab. YOUR machine. $HOME/cyberlium-lab. Printing a port number is NOT a scan. Do not add socket.connect here.

Command — copy this

mkdir -p "$HOME/cyberlium-lab"
cd "$HOME/cyberlium-lab"

Command — copy this

cat > ports_lab.py << 'EOF'
from pathlib import Path

lab = Path.home() / "cyberlium-lab"
lab.mkdir(parents=True, exist_ok=True)

host = "127.0.0.1"  # str — identity of MY loopback, not a target list
timeout_seconds = 1.0  # unused on purpose: no connect in this lesson
is_lab = True
ports = [22, 80, 443]  # ints in a list — fictional service numbers
sample_lines = [
    "sshd: Failed password for labuser from 10.0.0.5 (FICTIONAL)",
    "sshd: Accepted password for alice from 10.0.0.9 (FICTIONAL)",
]


def label_port(port: int) -> str:
    return f"[lab-list-only] would_check host={host} port={port} live_scan=NO"


def label_line(line: str) -> str:
    return f"[lab-line] {line}"


def main() -> None:
    if not is_lab:
        raise SystemExit("refusing to run: is_lab is False")
    print("types:", type(host).__name__, type(ports[0]).__name__, type(ports).__name__)
    for port in ports:
        print(label_port(port))
    needle = "Failed password"
    for line in sample_lines:
        if needle in line:
            print(label_line(line))
    # NEVER: socket.create_connection((host, port))
    # NEVER: copy-paste label_port twenty times


if __name__ == "__main__":
    main()
EOF

Command — copy this

python ports_lab.py || python3 ports_lab.py
chmod 600 "$HOME/cyberlium-lab/ports_lab.py"

Windows without chmod: WSL/Git Bash, or restrict the file in your profile.

NEVER: connect, scan, or brute-force NEVER: loop wordlists against logins you do not own NEVER: put live passwords in this file

Mission: ports_lab.py — loop + function, print only (mode 600)

1) Bind a str host, an int port or list of ints, and a list. Explain why "22" is not the same as 22. 2) Write def label_port (or similar) and for port in ports: print the label. No socket connect. 3) Save under $HOME/cyberlium-lab, run it, chmod 600. Optional: loop fictional log strings with if needle in line. Write one sentence: copy-paste twenty times fails because ___.

Stuck? Ask Cyberlium AI Mentor

If “a loop over ports is already a scanner” still feels true, ask for a hint — not a connect snippet. Try: "Hint only: why is for port in ports: print(...) not a scan, why do str vs int matter, and why does wrapping the label in def beat copying the print twenty times?" You still write ports_lab.py. No live targets. No wordlist attacks.

You can store str, int, and list, iterate with for, and reuse logic with def so the twentieth port is an append not a clone. Printing a fictional list is still not a scan. Next — File Handling — you will replace the tiny sample_lines list with with open() and pathlib on logs and practice wordlists you wrote yourself, only under $HOME/cyberlium-lab, never /etc/shadow or anyone else’s files.

Knowledge Check

1

APPLY: You need to “check” 22, 80, and 443 in a beginner script. A classmate pastes three nearly identical print blocks and adds socket.create_connection to a university IP. What is the right mechanism?

Multiple choice

Knowledge Check

2

APPLY: In host = "127.0.0.1" and port = 22, what are the types, and why does copy-pasting the check twenty times fail as the list grows?

Multiple choice

Knowledge Check

3

APPLY: True or False: for port in ports: print(port) against a list you typed is already an unauthorized scan, so you should practice instead by looping a wordlist at a live login page.

True or False

← Previous

Answer all 3 knowledge checks to continue. (0/3 answered)