Curriculum Lattes ORCID SciProfiles Scopus Web of Science substationworm LFFreitasGutierres

📝 Tasks

  • 1️⃣ Verify the IP address of the otlab-student workstation. OTLab06{XXX.XXX.X2.XXX}
  • 2️⃣ Discover the IP addresses of active hosts within the same subnet as identified in the previous answer. OTLab06{XXX.XXX.XX.2X, XXX.XXX.XX.XX, XXX.XXX.XX.XXX}
  • 3️⃣ Determine which ports are open on the IP address matching the format XXX.XXX.XX.2X, identified in the previous question. OTLab06{XXXX, XXXX, XXXX, XXXXX, XXXXX}
  • 4️⃣ Identify the MAC address of the active host corresponding to the IP address matching the format XXX.XXX.XX.2X from question 2. OTLab06{XX:XX:XX:XX:XX:XX}
  • 5️⃣ Locate the hidden flag on a web interface exposed by the active host mentioned in question 3.
    • Use curl. No further hints are provided.
  • 6️⃣ Which port is open on the IP address matching the format XXX.XXX.XX.XX from question 2? OTLab06{XXX}
  • 7️⃣ Determine the emulated base firmware version running on the active host referenced in the previous question. OTLab06{X.X.X.X}
  • 8️⃣ Two devices on different bridge-configured networks are communicating. What is the MAC address of the only active host on the other bridge segment? OTLab06{XX:XX:XX:XX:XX:XX}
  • 9️⃣ Two devices on different bridge-configured networks are communicating. What message is being transmitted? OTLab06{Xxxx xxx Xxxxxxxxxxx xx Xxxxxxxx xxx Xxxxxxxxxx xxx XX-XXX!}
  • 🔟 Which OID can be extracted through the SNMP service from an active host operating with an industrial communication protocol? OTLab06{(XXXX) X:XX:XX.XX}

🛠️ Tools

  • The following tools are available on the otlab-student workstation for completing OTLab 06: ifconfig, masscan, netdiscover, nmap, snmpwalk, plcscan, and tcpdump.

🔖 Nomenclature

  • IP: Internet Protocol.
  • MAC: Media Access Control.
  • OID: Object Identifier.
  • SNMP: Simple Network Management Protocol.
📝 RESOLUTION (view content)

      
#!/bin/bash

lab_name="OTLab06"
compose_file="${lab_name}.yml"

ot_container_name01="plc01-slave"
ot_container_name02="plc02-master"
ot_container_name03="plc03-scada"
ot_container_name04="plc04-s7"
ews_container_name="otlab-student"
kali_image="kalilinux/kali-rolling"
ubuntu_image="ubuntu:22.04"

lab_net01="plc01-net"
lab_net02="plc02-net"

# ----------------------------------------
# Function to display the banner
show_banner() {                       
    printf "\033[1;33m" # Yellow and bold
    echo " _____ _____ __        _   "
    echo "|     |_   _|  |   ___| |_ "
    echo "|  |  | | | |  |__| .'| . |"
    echo "|_____| |_| |_____|__,|___|"
    printf "\033[1;37m" # White and bold
    printf "Exercise:  06-Industrial Protocols and Web Interface Exposure\n"
    printf "Version:   1.1\n"
    printf "Author:    substationworm\n"
    printf "Contact:   in/lffreitas-gutierres\n"
    printf "\033[0m" # Reset all styles
    echo ""
}

