about summary refs log tree commit diff
path: root/fix.m4
blob: d896a5c7618d90c70c9f1d889722afc31ae4decf (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/bin/sh
# Executable patcher
# Copyright (C) 2024-2025  Nguyễn Gia Phong
#
# This file is part of taosc.
#
# Taosc is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taosc is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with taosc.  If not, see <https://www.gnu.org/licenses/>.

save_exit_code() {
  template="${@:3}"
  cmd="$(printf %s "$template" | sed "s#@@#$2#g")"
  set +e
  AFL_USE_QASAN=1 timeout -k 0 $1 afl-qemu-trace $cmd
  exit_code=$?
  set -e
}

bad() {
  save_exit_code $@ 1>/dev/null 2>&1
  test $exit_code -gt 128 ||
    test $exit_code -ge 124 -a $exit_code -le 127 # timeout
}

if test $# -lt 5
then
  echo Usage: taosc-fix TIMEOUT WORKDIR PROOFS_OF_CONCEPT EXECUTABLE ARG...
  exit 1
fi

timeout=$1
set -eux -o pipefail
wd="$(realpath "$2")"
test -d "$wd"
poc="$(realpath "$3")"
test -d "$poc"
test "$(ls -A "$poc")"
binary="$(realpath "$4")"
test -x "$binary"
bin="$wd/$(basename "$4")"
args="${@:5}"

mkdir -p "$wd"
rm -fr "$wd/poc"
cp -r "$poc" "$wd/poc"
for exploit in "$wd"/poc/*
do
  save_exit_code $timeout "$exploit" "$binary" $args 2>&1 1>/dev/null |
    grep '^    #' |
    grep -F "$binary" |
    sed 's/^    #\([0-9]\+ 0x[0-9a-f]\+\).*$/\1/'
done | sort -n | uniq > "$wd/stack-trace"

(grep '^0 0x[0-9a-f]\+$' "$wd/stack-trace" | sed 's/^0 0x0*//' ||
  true) > "$wd/call-trace"
# Stack trace contains return addresses, not call addresses:
# https://devblogs.microsoft.com/oldnewthing?p=96116
grep -v '^0 0x[0-9a-f]\+$' "$wd/stack-trace" |
  sed 's/^[0-9]\+ 0x0*//' |
  taosc-trace-call "$binary" >> "$wd/call-trace"

rm -f "$wd/patch-location"
pushd DATA_DIR 1>/dev/null
trap 'popd 1>/dev/null' EXIT
taosc-scout "$binary" < "$wd/call-trace" |
  while read loc destinations && test ! -f "$wd/patch-location"
  do
    e9tool -100 -M addr=0x$loc -P 'if dest()@jump goto' -o "$bin.jump" "$binary"
    rm -f "$wd/destinations"
    for dest in $destinations
    do
      # In case $wd/poc got poluted
      rm -fr "$wd/poc"
      cp -r "$poc" "$wd/poc"
      for exploit in "$wd/poc"/*
      do
        if TAOSC_DEST=0x$dest bad $timeout "$exploit" "$bin.jump" $args
        then
          continue 2 # next destination
        fi
      done
      echo $loc > "$wd/patch-location"
      echo $dest >> "$wd/destinations"
    done
  done
test -s "$wd/patch-location"
test -s "$wd/destinations"

stack_size=$(taosc-measure-stack "$binary" < "$wd/patch-location")
patch_loc=0x$(< "$wd/patch-location")
e9tool -100 -M addr=$patch_loc -P 'report()@cover' -o "$bin.covered" "$binary"
e9tool -100 -M addr=$patch_loc -P 'log(state)@collect'\
  -o "$bin.collect" "$binary"
e9tool -100 -M addr=$patch_loc -P 'if dest(state)@patch goto'\
  -o "$bin.patched" "$binary"

# TODO: FUZZOLIC's options
fuzzolic -kmprst 90000 -i "$poc" -o "$wd/fuzzolic" -- "$binary" $args ||
  true # FIXME: failing with the same status as the target program
rm -fr "$wd/input"
mkdir -p "$wd/input/benign"
cp -r "$poc" "$wd/input/malicious"
find "$wd/fuzzolic" -name 'test_case_*.dat' -print0 |
  xargs -I '{}' -0 -P$(nproc) -n1 \
  taosc-sort-inputs $timeout "$wd"/input/{malicious,benign} '{}' \
  "$bin.covered" $args

rm -fr "$wd/values"
mkdir -p "$wd"/values/{benign,malicious}
find "$wd/input" -print0 |
  xargs -I '{}' -0 -P$(nproc) -n1 \
  taosc-collect-values $timeout $stack_size "$wd/values" '{}' \
  "$bin.collect" $args
# TODO: split if the patch location is reached multiple times with an input
taosc-synth $stack_size "$wd"/values/{benign,malicious} > "$wd/predicates"
# vim: filetype=sh.m4