Shrayes Upadhyayula Notebook - cps-vip/cps-cosimulation-env GitHub Wiki
Locklier's work was mostly focused on Angular Velocity (ANGVEL) as the primary target, but he also points to Message Rate (MSGRATE) as a way to catch packet glitches when a MITM attack stops and restarts traffic with its own data. The other features he talksa bout are - ROS Publisher and Subscriber counts (ROSPUBC/ROSSUBC). Since the attacker has to jump in and start publishing and subscribing to the topics, you get a clear spike the moment they go active.
Locklier talks about implementing across diverse network topologies. One idea I had is a simple hierarchical tree topology of ROS2 distributed over DDS
- Layered Structure. Sensors and actuators (leaves) connect to intermediate regional controllers (branches), which aggregate data for a central mission planner (root).
- Traffic Isolation. DDS Partitions and Domain IDs isolate high-frequency local control traffic at the bottom of the tree from global telem that's at the top.
- Inter-Level Routing. DDS Routing Services bridge different subnets across the hierarchy, so only the necessary data will move between leaves and the root.
- Scalability. This hierarchy prevents network saturation by containing local traffic spikes and ensures that a failure in one branch does not crash the entire system.
A hierarchical tree topology could restrict ARP attacks to a single broadcast domain because ARP broadcasts are not typically forwarded by the routers or gateways connecting different branches. This segmentation prevents an attacker in one branch from poisoning the ARP caches of devices in other parts of the hierarchy, effectively containing the threat to a specific subnet.
Reviewed William Locklier’s thesis - https://www.proquest.com/docview/3263585187?fromopenview=true&pq-origsite=gscholar&sourcetype=Dissertations%20&%20Theses - on detecting MITM attacks in simulated environments. He used a hardware-in-the-loop testbed that integrated Gazebo sensors with Raspberry Pi nodes to show "real" traffic, akin to our planned physical environment.
His scenario involved an attacker node performing ARP spoofing to intercept UDP traffic between a master and slave robot. Once intercepted, the attacker negated the angular velocity, he used msg.angular.z, of the movement commands. Will look into this more.
-Becoming a bit unfeasible to do the planned physical MITM attack, so shifted towards more architecture work /research on it based on the current lit.
-According to Ryu et al, the standard Simple Participant Discovery Protocol (SPDP) and Simple Endpoint Discovery Protocol (SEDP) transmit discovery information entirely in plaintext. This exposure of participant identifiers and unicast locators directly creates the attack surface for cross-layer exploits. Since ARP protocol follows "last-writer-wins" logic, we can poison the cache of a target node without even needing to send an initial ARP request. This is why attackers can identify node existence and target specific IP/MAC addresses w/o requiring any encryption keys or certificates.
Got a bit sidetracked with CS 4117 project, but ended up ordering a machine and dual booted linux on my windows machine.
Begun looking at some good linux machines to use - refurbished mini PCs.