# ----------------------------------------
# Function to generate Docker Compose file
generate_compose_file() {
    local ews_image="$1"
    
    cat > "$compose_file" <<EOF
services:
  $ot_container_name01:
    image: $ubuntu_image
    container_name: $ot_container_name01
    hostname: $ot_container_name01
    mac_address: 00:00:23:3C:A1:01
    networks:
      $lab_net01:
        ipv4_address: 192.168.11.201
    cap_add:
      - NET_ADMIN
      - NET_RAW
    privileged: true
    command: >
      bash -c '
        apt update &&
        apt install -y python3 python3-pip iproute2 &&
        pip install pymodbus &&
        ip route add 192.168.12.0/24 via 192.168.11.200 &&
        printf "%s\n" \
"from pymodbus.server import StartTcpServer" \
"from pymodbus.datastore import ModbusDeviceContext, ModbusServerContext, ModbusSequentialDataBlock" \
"store = ModbusDeviceContext(hr=ModbusSequentialDataBlock(0, [0]*200))" \
"context = ModbusServerContext(devices=store, single=True)" \
"StartTcpServer(context, address=(\\"0.0.0.0\\", 502))" \
> /server.py &&
        python3 /server.py'
    ports:
      - "502:502"

  $ot_container_name02:
    image: $ubuntu_image
    container_name: $ot_container_name02
    hostname: $ot_container_name02
    mac_address: 00:00:23:A8:E2:77
    networks:
      $lab_net02:
        ipv4_address: 192.168.12.202
    cap_add:
      - NET_ADMIN
      - NET_RAW
    privileged: true
    command: >
      bash -c '
        apt update &&
        apt install -y python3 python3-pip netcat iproute2 &&
        pip install pymodbus &&
        ip route replace default via 192.168.12.200 &&
        printf "%s\n" \
"from pymodbus.client import ModbusTcpClient" \
"import time" \
"frames = [" \
"    [84, 104, 105, 115, 32, 119, 97, 115, 32, 70, 117, 110, 100, 97, 109, 101]," \
"    [110, 116, 97, 108, 115, 32, 111, 102, 32, 78, 101, 116, 119, 111, 114, 107]," \
"    [115, 32, 97, 110, 100, 32, 80, 114, 111, 116, 111, 99, 111, 108, 115, 32]," \
"    [102, 111, 114, 32, 79, 84, 45, 73, 67, 83, 33, 0, 0, 0, 0, 0]" \
"]" \
"client = ModbusTcpClient(\\"192.168.11.201\\", port=502)" \
"while True:" \
"    for i, frame in enumerate(frames):" \
"        result = client.write_registers(100, frame)" \
"        print(f\\"[CLIENT] Frame {i+1}: {frame} | Response: {result}\\")" \
"        time.sleep(60)" \
"client.close()" \
> /client.py &&
        echo "[CLIENT] Waiting for Modbus server. . ." &&
        until nc -z 192.168.11.201 502; do sleep 2; done &&
        echo "[CLIENT] Server is up. Starting client loop. . ." &&
        sleep 2 &&
        python3 /client.py'

  $ot_container_name03:
    image: honeynet/conpot:latest
    container_name: $ot_container_name03
    hostname: $ot_container_name03
    mac_address: 00:1D:9C:00:01:02
    networks:
      $lab_net02:
        ipv4_address: 192.168.12.20
    cap_add:
      - NET_ADMIN
      - NET_RAW
    privileged: true
    command: >
      sh -c '
        echo "<html><body><h1>OTLab06{LAPES-SCADA}</h1></body></html>" > /home/conpot/.local/lib/python3.6/site-packages/conpot-0.6.0-py3.6.egg/conpot/templates/default/http/htdocs/index.html &&
        /home/conpot/.local/bin/conpot -f --template default
      '

  $ot_container_name04:
    image: iotechsys/s7-sim
    container_name: $ot_container_name04
    hostname: $ot_container_name04 
    mac_address: 00:1C:06:33:B2:91
    networks:
      $lab_net02:
        ipv4_address: 192.168.12.30
    cap_add:
      - NET_ADMIN
      - NET_RAW
    ports:
      - "102:102"
    privileged: true

  $ews_container_name:
    image: $ews_image
    container_name: $ews_container_name
    hostname: $ews_container_name
    command: >
      bash -c '
        apt update && apt install -y python2 git ca-certificates iputils-ping nmap masscan net-tools netdiscover snmp tcpdump iproute2 procps curl &&
        sysctl -w net.ipv4.ip_forward=1 &&
        [ -d /opt/plcscan ] || git clone https://github.com/meeas/plcscan.git /opt/plcscan &&
        tail -f /dev/null
      '
    networks:
      $lab_net01:
        ipv4_address: 192.168.11.200
      $lab_net02:
        ipv4_address: 192.168.12.200
    privileged: true

networks:
  $lab_net01:
    name: $lab_net01
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.11.0/24

  $lab_net02:
    name: $lab_net02
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.12.0/24
EOF
}

