Skip to content

Commit 17e9515

Browse files
authored
Network namespaces support (aflnet#70)
* Add network namespaces support; a tutorial is included * This feature is experimental
1 parent 5d5e89a commit 17e9515

File tree

4 files changed

+150
-2
lines changed

4 files changed

+150
-2
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ CFLAGS += -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -Wno-unused-result
3333
-DBIN_PATH=\"$(BIN_PATH)\"
3434

3535
ifneq "$(filter Linux GNU%,$(shell uname))" ""
36-
LDFLAGS += -ldl -lgvc -lcgraph -lm
36+
LDFLAGS += -ldl -lgvc -lcgraph -lm -lcap
3737
endif
3838

3939
ifeq "$(findstring clang, $(shell $(CC) --version 2>/dev/null))" ""

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ AFLNet adds the following options to AFL. Run ```afl-fuzz --help``` to see all o
6969

7070
- ***-D usec***: (optional) waiting time (in microseconds) for the server to complete its initialization
7171

72+
- ***-e netnsname***: (optional) network namespace name to run the server in
73+
7274
- ***-K*** : (optional) send SIGTERM signal to gracefully terminate the server after consuming all request messages
7375

7476
- ***-E*** : (optional) enable state aware mode

afl-fuzz.c

+90-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
#include <sys/mman.h>
6767
#include <sys/ioctl.h>
6868
#include <sys/file.h>
69+
#include <sys/capability.h>
6970

7071
#include "aflnet.h"
7172
#include <graphviz/gvc.h>
@@ -372,6 +373,7 @@ u32 state_cycles = 0;
372373
u32 messages_sent = 0;
373374
EXP_ST u8 session_virgin_bits[MAP_SIZE]; /* Regions yet untouched while the SUT is still running */
374375
EXP_ST u8 *cleanup_script; /* script to clean up the environment of the SUT -- make fuzzing more deterministic */
376+
EXP_ST u8 *netns_name; /* network namespace name to run server in */
375377
char **was_fuzzed_map = NULL; /* A 2D array keeping state-specific was_fuzzed information */
376378
u32 fuzzed_map_states = 0;
377379
u32 fuzzed_map_qentries = 0;
@@ -2827,6 +2829,25 @@ static void destroy_extras(void) {
28272829

28282830
}
28292831

2832+
/* Move process to the network namespace "netns_name" */
2833+
2834+
static void move_process_to_netns() {
2835+
const char *netns_path_fmt = "/var/run/netns/%s";
2836+
char netns_path[272]; /* 15 for "/var/.." + 256 for netns name + 1 '\0' */
2837+
int netns_fd;
2838+
2839+
if (strlen(netns_name) > 256)
2840+
FATAL("Network namespace name \"%s\" is too long", netns_name);
2841+
2842+
sprintf(netns_path, netns_path_fmt, netns_name);
2843+
2844+
netns_fd = open(netns_path, O_RDONLY);
2845+
if (netns_fd == -1)
2846+
PFATAL("Unable to open %s", netns_path);
2847+
2848+
if (setns(netns_fd, CLONE_NEWNET) == -1)
2849+
PFATAL("setns failed");
2850+
}
28302851

28312852
/* Spin up fork server (instrumented mode only). The idea is explained here:
28322853
@@ -2893,6 +2914,11 @@ EXP_ST void init_forkserver(char** argv) {
28932914

28942915
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
28952916

2917+
/* Move the process to the different namespace. */
2918+
2919+
if (netns_name)
2920+
move_process_to_netns();
2921+
28962922
/* Isolate the process and configure standard descriptors. If out_file is
28972923
specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
28982924

@@ -3174,6 +3200,11 @@ static u8 run_target(char** argv, u32 timeout) {
31743200

31753201
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
31763202

3203+
/* Move the process to the different namespace. */
3204+
3205+
if (netns_name)
3206+
move_process_to_netns();
3207+
31773208
/* Isolate the process and configure standard descriptors. If out_file is
31783209
specified, stdin is /dev/null; otherwise, out_fd is cloned instead. */
31793210

@@ -8069,6 +8100,7 @@ static void usage(u8* argv0) {
80698100
" -D usec - waiting time (in micro seconds) for the server to initialize\n"
80708101
" -W msec - waiting time (in miliseconds) for receiving the first response to each input sent\n"
80718102
" -w usec - waiting time (in micro seconds) for receiving follow-up responses\n"
8103+
" -e netnsname - run server in a different network namespace\n"
80728104
" -K - send SIGTERM to gracefully terminate the server (see README.md)\n"
80738105
" -E - enable state aware mode (see README.md)\n"
80748106
" -R - enable region-level mutation operators (see README.md)\n"
@@ -8731,6 +8763,50 @@ static void save_cmdline(u32 argc, char** argv) {
87318763

87328764
}
87338765

8766+
/* Check that afl-fuzz (file/process) has some effective and permitted capability */
8767+
8768+
static int check_ep_capability(cap_value_t cap, const char *filename) {
8769+
cap_t file_cap, proc_cap;
8770+
cap_flag_value_t cap_flag_value;
8771+
int no_capability = 1;
8772+
int pid = getpid();
8773+
8774+
file_cap = cap_get_file(filename);
8775+
proc_cap = cap_get_proc();
8776+
8777+
if (!file_cap && !proc_cap)
8778+
return no_capability;
8779+
8780+
if (file_cap) {
8781+
if (cap_get_flag(file_cap, cap, CAP_EFFECTIVE, &cap_flag_value))
8782+
PFATAL("Could not get CAP_EFFECTIVE flag value from file \"%s\"", filename);
8783+
8784+
if (cap_flag_value != CAP_SET)
8785+
return no_capability;
8786+
8787+
if (cap_get_flag(file_cap, cap, CAP_PERMITTED, &cap_flag_value))
8788+
PFATAL("Could not get CAP_PERMITTED flag value from file \"%s\"", filename);
8789+
8790+
if (cap_flag_value != CAP_SET)
8791+
return no_capability;
8792+
}
8793+
8794+
if (proc_cap) {
8795+
if (cap_get_flag(proc_cap, cap, CAP_EFFECTIVE, &cap_flag_value))
8796+
PFATAL("Could not get CAP_EFFECTIVE flag value from process id %d", pid);
8797+
8798+
if (cap_flag_value != CAP_SET)
8799+
return no_capability;
8800+
8801+
if (cap_get_flag(proc_cap, cap, CAP_PERMITTED, &cap_flag_value))
8802+
PFATAL("Could not get CAP_PERMITTED flag value from process id %d", pid);
8803+
8804+
if (cap_flag_value != CAP_SET)
8805+
return no_capability;
8806+
}
8807+
8808+
return 0;
8809+
}
87348810

87358811
#ifndef AFL_LIB
87368812

@@ -8756,7 +8832,7 @@ int main(int argc, char** argv) {
87568832
gettimeofday(&tv, &tz);
87578833
srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());
87588834

8759-
while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:QN:D:W:w:P:KEq:s:RFc:l:")) > 0)
8835+
while ((opt = getopt(argc, argv, "+i:o:f:m:t:T:dnCB:S:M:x:QN:D:W:w:e:P:KEq:s:RFc:l:")) > 0)
87608836

87618837
switch (opt) {
87628838

@@ -8952,6 +9028,12 @@ int main(int argc, char** argv) {
89529028
socket_timeout = 1;
89539029
break;
89549030

9031+
case 'e': /* network namespace name */
9032+
if (netns_name) FATAL("Multiple -e options not supported");
9033+
9034+
netns_name = optarg;
9035+
break;
9036+
89559037
case 'P': /* protocol to be tested */
89569038
if (protocol_selected) FATAL("Multiple -P options not supported");
89579039

@@ -9052,6 +9134,13 @@ int main(int argc, char** argv) {
90529134

90539135
if (!protocol_selected) FATAL("Please specify the protocol to be tested using the -P option");
90549136

9137+
if (netns_name) {
9138+
if (check_ep_capability(CAP_SYS_ADMIN, argv[0]) != 0)
9139+
FATAL("Could not run the server under test in a \"%s\" network namespace "
9140+
"without CAP_SYS_ADMIN capability.\n You can set it by invoking "
9141+
"afl-fuzz with sudo or by \"$ setcap cap_sys_admin+ep /path/to/afl-fuzz\".", netns_name);
9142+
}
9143+
90559144
setup_signal_handlers();
90569145
check_asan_opts();
90579146

tutorials/network-namespace/README.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Tutorial - Using network namespaces
2+
3+
A network namespace is a logical copy of the network stack from the host system. Each namespace has its own IP addresses, network interfaces, routing tables, and so forth.
4+
You can ask AFLNet to run your target server inside a such namespace to isolate it from the rest of the system and operate completly independently.
5+
6+
7+
## Step-0. Network namespace setup
8+
9+
There is a good [article](ifeanyi.co/posts/linux-namespaces-part-4/) by [Ifeanyi Ubah](https://github.com/iffyio) explaining how to work with linux namespaces.
10+
Here's the short version:
11+
12+
```
13+
┌────────────────────────────────────────────┐
14+
│ │
15+
│ ┌──────────────────────┐ │
16+
│ │ │ │
17+
│ │ ┌─────┤ ┌─────┤
18+
│ │ │ │ │ │
19+
│ │ │veth1│◄───────────►│veth0│
20+
│ │ │ │ │ │
21+
│ │ └─────┤ └─────┤
22+
│ │ nspce │ │
23+
│ └──────────────────────┘ │
24+
│ │
25+
│ Host namespace│
26+
└────────────────────────────────────────────┘
27+
```
28+
29+
```bash
30+
# create network namespace
31+
sudo ip netns add nspce
32+
# Create a veth pair
33+
sudo ip link add veth0 type veth peer name veth1
34+
# Move veth1 to the new namespace
35+
sudo ip link set veth1 netns nspce
36+
# Bring loopback interface up
37+
sudo ip netns exec nscpe ip link set dev lo up
38+
# Assign ip to interface in the new namespace
39+
sudo ip netns exec nscpe ip addr add 10.0.0.2/24 dev veth1
40+
sudo ip netns exec nscpe ip link set dev veth1 up
41+
# Assign ip to interface in the host namespace
42+
sudo ip addr add 10.0.0.1/24 dev veth0
43+
sudo ip link set dev veth0 up
44+
```
45+
Now we should be able to do an inter-namespace communication between two processes running in both namespaces.
46+
47+
## Step-1. Fuzzing
48+
49+
That's all preparations required. Fuzz servers as usual.
50+
51+
You can use `-e <netns name>` option to specify network namespace to run the server in.
52+
53+
Considering the abovementioned setup:
54+
55+
```bash
56+
./afl-fuzz -i ~/paht/to/seed -o ~/path/to/fuzzer/findings -N tcp://10.0.0.2/port -P protocol -e nspce ~/path/to/server args
57+
```

0 commit comments

Comments
 (0)