Foundations · Lesson 5/7

Law, Ethics & Responsible Disclosure — Doing the Right Thing with What You Find

46 min 1 quiz Graded labs: 3

What you'll learn

By the end of this lesson you will be able to:

  • State what you may and may not analyze, and how licenses and anti-circumvention laws shape the work
  • Explain, in plain terms, the laws most often cited around reverse engineering, and why they differ by country
  • Choose between coordinated disclosure, full disclosure and a bug bounty, and run a clean disclosure timeline
  • Score a vulnerability with CVSS, write a useful report, and respect embargoes and third-party data
  • Recognize safe harbor language and know when to stop and ask for permission or legal advice

The locksmith's code 🔑

A skilled locksmith can open almost any lock. That skill is legal and valuable: it rescues people locked out of their homes and helps make better locks. The same skill, used on someone else's door without permission, is burglary. Nothing about the technique changed; only the authorization and the intent did.

Reverse engineering is the locksmithing of software. Everything in this track, reading binaries, debugging, decoding, analyzing malware, is a normal professional skill. What keeps it on the right side of the line is a small set of rules about what you touch, with whose permission, and what you do with what you learn. This lesson is that rulebook.

As always: this is general education, not legal advice. Laws differ by country and change over time, and a contract can restrict you even where the law would allow something. When the stakes are real, ask a lawyer.

What you may analyze

Start from the safe lane you met in the first lesson, now with the reasons:

  • Your own code and systems. Always fair game.
  • Open-source software, within its license. Reading and modifying is the point of open source, though licenses still set conditions (for example, sharing changes under the GPL).
  • Practice material: crackmes, CTF binaries, deliberately vulnerable apps, training labs. Made for exactly this.
  • With written permission: an employer's internal systems that you are assigned to, a client under a signed contract, or a target inside a bug bounty program's scope.

Outside that lane, two kinds of limit apply, and they are different things:

Contracts (licenses / EULAs). When you install or use software you often agree to terms that forbid reverse engineering. Breaking a contract is usually a civil matter (you could be sued, or lose your right to use the product), separate from any criminal law. In some places such clauses are limited by statute; for instance the EU Software Directive makes clauses that forbid decompilation for interoperability unenforceable, within strict conditions. Elsewhere the clause may hold. Read the terms, and do not assume.

Laws. Even with no contract, the law may restrict:

  • accessing systems you do not own (covered next),
  • circumventing technical protection measures such as DRM or copy protection,
  • what you may copy, publish or distribute (copyright).

The practical test before you begin: Is this mine, open, made for practice, or permitted in writing? If not, stop and get permission.

The laws most often cited

You do not need to be a lawyer, but you should recognize the instruments that come up, and remember that each one is a national law or a regional rule with its own exceptions.

  • Anti-circumvention (copyright). The United States' DMCA section 1201 forbids bypassing technical measures that control access to copyrighted works. It has exemptions, some permanent (such as a narrow interoperability provision) and some granted every three years by the Librarian of Congress, including recurring ones for good-faith security research. The EU's Information Society Directive has its own anti-circumvention rules. The details and the exemptions differ, so check the current rules in your country.
  • Interoperability. The EU Software Directive (2009/24/EC) permits limited decompilation strictly to achieve interoperability of an independently created program, under conditions, and forbids contracts from taking that right away. US courts have also, in several cases, treated reverse engineering for interoperability as a fair use.
  • Unauthorized access. The US Computer Fraud and Abuse Act (CFAA) criminalizes accessing a computer "without authorization". In Van Buren v. United States (2021) the Supreme Court read its "exceeds authorized access" clause narrowly: it covers entering parts of a system that are off-limits to you, not misusing information you were entitled to reach, which is why breaking a website's terms of use is generally not a crime by itself. Accessing systems or areas you have no right to remains illegal. Most countries have an equivalent (the UK Computer Misuse Act, and so on).
  • International baseline. The Council of Europe's Budapest Convention on Cybercrime asks its many state parties to criminalize illegal access, interception, data and system interference, and the misuse of attack tools, which is why these offences look similar across borders.
  • Personal data. If anything you decode or observe contains personal data, data-protection law applies (the EU/UK GDPR, and national laws elsewhere). Do not collect, keep or share it beyond what your authorized task needs.