Worked on Kickoff presentation, grayed out slides to cut down on time and reworded mission.
Big update is editing/making final presentation --> below is plans for future
- Install Ubuntu on the Windows laptop (dual boot) so there is one bare-metal Linux host on the Netgear LAN.
- Add a second Linux box (or NUC) dedicated to ROS 2 to separate “attacker” and “target/ICS” roles cleanly -> prob ask GTRI for one?
- Plug Linux host(s) and other endpoints directly into the Netgear switch/router on a single flat subnet.
- Run ROS 2 natively so traffic is visible directly on the physical NIC.
- From the Linux attacker host:
- Perform ARP poisoning against the ROS 2 host and gateway.
- Capture traffic with Wireshark while the ROS graph is active.
- Compare:
- Baseline ROS + baseline PCAPs vs. ARP-poisoned ROS + PCAPs.
- Apply prior created runbook
- ARP is the glue between IP (L3) and Ethernet (L2): it’s how a host maps IP → MAC in its ARP table.
- ARP poisoning only works if the attacker is on the same L2 broadcast domain as the victims (same switch/VLAN) so forged ARP replies actually update the victims’ ARP tables.
- If there’s NAT, a VM boundary, or a virtual bridge in between, we end up poisoning ARP in a virtual network that the real devices never see.
- Each container gets a virtual NIC (end of a veth pair) connected to a Linux bridge (docker0) on a private subnet
- The host routes/NATs that traffic out through the physical NIC to the real LAN.
- --network host → container shares the host’s network namespace and physical NIC.
- macvlan → give containers their own MAC/IP directly on the LAN.
- On macOS/Windows, Docker Desktop hides all of this inside a Linux VM, so the containers are at least one network hop away from the actual Ethernet segment.
- On Docker Desktop (macOS/Windows), --network host only shares the VM’s network stack, not the real host OS NIC.
- ARP frames from a container never directly hit the physical NIC or the Netgear switch -> die inside the VM’s virtual world.
- The Asahi installer explicitly reported: M3 MacBook Pro not supported yet.
- Device matrix shows M3 support as “no installer” and many critical features (GPU, Wi-Fi, Ethernet) as TBA/WIP.
- No supported path to run a stable Linux kernel directly on the M3.
- docker-mac-net-connect makes it easier for macOS → containers
- Does not move containers onto the physical LAN or give them direct access to the host’s NIC.
- wrong direction essentially
Here's my current Defensive Runbook
Initial Prep
a)Snapshot all VMs before testing b)Ensure test systems are isolated — no uplink to production or the internet. c)Sync system clocks:
sudo apt-get update && sudo apt-get install -y ntp sudo systemctl enable --now ntp
or
sudo timedatectl set-ntp true
Verify Network and Interfaces
ip addr show ip -4 neigh show # or legacy: arp -n
**Baseline Capture **
sudo tcpdump -i -s 0 -w baseline.pcap arp or udp sudo timeout 600 tcpdump -i -s 0 -w baseline_full.pcap ip -4 neigh show > /tmp/arp_snapshot_before.txt ip route show > /tmp/ip_route_before.txt ip addr show > /tmp/ip_addr_before.txt
ROS Baseline Capture rosbag record -O baseline_topics /topic1 /topic2 # ROS1 ros2 bag record -o baseline_topics /topic1 /topic2 # ROS2
Live Monitoring and Detection
watch -n 2 'ip -4 neigh show' sudo tshark -i -f "arp" -Y "arp.opcode == 2" -T fields -e frame.time_epoch | awk '{print int($1)}' | uniq -c sudo tcpdump -i -s 0 -w arp_replies.pcap arp and ether[6:2] == 0x0002
Post-Capture Analysis
tshark -r baseline.pcap -Y "arp" -T fields -e arp.src.proto_ipv4 -e arp.src.hw_mac | sort | uniq -c | sort -nr tshark -r baseline.pcap -Y "arp.opcode==2" -T fields -e frame.time -e arp.src.proto_ipv4 -e arp.src.hw_mac tshark -r baseline.pcap -Y "arp" -T fields -e arp.src.proto_ipv4 -e arp.src.hw_mac | sort | uniq | awk -F'\t' '{print $1"\t"$2}' | sort | awk '{a[$1]=a[$1]" "$2} END{for(i in a) print i,a[i]}' | awk 'NF>2' tshark -r baseline.pcap -Y "arp.opcode==2" -T fields -e arp.src.proto_ipv4 -e arp.dst.proto_ipv4 | awk '$1==$2{print $1}' | sort | uniq -c | sort -nr
Remediation and Recovery
sudo ip -s -s neigh flush all ip -4 neigh show sudo arp -s
Persistent static ARP entries can be defined in /etc/ethers or via systemd-networkd configuration.
I have not been great about updating my notebook, so here's a more detailed guide about my plan to conduct a MITM attack and the progress made to reach this.
Worked on presentation -consolidated all security slides so that they're officially under SROS. -Re-worked some stuff in the slide -Set new goals for next semester: set up actual auth controls within Gazebo environment before testing on the physical robot
docker --version
docker run hello-worldgit clone [email protected]:cps-vip/gazebo-physical-env.git
cd gazebo-physical-env
git remote set-url origin [email protected]:cps-vip/gazebo-physical-env.gitUsing SSH key from ~/.ssh/id_ed25519, public key added to GitHub Settings → SSH keys.
# ROS 2 Jazzy (Ubuntu 24.04)
FROM ros:jazzy
# Install SROS2 and example nodes
RUN apt-get update && apt-get install -y --no-install-recommends \
ros-jazzy-sros2 \
ros-jazzy-demo-nodes-cpp \
&& rm -rf /var/lib/apt/lists/*
# Always source ROS setup in new bash shells
SHELL ["/bin/bash", "-c"]
RUN echo "source /opt/ros/jazzy/setup.bash" >> /root/.bashrc
# Copy the project files into the container
WORKDIR /root/ros-ws
COPY . /root/ros-ws
# Build any packages in the workspace (safe if empty)
RUN . /opt/ros/jazzy/setup.bash && colcon build --symlink-install || echo "no build"
CMD ["/bin/bash"]
Build the Docker image:
docker build -t gazebo-humble-sros .docker run -it --name sros_box gazebo-humble-sros bash
source /opt/ros/humble/setup.bash
ros2 security --helpros2 security create_keystore /root/keystores/demo_keys
ros2 security create_enclave /root/keystores/demo_keys /talker
ros2 security create_enclave /root/keystores/demo_keys /listenerexport ROS_SECURITY_ENABLE=true
export ROS_SECURITY_STRATEGY=Enforce
export ROS_SECURITY_KEYSTORE=/root/keystores/demo_keysI added these to ~/.bashrc inside the container so I don't have to reset them manually.
First shell (already inside sros_box):
ros2 run demo_nodes_cpp talker --ros-args --enclave /talkerSecond shell (open a new terminal on the host, then exec into the container):
docker exec -it sros_box bash
source /opt/ros/humble/setup.bash
export ROS_SECURITY_ENABLE=true
export ROS_SECURITY_STRATEGY=Enforce
export ROS_SECURITY_KEYSTORE=/root/keystores/demo_keys
ros2 run demo_nodes_cpp listener --ros-args --enclave /listenerIf everything is correct, the listener prints chatter messages.
If the security settings are missing or wrong, communication fails — which is what we want.
git add Dockerfile
git commit -m "Added Dockerfile with ROS 2 Humble + SROS2 secure setup"
git push origin mainPushing over SSH, if you get a 403 error - ask for collaborator access.
Basically spent most of my time creating the guide and troubleshooting what's on the week's entry above here ^.
Spring Break - no updates.
Hell week w/ midterms - no updates.
Troubleshooting w/ Fast & Cyclone DDS. Had been using Fast DDS which led to some issues.
By default, Fast DDS (rmw_fastrtps_cpp) is what ROS 2 uses unless you explicitly set something else.
- Nodes couldn't discover each other reliably
- Setting up SROS2 (with security enforced) made everything even worse — nodes refused to talk after enabling encryption.
- Error messages about "inconsistent GUID prefixes"
- Wireshark showed discovery traffic (
RTPSpackets) but barely any real data after.
Switched to the middleware to Cyclone DDS (rmw_cyclonedds_cpp).
Cyclone handles networking way more reliably in containerized environments - so it plays nicer with SROS
What I did to fix it:
- Install Cyclone DDS:
apt-get update
apt-get install -y ros-jazzy-rmw-cyclonedds-cpp- Then, in my container (or add to
.bashrc), force ROS 2 to use Cyclone:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp- After doing that:
- Talker and listener nodes discovered each other instantly.
- SROS2 security handshake actually completed properly.
- Wireshark showed full secure communication happening normally
I spent this week troubleshooting and configuring Wireshark to capture ROS 2 traffic. Here’s a summary of what happened:
Permission Issues and wireshark Group
Initially, Wireshark didn’t show standard network interfaces (eth0, wlan0, lo). We discovered the user needed to be in the wireshark group, so we ran: sudo usermod -aG wireshark ubuntu newgrp wireshark
Then we had to set permissions on dumpcap so non-root users can capture packets: sudo chown root:wireshark /usr/bin/dumpcap sudo chmod 4750 /usr/bin/dumpcap
Verified permissions with: ls -l /usr/bin/dumpcap
After these changes, Wireshark could display network interfaces properly.
Checking Network Interfaces
Found the containerized environment interface named eth0@if26. Ran: ip link show
Confirmed it’s assigned an IP address: ip addr show eth0
Next, installed tshark to test interface detection: sudo apt update sudo apt install tshark
Next, captured Capturing ROS 2 Traffic ROS 2 uses UDP ports 7400 (discovery) and 7401-7500 (data). In Wireshark, we used filters:
udp.port == 7400
udp.port >= 7401 && udp.port <= 7500
udp.port >= 7400 && udp.port <= 7500 Verified that discovery packets appear with destination IP 239.255.0.1 on port 7400.
Data packets are shown in the range 7401-7500, containing RTPS (Real-Time Publish-Subscribe) data.
Wireshark Capture Options
Selected eth0@if26 (and lo if needed) in Capture → Options....
Enabled Promiscuous Mode to capture all traffic.
Used Follow → UDP Stream in Wireshark to view packet sequences.
Outcome

SROS (Secure ROS): A set of ROS 2 security features that leverages DDS-Security to ensure authentication, access control, and encryption. DDS Security: Uses Public Key Infrastructure (PKI) to authenticate ROS nodes. Enforces access control policies, so only authorized topics, publishers, and subscribers can communicate. Galois Counter Mode (AES-GCM): The default encryption algorithm in SROS. Provides both encryption and authentication (ensuring data integrity). Critical for protecting data in motion, especially for sensitive robotics applications.
I fixed my install issue of Ros GZ bridge from my previous semester. Turns out according to this reddit thread: https://www.reddit.com/r/ROS/comments/16wc472/unable_to_install_gazeboros_and_its_plugins_on/ "If you installed using sudo apt-get install ros-humble-ros-gz, the version that would have got installed is Gazebo Fortress. This version can't opened with either gz or gazebo. You need to use 'ign gazebo' instead."
I then followed the install instructions listed here: https://docs.ros.org/en/humble/Tutorials/Advanced/Simulators/Gazebo/Gazebo.html In Terminal 1: Run to launch Gazebo:
ign gazebo -v 4 -r visualize_lidar.sdf
In Terminal 2: Install Ros GZ Bridge Package
sudo apt-get install ros-humble-ros-ign-bridge
Launch bridge from ROS to Gazebo
source /opt/ros/humble/setup.bash
ros2 run ros_gz_bridge parameter_bridge /model/vehicle_blue/cmd_vel@geometry_msgs/msg/Twist]ignition.msgs.Twist
In Terminal 3: Install Twist Package
sudo apt-get install ros-humble-teleop-twist-keyboard
Send command using ros2 topic pub
ros2 topic pub /model/vehicle_blue/cmd_vel geometry_msgs/Twist "linear: { x: 0.1 }"
I'll look more into the visualizing lidar data later
Lookin at Jazzy
Jazzy docker pull: docker pull tiryoh/ros2-desktop-vnc:jazzy
Added a new port for jazzy impage
docker run -d --name upbeat_hypatia -p 6080:6080 tiryoh/ros2-desktop-vnc:latest
Presentation week! Met up w/ the group and consolidated slides. Still presented the Ros GZ bridge one as part of the presentation. Otherwise, didn't do much. Cleaned up last semester's notebook so that the Ros GZ bridge install is easier to install.
I'm still having my error persist when I try and launch one of the ros-gazebo packages due to weird placement of the CMAKE directly. Lots of debuging and unix nudging hasn't yielded anything valuable. Will continue to look at.

Successfully got the ros gz bridge to work and let the robot begin to move by having them speak with one another.

Step 1: In terminal 1, run: sudo apt install ros-humble-ros-gz-bridge, source your files by running: source /opt/ros/humble/setup.bash source /usr/share/gazebo/setup.sh then run
gz sim empty.sdf to open an empty world in gazebo
Step 2: In Terminal 2, run something like
ros2 run ros_gz_bridge parameter_bridge /scan@sensor_msgs/msg/[email protected], but you can replace the topic type, ROS msg, and GZ message, for whatever is appropriate for your work

I attempted to build a robot from the gazebo tutorial here but had no dice.

I began implementing the ros gz-bridge into my environment in order to have ros get the robot to move into the gazebo environment. Ran into some issues in terms of which software I was using. Talked w/ Joeseph and resorted it out for next week

I was following this but went through the wrong image.
I did successfully launch Gazeboo using Ros though, they weren't connected

-No Updates, Backpack stolen, reinstalled all software
I began messing around with the Turtle robot in order to get more comfortable with moving it in any environment. Big midterm week so I wasn't able to get much done
I completed all of the ROS CLI tutorials and am planning on moving to the intermediate tutorials/working more closley with Joeseph on ROS/Gazebo integrations.
I began by installing ROS2 Docker Image at https://hub.docker.com/r/tiryoh/ros2-desktop-vnc since I'm using an M3 Macbook.
I ran these commands under Joeseph's instructions to do so:
`docker pull tiryoh/ros2-desktop-vnc:humble-20240114T0416
docker run -p 6080:80 --security-opt seccomp=unconfined --shm-size=512m tiryoh/ros2-desktop-vnc:humble-20240114T0416`
Inside the server, I followed the steps of the ROS 2 Humble installation guide for Ubuntu (Debian packages): https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debs.html
I'm hoping to complete ROS tutorials for the next couple days so that I can assist Joeseph in ROS/Gazebo integration.