# ----------------------------------------
# System requirements check
check_requirements() {
    error_flag=0

    if ! command -v docker >/dev/null 2>&1; then
        printf "\033[31m[Error]\033[0m Docker is not installed on this system.\n"
        error_flag=1
    elif ! docker info >/dev/null 2>&1; then
        printf "\033[31m[Error]\033[0m Docker is installed, but not accessible.\n"
        error_flag=1
    fi

    if command -v docker-compose >/dev/null 2>&1; then
        DOCKER_COMPOSE_CMD="docker-compose"
    elif docker compose version >/dev/null 2>&1; then
        DOCKER_COMPOSE_CMD="docker compose"
    else
        printf "\033[31m[Error]\033[0m Docker Compose is not installed.\n"
        error_flag=1
    fi

    if [ "$error_flag" -eq 1 ]; then
        printf "\033[31m✘ The $lab_name system requirements check failed.\033[0m\n"
        exit 1
    fi
}

# ----------------------------------------
# Function to check if a container exists
container_exists() {
    docker ps -a --format '{{.Names}}' | grep -q "$1"
}

# ----------------------------------------
# Command handling
case "$1" in
    -start)
        show_banner
        distro="${2:-ubuntu}"

        if [[ "$distro" == "kali" ]]; then
            selected_image="$kali_image"
        elif [[ "$distro" == "ubuntu" ]]; then
            selected_image="$ubuntu_image"
        else
            printf "\033[31m[Error]\033[0m Invalid distro. Please use 'kali' or 'ubuntu'.\n"
            exit 1
        fi

        check_requirements
        printf "\033[1;33m[Working]\033[0m Starting $lab_name. . .\n"
        generate_compose_file "$selected_image"
        $DOCKER_COMPOSE_CMD -f "$compose_file" up -d
        if [ $? -eq 0 ]; then
            printf "\033[32m✔ $lab_name started.\033[0m\n"
        else
            printf "\033[31m✘ $lab_name failed to start.\033[0m\n"
            exit 1
        fi
        ;;
    -stop)
        show_banner
        if container_exists "$ot_container_name01" || container_exists "$ot_container_name02" || container_exists "$ews_container_name"; then
            check_requirements
            printf "\033[1;33m[Working]\033[0m Stopping $lab_name. . .\n"
            $DOCKER_COMPOSE_CMD -f "$compose_file" stop
            printf "\033[32m✔ $lab_name stopped.\033[0m\n"
        else
            printf "\033[34m[Information]\033[0m No containers to stop.\n"
            exit 1
        fi
        ;;
    -clean)
        show_banner
        if container_exists "$ot_container_name01" || container_exists "$ot_container_name02" || container_exists "$ews_container_name"; then
            check_requirements
            printf "\033[1;33m[Working]\033[0m Cleaning up all $lab_name resources. . .\n"
            $DOCKER_COMPOSE_CMD -f "$compose_file" down -v
            docker network rm "$lab_net01" "$lab_net02" 2>/dev/null
            rm -f "$compose_file"
            printf "\033[32m✔ All $lab_name resources removed.\033[0m\n"
        else
             printf "\033[34m[Information]\033[0m No containers found to clean.\n"
             exit 1
        fi
        ;;
    -run)
        show_banner
        if container_exists "$ews_container_name"; then
            check_requirements
            printf "\033[1;33m[Working]\033[0m Accessing $ews_container_name terminal. . .\n"
            docker exec -it "$ews_container_name" bash
        else
            printf "\033[31m[Error]\033[0m Container $ews_container_name not found.\n"
            exit 1
        fi
        ;;
    -restart)
        show_banner
        check_requirements
        if [ ! -f "$compose_file" ]; then
            printf "\033[31m[Error]\033[0m Cannot restart: $compose_file not found.\n"
            exit 1
        fi

        printf "\033[1;33m[Working]\033[0m Restarting $lab_name. . .\n"
        $DOCKER_COMPOSE_CMD -f "$compose_file" up -d
        if [ $? -eq 0 ]; then
            printf "\033[32m✔ $lab_name restarted.\033[0m\n"
        else
            printf "\033[31m✘ $lab_name failed to restart.\033[0m\n"
            exit 1
        fi
        ;;
    -status)
        show_banner
        check_requirements
        $DOCKER_COMPOSE_CMD -f "$compose_file" ps
        ;;
    *)
        show_banner
        echo "Usage: $0 -start [kali|ubuntu] | -stop | -clean | -run | -restart | -status"
        echo ""
        echo "  -start     Start the $lab_name environment using the specified distro (default: ubuntu)"
        echo "             Valid options: kali (rolling) or ubuntu (22.04)"
        echo "  -run       Open a terminal inside the $ews_container_name container"
        echo "  -clean     Remove containers, volumes, and networks"
        echo "  -stop      Stop all containers"
        echo "  -restart   Restart previously stopped containers"
        echo "  -status    Show current containers status"
        exit 1
        ;;
