{"id":1695,"date":"2025-07-20T09:26:48","date_gmt":"2025-07-20T06:26:48","guid":{"rendered":"https:\/\/zerontek.com\/zt\/?p=1695"},"modified":"2025-07-20T09:53:57","modified_gmt":"2025-07-20T06:53:57","slug":"using-modbus-cli-to-read-and-write-modbus-coils-in-labshock","status":"publish","type":"post","link":"https:\/\/zerontek.com\/zt\/2025\/07\/20\/using-modbus-cli-to-read-and-write-modbus-coils-in-labshock\/","title":{"rendered":"Using modbus-cli to Read and Write Modbus Coils in LabShock"},"content":{"rendered":"\n<p>In today&#8217;s article, we\u2019ll dive into using the command-line tool <code><a href=\"https:\/\/github.com\/tallakt\/modbus-cli\">modbus-cli<\/a><\/code> to interact with a PLC (OpenPLC) running inside<a href=\"https:\/\/labshock.github.io\/\" target=\"_blank\" rel=\"noreferrer noopener\"> LabShock<\/a> \u2014 a popular open-source developed by Zakhar Bernhardt. While LabShock provides a friendly browser-based GUI (Pentest Fury > Modbus section), our goal here is to get comfortable with CLI tools that you can use in the real world, beyond lab environments.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"466\" height=\"522\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-1.jpg\" alt=\"\" class=\"wp-image-1706\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-1.jpg 466w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-1-268x300.jpg 268w\" sizes=\"auto, (max-width: 466px) 100vw, 466px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"923\" height=\"752\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-gui-1.jpg\" alt=\"\" class=\"wp-image-1707\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-gui-1.jpg 923w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-gui-1-300x244.jpg 300w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/modbus-attacks-gui-1-768x626.jpg 768w\" sizes=\"auto, (max-width: 923px) 100vw, 923px\" \/><\/figure>\n\n\n\n<p>Let\u2019s get started by setting everything up and walking through how to <strong>read and write Modbus coils<\/strong> using <code>modbus-cli<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 What is Modbus, and What Are Coils?<\/h2>\n\n\n\n<p><strong>Modbus<\/strong> is one of the most widely used protocols in industrial environments. It allows communication between a client (such as a laptop or SCADA system) and field devices like PLCs.<\/p>\n\n\n\n<p>A <strong>coil<\/strong> in Modbus is a single-bit data point, representing an ON\/OFF (1\/0) state. Think of it like a digital switch:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>1<\/code> = ON (e.g., a pump running)<\/li>\n\n\n\n<li><code>0<\/code> = OFF (e.g., a valve closed)<\/li>\n<\/ul>\n\n\n\n<p>Each coil has an address, and we can read or write to these addresses using tools like <code><a href=\"https:\/\/github.com\/tallakt\/modbus-cli\" target=\"_blank\" rel=\"noreferrer noopener\">modbus-cli<\/a><\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2699\ufe0f Setting Up LabShock with Docker<\/h2>\n\n\n\n<p>First, make sure you have <a href=\"https:\/\/www.docker.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Docker<\/a> installed on your machine.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Clone LabShock, build it (see guide)  and run it:<\/strong> <code>git clone https:\/\/github.com\/zakharb\/labshock.git cd labshock docker-compose up -d<\/code><\/li>\n\n\n\n<li><strong>Access the penetration testing VM:<\/strong><ul><li>If you&#8217;re using <strong>Windows<\/strong>, open Ubuntu via WSL.<\/li><li>If you&#8217;re on <strong>Linux\/macOS<\/strong>, use your regular terminal.<\/li><\/ul>Connect to the VM: <code>ssh pentest@localhost -p 2222  (Note: Password: pentest<\/code>)<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd28 Installing <code>modbus-cli<\/code><\/h2>\n\n\n\n<p>Once inside the <code>pentest<\/code> container, follow these steps:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Update the system:<\/strong> <code>sudo apt update           (Note: Password: pentest<\/code>)<\/li>\n\n\n\n<li><strong>Install Ruby:<\/strong> <code>sudo apt install ruby-full<\/code><\/li>\n\n\n\n<li><strong>Install modbus-cli:<\/strong> <code>gem install modbus-cli<\/code>      (Note: By default, the binary will be placed in: <code>\/home\/pentest\/.local\/share\/gem\/ruby\/3.3.0\/bin\/<\/code> )<\/li>\n\n\n\n<li><strong>Add it to your PATH:<\/strong> <code>echo 'export PATH=\"$HOME\/.local\/share\/gem\/ruby\/3.3.0\/bin:$PATH\"' &gt;&gt; ~\/.bashrc source ~\/.bashrc<\/code><\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udce1 Connecting to the PLC (IP: 192.168.2.10)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Read Coil Values<\/h3>\n\n\n\n<p>We\u2019ll read coils starting from address 1 to 10:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>modbus read 192.168.2.10 1 10\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Coil 1 = true<\/strong>, which represents <strong>Pump 1<\/strong>.<\/li>\n\n\n\n<li>If you visualize this in the GUI, it shows as <strong>green<\/strong> (ON).<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"543\" height=\"301\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/mod-read.jpg\" alt=\"\" class=\"wp-image-1702\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/mod-read.jpg 543w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/mod-read-300x166.jpg 300w\" sizes=\"auto, (max-width: 543px) 100vw, 543px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"966\" height=\"466\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump1-on.jpg\" alt=\"\" class=\"wp-image-1703\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump1-on.jpg 966w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump1-on-300x145.jpg 300w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump1-on-768x370.jpg 768w\" sizes=\"auto, (max-width: 966px) 100vw, 966px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd04 Write to a Coil (Turn ON Pump 2)<\/h3>\n\n\n\n<p>Let\u2019s turn ON <strong>Pump 2<\/strong>, which is located at <strong>coil 8<\/strong>. Since <code>modbus-cli<\/code> uses zero-based addressing, we write to <strong>address 9<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>modbus write 192.168.2.10 9 1\n<\/code><\/pre>\n\n\n\n<p>This sets Pump 2 to ON (<code>1<\/code>).<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"608\" height=\"297\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/mod-read2.jpg\" alt=\"\" class=\"wp-image-1704\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/mod-read2.jpg 608w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/mod-read2-300x147.jpg 300w\" sizes=\"auto, (max-width: 608px) 100vw, 608px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"945\" height=\"503\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump2-on.jpg\" alt=\"\" class=\"wp-image-1705\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump2-on.jpg 945w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump2-on-300x160.jpg 300w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/pump2-on-768x409.jpg 768w\" sizes=\"auto, (max-width: 945px) 100vw, 945px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>You can find the exact <strong>coil and register addresses<\/strong> by visiting the <strong>SCADA Portal<\/strong> inside LabShock:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Navigate to <strong>SCADA Portal \u2192 Connections \u2192 PLC Device Settings<\/strong><br>Here, you\u2019ll see the configured Modbus addresses for coils and registers used by the simulated PLC. As you can see , pump1&#8217;s addrees is 1 and pump2&#8217;s address is 9.<\/p>\n<\/blockquote>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"752\" height=\"697\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/values.jpg\" alt=\"\" class=\"wp-image-1713\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/values.jpg 752w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2025\/07\/values-300x278.jpg 300w\" sizes=\"auto, (max-width: 752px) 100vw, 752px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddea Conclusion<\/h2>\n\n\n\n<p>In this tutorial, we:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Spun up LabShock using Docker<\/li>\n\n\n\n<li>Connected to the pentest VM via SSH<\/li>\n\n\n\n<li>Installed and configured <code>modbus-cli<\/code><\/li>\n\n\n\n<li>Read and wrote Modbus coil values to a PLC at <code>192.168.2.10<\/code><\/li>\n<\/ul>\n\n\n\n<p>Next time, we\u2019ll explore <strong>reading holding registers<\/strong>, writing multiple values, and combining CLI tools in larger assessments.<\/p>\n\n\n\n<p>Until then, happy hacking \u2014 the industrial way \ud83d\udd10\u2699\ufe0f<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In today&#8217;s article, we\u2019ll dive into using the command-line tool modbus-cli to interact with a PLC (OpenPLC) running inside LabShock \u2014 a popular open-source developed by Zakhar Bernhardt. While LabShock provides a friendly browser-based GUI (Pentest Fury > Modbus section), our goal here is to get comfortable with CLI tools that you can use in [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1716,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,3,46,288,289,283,48,106,67,260],"tags":[7,13,6,290,12,47,291],"class_list":["post-1695","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cyber-security","category-ics-security","category-ics-tools","category-labshock","category-modbus","category-openplc","category-ot-security","category-penetration-testing","category-plc","category-scada","tag-cyber-security","tag-ics","tag-ics-security","tag-modbus-cli","tag-ot","tag-ot-security","tag-scada"],"_links":{"self":[{"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts\/1695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/comments?post=1695"}],"version-history":[{"count":16,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts\/1695\/revisions"}],"predecessor-version":[{"id":1722,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts\/1695\/revisions\/1722"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/media\/1716"}],"wp:attachment":[{"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/media?parent=1695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/categories?post=1695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/tags?post=1695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}