taCTF (“ta-CTF”) is a side-channel timing attack tool that uses Valgrind. It reverses binaries that expect a string input and compare it to a stored “secret” string by counting instructions.
Python 3, Valgrind. No additional modules.
Clone this repository, then:
./taCTF.py binaryname -v
-v
makes the output verbose (recommended). There are more complex
ways to use taCTF, listed below.
Argument | Long Form | Function | Default |
---|---|---|---|
-h | --help | Help! | |
-v | --verbose | Verbose output, with each iteration printed | False |
-r | --reverse | Try string backwards. | False |
-f | --flag-format | Known flag format to try. | |
-l | --length | Length if known. | |
-c | --charset | Which ASCII character ‘set’ to try. | 0 |
-ml | --max-length | Maximum length to check till. | 35 |
The examples are in the test
directory.
gcc test/example.c
to get a.out
. Then, first try:
./taCTF.py ./a.out -v
taCTF first tries to figure out the length by counting instructions. It then bruteforces the string character by character, left to right.
If we know the flag format in advance, we can save some time:
./taCTF.py ./a.out -v -f sigpwny{
If we know the length in advance as well, taCTF will use what we ask it to:
./taCTF.py ./a.out -v -f sigpwny{ -l 14
Sometimes, we can guess the character set to reduce its size. In this case, we known the restriction is lowercase letters, so:
./taCTF.py ./a.out -v -f sigpwny{ -l 14 -c 1
To bruteforce the string right to left:
./taCTF.py test/ELF-NoSoftwareBreakpoints -r -l 25
Punctuation is included by default in all sets.
Code | Meaning |
---|---|
0 | Lowercase, uppercase, digits |
1 | Lowercase |
2 | Uppercase |
3 | Lowercase, uppercase |
4 | Lowercase, digits |
If you get Length guess: -1
, Valgrind is probably not installed, or
isn’t on your $PATH
. Install it with:
sudo apt install valgrind
sudo pacman -S valgrind
Depending on your distribution.
For any other problems: open an issue!
Binaries that use an insecure string compare of the form:
bool insecureStringCompare(const void *a, const void *b, size_t length) {
const char *ca = a, *cb = b;
for (size_t i = 0; i < length; i++)
if (ca[i] != cb[i])
return false;
return true;
}
- Add some stats stuff to compute mean, see deviation, etc. to decide on best value to use in case instruction counts are close
- Feature add (
--all
) compare valgrind and PIN counting: deal with case of disagreement between PIN and valgrind. - Fix the not_allowed_chars issue by using
subprocess
STDIN. (Will fix if requested in GitHub Issues).