IERG 4851 · Week 1

In-class Exercise Readmes

Six local observation and reasoning exercises. Work only on the supplied files in your own account.

Ungraded report due: submit your Week 1 report on Blackboard by 23:59, 17 September 2026, before the next class. Include the command(s) you used and a short answer for each task. Try your best: it is completely fine if you cannot finish every task.
01 · ELF BASICS

Identify an ELF binary

Run the binary once, then inspect it without executing it:

file elf-basics
readelf -h elf-basics
readelf -S elf-basics

Answer in your report

  1. Is this an ELF32 or ELF64 binary?
  2. Which CPU architecture does the header report?
  3. What is the entry-point address?
  4. Which section contains executable program instructions?

Useful habit: file gives a quick summary; readelf shows the actual ELF metadata.

02 · ARGUMENTS

Follow function arguments

The program calls combine with seven numbers. Inspect the call site:

./arguments
objdump -d -M intel arguments | less

Find main and the call instruction for combine.

Answer in your report

  1. Which registers carry arguments 1 through 6 on x86-64 Linux?
  2. Where does the seventh argument go?
  3. Which immediate values correspond to 10, 20, and 70 before the call?
  4. Does combine receive values or pointers in this example?

Hint: the standard calling convention here is the System V AMD64 ABI.

03 · BRANCHES

Read a conditional branch

Try two ordinary inputs, then inspect is_class_key:

./branches 1
./branches 4919
objdump -d -M intel branches | less

Answer in your report

  1. Which instruction compares the argument with the class key?
  2. Which conditional jump selects the non-matching path?
  3. Is the comparison made in decimal or hexadecimal at machine-code level?
  4. How can you confirm your reading by running the program with a normal input?

This is a control-flow reading exercise, not a bypass exercise.

04 · GDB STACK

Inspect a stack frame with GDB

This program is safe: snprintf is given the size of greeting. Use GDB to observe its stack frame:

gdb ./stack-frame
(gdb) break inspect_stack
(gdb) run Alice
(gdb) info registers
(gdb) disassemble /m inspect_stack
(gdb) print &greeting
(gdb) info frame

Answer in your report

  1. Which register initially holds the name pointer?
  2. Where is greeting stored while inspect_stack runs?
  3. What stack space does the compiler reserve? Read the instruction instead of guessing.
  4. Which source line copies text into greeting, and what size limit does it use?

Quit GDB with quit. Do not modify registers or return addresses in this first lesson.

05 · COPY REVIEW

Review an input boundary

This is a code-review exercise. Run the program only with a short name:

./copy-review Alice
objdump -d -M intel copy-review | less

Answer in your report

  1. How many bytes can name hold?
  2. Which function does not receive that destination size?
  3. Why is that a bug when the command-line input is longer than the buffer?
  4. Replace the copy with a bounded operation, compile your own fixed version, and test it with a short name.

Do not test oversized inputs in this exercise. The goal is to identify the boundary and implement a defensive fix.

06 · SHELLCODE SETUP

Guided ret2shellcode

This course-owned local demonstration shows control flow reaching bytes placed in a stack buffer. The supplied shellcode starts /bin/sh with your own student-account permissions; it does not use the network or elevate privileges.

Inspect the binary

file ret2shellcode
readelf -W -l ret2shellcode | grep GNU_STACK
objdump -d -M intel ret2shellcode | less
  1. What permissions are shown for the GNU_STACK program header, and why does this matter for this lab?
  2. Why does compiling without PIE make code addresses more stable, while the printed buffer address still changes between runs?
  3. Which source line lets more bytes be read than buffer can hold?
  4. In the vulnerable prologue, where is buffer relative to rbp? Use that to calculate the offset from the start of buffer to the saved return address.

Use GDB to verify the frame

gdb ./ret2shellcode
(gdb) break vulnerable
(gdb) run
(gdb) disassemble /m vulnerable
(gdb) print &buffer
(gdb) info frame

Do not change registers, memory, or return addresses in GDB. Use it only to read the frame layout for this guided exercise.

Read the shellcode

Open shellcode.py. It is x86 Linux shellcode that uses int 0x80 to call execve("/bin//sh", ["/bin//sh", NULL], NULL).

  1. Which register carries the syscall number?
  2. Which registers carry the filename, argument list, and environment pointers?
  3. Why is a NOP sled placed before the shellcode in the reference payload?
  4. Why does solution.py use struct.pack("<I", address)?