Skip to content

Commit

Permalink
data/lab/arena: Add exec-shellcode challenge
Browse files Browse the repository at this point in the history
A challenge to exercise working with mmap() and memory copying.

Signed-off-by: Razvan Deaconescu <[email protected]>
  • Loading branch information
razvand committed Nov 6, 2023
1 parent 9ccd2db commit 1cdd478
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 0 deletions.
32 changes: 32 additions & 0 deletions content/chapters/data/lab/content/arena.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,38 @@ test_res_10_res_20 ........................ passed ... 25
Total: 100/100
```

## Shellcode Executor

Navigate to the `support/exec-shellcode/` directory.

Your goal is to update the `src/exec-shellcode` source code file to be able to read and execute shellcodes from a given binary files.
The program thus acts as a shellcode tester.
Shellcodes end up in an `exit()` system call to ensure a graceful exit of the program after running the shellcode.
Use `mmap()` to reserve a virtual page.
Use anonymous mapping (i.e. the `MAP_ANONYMOUS`) flag.
Use the proper permissions required to enable the shellcode to be read from the file into memory and then executed.

To test the implementation, enter the `tests/` directory and run:

```console
make check
```

As an extra item, add a shellcode for the `brk()` system call in the `tests/brk.asm` file.
It should be a simple shellcode that calls `brk(NULL)`, i.e. with the purpose of getting the current program break.

In case of a correct solution, you will get an output such as:

```text
./run_all_tests.sh
test_helloworld ........................ passed ... 25
test_getpid ........................ passed ... 25
test_openfile ........................ passed ... 25
test_brk ........................ passed ... 25
Total: 100/100
```

## Memory Support

**Manual memory management** (MMM) is one of the most difficult tasks.
Expand Down
14 changes: 14 additions & 0 deletions content/chapters/data/lab/solution/exec-shellcode/src/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CFLAGS ?= -Wall -Wextra
CPPFLAGS ?= -I../utils

.PHONY: all clean

all: exec_shellcode

exec_shellcode: exec_shellcode.o ../utils/log/log.o

../utils/log/log.o: ../utils/log/log.c ../utils/log/log.h

clean:
-rm -f exec_shellcode exec_shellcode
-rm -f *~
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

#include "utils.h"

static void *shellcode_mapping;

static void usage(const char * const argv0)
{
fprintf(stderr, "Usage: %s shellcode_file\n", argv0);
}

static void create_shellcode_mapping(void)
{
/* TODO 2: Create mapping to fit the shellcode. */
shellcode_mapping = mmap(NULL, sysconf(_SC_PAGESIZE), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
DIE(shellcode_mapping == MAP_FAILED, "mmap");
}

static void read_shellcode(const char * const fname)
{
/* TODO 8: Read content from file in shellcode. */
FILE *f;

f = fopen(fname, "rb");
DIE(f == NULL, "fopen");

fread(shellcode_mapping, sysconf(_SC_PAGESIZE), 1, f);

fclose(f);
}

int main(int argc, char **argv)
{
if (argc != 2) {
usage(argv[0]);
exit(EXIT_FAILURE);
}

create_shellcode_mapping();
read_shellcode(argv[1]);

((void (*)(void)) shellcode_mapping)();

return 0;
}
15 changes: 15 additions & 0 deletions content/chapters/data/lab/solution/exec-shellcode/tests/brk.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
BITS 64
; call brk(0)
; rax <- 12 (__NR_brj)
; rdi <- 0
; TODO 3: Make brk syscall.
mov rax, 12
xor rdi,rdi
syscall

; call exit_group(0)
; rax <- 231 (__NR_exit_group)
; rdi <- 0 (exit status)
mov rax, 231
xor rdi, rdi
syscall
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/brk
/getpid
/helloworld
/openfile
34 changes: 34 additions & 0 deletions content/chapters/data/lab/support/exec-shellcode/tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
SRC_PATH ?= ../src
FULL_SRC_PATH = $(realpath $(SRC_PATH))
CPPFLAGS = -I. -I$(realpath $(SRC_PATH)) -I../utils
CFLAGS = -Wall -Wextra
# Remove the line below to disable debugging support.
CFLAGS += -g -O0

SRCS = $(wildcard *.asm)
SHELLCODES = $(patsubst %.asm,%,$(SRCS))

.PHONY: all src check lint clean

all: $(SHELLCODES) src

$(SHELLCODES): %:%.asm | src
nasm -o $@ $<

src:
make -C $(FULL_SRC_PATH)

check: $(SHELLCODES)
make -C $(FULL_SRC_PATH) clean
make clean
make -i SRC_PATH=$(FULL_SRC_PATH)
./run_all_tests.sh

lint:
-cd .. && checkpatch.pl -f src/*.c
-cd .. && checkpatch.pl -f tests/*.sh
-cd .. && cpplint --recursive src/
-cd .. && shellcheck tests/*.sh

clean:
-rm -f *~
12 changes: 12 additions & 0 deletions content/chapters/data/lab/support/exec-shellcode/tests/brk.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BITS 64
; call brk(0)
; rax <- 12 (__NR_brj)
; rdi <- 0
; TODO 3: Make brk syscall.

; call exit_group(0)
; rax <- 231 (__NR_exit_group)
; rdi <- 0 (exit status)
mov rax, 231
xor rdi, rdi
syscall
12 changes: 12 additions & 0 deletions content/chapters/data/lab/support/exec-shellcode/tests/getpid.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BITS 64
; call getpid()
; rax <- 39 (__NR_write)
mov rax, 39
syscall

; call exit_group(0)
; rax <- 231 (__NR_exit_group)
; rdi <- 0 (exit status)
mov rax, 231
xor rdi, rdi
syscall
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause

#
# Print test result. Printed message should fit in 72 characters.
#
# Print format is:
#
# description ...................... passed ... NNN
# description ...................... failed ... NNN
# 32 chars 24 chars 6 3 3
#

print_test()
{
func="$1"
result="$2"
points="$3"

if test "$points" -gt 999; then
points=999
fi

printf "%-32s " "${func:0:31}"
printf "........................"
if test "$result" -eq 0; then
printf " passed ... %3d\n" "$points"
else
printf " failed ... 0\n"
fi
}

run_test()
{
func="$1"
points="$2"

# Run in subshell.
(eval "$func")
print_test "$func" "$?" "$points"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BITS 64
; use jmp / call trick to get string address in RCX
jmp hello
back:
; call write(1, "Hello, World!\n", 14);
; rax <- 1 (__NR_write)
; rdi <- 1 (stdout fileno)
; rsi <- "Hello, World!\n"
; rdx <- 14 (string length)
mov rax, 1
mov rdi, 1
pop rsi
mov rdx, 14
syscall

; call exit_group(0)
; rax <- 231 (__NR_exit_group)
; rdi <- 0 (exit status)
mov rax, 231
xor rdi, rdi
syscall

hello:
call back
db "Hello, World!", 10, 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
BITS 64
; use jmp / call trick to get filename address in RCX
jmp filename
back:
; call open("uberfile", O_RDWR | O_TRUNC | O_CREAT, 0644)
; rax <- 2 (__NR_open)
; rdi <- "uberfile"
; rsi <- 578 (O_RDWR | O_TRUNC | O_CREAT - 01102)
; rdx <- 420 (0644)
mov rax, 2
pop rdi
mov rsi, 578
mov rdx, 420
syscall

; call exit_group(0)
; rax <- 231 (__NR_exit_group)
; rdi <- 0 (exit status)
mov rax, 231
xor rdi, rdi
syscall

filename:
call back
db "uberfile", 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause

if test -z "$SRC_PATH"; then
SRC_PATH=../src
fi

export SRC_PATH

(
./test_helloworld.sh
./test_getpid.sh
./test_openfile.sh
./test_brk.sh
) | tee results.txt

total=$(grep '\( passed \| failed \)' results.txt | rev | cut -d ' ' -f 1 | rev | paste -s -d'+' | bc)
echo ""
echo -n "Total: "
echo -n " "
LC_ALL=C printf "%3d/100\n" "$total"

rm results.txt
39 changes: 39 additions & 0 deletions content/chapters/data/lab/support/exec-shellcode/tests/test_brk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause

source graded_test.inc.sh

shellcode=./brk

if test -z "$SRC_PATH"; then
SRC_PATH=../src
fi

test_brk()
{
if test ! -f "$shellcode"; then
echo "No such file $shellcode" 1>&2
exit 1
fi

objdump -D -M intel -b binary -m i386:x86-64 "$shellcode" > /dev/null 2>&1
if test $? -ne 0; then
echo "Incorrect shellcode file" 1>&2
exit 1
fi

timeout -k 1 3 "$SRC_PATH"/exec_shellcode "$shellcode" > /dev/null 2>&1
if test $? -ne 0; then
echo "Program runs unsuccessfully" 1>&2
exit 1
fi

timeout -k 1 3 strace "$SRC_PATH"/exec_shellcode "$shellcode" 2>&1 | grep -A 1 close | tail -2 | grep 'brk(NULL)[ \t]\+= 0x' > /dev/null 2>&1
if test $? -ne 0; then
echo "brk not called correctly" 1>&2
exit 1
fi
exit 0
}

run_test test_brk 25
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause

source graded_test.inc.sh

shellcode=./getpid

if test -z "$SRC_PATH"; then
SRC_PATH=../src
fi

test_getpid()
{
if test ! -f "$shellcode"; then
echo "No such file $shellcode" 1>&2
exit 1
fi

objdump -D -M intel -b binary -m i386:x86-64 "$shellcode" > /dev/null 2>&1
if test $? -ne 0; then
echo "Incorrect shellcode file" 1>&2
exit 1
fi

timeout -k 1 3 "$SRC_PATH"/exec_shellcode "$shellcode" > /dev/null 2>&1
if test $? -ne 0; then
echo "Program runs unsuccessfully" 1>&2
exit 1
fi

timeout -k 1 3 strace "$SRC_PATH"/exec_shellcode "$shellcode" 2>&1 | grep -A 1 close | tail -2 | grep 'getpid()[ \t]\+= [0-9]\+' > /dev/null 2>&1
if test $? -ne 0; then
echo "getpid() not called (successfully)" 1>&2
exit 1
fi
exit 0
}

run_test test_getpid 25
Loading

0 comments on commit 1cdd478

Please sign in to comment.