Skip to content

Commit

Permalink
Added example 08 - a mujs fuzzing harness using the new TextInput d…
Browse files Browse the repository at this point in the history
…ata.

Fixed some issues with the text-based mutator code along the way.

* The new example has two artificial vulnerabilities that can be triggered
  using the right arguments to a JS function.
* Added some tweaks to the text-based mutations.
* In order to avoid breakpoints on longjump and similar functions, we
  need to properly detach gdb. This requires a pattern of
  ```
    __asm("int3");
    sleep(1);
    __asm("vmcall");
  ```
  and also the `SNAPSHOT_GDB_MODE="detach"` definition. To allow
  gdbsnapshot.py to do its thing, but then also to detach gdb before the
  snapshot is triggered.
  • Loading branch information
f0rki committed Feb 6, 2024
1 parent 1e0f9ca commit b7a76e8
Show file tree
Hide file tree
Showing 25 changed files with 663 additions and 22 deletions.
2 changes: 2 additions & 0 deletions docker/utils/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ echo "" >> $RC_LOCAL

echo "echo [+] waiting for processes to finish" >> $RC_LOCAL
echo "wait" >> $RC_LOCAL
echo "sleep 5" >> $RC_LOCAL
echo "wait" >> $RC_LOCAL

# Add a newline
echo "" >> $RC_LOCAL
Expand Down
1 change: 1 addition & 0 deletions examples/08_textinput_mujs/.dockerignore
23 changes: 23 additions & 0 deletions examples/08_textinput_mujs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# snapshot-specific
snapshot/
snapshot_image
src/constants.rs
dict/