Two honest takeaways: reverse engineering is not illegal in itself, and the exact lines move with country, contract and year. When a project could touch any of these, written authorization and, for anything serious, a lawyer are not optional extras.

A professional ethic

Law is the floor, not the ceiling. Something can be arguably legal and still wrong. A short working ethic for this field:

  • Authorization before action. Only touch what you are clearly allowed to touch. If you are unsure, you do not yet have permission.
  • Minimize harm. Use the least intrusive method that answers the question. Do not degrade a service, delete data or pivot to other systems to "prove" a point.
  • Protect other people's data. If you stumble on personal data, stop, do not copy it, and report that it is exposed. Do not read more than you must.
  • No hack-back. Even against an attacker's own infrastructure, retaliating is unauthorized access. Report it instead.
  • Honour confidentiality and embargoes. If a vendor asks for time, or you are under an NDA, keep the details private until the agreed moment.
  • Be honest and proportionate. Report what you actually found, not an exaggeration; claim the impact you can demonstrate, no more.
  • Act in good faith. Your purpose is to understand and to improve security, not to extort, show off, or gain an unfair advantage.

These are not just nice words: bug bounty rules, coordinated-disclosure policies and professional codes (for example the ISC2 Code of Ethics) encode exactly these expectations, and breaking them can cost you a reward, a job or your standing in the community.

When to stop and ask

Pause the work and get an answer in writing before you continue when:

  • the target is not clearly listed in your scope, or the scope is ambiguous;
  • the next step means bypassing a protection measure (DRM, a license check, encryption) on something you do not own;
  • you reach personal data, credentials, or another customer's systems;
  • the work would affect production, other users, or a third party's service;
  • you plan to publish a tool, key or technique that lets others bypass a protection;
  • the vendor answers with a legal threat, or asks you to sign terms you do not understand.

Ask the right person: the program owner or client contact for permission, your employer's legal team or a lawyer for advice. In some countries digital-rights groups (in the US, for example, the EFF) help researchers find legal help. Asking costs a day; guessing wrong can cost far more.

Disclosure: coordinated, full, or a bounty

You found a real vulnerability, in your own research or on an authorized target. What now? The goal is to get it fixed while causing the least harm to users. The recognized models:

  • Coordinated vulnerability disclosure (CVD), once called "responsible disclosure". You tell the vendor privately, agree on a timeline, and go public only after a fix (or after a fair deadline passes). This is the default and the one the standards describe: ISO/IEC 29147 covers how a vendor should receive vulnerability reports and publish advisories, and ISO/IEC 30111 how it should handle them internally.
  • Full disclosure. Publishing all details at once, with no private window. Sometimes used when a vendor is unreachable or has ignored repeated reports; it pressures a fix but also arms attackers immediately, so it is a last resort and ethically heavy.
  • Bug bounty / VDP. Many organizations run a Vulnerability Disclosure Program or a paid bug bounty with a published scope (what you may test), rules, and often safe-harbor language promising not to pursue good-faith researchers who follow the policy. Stay strictly inside the scope: testing something not listed is unauthorized, bounty or not.

A healthy coordinated timeline:

day 0      report sent privately, with clear steps to reproduce and an impact assessment
days 1-3   vendor acknowledges receipt (a typical target, not a rule)
...        vendor triages and confirms; you answer questions, you do NOT go public
...        a fix is developed, tested and released; a CVE id is often assigned
fix/day N  public write-up after the fix (or after the agreed deadline), crediting fairly

Common deadlines are around 90 days (used by, for example, Google Project Zero), often with a short extension if a fix is nearly ready. A deadline is a tool to prevent a report from being ignored forever, not a threat. Always leave a trail: dated messages, what you sent, what you agreed.

