{"id":1811,"date":"2026-04-30T13:07:39","date_gmt":"2026-04-30T10:07:39","guid":{"rendered":"https:\/\/zerontek.com\/zt\/?p=1811"},"modified":"2026-04-30T13:09:41","modified_gmt":"2026-04-30T10:09:41","slug":"using-modbus-cli-to-read-and-write-modbus-registers-in-labshock","status":"publish","type":"post","link":"https:\/\/zerontek.com\/zt\/2026\/04\/30\/using-modbus-cli-to-read-and-write-modbus-registers-in-labshock\/","title":{"rendered":"Using modbus-cli to Read and Write Modbus registers in LabShock"},"content":{"rendered":"\n<p>In the previous post \u2014 <em>\u201c<a href=\"https:\/\/zerontek.com\/zt\/2025\/07\/20\/using-modbus-cli-to-read-and-write-modbus-coils-in-labshock\/\" target=\"_blank\" rel=\"noreferrer noopener\">Using modbus-cli to Read and Write Modbus Coils in LabShock<\/a>\u201d<\/em> \u2014 I showed something simple:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>We were able to <strong>read and write Modbus coils<\/strong>\u2026 with no authentication.<\/p>\n<\/blockquote>\n\n\n\n<p>In this part, I continued the experiment inside <strong><a href=\"https:\/\/www.labshocksecurity.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">LabShock<\/a> (OilSpring Air lab)<\/strong>, but instead of coils, I focused on <strong>actual process values<\/strong>\u2014and also writing directly to registers.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The Setup (Operator View)<\/h1>\n\n\n\n<p>From the OpenPLC monitoring dashboard, everything looks clean:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>pump1_speed_in \u2192 %QW10 \u2192 50<\/code><\/li>\n\n\n\n<li><code>pump1_speed_out \u2192 %IW10 \u2192 50<\/code><\/li>\n\n\n\n<li><code>pump1_temp \u2192 %IW11 \u2192 16<\/code><\/li>\n\n\n\n<li><code>pump1_pressure \u2192 %IW12 \u2192 3158<\/code><\/li>\n<\/ul>\n\n\n\n<p>You just see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Speed<\/li>\n\n\n\n<li>Temperature<\/li>\n\n\n\n<li>Pressure<\/li>\n<\/ul>\n\n\n\n<p>That\u2019s the <strong>operator view<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"543\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-2-1024x543.png\" alt=\"\" class=\"wp-image-1818\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-2-1024x543.png 1024w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-2-300x159.png 300w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-2-768x408.png 768w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-2-1536x815.png 1536w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-2.png 1568w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Reading via Modbus (What We Did)<\/h1>\n\n\n\n<p>After installing <code><a href=\"https:\/\/github.com\/tallakt\/modbus-cli\" target=\"_blank\" rel=\"noreferrer noopener\">modbus-cli<\/a><\/code>, I ran:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>modbus read 192.168.2.10 %MW10 20\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Quick breakdown:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>modbus read<\/code> \u2192 read from device<\/li>\n\n\n\n<li><code>192.168.2.10<\/code> \u2192 PLC IP<\/li>\n\n\n\n<li><code>%MW10<\/code> \u2192 start from memory word 10<\/li>\n\n\n\n<li><code>20<\/code> \u2192 read 20 values<\/li>\n<\/ul>\n\n\n\n<p>So we dump:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>%MW10 \u2192 %MW29<\/code><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What We Got<\/h1>\n\n\n\n<p>From the output:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>%MW10 = 50<\/code><\/li>\n\n\n\n<li><code>%MW20 = 50<\/code><\/li>\n\n\n\n<li>others mostly <code>0<\/code><\/li>\n<\/ul>\n\n\n\n<p>One command \u2192 raw memory view.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"616\" height=\"486\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image.png\" alt=\"\" class=\"wp-image-1813\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image.png 616w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-300x237.png 300w\" sizes=\"auto, (max-width: 616px) 100vw, 616px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Making Sense of <code>%QW<\/code>, <code>%IW<\/code>, <code>%MW<\/code><\/h1>\n\n\n\n<h3 class=\"wp-block-heading\"><code>%QW10<\/code> \u2192 Output<\/h3>\n\n\n\n<p>What the PLC sends (e.g., pump speed)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>%IW10<\/code> \u2192 Input<\/h3>\n\n\n\n<p>What the PLC reads (sensor values)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>%MW10<\/code> \u2192 Memory<\/h3>\n\n\n\n<p>Internal storage used by the program<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why We Saw Those Values<\/h1>\n\n\n\n<p>Even though the dashboard shows <code>%QW<\/code> and <code>%IW<\/code>, we read <code>%MW<\/code>.<\/p>\n\n\n\n<p>That\u2019s because PLC logic often copies values internally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>%QW10 \u2192 %MW10\n%IW10 \u2192 %MW20\n<\/code><\/pre>\n\n\n\n<p>So:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>We didn\u2019t read outputs or inputs directly\u2014we read their copies.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Writing to a Register (New Part)<\/h1>\n\n\n\n<p>Now the important part.<\/p>\n\n\n\n<p>We didn\u2019t just read\u2014we also <strong>wrote to memory<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>modbus write 192.168.2.10 %MW10 60<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Breaking It Down<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>modbus write<\/code> \u2192 send value to PLC<\/li>\n\n\n\n<li><code>192.168.2.10<\/code> \u2192 PLC IP<\/li>\n\n\n\n<li><code>%MW10<\/code> \u2192 target register (memory word 10)<\/li>\n\n\n\n<li><code>60<\/code> \u2192 value to write<\/li>\n<\/ul>\n\n\n\n<p>So:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Write value <strong>60<\/strong> into memory address <code>%MW10<\/code><\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What Happens After Writing<\/h2>\n\n\n\n<p>If you check:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>OpenPLC dashboard (or SCADA in LabShock)<\/li>\n<\/ul>\n\n\n\n<p>You will see the value change.<\/p>\n\n\n\n<p>To confirm from CLI:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>modbus read 192.168.2.10 %MW10 1\n<\/code><\/pre>\n\n\n\n<p>This reads <strong>only one value<\/strong>.<\/p>\n\n\n\n<p>You should now see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>%MW10 = 60<\/code><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"642\" height=\"531\" src=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-1.png\" alt=\"\" class=\"wp-image-1816\" srcset=\"https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-1.png 642w, https:\/\/zerontek.com\/zt\/wp-content\/uploads\/2026\/04\/image-1-300x248.png 300w\" sizes=\"auto, (max-width: 642px) 100vw, 642px\" \/><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why This Is Important<\/h1>\n\n\n\n<p>This is not just reading anymore.<\/p>\n\n\n\n<p>This is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Direct modification of PLC data<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p>Depending on the logic, this could:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Change setpoints<\/li>\n\n\n\n<li>Affect process behavior<\/li>\n\n\n\n<li>Influence outputs indirectly<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Operator vs What We Did<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Operator<\/th><th>What We Did<\/th><\/tr><\/thead><tbody><tr><td>Uses dashboard<\/td><td>Used Modbus CLI<\/td><\/tr><tr><td>Sees values<\/td><td>Reads raw memory<\/td><\/tr><tr><td>No direct memory access<\/td><td>Full access<\/td><\/tr><tr><td>Controlled UI<\/td><td>No restrictions<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What This Proves<\/h1>\n\n\n\n<p>This reinforces the same point as Part 1:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Modbus is not secure by design.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p>We were able to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Read process values<\/li>\n\n\n\n<li>Dump memory<\/li>\n\n\n\n<li>Write to registers<\/li>\n\n\n\n<li>Write to coils (Part 1)<\/li>\n<\/ul>\n\n\n\n<p>All of this:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No authentication<\/li>\n\n\n\n<li>No encryption<\/li>\n\n\n\n<li>No validation<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why This Matters<\/h1>\n\n\n\n<h3 class=\"wp-block-heading\">1. Read = Recon<\/h3>\n\n\n\n<p>You can map the system just by reading.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">2. Write = Impact<\/h3>\n\n\n\n<p>You can influence the system by writing.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3. No Barriers<\/h3>\n\n\n\n<p>If you can reach the PLC:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>You can interact with it.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What Defenders Should Take From This<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Restrict Modbus access (segmentation is critical)<\/li>\n\n\n\n<li>Monitor <strong>read activity<\/strong>, not just writes<\/li>\n\n\n\n<li>Understand what each register exposes<\/li>\n\n\n\n<li>Treat internal memory as sensitive<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final Thought<\/h1>\n\n\n\n<p>Part 1 showed we can <strong>control coils<\/strong>.<br>Part 2 shows we can <strong>read and modify process values<\/strong>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>When a protocol lets you read and write critical data with no authentication,<br>that\u2019s not a misconfiguration \u2014 that\u2019s the design.<\/p>\n<\/blockquote>\n\n\n\n<p>And once you see that layer,<br>you\u2019re no longer looking at the system like an operator\u2026<br>you\u2019re seeing it like an attacker.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the previous post \u2014 \u201cUsing modbus-cli to Read and Write Modbus Coils in LabShock\u201d \u2014 I showed something simple: We were able to read and write Modbus coils\u2026 with no authentication. In this part, I continued the experiment inside LabShock (OilSpring Air lab), but instead of coils, I focused on actual process values\u2014and also [&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,48,106,67,260],"tags":[7,6,301,290,47],"class_list":["post-1811","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-ot-security","category-penetration-testing","category-plc","category-scada","tag-cyber-security","tag-ics-security","tag-labshock","tag-modbus-cli","tag-ot-security"],"_links":{"self":[{"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts\/1811","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=1811"}],"version-history":[{"count":6,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts\/1811\/revisions"}],"predecessor-version":[{"id":1820,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/posts\/1811\/revisions\/1820"}],"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=1811"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/categories?post=1811"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zerontek.com\/zt\/wp-json\/wp\/v2\/tags?post=1811"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}