about summary refs log tree commit diff
path: root/fix.m4
blob: 3425e53d0c3127464e7ad5d9aaa3cd041423b350 (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
127
128
129
130
131
132
133
134
135
136
137
138
#!/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/>.

set -eux -o pipefail
save_exit_code() {
  set +e
  timeout -k 1 $1 ${@:2} 1>/dev/null 2>&1
  exit_code=$?
  set -e
}

bad() {
  save_exit_code $@
  test $exit_code -gt 128 ||
    test $exit_code -ge 124 -a $exit_code -le 127 # timeout
}

if test $# -lt 4
then
  echo Usage: taosc-fix WORKDIR TIMEOUT EXECUTABLE PROOFS-OF-CONCEPT [OPTION]...
  exit 1
fi
wd="$(realpath $1)"
test -d "$wd"
timeout=$2
bin="$wd/$(basename $3)"
binary="$(realpath $3)"
test -x "$binary"
poc="$(realpath $4)"
test -d "$poc"
test "$(ls -A "$poc")"
options="${@:5}" # TODO: interpolation

mkdir -p "$wd"
rm -fr "$wd/poc"
cp -r "$poc" "$wd/poc"
for exploit in "$wd"/poc/*
do
  gdb --batch --ex run --ex backtrace --args\
    "$binary" $options "$exploit" 2>/dev/null |
    grep '^#[0-9]\+ \+0x[0-9a-f]\+' |
    awk '!$7 || $7 == bin {print $1, $2}' "bin=$binary" |
    sed 's/^#//'
done | sort -n | uniq > "$wd/stack-trace"

grep '^0 0x[0-9a-f]\+$' "$wd/stack-trace" |
  sed 's/^0 0x0*//' > "$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 bad $timeout env TAOSC_DEST=0x$dest "$bin.jump" $options "$exploit"
        then
          continue 2 # next destination
        fi
      done
      echo $loc > "$wd/patch-location"
      echo $dest >> "$wd/destinations"
    done
  done 1>/dev/null 2>&1
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" $option @@
rm -fr "$wd/input"
mkdir -p "$wd/input/benign"
cp -r "$poc" "$wd/input/malicious"
# TODO: use parallel
for dat in "$wd"/fuzzolic/fuzzolic-*/test_case_*.dat
do
  if taosc-reach $timeout "$bin.covered" $options "$dat" 1>/dev/null 2>&1
  then
    if bad $timeout "$binary" $options "$dat"
    then
      cp $dat "$wd/input/malicious"
    else
      cp $dat "$wd/input/benign"
    fi
  fi
done

rm -fr "$wd/values"
for input_dir in "$wd"/input/*
do
  output_dir="$wd/values/$(basename "$input_dir")"
  mkdir -p "$output_dir"
  # TODO: use parallel
  for input in "$input_dir"/*
  do
    output="$output_dir/$(basename "$input")"
    save_exit_code $timeout\
      env TAOSC_STACK_SIZE=$stack_size TAOSC_OUTPUT=$output\
      "$bin.collect" $options "$input"
  done
done
# vim: filetype=sh.m4