Lab exercise 1

GoGraded in Code Lab

Run a disclosure timeline

Track a coordinated-disclosure case against a policy window and classify how it ended.

Input: one event per line.

  • policy N: the disclosure window is N days from the report (appears once, before the events)
  • YYYY-MM-DD report: the private report was sent (appears once)
  • YYYY-MM-DD ack: the vendor acknowledged (keep the earliest if repeated)
  • YYYY-MM-DD extend K: the deadline was extended by K days (may appear several times; add them up)
  • YYYY-MM-DD fix: a fix was released (keep the earliest)
  • YYYY-MM-DD publish: details were made public (keep the earliest)

The deadline is the report date plus policy + total extensions days.

Output

reported: <report date>
acknowledged: after <n> days          OR  acknowledged: no
deadline: <deadline date>
fixed: after <n> days                 OR  fixed: no
published: <publish date>             OR  published: no
verdict: <one of the four below>

"after n days" counts whole days from the report. The verdict:

  • not public yet if nothing was published
  • coordinated disclosure (published after the fix) if a fix exists and publication is on or after the fix date
  • deadline disclosure (no fix by the deadline) if there was no fix on or before the deadline (a fix released later still counts as none) and publication is on or after the deadline
  • early disclosure (before the fix and the deadline) otherwise

Example

Input:

policy 90
2026-01-10 report
2026-01-12 ack
2026-02-20 extend 14
2026-03-30 fix
2026-04-02 publish

Output:

reported: 2026-01-10
acknowledged: after 2 days
deadline: 2026-04-24
fixed: after 79 days
published: 2026-04-02
verdict: coordinated disclosure (published after the fix)
Visible tests (6)
  • The worked example: coordinated, with an extension
  • No acknowledgement, no fix, published at the deadline
  • Published before the fix and before the deadline
  • Two extensions, fixed late, not yet public
  • The fix came only after the deadline, publication in between: deadline disclosure
  • A leap-year deadline, only the first ack kept
Example run
Input
policy 90
2026-01-10 report
2026-01-12 ack
2026-02-20 extend 14
2026-03-30 fix
2026-04-02 publish
Expected output
reported: 2026-01-10
acknowledged: after 2 days
deadline: 2026-04-24
fixed: after 79 days
published: 2026-04-02
verdict: coordinated disclosure (published after the fix)
Starter code
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

const layout = "2006-01-02"

More lines in Code Lab: 37

Try in Code Lab

Hints and instant feedback are in Code Lab. Optional practice: it does not block finishing the lesson.

JavaScript and Python also run in your browser without an account; other languages use the secure server runner and need a free account.

The report, the CVE, and security.txt

A vulnerability report that gets fixed quickly has a predictable shape:

Title           short and specific
Summary         what the bug is and why it matters, in two or three sentences
Affected        product, exact versions, configuration
Steps           minimal, reliable steps to reproduce
Impact          what an attacker gains; a CVSS vector and score
Proof           the least proof needed (a screenshot or short log), no real victim data
Suggested fix   optional, if you have one
Reporter        how to reach you, your disclosure expectations, any embargo
  • Give reproduction steps, not just a claim. The faster a vendor can confirm, the faster the fix.
  • Attach the least proof necessary. A proof of concept that only demonstrates the flaw is right; one that exfiltrates real data or runs on other users is not.
  • Vulnerabilities in software that others install usually get a CVE identifier (the industry catalogue of publicly known vulnerabilities, written CVE-<year>-<number>). A CVE Numbering Authority (CNA), often the vendor itself, reserves the id while the issue is coordinated, and the record is published with the advisory. CVEs let everyone refer to the same bug.
  • Keep the whole exchange professional and private until the agreed disclosure date.