# rust build artifacts
debug/
target/
Cargo.lock
**/*.rs.bk
*.pdb

# built harness binaries
harness/mujs_harness
harness/mujs-*

# other misc data
perf.data*
flamegraph.svg
strace.log
fuzz*.log
*.log
2 changes: 2 additions & 0 deletions examples/08_textinput_mujs/.solutions/magic_crash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var r = magic(-10000000);
console.log(r);
1 change: 1 addition & 0 deletions examples/08_textinput_mujs/.solutions/party_crash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
party(1, 1, 1, 40);
25 changes: 25 additions & 0 deletions examples/08_textinput_mujs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "mujs_fuzzer"
version = "0.1.0"
edition = "2021"
exclude = ["qemu_snapshot", "snapshot"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
snapchange = { path = "../../" }
log = "0.4"

[build-dependencies]
regex = "1"

[features]
default = []
redqueen = ["snapchange/redqueen"]

[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
25 changes: 25 additions & 0 deletions examples/08_textinput_mujs/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# build environment -- feel free to adapt to your needs.

FROM alpine:edge as base

RUN apk --no-cache upgrade \
&& apk add --no-cache --initramfs-diskless-boot \
python3 gdb musl-dbg \
curl tar \
build-base perf \
clang lld compiler-rt

COPY ./harness/ /opt/
RUN ls -alR /opt/
RUN make -C /opt/

#### switch to snapchang container ####
FROM ghcr.io/awslabs/snapchange

# copy whole root filesystem from build environment to this container layer
# SNAPSHOT_INPUT is the location that the snapshotting script will create.
COPY --from=base / "$SNAPSHOT_INPUT"

# set the path to the target within the root filesystem of the build environment
ENV SNAPSHOT_ENTRYPOINT="/opt/mujs_harness"
ENV SNAPSHOT_GDB_MODE="detach"
64 changes: 64 additions & 0 deletions examples/08_textinput_mujs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
DOCKER ?= docker
FUZZ_CORES ?= /2

DOCKER_IMAGE_NAME ?= snapchange_example8

all: base_images test

base_images:
# Build the base snapchange image used for snapshotting
$(MAKE) -C ../../docker

snapshot_image: Dockerfile harness/main.c harness/Makefile
ifeq ($(VERBOSE),1)
$(DOCKER) build -t $(DOCKER_IMAGE_NAME):snapshot . -f $<
endif
$(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@

snapshot: snapshot_image
ifeq ($(VERBOSE),1)
$(DOCKER) run --rm -i \
-v $(shell realpath -m ./snapshot):/snapshot \
-e SNAPSHOT_IMGTYPE=initramfs \
$(shell cat $<)
else
$(DOCKER) run --rm -i \
-v $(shell realpath -m ./snapshot):/snapshot \
-e SNAPSHOT_IMGTYPE=initramfs \
$(shell cat $<) \
>/dev/null 2>&1
endif
cd snapshot; if ! test -L input; then rm -rf input || true; ln -s ../input; fi
cd snapshot; if ! test -L dict; then rm -rf dict || true; ln -s ../dict/; fi

dict: dict.txt
-mkdir -p dict
python make_dict.py $<
echo "magic" > dict/magic
echo "party" > dict/party

fuzzer: dict
cargo build -r

fuzz: snapshot
cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES)

fuzz-%: snapshot
cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m
# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5

test: snapshot fuzzer reset
./test.sh

reset: snapshot
cd snapshot && ./reset.sh

clean: clean-docker
-$(RM) -rf snapshot target

clean-docker:
-$(DOCKER) rmi `cat ./snapshot_image`
-$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot
-$(RM) snapshot_image

.PHONY: fuzzer all base_images test reset fuzz
5 changes: 5 additions & 0 deletions examples/08_textinput_mujs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Tutorial 8 - Using the text input type

This fuzzer uses the `TextInput` type to introduce more useful mutations for
common text-based formats such as programming or markup languages.

30 changes: 30 additions & 0 deletions examples/08_textinput_mujs/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use regex::Regex;
use std::fs;
use std::fs::File;
use std::io::Write;

fn main() {
println!("cargo:rerun-if-changed=snapshot/fuzzvm.qemuregs");
println!("cargo:rerun-if-changed=snapshot/vm.log");

let qemuregs = fs::read_to_string("./snapshot/fuzzvm.qemuregs").unwrap();
let mut w = File::create("src/constants.rs").unwrap();

writeln!(w, "#![allow(unused)]").unwrap();

let re = Regex::new(r"CR3=([0-9A-Fa-f]+)").unwrap();
let captures = re.captures(&qemuregs).unwrap();
let cr3 = &captures.get(1).unwrap().as_str();
writeln!(w, "pub const CR3: u64 = 0x{};", cr3).unwrap();

let re = Regex::new(r"RIP=([0-9A-Fa-f]+)").unwrap();
let captures = re.captures(&qemuregs).unwrap();
let rip = &captures.get(1).unwrap().as_str();
writeln!(w, "pub const RIP: u64 = 0x{};", rip).unwrap();

let vmlog = fs::read_to_string("./snapshot/vm.log").unwrap();
let re = Regex::new(r"SNAPSHOT buffer: (0x[0-9A-Fa-f]+)").unwrap();
let captures = re.captures(&vmlog).unwrap();
let input_addr = &captures.get(1).unwrap().as_str();
writeln!(w, "pub const INPUT: u64 = {};", input_addr).unwrap();
}
125 changes: 125 additions & 0 deletions examples/08_textinput_mujs/dict.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
abstract
arguments
await
boolean
break
byte
case
catch
char
class
const
continue
debugger
default
delete
do
double
else
enum
eval
export
extends
false
final
finally
float
for
function
goto
if
implements
import
in
instanceof
int
interface
let
long
native
new
null
package
private
protected
public
return
short
static
super
switch
synchronized
this
throw
throws
transient
true
try
typeof
var
void
volatile
while
with
yield

Array
Date
eval
function
hasOwnProperty
Infinity
isFinite
isNaN
isPrototypeOf
length
Math
NaN
name
Number
Object
prototype
String
toString
undefined
valueOf

+
-
*
**
/
%
++
--
==
===
<
>
>=
<=
=
+=
-=
*=
/=
%=
**=
<<=
>>=
&
&=
;

()
[0]
[1]

Math.pow

0.0
1e10
-1
-254
-255

5 changes: 5 additions & 0 deletions examples/08_textinput_mujs/harness/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mujs-1.3.4.tar.gz
mujs-1.3.4
mujs_harness
.cache
compile_commands.json
17 changes: 17 additions & 0 deletions examples/08_textinput_mujs/harness/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
ifeq ($(origin CC),default)
CC = clang
endif
# CFLAGS ?= -O3 -ggdb -fuse-ld=lld -fsanitize=address -fno-omit-frame-pointer -flto
CFLAGS ?= -O3 -ggdb -fuse-ld=lld -fno-omit-frame-pointer -flto

mujs_harness: main.c mujs-1.3.4 Makefile
$(CC) $(CFLAGS) -o $@ $< ./mujs-1.3.4/one.c -I./mujs-1.3.4/ -lm

mujs_harness.asan: main.c mujs-1.3.4 Makefile
$(CC) $(CFLAGS) -fsanitize=address -o $@ $< ./mujs-1.3.4/one.c -I./mujs-1.3.4/ -lm

clean:
-$(RM) mujs_harness

mujs-1.3.4:
curl -q https://mujs.com/downloads/mujs-1.3.4.tar.gz | tar xz
Loading

0 comments on commit b7a76e8

Please sign in to comment.