esac
      

    
📝 RESOLUTION OFFLINE (view content)

      
#!/bin/bash

lab_name="OTLab06"
compose_file="${lab_name}.yml"

ot_container_name01="plc01-slave"
ot_container_name02="plc02-master"
ot_container_name03="plc03-scada"
ot_container_name04="plc04-s7"
ews_container_name="otlab-student"
kali_image="ews-image-kali01"
ubuntu_image="ews-image-ubuntu01"

lab_net01="plc01-net"
lab_net02="plc02-net"

# ----------------------------------------
# Function to display the banner
show_banner() {                       
    printf "\033[1;33m" # Yellow and bold
    echo " _____ _____ __        _   "
    echo "|     |_   _|  |   ___| |_ "
    echo "|  |  | | | |  |__| .'| . |"
    echo "|_____| |_| |_____|__,|___|"
    printf "\033[1;37m" # White and bold
    printf "Exercise:  06-Industrial Protocols and Web Interface Exposure\n"
    printf "Version:   1.1-Offline\n"
    printf "Author:    substationworm\n"
    printf "Contact:   in/lffreitas-gutierres\n"
    printf "\033[0m" # Reset all styles
    echo ""
}

# ----------------------------------------
# Function to generate Docker Compose file
generate_compose_file() {
    local ews_image="$1"
    
    cat > "$compose_file" <<EOF
services:
  $ot_container_name01:
    image: $ubuntu_image
    container_name: $ot_container_name01
    hostname: $ot_container_name01
    mac_address: 00:00:23:3C:A1:01
    networks:
      $lab_net01:
        ipv4_address: 192.168.11.201
    cap_add:
      - NET_ADMIN
      - NET_RAW
    privileged: true
    command: >
      bash -c '
        ip route add 192.168.12.0/24 via 192.168.11.200 &&
        printf "%s\n" \
"from pymodbus.server import StartTcpServer" \
"from pymodbus.datastore import ModbusDeviceContext, ModbusServerContext, ModbusSequentialDataBlock" \
"store = ModbusDeviceContext(hr=ModbusSequentialDataBlock(0, [0]*200))" \
"context = ModbusServerContext(devices=store, single=True)" \
"StartTcpServer(context, address=(\\"0.0.0.0\\", 502))" \
> /server.py &&
        python3 /server.py'
    ports:
      - "502:502"

  $ot_container_name02:
    image: $ubuntu_image
    container_name: $ot_container_name02
    hostname: $ot_container_name02
    mac_address: 00:00:23:A8:E2:77
    networks:
      $lab_net02:
        ipv4_address: 192.168.12.202
    cap_add:
      - NET_ADMIN
      - NET_RAW
    privileged: true
    command: >
      bash -c '
        ip route replace default via 192.168.12.200 &&
        printf "%s\n" \
"from pymodbus.client import ModbusTcpClient" \
"import time" \
"frames = [" \
"    [84, 104, 105, 115, 32, 119, 97, 115, 32, 70, 117, 110, 100, 97, 109, 101]," \
"    [110, 116, 97, 108, 115, 32, 111, 102, 32, 78, 101, 116, 119, 111, 114, 107]," \
"    [115, 32, 97, 110, 100, 32, 80, 114, 111, 116, 111, 99, 111, 108, 115, 32]," \
"    [102, 111, 114, 32, 79, 84, 45, 73, 67, 83, 33, 0, 0, 0, 0, 0]" \
"]" \
"client = ModbusTcpClient(\\"192.168.11.201\\", port=502)" \
"while True:" \
"    for i, frame in enumerate(frames):" \
"        result = client.write_registers(100, frame)" \
"        print(f\\"[CLIENT] Frame {i+1}: {frame} | Response: {result}\\")" \
"        time.sleep(60)" \
"client.close()" \
> /client.py &&
        echo "[CLIENT] Waiting for Modbus server. . ." &&
        until nc -z 192.168.11.201 502; do sleep 2; done &&
        echo "[CLIENT] Server is up. Starting client loop. . ." &&
        sleep 2 &&
        python3 /client.py'

  $ot_container_name03:
    image: honeynet/conpot:latest
    container_name: $ot_container_name03
    hostname: $ot_container_name03
    mac_address: 00:1D:9C:00:01:02
    networks:
      $lab_net02:
        ipv4_address: 192.168.12.20
    cap_add:
      - NET_ADMIN
      - NET_RAW
    privileged: true
    command: >
      sh -c '
        echo "<html><body><h1>OTLab06{LAPES-SCADA}</h1></body></html>" > /home/conpot/.local/lib/python3.6/site-packages/conpot-0.6.0-py3.6.egg/conpot/templates/default/http/htdocs/index.html &&
        /home/conpot/.local/bin/conpot -f --template default
      '

  $ot_container_name04:
    image: iotechsys/s7-sim
    container_name: $ot_container_name04
    hostname: $ot_container_name04 
    mac_address: 00:1C:06:33:B2:91
    networks:
      $lab_net02:
        ipv4_address: 192.168.12.30
    cap_add:
      - NET_ADMIN
      - NET_RAW
    ports:
      - "102:102"
    privileged: true

  $ews_container_name:
    image: $ews_image
    container_name: $ews_container_name
    hostname: $ews_container_name
    command: >
      bash -c '
        sysctl -w net.ipv4.ip_forward=1 &&
        tail -f /dev/null
      '
    networks:
      $lab_net01:
        ipv4_address: 192.168.11.200
      $lab_net02:
        ipv4_address: 192.168.12.200
    privileged: true

networks:
  $lab_net01:
    name: $lab_net01
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.11.0/24

  $lab_net02:
    name: $lab_net02
    driver: bridge
    ipam:
      config:
        - subnet: 192.168.12.0/24
EOF
}