Severity with CVSS. The Impact line usually carries a CVSS vector and score. CVSS (the Common Vulnerability Scoring System) is published by FIRST. Version 3.1 (2019) is still the one most advisories and databases quote, and version 4.0 (2023) is being adopted alongside it, so always say which version you used. The base score, from 0.0 to 10.0, describes the vulnerability's intrinsic severity; other metric groups adjust it for how mature exploits are and for a particular environment. A score summarizes the report; it never replaces the explanation.

How to reach a vendor. Many publish a security.txt file at /.well-known/security.txt (standardized in RFC 9116) that lists a Contact, an Expires date, encryption keys and a policy link. If there is none, look for a security page, a VDP, or a general contact, and keep trying through more than one channel before concluding that the vendor is unreachable.

Before the labs, one habit for the Proof line: redact before you attach. The demo below masks personal data in a log excerpt while keeping what proves the bug. Then the last two labs build two tools of the trade: a CVSS v3.1 calculator and a security.txt validator.

Run it: emails become consistent pseudonyms (<user-1> stays <user-1>), so the redacted log still proves the bug, one user reading another user's order, while no address, session token, card number or IP leaves your machine. Keep what demonstrates the flaw (the endpoint, the ids, the status codes) and remove what identifies people or grants access. Regexes are a safety net, not a guarantee: read the result yourself before you attach it, and invalidate any real token that appeared in a log. 4111 1111 1111 1111 is a well-known test card number, never a real one.

import re

# A made-up excerpt from an authorized test of your own staging app (example.com, RFC 5737 IPs).
# It proves an access-control bug: the user who owns order 1043 can also read order 1044.
log = """09:14:02 GET /api/orders/1043 user=lina.k@example.com ip=203.0.113.24 -> 200
09:14:02 Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.ZGVtbw.c2lnbmF0dXJl
09:14:05 GET /api/orders/1044 user=lina.k@example.com ip=203.0.113.24 -> 200
09:14:05 body={"order": 1044, "owner": "omar.h@example.com", "card": "4111 1111 1111 1111"}"""

people = {}  # the same address always gets the same pseudonym, so the proof survives


def pseudonym(match):
    return people.setdefault(match.group(0), f"<user-{len(people) + 1}>")


