Opened 6 years ago
Last modified 5 years ago
#12837 assigned Bugs
Binary serialization: crash that may allow jump to attacker-controlled address
Reported by: | Owned by: | Robert Ramey | |
---|---|---|---|
Milestone: | To Be Determined | Component: | serialization |
Version: | Boost Development Trunk | Severity: | Problem |
Keywords: | security | Cc: | jepler@… |
Description
Using afl-fuzz on some simple programs that use boost binary and xml serialization, I have found a number of crashes and assertion failures.
To test the waters, I am beginning by filing a single bug which may allow a jump to an attacker-controlled address (i.e., the most exploitable-looking crash that has turned up so far). On my test system, it crashes on the instruction shown:
=> 0x000000000044849c <+524>: mov (%r12),%rax 0x00000000004484a0 <+528>: mov 0x20(%rbp),%r15d 0x00000000004484a4 <+532>: mov %r12,%rdi 0x00000000004484a7 <+535>: callq *0x10(%rax)
Notice that the value loaded at instruction <+524> is used to determine the address to call at instruction <+535>. The value of %r12 interpreted as bytes is "\0\0hive\0\0", which makes me strongly suspect that it is data from the input file (part of the 'serialization::archive' signature, masked off). Generally, the whole text of the input file would be considered under the control of the adversary.
I ran my tests on Debian Jessie amd64 with gcc version 4.9.2 (Debian 4.9.2-10). The following revisions of modularized boost were used (the tip of each master branch at the time of writing, as described by 'git describe --tags --always):
config: boost-1.62.0-57-g1abc59c core: boost-1.61.0-59-gd753d9a move: boost-1.63.0 serialization: boost-1.61.0-57-g62bf8fc
The commandline to compile the (non-fuzzing) version of the input test program is:
g++-4.9 -std=c++11 -Os -I serialization/include -I core/include -I move/include -I config/include -o pvec_in pvec_in.cc serialization/src/extended_type_info.cpp serialization/src/extended_type_info_typeid.cpp serialization/src/archive_exception.cpp serialization/src/basic_archive.cpp serialization/src/basic_serializer_map.cpp serialization/src/void_cast.cpp serialization/src/singleton.cpp serialization/src/basic_iarchive.cpp serialization/src/binary_iarchive.cpp serialization/src/basic_iserializer.cpp serialization/src/basic_pointer_iserializer.cpp
The breaking input file is (base-64 encoded):
FgAAAAAAAABzZXJpYWxpemF0aW9uOjphcmNoaXZlDwAECAQIAQAAAAAAAAAAAwAAAAAAAAABAAAA AAEAAAD0/wEAAAAAAAAAAAEAAAACAAEAAAACAAAAAgAAAAAA
The source for the test harness program (pvec_in.cc) is
#include <fstream> #include <vector> #include <boost/shared_ptr.hpp> #include <boost/archive/binary_iarchive.hpp> #include <boost/serialization/nvp.hpp> #include <boost/serialization/shared_ptr.hpp> #include <boost/serialization/vector.hpp> struct boxed_int { boxed_int() : i(0) {} boxed_int(int i) : i(i) {} int i; template<class Archive> void serialize(Archive &ar, unsigned version) { ar & BOOST_SERIALIZATION_NVP(i); } }; typedef boost::shared_ptr<boxed_int> pi; void go(std::istream &is) { std::vector<pi> vv; try { boost::archive::binary_iarchive ia(is); ia & vv; } catch(std::exception &e) {} } int main(int argc, char **argv) { if(argc > 1) { std::ifstream is(argv[1]); go(is); } else { go(std::cin); } return 0; }
The test harness program either takes the input file on stdin or the name of the input file as the first positional argument.
I can share details about the fuzzing setup if you like.
Thank you for taking the time to consider this issue.
--Jeff
This is pretty interesting. I'm curious about the fuzz testing setup. But I'm not sure what to do about it without undertaking a huge effort - which I'm not prepared to do. But you're issue is legitimate and I'm going to accept and leave open this issue in case someone wants to take it on.
Thanks for your efforts here. I'm sorry I can't be more helpful.
Robert Ramey