# ----------------------------------------
# System requirements check
check_requirements() {
    error_flag=0

    if ! command -v docker >/dev/null 2>&1; then
        printf "\033[31m[Error]\033[0m Docker is not installed on this system.\n"
        error_flag=1
    elif ! docker info >/dev/null 2>&1; then
        printf "\033[31m[Error]\033[0m Docker is installed, but not accessible.\n"
        error_flag=1
    fi

    if command -v docker-compose >/dev/null 2>&1; then
        DOCKER_COMPOSE_CMD="docker-compose"
    elif docker compose version >/dev/null 2>&1; then
        DOCKER_COMPOSE_CMD="docker compose"
    else
        printf "\033[31m[Error]\033[0m Docker Compose is not installed.\n"
        error_flag=1
    fi

    if [ "$error_flag" -eq 1 ]; then
        printf "\033[31m✘ The $lab_name system requirements check failed.\033[0m\n"
        exit 1
    fi
}

# ----------------------------------------
# Function to check if a container exists
container_exists() {
    docker ps -a --format '{{.Names}}' | grep -q "$1"
}

# ----------------------------------------
# Command handling
case "$1" in
    -start)
        show_banner
        distro="${2:-ubuntu}"

        if [[ "$distro" == "kali" ]]; then
            selected_image="$kali_image"
        elif [[ "$distro" == "ubuntu" ]]; then
            selected_image="$ubuntu_image"
        else
            printf "\033[31m[Error]\033[0m Invalid distro. Please use 'kali' or 'ubuntu'.\n"
            exit 1
        fi

        check_requirements
        printf "\033[1;33m[Working]\033[0m Starting $lab_name. . .\n"
        generate_compose_file "$selected_image"
        $DOCKER_COMPOSE_CMD -f "$compose_file" up -d
        if [ $? -eq 0 ]; then
            printf "\033[32m✔ $lab_name started.\033[0m\n"
        else
            printf "\033[31m✘ $lab_name failed to start.\033[0m\n"
            exit 1
        fi
        ;;
    -stop)
        show_banner
        if container_exists "$ot_container_name01" || container_exists "$ot_container_name02" || container_exists "$ews_container_name"; then
            check_requirements
            printf "\033[1;33m[Working]\033[0m Stopping $lab_name. . .\n"
            $DOCKER_COMPOSE_CMD -f "$compose_file" stop
            printf "\033[32m✔ $lab_name stopped.\033[0m\n"
        else
            printf "\033[34m[Information]\033[0m No containers to stop.\n"
            exit 1
        fi
        ;;
    -clean)
        show_banner
        if container_exists "$ot_container_name01" || container_exists "$ot_container_name02" || container_exists "$ews_container_name"; then
            check_requirements
            printf "\033[1;33m[Working]\033[0m Cleaning up all $lab_name resources. . .\n"
            $DOCKER_COMPOSE_CMD -f "$compose_file" down -v
            docker network rm "$lab_net01" "$lab_net02" 2>/dev/null
            rm -f "$compose_file"
            printf "\033[32m✔ All $lab_name resources removed.\033[0m\n"
        else
             printf "\033[34m[Information]\033[0m No containers found to clean.\n"
             exit 1
        fi
        ;;
    -run)
        show_banner
        if container_exists "$ews_container_name"; then
            check_requirements
            printf "\033[1;33m[Working]\033[0m Accessing $ews_container_name terminal. . .\n"
            docker exec -it "$ews_container_name" bash
        else
            printf "\033[31m[Error]\033[0m Container $ews_container_name not found.\n"
            exit 1
        fi
        ;;
    -restart)
        show_banner
        check_requirements
        if [ ! -f "$compose_file" ]; then
            printf "\033[31m[Error]\033[0m Cannot restart: $compose_file not found.\n"
            exit 1
        fi

        printf "\033[1;33m[Working]\033[0m Restarting $lab_name. . .\n"
        $DOCKER_COMPOSE_CMD -f "$compose_file" up -d
        if [ $? -eq 0 ]; then
            printf "\033[32m✔ $lab_name restarted.\033[0m\n"
        else
            printf "\033[31m✘ $lab_name failed to restart.\033[0m\n"
            exit 1
        fi
        ;;
    -status)
        show_banner
        check_requirements
        $DOCKER_COMPOSE_CMD -f "$compose_file" ps
        ;;
    *)
        show_banner
        echo "Usage: $0 -start [kali|ubuntu] | -stop | -clean | -run | -restart | -status"
        echo ""
        echo "  -start     Start the $lab_name environment using the specified distro (default: ubuntu)"
        echo "             Valid options: kali (rolling) or ubuntu (22.04)"
        echo "  -run       Open a terminal inside the $ews_container_name container"
        echo "  -clean     Remove containers, volumes, and networks"
        echo "  -stop      Stop all containers"
        echo "  -restart   Restart previously stopped containers"
        echo "  -status    Show current containers status"
        echo ""
        echo "[i] Local images required: ews-image-kali01 and ews-image-ubuntu01"
        exit 1
        ;;
esac