RULES = [
    ("email", re.compile(r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+"), pseudonym),
    ("token", re.compile(r"(Bearer\s+)[\w.-]+"), r"\1<token>"),
    ("card", re.compile(r"\b(?:\d[ -]?){12,18}\d\b"), "<card>"),
    ("ipv4", re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b"), "<ip>"),
]

redacted = log
for name, pattern, replacement in RULES:
    redacted, count = pattern.subn(replacement, redacted)
    print(f"{name:5} {count} replaced")

print()
print(redacted)

Lab exercise 2

PythonGraded in Code Lab

Score a vulnerability with CVSS

The Impact line of your report needs a severity. Compute the CVSS v3.1 base score from a vector string, exactly as FIRST's specification defines it, so your number matches the official calculator.

A vector is CVSS:3.1 followed by eight metrics, each CODE:VALUE, separated by /, in any order:

AV Attack Vector        N 0.85  A 0.62  L 0.55  P 0.20
AC Attack Complexity    L 0.77  H 0.44
PR Privileges Required  N 0.85  L 0.62  H 0.27   (if Scope is Changed: L 0.68, H 0.50)
UI User Interaction     N 0.85  R 0.62
S  Scope                U       C
C  Confidentiality      H 0.56  L 0.22  N 0.00
I  Integrity            H 0.56  L 0.22  N 0.00
A  Availability         H 0.56  L 0.22  N 0.00

The formulas (ISS is the impact sub-score):

ISS   = 1 - (1-C) * (1-I) * (1-A)
Impact = 6.42 * ISS                                        if Scope Unchanged
       = 7.52 * (ISS-0.029) - 3.25 * (ISS-0.02)**15        if Scope Changed
Exploitability = 8.22 * AV * AC * PR * UI
BaseScore = 0                                              if Impact <= 0
          = Roundup(min(Impact + Exploitability, 10))      if Scope Unchanged
          = Roundup(min(1.08 * (Impact + Exploitability), 10))   if Scope Changed

Roundup(x) is the smallest one-decimal number that is not less than x (so 4.02 rounds up to 4.1, and 4.00 stays 4.0). Severity: 0.0 None, 0.1-3.9 Low, 4.0-6.9 Medium, 7.0-8.9 High, 9.0-10.0 Critical.

For each input line print <vector> -> <score to one decimal> <severity>, or <vector> -> invalid vector when the prefix is wrong, a metric is missing, repeated or unknown, or a value is not allowed.

Example

Input:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Output:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H -> 9.8 Critical
Visible tests (6)
  • Textbook critical: remote, no interaction, full impact
  • Scope change pushes the score to the maximum and uses the Scope-Changed PR weights
  • A reflected-XSS-style vector, and a local privilege escalation
  • No impact scores zero; order of metrics does not matter
  • Physical access, availability only; and a high-complexity info leak
  • Malformed vectors are rejected
Example run
Input
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Expected output
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H -> 9.8 Critical
Starter code
import math
import sys

WEIGHTS = {
    "AV": {"N": 0.85, "A": 0.62, "L": 0.55, "P": 0.2},
    "AC": {"L": 0.77, "H": 0.44},
    "PR": {"N": 0.85, "L": 0.62, "H": 0.27},   # L/H differ when Scope is Changed
    "UI": {"N": 0.85, "R": 0.62},
    "S": {"U": "U", "C": "C"},
    "C": {"H": 0.56, "L": 0.22, "N": 0.0},
    "I": {"H": 0.56, "L": 0.22, "N": 0.0},
    "A": {"H": 0.56, "L": 0.22, "N": 0.0},

More lines in Code Lab: 30

Try in Code Lab

Hints and instant feedback are in Code Lab. Optional practice: it does not block finishing the lesson.

JavaScript and Python also run in your browser without an account; other languages use the secure server runner and need a free account.

Lab exercise 3

JavaScript (Node.js)Graded in Code Lab

Lint a security.txt file

Check a security.txt file (RFC 9116) so a researcher knows how to reach the vendor and whether the file is still valid.

Input: the first line is today <RFC3339 timestamp> (the moment of the check). The rest is the file: Field: value lines (field names are case-insensitive), with blank lines and # comment lines ignored; a line without a colon is ignored.

Checks

  • Collect valid Contact values (in file order). A Contact is valid when it starts with mailto:, https:// or tel: (case-insensitive scheme). That is this lab's simplification: RFC 9116 accepts any URI but requires https:// for web addresses, and those three schemes are the ones it shows. Record every invalid Contact as an error, and if no valid Contact exists, add the error no valid Contact.
  • Expires is required and must appear once. Missing: error. More than once: error with the count. Present once but not a valid RFC3339 timestamp (YYYY-MM-DDThh:mm:ss then Z or a numeric offset): error. Valid but on or before today: error (the file has expired). Valid but more than 365 days after today: a warning (not an error).
  • Preferred-Languages may appear at most once; more than once is an error.

Output. First, for every recognized field, a normalized line (in this order, keeping file order within a field):

contact: <value>
encryption: <value>
policy: <value>
languages: <comma+space-joined, trimmed list>

Then every problem in this order: invalid Contacts, the no-valid-Contact error, the Expires problems, the Preferred-Languages problem. Each error line starts with error: and each warning with warning: . Finish with status: valid if there were no errors (warnings are allowed), otherwise status: invalid.

Example

Input:

today 2026-10-17T00:00:00Z
Contact: mailto:security@example.com
Expires: 2026-01-01T00:00:00Z

Output:

contact: mailto:security@example.com
error: the file has expired
status: invalid
Visible tests (6)
  • A complete, valid file
  • A bare email and an http URL are not valid contacts, and a file that expires at the moment of the check has expired
  • Duplicate Expires and Preferred-Languages, a tel: contact
  • Valid, but Expires is more than a year away (a warning, still valid)
  • Expires is present but not a valid timestamp
  • Nothing useful at all
Example run
Input
today 2026-10-17T00:00:00Z
# security.txt for example.com (RFC 9116)
Contact: mailto:security@example.com
Contact: https://example.com/security/report
Expires: 2027-06-30T23:59:59Z
Encryption: https://example.com/pgp-key.txt
Preferred-Languages: en,ar
Policy: https://example.com/security/policy
Canonical: https://example.com/.well-known/security.txt
Expected output
contact: mailto:security@example.com
contact: https://example.com/security/report
encryption: https://example.com/pgp-key.txt
policy: https://example.com/security/policy
languages: en, ar
status: valid
Starter code
const lines = require("fs").readFileSync(0, "utf8").split("\n");
const today = new Date(lines[0].replace(/^today\s+/, "").trim());

const fields = {}; // lower-case field name -> values, in file order
for (const raw of lines.slice(1)) {
  const line = raw.trim();
  if (!line || line.startsWith("#")) continue;
  const colon = line.indexOf(":");
  if (colon < 0) continue;
  const name = line.slice(0, colon).trim().toLowerCase();
  const value = line.slice(colon + 1).trim();
  (fields[name] ??= []).push(value);

More lines in Code Lab: 15

Try in Code Lab

Hints and instant feedback are in Code Lab. Optional practice: it does not block finishing the lesson.

JavaScript and Python also run in your browser without an account; other languages use the secure server runner and need a free account.

Quick quiz

Question 1 of 5

A commercial program's license agreement forbids reverse engineering. What does that mean for you?

Question 2 of 5

You find a serious flaw in a product during authorized research. What is the standard first step?

Question 3 of 5

What did the U.S. Supreme Court's Van Buren v. United States (2021) decision clarify about the CFAA?

Question 4 of 5

While testing inside a bug bounty program's scope, you discover a database of real users' personal data is exposed. What should you do?

Question 5 of 5

A CVSS v3.1 base score is 8.1. Which severity band is that, and what does the number mainly convey?

Summary and what to practice next

  • Reverse engineering is a legitimate skill; what matters is authorization and intent. Analyze only what is yours, open, made for practice, or permitted in writing.
  • Licenses (contracts) and laws are different limits. Laws commonly cited include anti-circumvention rules (DMCA 1201 and its security-research exemptions), the EU Software Directive on interoperability, unauthorized-access laws (CFAA, narrowed by Van Buren), the Budapest Convention baseline, and data-protection law (GDPR). They differ by country and change over time.
  • A professional ethic sits above the law: minimize harm, protect other people's data, never hack back, honour embargoes, act in good faith.
  • Prefer coordinated disclosure; use full disclosure only as a last resort; stay inside a bug bounty's scope and look for safe-harbor terms.
  • Rate issues with CVSS, write clear reports with reproduction steps and the least necessary proof, respect the timeline and CVE process, and reach vendors via security.txt (RFC 9116).
  • When in doubt, stop and ask for permission or legal advice. This lesson is education, not legal advice.

Practice next: open the /.well-known/security.txt of a few services you use (reading a public file is harmless) and run your validator on them. Read one published vulnerability disclosure policy and find its scope, its rules and its safe-harbor wording. Take three published advisories, score their CVSS v3.1 vectors with your calculator and compare with the score each advisory states. Then write a full mock report, with redacted proof, for a bug you find in a deliberately vulnerable app running on your own machine, such as OWASP Juice Shop. Next: binary formats in depth, ELF and PE and the tools that read them.

This is the ethical backbone of the whole track. Every skill in it, reading binaries, debugging, deobfuscation, and the malware analysis still to come, becomes valuable precisely because you apply it responsibly: to defend systems, to make software safer, and to help others, never to harm. Carry these rules into every project.

Practice

Reading is not learning until you run something. Pick one.

Graded lab exercises

Open an example in Code Lab